5 minutes
Bonne pratique pour les subgraphs 3 - Améliorer l'Indexation et les Performances de Recherche en Utilisant des Entités Immuables et des Bytes comme IDs
TLDR
Utiliser des Entités Immuables et des Bytes pour les IDs dans notre fichier schema.graphql
améliore considérablement la vitesse d’indexation et les performances de recherche.
Entités Immuables
Pour rendre une entité immuable, il suffit d’ajouter (immutable: true)
à cette entité.
1type Transfer @entity(immutable: true) {2 id: Bytes!3 from: Bytes!4 to: Bytes!5 value: BigInt!6}
En rendant l’entité Transfer
immuable, graph-node est capable de traiter l’entité plus efficacement, améliorant la vitesse d’indexation et la réactivité des requêtes.
Les structures des entités immuables ne changeront pas dans le futur. Une entité idéale pour devenir une Entité immuable serait une entité qui enregistre directement les données d’un événement onchain, comme un événement Transfer
enregistré en tant qu’entité Transfer
.
Sous le capot
Les entités mutables ont une “plage de blocs” indiquant leur validité. La mise à jour de ces entités nécessite que le graph node ajuste la plage de blocs des versions précédentes, augmentant la charge de travail de la base de données. Les requêtes nécessitent également un filtrage pour trouver uniquement les entités actives. Les entités immuables sont plus rapides car elles sont toutes actives et, puisqu’elles ne changeront pas, aucun contrôle ou mise à jour n’est requis lors de l’écriture, et aucun filtrage n’est requis pendant les requêtes.
Quand ne pas utiliser les Entités Immuables
Si vous avez un champ comme status
qui doit être modifié au fil du temps, alors vous ne devriez pas rendre l’entité immuable. Autrement, vous devriez utiliser des entités immuables dès que possible.
Bytes comme IDs
Chaque entité nécessite un ID. Dans l’exemple précédent, nous pouvons voir que l’ID est déjà du type Bytes.
1type Transfer @entity(immutable: true) {2 id: Bytes!3 from: Bytes!4 to: Bytes!5 value: BigInt!6}
Bien que d’autres types d’ID soient possibles, tels que String et Int8, il est recommandé d’utiliser le type Bytes pour tous les IDs en raison des chaînes de caractères prenant deux fois plus d’espace que les chaînes Byte pour stocker des données binaires, et les comparaisons de chaînes de caractères UTF-8 doivent tenir compte de la locale, ce qui est beaucoup plus coûteux que la comparaison binaire utilisée pour comparer les chaînes de caractères Byte.
Raisons de ne pas utiliser les Bytes comme IDs
- Si les IDs d’entité doivent être lisibles par les humains, comme les IDs numériques auto-incrémentés ou les chaînes lisibles, les Bytes pour les IDs ne doivent pas être utilisés.
- Si vous intégrez les données d’un Subgraph dans un autre modèle de données qui n’utilise pas les Bytes en tant qu’ID, il ne faut pas utiliser les Bytes en tant qu’ID.
- Les améliorations de performances d’indexation et de recherche ne sont pas souhaitées.
Concatenation Avec Bytes comme IDs
Dans de nombreux subgraphs, il est courant d’utiliser la concaténation de chaînes pour combiner deux propriétés d’un événement en un seul identifiant, par exemple en utilisant event.transaction.hash.toHex() + "-" + event.logIndex.toString()
. Cependant, comme cette méthode renvoie une chaîne de caractères, elle entrave considérablement les performances d’indexation et d’interrogation du Subgraph.
Au lieu de cela, nous devrions utiliser la méthode concatI32()
pour concaténer les propriétés des événements. Cette stratégie donne un ID de type Bytes beaucoup plus performant.
1export function handleTransfer(event: TransferEvent): void {2 let entity = new Transfer(event.transaction.hash.concatI32(event.logIndex.toI32()))3 entity.from = event.params.from4 entity.to = event.params.to5 entity.value = event.params.value67 entity.blockNumber = event.block.number8 entity.blockTimestamp = event.block.timestamp9 entity.transactionHash = event.transaction.hash1011 entity.save()12}
Tri avec Bytes comme IDs
Le tri utilisant les Bytes comme IDs n’est pas optimal, comme le montre cet exemple de requête et de réponse.
Requête:
1{2 transfers(first: 3, orderBy: id) {3 id4 from5 to6 value7 }8}
Réponse de la requête:
1{2 "data": {3 "transfers": [4 {5 "id": "0x00010000",6 "from": "0xabcd...",7 "to": "0x1234...",8 "value": "256"9 },10 {11 "id": "0x00020000",12 "from": "0xefgh...",13 "to": "0x5678...",14 "value": "512"15 },16 {17 "id": "0x01000000",18 "from": "0xijkl...",19 "to": "0x9abc...",20 "value": "1"21 }22 ]23 }24}
Les IDs sont renvoyés sous forme hexadécimale.
Pour améliorer le tri, nous devrions créer un autre champ sur l’entité qui est un BigInt.
1type Transfer @entity {2 id: Bytes!3 from: Bytes! # address4 to: Bytes! # address5 value: BigInt! # unit2566 tokenId: BigInt! # uint2567}
Ceci permettra d’optimiser le tri de manière séquentielle.
Requête:
1{2 transfers(first: 3, orderBy: tokenId) {3 id4 tokenId5 }6}
Réponse de la requête:
1{2 "data": {3 "transfers": [4 {5 "id": "0x…",6 "tokenId": "1"7 },8 {9 "id": "0x…",10 "tokenId": "2"11 },12 {13 "id": "0x…",14 "tokenId": "3"15 }16 ]17 }18}
Conclusion
L’utilisation d’entités immuables et de Bytes comme IDs a permis d’améliorer sensiblement l’efficacité de Subgraph. Plus précisément, les tests ont mis en évidence une augmentation de 28 % des performances des requêtes et une accélération de 48 % des vitesses d’indexation.
En savoir plus sur l’utilisation des Entités immuables et des Bytes en tant qu’IDs dans cet article de blog de David Lutterkort, un ingénieur logiciel chez Edge & Node : Deux améliorations simples des performances des subgraphs.