Drift av Graf Node
Reading time: 15 min
Graf Node är komponenten som indexerar subgraffar och gör den resulterande datan tillgänglig för förfrågan via en GraphQL API. Som sådan är den central för indexeringsstacken, och korrekt drift av Graph Node är avgörande för att driva en framgångsrik indexerare.
Detta ger en kontextuell översikt över Graph Node och några av de mer avancerade alternativ som är tillgängliga för indexerare. Detaljerad dokumentation och instruktioner finns i .
är referensimplementationen för indexeringsavsnitt på The Graph Nätverk, som ansluter till blockchain-klienter, indexerar subgraffar och gör indexerad data tillgänglig för förfrågan.
Graf Node (och hela indexeringsstacken) kan köras på rå metall eller i en molnmiljö. Flexibiliteten hos den centrala indexeringskomponenten är avgörande för robustheten i The Graph Protocol. På samma sätt kan Graph Node eller indexerare kan använda en av de .
Huvudlagret för Graph Node, här lagras subgrafdata, liksom metadata om subgraffar och nätverksdata som är oberoende av subgraffar, som blockcache och eth_call-cache.
För att indexera ett nätverk behöver Graf Node åtkomst till en nätverksklient via ett EVM-kompatibelt JSON-RPC API. Denna RPC kan ansluta till en enda klient eller så kan det vara en mer komplex konfiguration som lastbalanserar över flera.
Medan vissa subgrafer kan kräva en fullständig nod, kan vissa ha indexeringsfunktioner som kräver ytterligare RPC-funktionalitet. Specifikt subgrafer som gör eth_calls
som en del av indexering kommer att kräva en arkivnod som stöder , och subgrafer med callHandlers
eller blockHandlers
med en call
-filtrering kräver trace_filter
-stöd ().
Network Firehoses - a Firehose is a gRPC service providing an ordered, yet fork-aware, stream of blocks, developed by The Graph's core developers to better support performant indexing at scale. This is not currently an Indexer requirement, but Indexers are encouraged to familiarise themselves with the technology, ahead of full network support. Learn more about the Firehose .
Metadata för distribution av subgraffar lagras på IPFS-nätverket. Graf Node har främst åtkomst till IPFS-noden under distributionen av subgraffar för att hämta subgrafens manifest och alla länkade filer. Nätverksindexerare behöver inte värd sin egen IPFS-nod. En IPFS-nod för nätverket är värd på .
För att möjliggöra övervakning och rapportering kan Graf Node valfritt logga metrik till en Prometheus-metrisk server.
-
Rust
-
PostgreSQL
-
IPFS
-
Ytterligare krav för Ubuntu-användare - För att köra en Graf Node på Ubuntu kan några ytterligare paket behövas.
sudo apt-get install -y clang libpq-dev libssl-dev pkg-config
- Starta en PostgreSQL-databasserver
initdb -D .postgrespg_ctl -D .postgres -l logfile startcreatedb graph-node
Nu när alla beroenden är konfigurerade startar du Graf 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
En komplett exempelkonfiguration för Kubernetes finns i .
När Graph Node är igång exponerar den följande portar:
Port | Syfte | Rutter | Argument för CLI | Miljö Variabel |
---|---|---|---|---|
8000 | GraphQL HTTP-server (för frågor om undergrafer) | /subgraphs/id/... /subgraphs/name/.../... | --http-port | - |
8001 | GraphQL WS (för prenumerationer på undergrafer) | /subgraphs/id/... /subgraphs/name/.../... | --ws-port | - |
8020 | JSON-RPC (för hantering av distributioner) | / | --admin-port | - |
8030 | Status för indexering av undergrafer API | /graphql | --index-node-port | - |
8040 | Prometheus mätvärden | /metrics | --metrics-port | - |
Viktigt: Var försiktig med att exponera portar offentligt - administrationsportar bör hållas säkra. Detta inkluderar JSON-RPC-slutpunkten för Graph Node.
På sitt enklaste sätt kan Graph Node användas med en enda instans av Graph Node, en enda PostgreSQL-databas, en IPFS-nod och nätverksklienter som krävs av de subgrafer som ska indexeras.
Denna konfiguration kan skalas horisontellt genom att lägga till flera Graph Nodes och flera databaser för att stödja dessa Graph Nodes. Avancerade användare kan vilja dra nytta av vissa av de horisontella skalningsfunktionerna i Graph Node, liksom några av de mer avancerade konfigurationsalternativen via filen config.toml
och Graph Nodes miljövariabler.
En konfigurationsfil kan användas för att ställa in mer komplexa konfigurationer än de som exponeras i CLI. Platsen för filen överförs med kommandoradsomkopplaren --config.
När du använder en konfigurationsfil är det inte möjligt att använda alternativen --postgres-url, --postgres-secondary-hosts och --postgres-host-weights.
En minimal config.toml
-fil kan tillhandahållas; följande fil är ekvivalent med att använda kommandoradsalternativet --postgres-url:
[store][store.primary]connection="<.. postgres-url argument ..>"[deployment][[deployment.rule]]indexers = [ "<.. list of all indexing nodes ..>" ]
Fullständig dokumentation av config.toml
hittar du i .
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 .
Observera att flera Graph Nodes alla kan konfigureras att använda samma databas, som i sig kan skalas horisontellt via sharding.
Med flera Graph Nodes är det nödvändigt att hantera deployering av nya subgrafer så att samma subgraf inte indexeras av två olika noder, vilket skulle leda till kollisioner. Detta kan göras genom att använda deployeringsregler, som också kan specificera vilken shard
subgrafens data ska lagras i om databasens sharding används. Deployeringsregler kan matcha subgrafens namn och nätverket som deployeringen indexerar för att fatta ett beslut.
Exempel på konfiguration av deployeringsregler:
[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"]
Läs mer om implementeringsregler .
Noder kan konfigureras för att uttryckligen vara frågenoder genom att inkludera följande i konfigurationsfilen:
[general]query = "<regular expression>"
Alla noder vars --node-id matchar reguljärt uttryck kommer att konfigureras för att endast svara på förfrågningar.
För de flesta användningsfall är en enda Postgres-databas tillräcklig för att stödja en graph-node-instans. När en graph-node-instans växer utöver en enda Postgres-databas är det möjligt att dela upp lagringen av graph-node-data över flera Postgres-databaser. Alla databaser tillsammans bildar lagringsutrymmet för graph-node-instansen. Varje individuell databas kallas en shard.
Shards kan användas för att dela upp subgraffsdeployeringar över flera databaser och kan också användas för att använda kopior för att sprida frågebelastningen över databaser. Detta inkluderar konfigurering av antalet tillgängliga databasanslutningar som varje graph-node
bör behålla i sin anslutningspool för varje databas, vilket blir allt viktigare när fler subgrafer blir indexerade.
Sharding blir användbart när din befintliga databas inte kan hålla jämna steg med belastningen som Graph Node sätter på den och när det inte längre är möjligt att öka databasens storlek.
Det är generellt sett bättre att göra en enda databas så stor som möjligt innan man börjar med shards. Ett undantag är när frågetrafiken är mycket ojämnt fördelad mellan subgrafer; i dessa situationer kan det hjälpa dramatiskt om högvolymsubgraferna hålls i en shard och allt annat i en annan, eftersom den konfigurationen gör det mer troligt att data för högvolymsubgraferna stannar i databasens interna cache och inte ersätts av data som inte behövs lika mycket från lågvolymsubgrafer.
När det gäller att konfigurera anslutningar, börja med max_connections i postgresql.conf som är inställt på 400 (eller kanske till och med 200) och titta på Prometheus-metrarna store_connection_wait_time_ms och store_connection_checkout_count. Märkbara väntetider (något över 5 ms) är en indikation på att det finns för få anslutningar tillgängliga; höga väntetider beror också på att databasen är mycket upptagen (som hög CPU-belastning). Om databasen verkar annars stabil, indikerar höga väntetider att antalet anslutningar behöver ökas. I konfigurationen är det en övre gräns för hur många anslutningar varje graph-node-instans kan använda, och Graph Node kommer inte att hålla anslutningar öppna om det inte behöver dem.
Läs mer om konfiguration av lagring .
Om det finns flera konfigurerade noder är det nödvändigt att specificera en nod som är ansvarig för inhämtning av nya block, så att alla konfigurerade indexnoder inte frågar huvudet av kedjan. Detta görs som en del av namnrymden chains
, där du anger node_id
som ska användas för blockinhämtning:
[chains]ingestor = "block_ingestor_node"
Graf Protocol ökar antalet nätverk som stöds för indexering av belöningar, och det finns många undergrafer som indexerar icke-stödda nätverk som en indexerare skulle vilja bearbeta. Filen config.toml
möjliggör uttrycksfull och flexibel konfiguration av:
- Flera nätverk
- Flera leverantörer per nätverk (detta kan göra det möjligt att dela upp belastningen mellan leverantörer, och kan också möjliggöra konfiguration av fullständiga noder samt arkivnoder, där Graph Node föredrar billigare leverantörer om en viss arbetsbelastning tillåter det).
- Ytterligare information om leverantören, t. ex. funktioner, autentisering och typ av leverantör (för stöd för experimentell Firehose)
Avsnittet [chains]
styr de ethereum-providers som graph-node ansluter till, och var block och andra metadata för varje kedja lagras. Följande exempel konfigurerar två kedjor, mainnet och kovan, där block för mainnet lagras i vip-sharden och block för kovan lagras i den primära sharden. Mainnet-kedjan kan använda två olika leverantörer, medan kovan bara har en leverantör.
[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 = [] } ]
Läs mer om leverantörsconfiguration .
Graph Node stöder ett utbud av miljövariabler som kan aktivera funktioner eller ändra Graph Node-beteendet. Dessa är dokumenterade .
Användare som driver en skalad indexering med avancerad konfiguration kan dra nytta av att hantera sina Graph Nodes med Kubernetes.
- Indexeringsförrådet har en
- är en verktygslåda för att köra en Graph Protocol Indexet på Kubernetes som underhålls av GraphOps. Den tillhandahåller en uppsättning Hjelm-diagram och en CLI för att hantera en grafnod-distribution.
Med en körande Graph Node (eller Graph Nodes!) är utmaningen sedan att hantera distribuerade subgrafer över dessa noder. Graph Node erbjuder en rad verktyg för att hjälpa till med hanteringen av subgrafer.
Graph Nodes loggar kan ge användbar information för felsökning och optimering av Graph Node och specifika subgrafer. Graph Node stöder olika loggnivåer via miljövariabeln GRAPH_LOG
, med följande nivåer: error, warn, info, debug eller trace.
Dessutom ger inställningen GRAPH_LOG_QUERY_TIMING
till gql
mer information om hur GraphQL-frågor körs (dock kommer detta att generera en stor mängd loggar).
Graph Node tillhandahåller metrikerna via Prometheus-endpunkt på port 8040 som standard. Grafana kan sedan användas för att visualisera dessa metriker.
Indexer-repositoriet tillhandahåller en .
graphman
är ett underhållsverktyg för Graph Node som hjälper till med diagnos och lösning av olika dagliga och exceptionella uppgifter.
Kommandot graphman ingår i de officiella containrarna, och du kan köra det med docker exen in i din graph-node-container. Det kräver en config.toml
-fil.
Fullständig dokumentation om graphman
-kommandon finns i Graph Node-repositoriet. Se [/docs/graphman.md] () i Graph Node /docs
Tillgänglig som standard på port 8030/graphql, exponerar indexeringstatus-API: en en rad metoder för att kontrollera indexeringstatus för olika subgrafer, kontrollera bevis för indexering, inspektera subgrafegenskaper och mer.
Hela schemat är tillgängligt .
Det finns tre separata delar av indexeringsprocessen:
- Hämta intressanta händelser från leverantören
- Bearbeta händelser i rätt ordning med lämpliga hanterare (detta kan innebära att kedjan anropas för status och att data hämtas från lagret)
- Skriva de resulterande data till butiken
Dessa stadier är pipelinerade (det vill säga de kan utföras parallellt), men de är beroende av varandra. När subgrafer är långsamma att indexera beror orsaken på den specifika subgrafgen.
Vanliga orsaker till indexeringslångsamhet:
- Tidsåtgång för att hitta relevanta händelser från kedjan (särskilt anropshanterare kan vara långsamma, eftersom de förlitar sig på
trace_filter
) - Göra ett stort antal
eth_calls
som en del av handläggare - En stor mängd butiksinteraktion under exekvering
- En stor mängd data att spara i butiken
- Ett stort antal evenemang att bearbeta
- Långsam databasanslutningstid, för överbelastade noder
- Leverantören själv faller bakom kedjehuvudet
- Långsamhet vid hämtning av nya kvitton från leverantören vid kedjehuvudet
Subgrafindexeringsmetriker kan hjälpa till att diagnostisera grunden till indexeringens långsamhet. I vissa fall ligger problemet med subgrafgenen själv, men i andra fall kan förbättrade nätverksleverantörer, minskad databaskonflikt och andra konfigurationsförbättringar markant förbättra indexeringens prestanda.
Under indexering kan subgrafer misslyckas om de stöter på data som är oväntad, om någon komponent inte fungerar som förväntat eller om det finns något fel i händelsehanterare eller konfiguration. Det finns två allmänna typer av misslyckande:
- Deterministiska fel: detta är fel som inte kommer att lösas med retries
- Icke-deterministiska fel: dessa kan bero på problem med leverantören eller något oväntat Graph Node-fel. När ett icke-deterministiskt fel inträffar kommer Graph Node att försöka igen med de felande hanterarna och backa över tid.
I vissa fall kan ett misslyckande vara lösbart av indexören (till exempel om felet beror på att det inte finns rätt typ av leverantör, kommer att tillåta indexering att fortsätta om den nödvändiga leverantören läggs till). Men i andra fall krävs en ändring i subgrafkoden.
Deterministiska misslyckanden betraktas som "slutliga", med en Proof of Indexing genererad för det misslyckande blocket, medan icke-deterministiska misslyckanden inte är det, eftersom subgrafen kanske lyckas "avmisslyckas" och fortsätta indexeringen. I vissa fall är den icke-deterministiska etiketten felaktig, och subgrafen kommer aldrig att övervinna felet; sådana misslyckanden bör rapporteras som problem i Graf Node-repositoriet.
Graf Node cachar viss data i lagringen för att undvika att hämtas från leverantören. Block cachas, liksom resultaten av eth_calls
(det senare cachas från en specifik block). Denna cachning kan dramatiskt öka indexeringens hastighet under "omjustering" av en något ändrad subgraf.
Men i vissa fall, om en Ethereum-nod har tillhandahållit felaktig data under en period, kan det ta sig in i cachen, vilket leder till felaktig data eller misslyckade subgrafer. I det här fallet kan indexerare använda graphman
för att rensa den förgiftade cachen och sedan spola tillbaka de påverkade subgraferna, som sedan hämtar färsk data från den (förhoppningsvis) friska leverantören.
Om en blockcache-inkonsekvens misstänks, som att en tx-kvitto saknar händelse:
graphman chain list
för att hitta kedjans namn.graphman chain check-blocks <CHAIN> by-number <NUMBER>
kontrollerar om det cachade blocket matchar leverantören, och tar bort blocket från cacheminnet om det inte gör det.- Om det finns en skillnad kan det vara säkrare att trunkera hela cacheminnet med
graphman chain truncate <CHAIN>
. - Om blocket matchar leverantören kan problemet felsökas direkt mot leverantören.
När en subgraf har indexeras kan indexörer förvänta sig att servera frågor via subgrafens dedikerade frågendpunkt. Om indexören hoppas på att betjäna en betydande mängd frågor rekommenderas en dedikerad frågenod, och vid mycket höga frågevolymer kan indexörer vilja konfigurera replikskivor så att frågor inte påverkar indexeringsprocessen.
Men även med en dedikerad frågenod och repliker kan vissa frågor ta lång tid att utföra, och i vissa fall öka minnesanvändningen och negativt påverka frågetiden för andra användare.
Det finns inte en "silverkula", men en rad verktyg för att förebygga, diagnostisera och hantera långsamma frågor.
Graf Node cachar GraphQL-frågor som standard, vilket kan minska belastningen på databasen avsevärt. Detta kan konfigureras ytterligare med inställningarna GRAPH_QUERY_CACHE_BLOCKS
och GRAPH_QUERY_CACHE_MAX_MEM
- läs mer .
Problematiska frågor dyker oftast upp på ett av två sätt. I vissa fall rapporterar användare själva att en viss fråga är långsam. I det fallet är utmaningen att diagnostisera orsaken till långsamheten - om det är ett generellt problem eller specifikt för den subgraf eller fråga. Och naturligtvis att lösa det om det är möjligt.
I andra fall kan utlösaren vara hög minnesanvändning på en frågenod, i vilket fall utmaningen först är att identifiera frågan som orsakar problemet.
Indexörer kan använda för att bearbeta och sammanfatta Graph Nodes frågeloggar. GRAPH_LOG_QUERY_TIMING
kan också aktiveras för att hjälpa till att identifiera och felsöka långsamma frågor.
Med en långsam fråga har indexörer några alternativ. Självklart kan de ändra sin kostnadsmodell för att kraftigt öka kostnaden för att skicka den problematiska frågan. Detta kan resultera i att frekvensen av den frågan minskar. Men det löser ofta inte grunden till problemet.
Databastabeller som lagrar enheter verkar generellt komma i två varianter: 'transaktionsliknande', där enheter, när de väl är skapade, aldrig uppdateras, dvs. de lagrar något liknande en lista över finansiella transaktioner, och 'konto-liknande', där enheter uppdateras mycket ofta, dvs. de lagrar något som finansiella konton som ändras varje gång en transaktion registreras. Tabeller med konto-liknande tabeller karakteriseras av att de innehåller ett stort antal enhetsversioner, men relativt få distinkta enheter. Ofta är antalet distinkta enheter i sådana tabeller 1% av det totala antalet rader (enhetsversioner)
För konto-liknande tabeller kan graph-node
generera frågor som utnyttjar detaljer om hur Postgres slutligen lagrar data med en så hög förändringsfrekvens, nämligen att alla versioner för nyligen block är i en liten del av den övergripande lagringen för en sådan tabell.
Kommandot graphman stats show <sgdNNNN
> visar, för varje enhetstyp/tabell i en deployment, hur många distinkta enheter och hur många enhetsversioner varje tabell innehåller. Den data är baserad på Postgres-interna uppskattningar och är därför nödvändigtvis oprecis och kan vara fel med en ordning av storlek. Ett -1
i kolumnen entities
innebär att Postgres tror att alla rader innehåller en distinkt enhet.
I allmänhet är tabeller där antalet distinkta enheter är mindre än 1% av det totala antalet rader/enhetsversioner bra kandidater för konto-liknande optimering. När utdata från graphman stats show
indikerar att en tabell kan dra nytta av denna optimering, kommer att köra graphman stats show <sgdNNN> <table>
att utföra en full räkning av tabellen - det kan vara långsamt, men ger en precis mätning av förhållandet mellan distinkta enheter till totalt enhetsversioner.
När en tabell har fastställts som konto-liknande, kommer att köra graphman stats account-like <sgdNNN>.<table>
att aktivera konto-liknande optimeringen för frågor mot den tabellen. Optimeringen kan stängas av igen med graphman stats account-like --clear <sgdNNN>.<table>
Det tar upp till 5 minuter för frågenoder att märka att optimeringen har aktiverats eller stängts av. Efter att ha aktiverat optimeringen är det nödvändigt att verifiera att ändringen faktiskt inte gör att frågor blir långsammare för den tabellen. Om du har konfigurerat Grafana för att övervaka Postgres, skulle långsamma frågor dyka upp i pg_stat_activity
i stora mängder, ta flera sekunder. I det fallet måste optimeringen stängas av igen.
För subgrafer som liknar Uniswap är pair
och token
tabeller primära kandidater för denna optimering och kan ha en dramatisk effekt på databasbelastningen.
Detta är ny funktionalitet, som kommer att vara tillgänglig i Graf Node 0.29.x
Vid någon tidpunkt kan en indexer vilja ta bort en given subgraf. Detta kan enkelt göras via graphman drop
, som raderar en distribution och all dess indexerade data. Distributionen kan specificeras antingen som ett subgrafnamn, en IPFS-hash Qm..
, Eller databasens namnrymd sgdNNN
. Ytterligare dokumentation finns tillgänglig .