Prototype de GVA

Aujourd’hui j’ai travaillé sur :

  • La configurabilité de GVA (uniquement via le fichier conf.json pour le moment)
  • La création de la doc de configuration de gva
  • La génération des endpoint GVA et leur ajout dans la fiche de peer
  • La récupération de la fiche de peer du nœud via GVA:

J’ai gardé les mêmes conventions que pour les autres types d’endpoint à une différence près : la couche TLS est explicitée par un S. Cela permettra à ceux qui le souhaitent de pouvoir fournir un endpoint sécurisé sur un autre port que 443 (via l’option remoteTls, voir doc).

Maintenant j’aurais besoin qu’un ou deux d’entre vous essayent de mettre en place un serveur GVA, voici comment faire :

  1. Compilez manuellement Duniter sur la branche gva-proto-2
  2. Indexez la db de GVA, 2 méthodes possibles :
    A. Avec la commande dex migrate (nécessite de compiler le binaire dex au préalable). L’indexation prend environ 2 minutes ce qui est bien plus rapide que de resynchroniser Duniter, donc même en comptant le temps de compilation de dex cette méthode devrait rester plus rapide.
    B. Vous pouvez aussi resynchroniser Duniter avec l’option --gva (duniter sync --gva g1.duniter.org) mais cette méthode est plus longue forcément
  3. Configurez GVA en modifiant directement le fichier conf.json, voici ma conf par exemple :
{
  ...,
  "gva": {
    "host": "localhost",
    "port": 30901,
    "path": "gva",
    "subscriptionsPath": "gva-sub",
    "remoteHost": "g1.librelois.fr",
    "remotePort": 443
  }
}
  1. Lancez votre nœud duniter avec l’option --gva (nécessaire car gva est encore désactivée par défaut) : duniter start --gva
  2. Ouvrez l’url http://localhost:30901/gva dans votre navigateur (ou l’url adaptée a votre configuration le cas échéant).
  3. Faite moi un retour

Merci :slight_smile:

5 Likes

De mon côté je suis charette sur des fins de projet pour mon boulot. Donc pas eu le temps d’avancer sur Cesium2…
Ne t’excuses pas @elois :-).
Concernant la mauvaise nouvelle que tu annonce, je ne sais pas trop quoi te dire…
Saches en tous cas que cela me touche pour toi. La vie est vraiment quelque chose de fragile… et reste pour moi un mystère.

4 Likes

Du coup j’ai pu le compiler, j’ai bien accès à l’interface web GVA, mais :

  • dex migrate a pris environ 10 minutes après avoir affiché le dernier message « Apply chunk » sans rien dire, à écrire à 16 Mo/s dans le disque. Ça serait bien d’ajouter un message pour qu’on ne se dise pas que ça a planté. (oui j’avais oublié de le copier dans la RAM)
  • L’option --home PATH place le dossier de duniter à ~/.config/duniter/PATH apparemment, donc j’ai mis longtemps à comprendre pourquoi avec --home /dev/shm/duniter rien ne marchait (sauf l’interface web GVA, mais les requêtes renvoyaient des résultats vides), jusqu’à voir que tout était dans ~/.config/duniter/dev/shm/duniter
2 Likes

Chez moi ça prend 1m39s en tout, et que quelques secondes après le log du dernier chunk.
Tu es le premier à tester sled sur HDD (je n’ai que des SSD depuis des années, donc je ne pouvais pas tester).
sled est optimisé pour les SSD, mais je ne pensais pas que les perfs seraient si mauvaises sur HDD, je suis en train de regarder du côté de RocksDb mais c’est aussi optimisé pour SSD donc ce sera peut-être pareil, on verra à l’expérience. En vrai je pense que quel que soit le sgdb choisi les HDD sont de toute façon beaucoup trop lent pour le type de traitement que Duniter doit faire (indexation en particulier).

Il y a déjà un message de succès à la fin. Je viens d’ajouter un message intermédiaire pour le flush.

Ça ce n’est pas normal, je viens de vérifier dans le code et l’option --home est codée pour se comporter comme un home absolu, c’est avec le binaire dex ou duniter que tu as eu ce souci ? Que j’essaye de reproduire ! EDIT: bug trouvé et corrigé :slight_smile:

Et du coup ça y ai ton playground GVA fonctionne ? Une screen ?

2 Likes

Yes ça marche maintenant !

Par contre la commande logs ne sort rien, mais le fichier duniter.log est bien alimenté.

3 Likes

Penses-tu que je puisse implémenter le endpoint GVA au format définit dans WS2P v2 ou bien tu vas faire un ajout à WS2P V1 ?

Bref j’ai besoin du format du endpoint GVA genre « GVA hostname ipv4 ipv6 path » pour DuniterPy… si tu l’as…

Le endpoint GVA est normalement déjà ajouté à la fiche de peer relayée par WS2Pv1, d’ailleurs si tu regardes les fiches de peer sur le réseau tu vois les 3 nœuds GVA : https://g1.elo.tf/network/peers

Je l’ai déjà indiqué dans le message #14 que tu à lu puisse que tu l’a liké :wink:

2 Likes

Du nouveau sur GVA, le fruit des dev de la semaine :

  1. Ajout query genComplexTx pour générer des transactions plus complexes
  2. Correction d’un bug sur l’indexation gva de la g1-test.
  3. Ajout de la commande duniter wizard gva pour configurer gva. L’option --gva n’existe plus, il vous suffit d’activer gva dans la conf une bonne fois pour toutes et c’est bon :grinning:
  4. Possibilité d’écouter sur une ipv6
  5. Ajout de la requête balance, pour avoir le solde d’un compte :

EDIT: pour profiter de la requête balance il faut ré-indexer GVA, pour cela supprimer les bases sled puis faites un dex migrate :

rm -rf .config/duniter/duniter_default/data/*_sled
dex migrate
4 Likes

En faisant:

git fetch && ./bin/duniter stop && git reset --hard origin/gva-proto-2 && cargo xtask build --production && ./bin/duniter webstart

Pas de soucis puis:

rm -rf .config/duniter/duniter_default/data/*_sled
ok
Mais:
./target/release/dex migrate
Donne pour résultat:

[...]
Apply chunk #69750-#69999 ..
Apply chunk #70000-#70249 ..
Apply chunk #70250-#70499 ..
Apply chunk #70500-#70749 ..
Apply chunk #70750-#70999 ..
Apply chunk #71000-#71249 ..
Apply chunk #71250-#71499 ..
Apply chunk #71500-#71749 ..
Apply chunk #71750-#71999 ..
Error: DB corrupted:Not found origin tx of uxto B28939C91BBB386AD00B53566FFE73B250CE0982287CB1068FD119473D28B6F0:0
$ ./target/release/dex --version
duniter-dbex 0.1.0

Je suis sur la ḠTest.

Normal tu n’a pas recompilé dex, un binaire n’à pas le pouvoir magique de se maj tout seul :wink:

1 Like

Mieux …

Apply chunk #664500-#664749 ..
Apply chunk #664750-#664999 ..
Apply chunk #665000-#665155 ..
Flush DBs caches on disk...
Migration successfully completed on 458 seconds.

Je savais pas que tu avais fait des modifs à dex c’est pour ça

1 Like

En fait je n’ai pas fait de modif à dex mais dex utilise en très grande partie le même code Rust que Duniter, notamment le code d’indexation de GVA est le même donc quand ce code change il faut recompiler dex aussi.

2 Likes

Tu as changé le champs genTxs en genTx aussi ?

Ha oui je trouvais ça plus logique. C’est aussi pour être consistant avec genComplexTx :slight_smile:

2 Likes

Rapide GVA !

4 Likes

Du nouveau sur GVA, le fruit des dev de la semaine :

1. Pagination

La pagination était en place sur utxosOfScript seulement mais elle était factice, le serveur récupérait toutes les UTXO en base puis renvoyait la page demandée.

J’ai réalisé un gros travail de fond pour mettre en place un vrai système de pagination qui s’applique au plus près de la base de donnée. C’est à dire que désormais seule la page demandée sera récupérée en base, ce qui devrait permettre de réduire les traitements à chaque requête et donc de les accélérer.

Cela à également nécessité un changement dans la manière de stocker les données, une réindexation est donc indispensable !

J’ai aussi modifié les inputs gérant la pagination, c’est désormais un «objet» Pagination qu’il faut renseigner avec les 3 champs suivant :

  • cursor: position de départ (ou de fin si ordre décroissant)
  • ord: ordre croissant (ASC) ou décroissant (DESC). Par défaut l’ordre est ASC. On parle aussi d’ordre normal pour ASC et d’ordre inverse pour DESC.
  • pageSize: ai-je vraiment besoin d’expliquer ce champ ?

2. Historique des DU créé

Suite à une demande de @vit j’ai implémenté une requête fournissant l’historique de tous les DU créé (consommés ou non).

Dans le schéma c’est la requête existante udsOfPubkey qui fourni désormais au choix tous les DU ou les DU non-consommés via le paramètre d’entrée filter de type UdsFilter :

enum UdsFilter {
  ALL
  UNSPENT
}

Si filter n’est pas renseigné il vaut ALL par défaut, c’est indiqué dans le schéma :

image

Exemple : récupérer les 2 derniers DU créé

Évidemment j’ai choisi 2 pour l’exemple mais il suffit d’augmenter pageSize pour avoir des pages plus grande.

Comment passer à la page suivante ?

Si vous parcourez les données dans l’ordre décroissant, il faut récupérer la valeur du champ endCursor et la placée dans le champ cursor en input :slight_smile:
Si en revanche vous parcourez les données dans l’ordre croissant, c’est le champ startCursor qu’il faut utiliser.

Enfin le champ hasPreviousPage vous indique s’il y a une page précédente (donc une page suivante pour vous si vous parcourez les données dans l’ordre DESC). Et si vous parcourez les données dans l’ordre ASC il faut utiliser hasNextPage à la place).

3. Récupérer des sources jusqu’à un certain montant

Les requêtes udsOfPubkey et utxosOfScript acceptent désormais un paramètre d’entrée amount, si ce paramètre est renseigné la requête va cesser son parcours de la DB dès que le montant demandé est atteint ou dépassé.

Cela permet à ceux qui le souhaitent de pouvoir continuer à sélectionner les sources eux-mêmes.

4. Champ sum sur les requetes udsOfPubkey et utxosOfScript

Les requetes udsOfPubkey et utxosOfScript exposent désormais un champ sum indiquant la somme des montants des sources de la page (avec gestion des bases).

Cela permet par exemple de calculer la somme de tout les DU d’un compte (attention requête lourde si le compte à beaucoup de DU). Je sais par exemple que tous les DU que j’ai créé représentent 13741,69 Ğ1 :

Et que mes DU non-consommés représentent 705,87 Ğ1 :


Est-ce que c’est clair au niveau du système de pagination ? Vous avez des questions ?

4 Likes

Excellent pour l’historique des DUs ! Merci beaucoup ! :+1:

Par contre la gestion de la pagination me paraît bien compliquée par le fait que les noms des paramètres à gérer changent selon le sens du tri, et qu’il faut gérer différemment le curseur si on est en descendant.

Si je me base sur mes habitudes en SQL, depuis des années, la pagination est assez triviale, car on joue simplement sur les paramètres OFFSET (ce qui ici se nomme cursor) et LIMIT (ce qui ici se nomme pageSize). Et ceci que je sois en tri ascendant ou descendant peu importe.

Ainsi, pour parcourir les pages en ascendant comme en descendant, j’incrémente simplement mon curseur OFFSET de numéro_itération * LIMIT (ici cursor sera incrémenté de numéro_itération * pageSize).

Je ne comprends pas pourquoi j’aurais besoin de valeurs en retour pour parcourir des pages. Et gérer la pagination différemment dans un sens et pas dans l’autre. Si tu pouvais m’éclairer sur ce point.

Parce que c’est les spec de graphql pour la pagination qui sont faites ainsi. Et que la lib que j’utilise implémente ces spec :

https://relay.dev/graphql/connections.htm

Le seul endroit où je ne respecte pas ces spec c’est pour les paramètres en input, car les paramètres attendus en input par ces spec (first, last, before, after) impliquent des contrôles supplémentaires, je peux toutefois proposer ces paramètres si quelqu’un utilise une lib client implémentant ces spec.

Il y a aussi une autre raison, pour que la pagination s’applique dès la base de donnée (et donc que ce soi une vraie pagination), il faut nécessairement fournir un cursor indiquant la clé de départ (sauf si on souhaite partir du tout début ou de la toute fin), cursor qui doit être une valeur correspondant à la clé stockée en base. Cela est dû au fait que la DB est un stockage clé-valeur, ce n’est pas du SQL, donc le cursor ne peut pas être un nombre entier qui s’incrémente comme en SQL.

3 Likes

@vit @poka @Moul @tuxmain et tous les potentiels utilisateurs de GVA, voici un article officiel de graphql sur la pagination, je vous invite à le lire pour comprendre pourquoi ils ont fait ça ainsi :

3 Likes

Ça ne vient pas de nulle part : soit on fait de la pagination par offset, soit par curseur. En demandant la page https://example.org/?page=2, cette page va changer dans le temps car de nouveaux items vont être créés. Avec la page https://example.org/?cursor=xxxx, on est sûr d’avoir toujours le même résultat !
Je prend l’exemple d’un lien, mais le principe est le même avec une bdd.

La pagination de graphql.org est un standard, et beaucoup de librairies le suivent comme relay.dev qui est méga utilisé par React. Vraiment super d’utiliser cette spec ! Ça simplifie aussi les choses en front pour les composants de pagination… mais à l’usage, il manque un truc.

En ayant tous les champs du pageInfo, on peut afficher une pagination du type :
« first < prev next > last ». Et griser next quand c’est hasNextPage est null par exemple.
Mais quand on ne connaît pas le nombre de page, on ne peut pas faire de pagination avec les numéros de page, ni afficher une info du type node 1 à 10 sur 97. Exemple ici.
Pour ça on a besoin d’avoir le nombre de edges.

Je vois que tu as sum { amount }. J’ai vu des API graphql qui utilisent aggregate et qui permet d’obtenir plein d’infos sur l’ensemble des edges, ex :

aggregate {
  avg
  count
  max
  min
  stddev
  stddev_pop
  stddev_samp
  sum
  var_pop
  var_samp
  variance
}

Je ne sais pas à quoi sert tout les champs, mais je pense que ce type d’interface est plus facile à maintenir et faire évoluer…

Gva est en panne là, mais j’ai retrouver une requête que j’ai fait il y a quelques jours… Avec aggregate ça donnerait :

query fetchRelayUtxos {
  utxosOfScript(
    script: "Hey elois !? Tu loggues les erreurs ? C'est quoi ce champ script ??"
  ) {
    pageInfo {
      startCursor
      endCursor
      hasNextPage
      hasPreviousPage
    }
    aggregate {
      count
      sum
    }
    edges {
      cursor
      node {
        amount
        base
        txHash
        outputIndex
        writtenTime
      }
    }
  }
}

Après, je ne sais pas si c’est pertinent pour toutes les queries et les types d’object utilisés pour duniter…

4 Likes