Nœud de graph opérationnel
Reading time: 19 min
Graph Node est le composant qui indexe les subgraphs et rend les données résultantes disponibles pour interrogation via une API GraphQL. En tant que tel, il est au cœur de la pile de l’indexeur, et le bon fonctionnement de Graph Node est crucial pour exécuter un indexeur réussi.
Cela fournit un aperçu contextuel de Graph Node et de certaines des options les plus avancées disponibles pour les indexeurs. Une documentation et des instructions détaillées sont disponibles dans le .
Cela fournit un aperçu contextuel de Graph Node et de certaines des options les plus avancées disponibles pour les indexeurs. Une documentation et des instructions détaillées sont disponibles dans le .
Graph Node (et l'ensemble de la pile d'indexation) peut être exécuté sur du métal nu ou dans un environnement cloud. Cette flexibilité du composant d’indexation central est cruciale pour la robustesse du Graph Protocol. De même, Graph Node peut être , ou les indexeurs peuvent utiliser l'une des .
Le magasin principal du nœud de graph, c'est là que les données des sous-graphes sont stockées, ainsi que les métadonnées sur les subgraphs et les données réseau indépendantes des subgraphs telles que le cache de blocs et le cache eth_call.
Pour indexer un réseau, Graph Node doit avoir accès à un client réseau via une API JSON-RPC compatible avec EVM. Cette RPC peut se connecter à un seul client ou à une configuration plus complexe qui équilibre la charge entre plusieurs clients.
Si certains subgraphs ne nécessitent qu'un noed complet, d'autres présentent des caractéristiques d'indexation qui requièrent des fonctionnalités RPC supplémentaires. En particulier, les subgraphs qui effectuent des eth_calls
dans le cadre de l'indexation nécessiteront un noed d'archive prenant en charge , et les subgraphs avec des callHandlers
, ou des blockHandlers
avec un filtre call
, nécessitent la prise en charge de trace_filter
().
Network Firehoses : un Firehose est un service gRPC fournissant un flux de blocs ordonné, mais compatible avec les fork, développé par les principaux développeurs de The Graph pour mieux prendre en charge une indexation performante à grande échelle. Ce n'est pas actuellement une exigence de l'indexeur, mais les indexeurs sont encouragés à se familiariser avec la technologie, avant la prise en charge complète du réseau. Apprenez-en davantage sur le Firehose .
Les métadonnées de déploiement de subgraphs sont stockées sur le réseau IPFS. The Graph Node accède principalement au noed IPFS pendant le déploiement du subgraph pour récupérer le manifeste du subgraph et tous les fichiers liés. Les indexeurs de réseau n'ont pas besoin d'héberger leur propre noed IPFS. Un noed IPFS pour le réseau est hébergé sur .
Pour activer la surveillance et la création de rapports, Graph Node peut éventuellement enregistrer les métriques sur un serveur de métriques Prometheus.
-
Rust
-
PostgreSQL
-
IPFS
-
Exigences supplémentaires pour les utilisateurs d'Ubuntu - Pour exécuter un Graph Node sur Ubuntu, quelques packages supplémentaires peuvent être nécessaires.
sudo apt-get install -y clang libpq-dev libssl-dev pkg-config
- Démarrez un serveur de base de données PostgreSQL
initdb -D .postgrespg_ctl -D .postgres -l logfile startcreatedb graph-node
Maintenant que toutes les dépendances sont configurées, démarrez le Graph Node :
cargo run -p graph-node --release -- \--postgres-url postgresql://[USERNAME]:[PASSWORD]@localhost:5432/graph-node \--ethereum-rpc [NETWORK_NAME]:[URL] \--ipfs https://ipfs.network.thegraph.com
Un exemple complet de configuration de Kubernetes est disponible dans le .
Lorsqu'il est en cours d'exécution, Graph Node expose les ports suivants :
Port | Objectif | Routes | Argument CLI | Variable d'environnement |
---|---|---|---|---|
8000 | Serveur HTTP GraphQL (pour les requêtes de subgraphs) | /subgraphs/id/... /subgraphs/name/.../... | --http-port | - |
8001 | GraphQL WS (pour les abonnements aux subgraphs) | /subgraphs/id/... /subgraphs/name/.../... | --ws-port | - |
8020 | JSON-RPC (pour gérer les déploiements) | / | --admin-port | - |
8030 | API de statut d'indexation des subgraphs | /graphq | --index-node-port | - |
8040 | Métriques Prometheus | /metrics | --metrics-port | - |
Important : Soyez prudent lorsque vous exposez les ports publiquement : les ports d'administration doivent rester verrouillés. Cela inclut le point de terminaison Graph Node JSON-RPC.
Dans sa forme la plus simple, Graph Node peut être utilisé avec une seule instance de Graph Node, une seule base de données PostgreSQL, un nœud IPFS et les clients réseau selon les besoins des subgraphs à indexer.
Cette configuration peut être mise à l'échelle horizontalement, en ajoutant plusieurs nœuds graphs et plusieurs bases de données pour prendre en charge ces nœuds graphs. Les utilisateurs avancés voudront peut-être profiter de certaines des capacités de mise à l'échelle horizontale de Graph Node, ainsi que de certaines des options de configuration les plus avancées, via le fichier config.toml
et les variables d'environnement de Graph Node.
Un fichier de configuration peut être utilisé pour définir des configurations plus complexes que celles exposées dans la CLI. L'emplacement du fichier est transmis avec le commutateur de ligne de commande --config.
Lors de l'utilisation d'un fichier de configuration, il n'est pas possible d'utiliser les options --postgres-url, --postgres-secondary-hosts et --postgres-host-weights.
Un fichier config.toml
minimal peut être fourni ; le fichier suivant équivaut à l'utilisation de l'option de ligne de commande --postgres-url :
[store][store.primary]connection="<.. postgres-url argument ..>"[deployment][[deployment.rule]]indexers = [ "<.. liste de tous les nœuds d'indexation ..>" ]
La documentation complète de config.toml
est disponible dans la .
Graph Node indexing can scale horizontally, running multiple instances of Graph Node to split indexing and querying across different nodes. This can be done simply by running Graph Nodes configured with a different node_id
on startup (e.g. in the Docker Compose file), which can then be used in the config.toml
file to specify , , and splitting subgraphs across nodes with .
Notez que plusieurs nœuds de graph peuvent tous être configurés pour utiliser la même base de données, qui elle-même peut être mise à l'échelle horizontalement via le partitionnement.
Étant donné plusieurs nœuds de graph, il est nécessaire de gérer le déploiement de nouveaux subgraphs afin qu'un même subgraph ne soit pas indexé par deux nœuds différents, ce qui entraînerait des collisions. Cela peut être fait à l'aide de règles de déploiement, qui peuvent également spécifier dans quelle partition
les données d'un subgraph doivent être stockées, si la partition de base de données est utilisée. Les règles de déploiement peuvent correspondre au nom du subgraph et au réseau que le déploiement indexe afin de prendre une décision.
Exemple de configuration de règle de déploiement :
[deployment][[deployment.rule]]match = { name = "(vip|important)/.*" }shard = "vip"indexers = [ "index_node_vip_0", "index_node_vip_1" ][[deployment.rule]]match = { network = "kovan" }# No shard, so we use the default shard called 'primary'indexers = [ "index_node_kovan_0" ][[deployment.rule]]match = { network = [ "xdai", "poa-core" ] }indexers = [ "index_node_other_0" ][[deployment.rule]]# There's no 'match', so any subgraph matchesshards = [ "sharda", "shardb" ]indexers = ["index_node_community_0","index_node_community_1","index_node_community_2","index_node_community_3","index_node_community_4","index_node_community_5"]
En savoir plus sur les règles de déploiement .
Les nœuds peuvent être configurés pour être explicitement des nœuds de requête en incluant les éléments suivants dans le fichier de configuration :
[general]query = "<regular expression>"
Tout nœud dont --node-id correspond à l'expression régulière sera configuré pour répondre uniquement aux requêtes.
Pour la plupart des cas d'utilisation, une seule base de données Postgres suffit pour prendre en charge une instance de nœud graph. Lorsqu'une instance de nœud graph dépasse une seule base de données Postgres, il est possible de diviser le stockage des données de nœud graph sur plusieurs bases de données Postgres. Toutes les bases de données forment ensemble le magasin de l’instance de nœud graph. Chaque base de données individuelle est appelée une partition.
Les fragments peuvent être utilisés pour répartir les déploiements de subgraphs sur plusieurs bases de données, et peuvent également être utilisés pour utiliser des réplicas afin de répartir la charge des requêtes entre les bases de données. Cela inclut la configuration du nombre de connexions de base de données disponibles que chaque nœud de graph
doit conserver dans son pool de connexions pour chaque base de données, ce qui devient de plus en plus important à mesure que de plus en plus de subgraphs sont indexés.
Le partage devient utile lorsque votre base de données existante ne peut pas suivre la charge que Graph Node lui impose et lorsqu'il n'est plus possible d'augmenter la taille de la base de données.
Il est généralement préférable de créer une base de données unique aussi grande que possible avant de commencer avec des fragments. Une exception est lorsque le trafic des requêtes est réparti de manière très inégale entre les subgraphs ; dans ces situations, cela peut être considérablement utile si les subgraphs à volume élevé sont conservés dans une partition et tout le reste dans une autre, car cette configuration rend plus probable que les données des subgraphs à volume élevé restent dans le cache interne de la base de données et ne le font pas. sont remplacés par des données qui ne sont pas autant nécessaires à partir de subgraphs à faible volume.
En termes de configuration des connexions, commencez par max_connections dans postgresql.conf défini sur 400 (ou peut-être même 200) et regardez les métriques store_connection_wait_time_ms et store_connection_checkout_count Prometheus. Des temps d'attente notables (tout ce qui dépasse 5 ms) indiquent qu'il y a trop peu de connexions disponibles ; des temps d'attente élevés seront également dus au fait que la base de données est très occupée (comme une charge CPU élevée). Cependant, si la base de données semble par ailleurs stable, des temps d'attente élevés indiquent la nécessité d'augmenter le nombre de connexions. Dans la configuration, le nombre de connexions que chaque instance de nœud graph peut utiliser constitue une limite supérieure, et Graph Node ne maintiendra pas les connexions ouvertes s'il n'en a pas besoin.
En savoir plus sur la configuration du magasin .
Si plusieurs nœuds sont configurés, il sera nécessaire de spécifier un nœud responsable de l'ingestion de nouveaux blocs, afin que tous les nœuds d'index configurés n'interrogent pas la tête de chaîne. Cela se fait dans le cadre de l'espace de noms chains
, en spécifiant le node_id
à utiliser pour l'ingestion de bloc :
[chains]ingestor = "block_ingestor_node"
Le protocole Graph augmente le nombre de réseaux pris en charge pour l'indexation des récompenses, et il existe de nombreux subgraphs indexant des réseaux non pris en charge qu'un indexeur aimerait traiter. Le fichier config.toml
permet une configuration expressive et flexible de :
- Plusieurs réseaux
- Plusieurs fournisseurs par réseau (cela peut permettre de répartir la charge entre les fournisseurs, et peut également permettre la configuration de nœuds complets ainsi que de nœuds d'archives, Graph Node préférant les fournisseurs moins chers si une charge de travail donnée le permet).
- Détails supplémentaires sur le fournisseur, tels que les fonctionnalités, l'authentification et le type de fournisseur (pour la prise en charge expérimentale de Firehose)
La section [chains]
contrôle les fournisseurs Ethereum auxquels graph-node se connecte et où sont stockés les blocs et autres métadonnées de chaque chaîne. L'exemple suivant configure deux chaînes, mainnet et kovan, où les blocs pour le réseau principal sont stockés dans la partition vip et les blocs pour kovan sont stockés dans la partition principale. La chaîne du mainnet peut utiliser deux fournisseurs différents, alors que kovan n'a qu'un seul fournisseur.
[chains]ingestor = "block_ingestor_node"[chains.mainnet]shard = "vip"provider = [{ label = "mainnet1", url = "http://..", features = [], headers = { Authorization = "Bearer foo" } },{ label = "mainnet2", url = "http://..", features = [ "archive", "traces" ] }][chains.kovan]shard = "primary"provider = [ { label = "kovan", url = "http://..", features = [] } ]
En savoir plus sur la configuration du fournisseur .
Graph Node prend en charge une gamme de variables d'environnement qui peuvent activer des fonctionnalités ou modifier le comportement de Graph Node. Ceux-ci sont documentés .
Les utilisateurs qui utilisent une configuration d'indexation à grande échelle avec une configuration avancée peuvent bénéficier de la gestion de leurs nœuds graph avec Kubernetes.
- Le dépôt de l'indexeur contient un
- est une boîte à outils permettant d'exécuter un indexeur de protocole graph sur Kubernetes géré par GraphOps. Il fournit un ensemble de graph Helm et une CLI pour gérer un déploiement de Graph Node.
Étant donné un nœud de graph en cours d'exécution (ou des nœuds de graph !), le défi consiste alors à gérer les subgraphs déployés sur ces nœuds. Graph Node propose une gamme d'outils pour vous aider à gérer les subgraphs.
Les journaux de Graph Node peuvent fournir des informations utiles pour le débogage et l'optimisation de Graph Node et de subgraphs spécifiques. Graph Node prend en charge différents niveaux de journalisation via la variable d'environnement GRAPH_LOG
, avec les niveaux suivants : erreur, avertissement, information, débogage ou trace.
De plus, définir GRAPH_LOG_QUERY_TIMING
sur gql
fournit plus de détails sur la façon dont les requêtes GraphQL sont exécutées (bien que cela génère un grand volume de journaux).
Graph Node fournit les métriques via le point de terminaison Prometheus sur le port 8040 par défaut. Grafana peut ensuite être utilisé pour visualiser ces métriques.
Le référentiel de l'indexeur fournit un .
graphman
est un outil de maintenance pour Graph Node, aidant au diagnostic et à la résolution de différentes tâches quotidiennes et exceptionnelles.
La commande graphman est incluse dans les conteneurs officiels et vous pouvez docker exec dans votre conteneur graph-node pour l'exécuter. Il nécessite un fichier config.toml
.
La documentation complète des commandes graphman
est disponible dans le référentiel Graph Node. Voir [/docs/graphman.md] () dans le nœud graphique /docs
Disponible sur le port 8030/graphql par défaut, l'API d'état d'indexation expose une gamme de méthodes pour vérifier l'état d'indexation de différents subgraphs, vérifier les preuves d'indexation, inspecter les fonctionnalités des subgraphs et bien plus encore.
Le schéma complet est disponible .
Le processus d'indexation comporte trois parties distinctes :
- Récupération des événements d'intérêt auprès du fournisseur
- Traiter les événements dans l'ordre avec les gestionnaires appropriés (cela peut impliquer d'appeler la chaîne pour connaître l'état et de récupérer les données du magasin)
- Écriture des données résultantes dans le magasin
Ces étapes sont pipeline (c’est-à-dire qu’elles peuvent être exécutées en parallèle), mais elles dépendent les unes des autres. Lorsque les subgraphs sont lents à indexer, la cause sous-jacente dépendra du subgraph spécifique.
Causes courantes de lenteur d’indexation :
- Temps nécessaire pour trouver les événements pertinents de la chaîne (les gestionnaires d'appels en particulier peuvent être lents, étant donné le recours à
trace_filter
) - Effectuer un grand nombre d'
eth_calls
dans le cadre des gestionnaires - Une grande quantité d'interactions avec le magasin pendant l'exécution
- Une grande quantité de données à sauvegarder dans le magasin
- Un grand nombre d'événements à traiter
- Temps de connexion à la base de données lent, pour les nœuds encombrés
- Le prestataire lui-même prend du retard sur la tête de la chaîne
- Lenteur dans la récupération des nouvelles recettes en tête de chaîne auprès du prestataire
Les métriques d’indexation de subgraphs peuvent aider à diagnostiquer la cause première de la lenteur de l’indexation. Dans certains cas, le problème réside dans le subgraph lui-même, mais dans d'autres, des fournisseurs de réseau améliorés, une réduction des conflits de base de données et d'autres améliorations de configuration peuvent améliorer considérablement les performances d'indexation.
Lors de l'indexation, les subgraphs peuvent échouer s'ils rencontrent des données inattendues, si certains composants ne fonctionnent pas comme prévu ou s'il y a un bogue dans les gestionnaires d'événements ou la configuration. Il existe deux types généraux de pannes :
- Échecs déterministes : ce sont des échecs qui ne seront pas résolus par de nouvelles tentatives
- Échecs non déterministes : ils peuvent être dus à des problèmes avec le fournisseur ou à une erreur inattendue de Graph Node. Lorsqu'un échec non déterministe se produit, Graph Node réessaiera les gestionnaires défaillants, en reculant au fil du temps.
Dans certains cas, un échec peut être résolu par l'indexeur (par exemple, si l'erreur est due au fait de ne pas disposer du bon type de fournisseur, l'ajout du fournisseur requis permettra de poursuivre l'indexation). Cependant, dans d'autres cas, une modification du code du subgraph est requise.
Les échecs déterministes sont considérés comme « définitifs », avec une preuve d'indexation générée pour le bloc défaillant, tandis que les échecs non déterministes ne le sont pas, car le subgraph peut réussir à « échouer » et continuer l'indexation. Dans certains cas, l'étiquette non déterministe est incorrecte et le subgraph ne surmontera jamais l'erreur ; ces échecs doivent être signalés en tant que problèmes sur le référentiel Graph Node.
Graph Node met en cache certaines données dans le magasin afin d'économiser la récupération auprès du fournisseur. Les blocs sont mis en cache, tout comme les résultats de eth_calls
(ces derniers étant mis en cache à partir d'un bloc spécifique). Cette mise en cache peut augmenter considérablement la vitesse d'indexation lors de la « resynchronisation » d'un subgraph légèrement modifié.
However, in some instances, if an Ethereum node has provided incorrect data for some period, that can make its way into the cache, leading to incorrect data or failed subgraphs. In this case indexers can use graphman
to clear the poisoned cache, and then rewind the affected subgraphs, which will then fetch fresh data from the (hopefully) healthy provider.
Si une incohérence du cache de blocs est suspectée, telle qu'un événement de réception de transmission manquant :
liste de chaînes graphman
pour trouver le nom de la chaîne.graphman chain check-blocks <CHAIN> par numéro <NUMBER>
vérifiera si le bloc mis en cache correspond au fournisseur et supprimera le bloc du cache si ce n'est pas le cas.- S'il y a une différence, il peut être plus sûr de tronquer tout le cache avec
graphman chain truncate <CHAIN>
. - Si le bloc correspond au fournisseur, le problème peut être débogué directement auprès du fournisseur.
Une fois qu'un subgraph a été indexé, les indexeurs peuvent s'attendre à traiter les requêtes via le point de terminaison de requête dédié du subgraph. Si l'indexeur espère traiter un volume de requêtes important, un nœud de requête dédié est recommandé, et en cas de volumes de requêtes très élevés, les indexeurs peuvent souhaiter configurer des fragments de réplique afin que les requêtes n'aient pas d'impact sur le processus d'indexation.
However, even with a dedicated query node and replicas, certain queries can take a long time to execute, and in some cases increase memory usage and negatively impact the query time for other users.
Il n'existe pas de solution miracle, mais une gamme d'outils permettant de prévenir, de diagnostiquer et de traiter les requêtes lentes.
Graph Node met en cache les requêtes GraphQL par défaut, ce qui peut réduire considérablement la charge de la base de données. Cela peut être configuré davantage avec les paramètres GRAPH_QUERY_CACHE_BLOCKS
et GRAPH_QUERY_CACHE_MAX_MEM
- pour en savoir plus .
Les requêtes problématiques apparaissent le plus souvent de deux manières. Dans certains cas, les utilisateurs eux-mêmes signalent qu'une requête donnée est lente. Dans ce cas, le défi consiste à diagnostiquer la raison de la lenteur, qu'il s'agisse d'un problème général ou spécifique à ce subgraph ou à cette requête. Et puis bien sûr de le résoudre, si possible.
Dans d'autres cas, le déclencheur peut être une utilisation élevée de la mémoire sur un nœud de requête, auquel cas le défi consiste d'abord à identifier la requête à l'origine du problème.
Les indexeurs peuvent utiliser pour traiter et résumer les journaux de requêtes de Graph Node. GRAPH_LOG_QUERY_TIMING
peut également être activé pour aider à identifier et déboguer les requêtes lentes.
Étant donné une requête lente, les indexeurs disposent de quelques options. Bien entendu, ils peuvent modifier leur modèle de coûts pour augmenter considérablement le coût d’envoi de la requête problématique. Cela peut entraîner une réduction de la fréquence de cette requête. Cependant, cela ne résout souvent pas la cause première du problème.
Les tables de base de données qui stockent les entités semblent généralement se décliner en deux variétés : les tables de type « transaction », où les entités, une fois créées, ne sont jamais mises à jour, c'est-à-dire qu'elles stockent quelque chose qui s'apparente à une liste de transactions financières, et les « de type compte », où les entités sont mis à jour très souvent, c'est-à-dire qu'ils stockent quelque chose comme des comptes financiers qui sont modifiés à chaque fois qu'une transaction est enregistrée. Les tables de type compte se caractérisent par le fait qu'elles contiennent un grand nombre de versions d'entités, mais relativement peu d'entités distinctes. Souvent, dans de tels tableaux, le nombre d'entités distinctes représente 1 % du nombre total de lignes (versions d'entités)
Pour les tables de type compte, graph-node
peut générer des requêtes qui tirent parti des détails de la façon dont Postgres finit par stocker les données avec un taux de changement si élevé, à savoir que toutes les versions des blocs récents sont en une petite sous-section du stockage global pour une telle table.
La commande graphman stats show <sgdNNNN
> indique, pour chaque type/table d'entité dans un déploiement, le nombre d'entités distinctes et le nombre de versions d'entité que chaque table contient. Ces données sont basées sur des estimations internes à Postgres et sont donc nécessairement imprécises et peuvent être erronées d'un ordre de grandeur. Un -1
dans la colonne entités
signifie que Postgres estime que toutes les lignes contiennent une entité distincte.
En général, les tables dans lesquelles le nombre d'entités distinctes est inférieur à 1 % du nombre total de lignes/versions d'entités sont de bons candidats pour l'optimisation de type compte. Lorsque la sortie de graphman stats show
indique qu'une table pourrait bénéficier de cette optimisation, l'exécution de graphman stats show <sgdNNN> <table>
effectuera un décompte complet de la table - cela peut être lent, mais donne une mesure précise du rapport entre les entités distinctes et les versions globales de l'entité.
Une fois qu'il a été déterminé qu'une table ressemble à un compte, l'exécution de graphman stats account-like <sgdNNN>.<table>
activera l'optimisation de type compte pour les requêtes sur cette table. L'optimisation peut être à nouveau désactivée avec graphman stats account-like --clear <sgdNNN>.<table>
Il faut jusqu'à 5 minutes aux nœuds de requête pour remarquer que l'optimisation a été activée ou désactivée. . Après avoir activé l'optimisation, il est nécessaire de vérifier que le changement ne ralentit pas réellement les requêtes pour cette table. Si vous avez configuré Grafana pour surveiller Postgres, des requêtes lentes apparaîtraient dans pg_stat_activity
en grand nombre, prenant plusieurs secondes. Dans ce cas, l’optimisation doit être à nouveau désactivée.
Pour les subgraphs de type Uniswap, les tables pair
et token
sont les meilleurs candidats pour cette optimisation et peuvent avoir un effet considérable sur la charge de la base de données.
Il s'agit d'une nouvelle fonctionnalité qui sera disponible dans Graph Node 0.29.x
À un moment donné, un indexeur souhaitera peut-être supprimer un subgraph donné. Cela peut être facilement fait via graphman drop
, qui supprime un déploiement et toutes ses données indexées. Le déploiement peut être spécifié sous la forme d'un nom de subgraph, d'un hachage IPFS Qm..
ou de l'espace de noms de base de données sgdNNN
. Une documentation supplémentaire est disponible .