Prototype de GVA

Je viens d’ajouter le support des batchs dans GVA.

Vous pouvez désormais envoyer un tableau de requêtes à un serveur GVA, ce dernier exécutera toutes vos requêtes en parallèle puis vous enverra un tableau de réponses dans le même ordre que les requêtes fournies !

Le playground ne supporte pas les batchs, j’ai donc du tester avec curl :

La requête curl au format texte :

curl 'https://g1.librelois.fr/gva' \
-H 'Accept-Encoding: gzip, deflate, br' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Connection: keep-alive' \
-H 'DNT: 1' \
-H 'Origin: https://g1.librelois.fr' \
--data-binary '[{"query":"{balance(script: \"D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\") {amount}}"},{"query":"{balance(script: \"Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P\") {amount}}"}]' \
--compressed

J’invite les développeurs des clients à grouper autant que possible leurs requêtes dans des batchs, cela permet de limiter les appels réseaux et donc de gagner en performances globales sur vos applications :smiley:

De plus, je viens d’activer la compression gzip sur mon nginx, mon serveur GVA vous renvoi désormais des réponses compressées, sur ce tableau vous pouvez comparer la taille compréssée (Transfered) et la taille normale (Size) :

Les batchs sont d’autant plus intéressants avec de la compression, car cela permet de compresser plusieurs réponses ensembles :slight_smile:

Pour la compression, j’ai ajouté ces 3 lignes dans le bloc location /gva de ma config nginx :

gzip on;
gzip_types application/json;
gzip_min_length 1000;

En revanche, les batchs permettent de spammer davantage le serveur, c’est pourquoi :

  • La taille d’un batch est limitée à 5 requêtes
  • Le nombre max de requêtes par tranche de 20 secondes passe de 40 à 10.

ces restrictions ne s’appliquent pas aux IPs whitelistés.

4 Likes

Pour plus de lisibilité:

curl 'https://g1.librelois.fr/gva' \
-H 'Accept-Encoding: gzip, deflate, br' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Connection: keep-alive' \
-H 'DNT: 1' \
-H 'Origin: https://g1.librelois.fr' \
--data-binary '[{"query":"{balance(script: \"D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\") {amount}}"},{"query":"{balance(script: \"Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P\") {amount}}"}]' \
--compressed

Mais je ne comprends pas un truc, dans Gecko je fais ça par exemple :

const String getHistory = r'''
  query ($pubkey: String!, $number: Int!, $cursor: String) {
        txsHistoryBc(
            script: $pubkey
            pagination: { pageSize: $number, ord: DESC, cursor: $cursor }
        ) {
            both {
                pageInfo {
                    hasPreviousPage
                    hasNextPage
                    startCursor
                    endCursor
                }
                edges {
                    direction
                    node {
                        currency
                        issuers
                        outputs
                        comment
                        writtenTime
                    }
                }
            }
        }
        txsHistoryMp(pubkey: $pubkey) {
            receiving {
                currency
                issuers
                comment
                outputs
                writtenTime
            }
            sending {
                currency
                issuers
                comment
                outputs
                writtenTime
            }
      }
      currentUd {
        amount
        base
      }
      balance(script: $pubkey) {
        amount
        base
    }
  }
  ''';

Ici je fait 4 requêtes en une non ??

1 Like

Non c’est une seule requête graphql.

Bon faudra qu’on en discute au phone je vois pas la différence entre les 2 approches j’ai dû louper kkchose ^^

Une requête graphql peut demander plusieurs champs, mais ça reste une seule requête. Ici tu demandes 4 champs à la racine : txsHistoryBc, txsHistoryMp, currentUd et balance.
Mais un champ n’est pas une requête, c’est… un champ.

Avec un batch tu pourrais par exemple faire cette même grosse requête graphql 5 fois pour 5 clés publiques différentes en un seul appel réseau :slight_smile:

1 Like

Bah avec cette même méthode je peux déjà le faire pour 5 clés publiques différentes, il suffit que j’utilise 5 variables pubkey1 pubkey2 ect …

Je fais pas expret, ya un truc j’ai pas compris ^^

En décortiquant ton batch:

[
    {
        "query":" {
            balance(
                script: \"D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\"
            ) {
                amount
            }
        }"
    },
    {
        "query":" {
            balance(
                script: \"Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P\"
            ){
                amount
            }
        }"
    }
]

Je vois pas ce que ça change que de faire ainsi (EDIT: Ca change que ça marche pas ainsi…):

[
    {
        "query":" {
            balance(
                script: \"D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\"
            ) {
                amount
            },
            balance(
                script: \"Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P\"
            ){
                amount
            }
        }"
    }
]

(non testé en l’occurence mais c’est comme ça que je fais dans jaklis et gecko)


Bon à l’essai, je me rends compte d’une chose que j’avais pas remarqué: GQL refuse d’utiliser 2 fois le même champs dans une même requête, comme 2 fois balance, juste des champs différents, je trouve ça étrange mais c’est ainsi…

Ça change que ça ne fonctionne pas :stuck_out_tongue: :

Tu ne peux pas toujours tout faire dans une seule requête graphql, les batchs te permettent de lever ces limitations.

Et même si tu pouvais tout faire dans une seule très grosse requête graphql, cela aurait des inconvénients :

  • C’est plus difficile de concevoir la requête.
  • Tu ne profites pas du parallélisme apporté par le batch.
  • Ça fait une requête très complexe, et je réfléchis à mettre en place une limite à la complexité d’une requête pour protéger les serveurs.
1 Like

En effet j’ai édité mon message précédent au même moment, GQL refuse d’utiliser 2 fois le même champ dans une même requête.

Ok je vois un peu mieux les avantages maintenant, c’est parfois subtile, mais à la longue et pour des requêtes complexes, ça sera mieux.

C’est normal de refuser deux fois le même champ, car sinon, il vont s’écraser l’un l’autre dans la réponse s’ils ont la même clef… My two cents…

1 Like

Est-ce que ces informations peuvent être mises dans un document de référence à côté de celle de BMA par exemple ? Je suppose que ces informations ne sont pas disponibles via le schéma ou autre. Du coup, ça serait bien d’avoir un endroit où s’y référer au besoin. Les chercher dans un post de forum qui se noie, c’est pas pratique.

1 Like

Est-ce possible d’avoir uniquement des queries au pluriel pour pouvoir y passer une liste de champs ? La query balances remplace balance. Est-ce possible d’avoir une query identities qui remplacerait idty ? C’est tout l’intérêt de GraphQL de récupérer toutes les informations en faisant le moins de requêtes. C’est possible de passer par des batchs, mais c’est plus pratique quand il est possible de passer une liste et d’en récupérer une.

Ok voici un fichier pour ça, n’hésitez pas à soumettre des MR pour le compléter :slight_smile: :

Je souhaite garder les deux, car les query au singulier sont plus performantes dans le cas où tu as besoin d’un seul élément.

Oui, c’est facile à faire, @tuxmain ou @vit ou @HugoTrentesaux peuvent y arriver :slight_smile:

2 Likes

@elois, @poka,
Ben alors, vous ne connaissez pas les alias ?


(J’ai pas lu la discussion)

3 Likes

Si, mais j’ai pas pensé aux alias sur le coup.
Mais surtout même avec alias, la requête sera plus longue à exécuter coté serveur.

Duniter créer une “async task” pour chaque requête graphql. Si tu envoies un batch de requêtes, tu auras une async task dédiée pour chaque requête, elles pourront donc s’exécuter en parallèle si le serveur n’est pas saturé.
Alors que si tu fusionnes tout dans une seule grosse requête graphql, une seule async task résoudras tous les champs de ta requête séquentiellement.

Si vous avez une grosse requête à faire, la découpée en plusieurs petites requêtes au sein d’un batch peut améliorer les performances :slight_smile:

1 Like

Aaaaaaaaaah je savais bien que y’avais moyen de faire comme ça ahaha

Je ne sais pas du tout comment ça ce passe avec Rust, mais avec apollo server en nodejs, tu peux créer des loaders pour éviter de requêter plusieurs fois la même donnée :

https://www.apollographql.com/docs/apollo-server/data/data-sources/#what-about-dataloader

On a l’equivalent, la lib rust async_graphql à son propre système de cache. Et pas dessus ça on a en plus le cache de la db sled.

tu verras que si te refait deux fois la même requête c’est plus rapide là 2ᵉ fois :slight_smile:

3 Likes

Petit question relative à des bugs qu’on me soumet souvent sur Cesium (exemple : ticket #931) : est-ce possible via GVA de faire des recherches insensible à la casse ? (sur les UID)

Pour le moment il n’est pas encore possible de faire des recherches par UserID. Mais quand ce sera implémenté oui bien sûr que ça pourra être insensible à la casse, je n’y vois pas d’
inconvénient :slight_smile:

En revanche, je souhaiterais pouvoir rapidement supprimer les UserID (probablement dès DUBPv14), car ils ne servent à rien et font doublons inutiles avec les noms de profil Cesium+.

2 Likes