オペレーティンググラフノード
Reading time: 31 min
グラフノードはサブグラフのインデックスを作成し、得られたデータをGraphQL API経由でクエリできるようにするコンポーネントです。そのため、インデクサースタックの中心的存在であり、グラフノードの正しい動作はインデクサーを成功させるために非常に重要です。
これは、グラフノードの文脈的な概要と、インデクサーに利用可能なより高度なオプションのいくつかを提供します。詳細なドキュメントと説明は で見ることができます。
は、グラフネットワーク上のサブグラフにインデックスを付け、ブロックチェーンクライアントに接続し、サブグラフにインデックスを付け、インデックス付けしたデータをクエリで利用できるようにするためのリファレンス実装です。
グラフノード(およびインデクサスタック全体)は、ベアメタルでもクラウド環境でも実行可能です。中央のインデックス作成コンポーネントのこの柔軟性は、グラフプロトコルの堅牢性にとって非常に重要です。同様に、グラフノードは、インデクサーはの1つを使用することができます。また
グラフノードのメインストアで、サブグラフデータ、サブグラフに関するメタデータ、ブロックキャッシュやeth_callキャッシュなどのサブグラフに依存しないネットワークデータが格納されます。
ネットワークにインデックスを付けるために、グラフ ノードは EVM 互換の JSON-RPC API を介してネットワーク クライアントにアクセスする必要があります。この RPC は単一のクライアントに接続する場合もあれば、複数のクライアントに負荷を分散するより複雑なセットアップになる場合もあります。
一部のサブグラフは完全なノードのみを必要とする場合がありますが、一部のサブグラフには追加の RPC 機能を必要とするインデックス機能が含まれる場合があります。具体的には、インデックス作成の一部として eth_calls
を作成するサブグラフには、 をサポートするアーカイブ ノードが必要になります。、および callHandlers
を持つサブグラフ、または call
フィルタを持つ blockHandlers
には、trace_filter
サポートが必要です ()。
近日公開です。Network Firehoses - Firehose は、順序付けられた、しかしフォークを意識したブロックのストリームを提供する gRPC サービスで、The Graph のコア開発者により、大規模で高性能なインデックス作成をより良くサポートするために開発されました。これは現在、インデクサーの要件ではありませんが、インデクサーは、ネットワークの完全サポートに先立って、この技術に慣れることが推奨されています。Firehose の詳細については、を参照してください。
IPFS ノード(バージョン 未満) - サブグラフのデプロイメタデータは IPFS ネットワーク上に保存されます。 グラフノードは、サブグラフのデプロイ時に主に IPFS ノードにアクセスし、サブグラフマニフェストと全てのリンクファイルを取得します。 ネットワーク・インデクサーは独自の IPFS ノードをホストする必要はありません。 ネットワーク用の IPFS ノードは、 でホストされています。
監視とレポート作成を可能にするため、Graph NodeはオプションでPrometheusのメトリクスサーバーにメトリクスを記録することができます。
-
Rust
-
PostgreSQL
-
IPFS
-
Ubuntu ユーザーのための追加要件 - グラフノードを Ubuntu 上で動作させるためには、いくつかの追加パッケージが必要になります。
sudo apt-get install -y clang libpq-dev libssl-dev pkg-config
- PostgreSQL データベースサーバを起動します。
initdb -D .postgrespg_ctl -D .postgres -l logfile startcreatedb 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
Kubernetesの完全な設定例は、で見ることができます。
グラフノードは起動時に以下のポートを公開します。
Port | Purpose | Routes | CLI Argument | Environment Variable |
---|---|---|---|---|
8000 | GraphQL HTTP server (for subgraph queries) | /subgraphs/id/... /subgraphs/name/.../... | --http-port | - |
8001 | GraphQL WS (for subgraph subscriptions) | /subgraphs/id/... /subgraphs/name/.../... | --ws-port | - |
8020 | JSON-RPC (for managing deployments) | / | --admin-port | - |
8030 | Subgraph indexing status API | /graphql | --index-node-port | - |
8040 | Prometheus metrics | /metrics | --metrics-port | - |
重要: ポートを公に公開する場合は注意してください。管理ポートはロックしておく必要があります。ノードの JSON-RPC エンドポイント
最も単純な場合、Graph Node は、Graph Node の単一のインスタンス、単一の PostgreSQL データベース、IPFS ノード、およびサブグラフのインデックス作成に必要なネットワーク クライアントで操作できます。
このセットアップは、複数のグラフノードと、それらのグラフノードをサポートする複数のデータベースを追加することで、水平方向に拡張することができます。上級ユーザは、config.toml
ファイルやグラフノードの環境変数を使って、グラフノードの水平スケーリング機能、およびより高度な設定オプションを利用することができます。
設定ファイルを使用すると、CLI で公開される設定よりも複雑な設定を行うことができます。ファイルの場所は --config コマンドラインスイッチで渡します。
設定ファイルを使用する場合、-postgres-url、-postgres-secondary-hosts、および --postgres-host-weights オプションを使用することはできません。
最小限のconfig.toml
ファイルを提供することができます。以下のファイルは、-postgres-urlコマンドラインオプションを使用することと同等です。
[store][store.primary]connection="<.. postgres-url argument ..>"[deployment][[deployment.rule]]indexers = [ "<.. list of all indexing nodes ..>" ]
config.toml
の完全なドキュメントは、 で見ることができます。
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 .
なお、複数のGraph Nodeはすべて同じデータベースを使用するように設定することができ、Shardingによって水平方向に拡張することができます。
複数のグラフ ノードがある場合、同じサブグラフが 2 つの異なるノードによってインデックス付けされないように、新しいサブグラフの展開を管理する必要があります。これにより衝突が発生します。これは、データベース シャーディングが使用されている場合、サブグラフのデータを保存する shard
も指定できるデプロイメント ルールを使用して実行できます。デプロイメント ルールは、決定を下すために、サブグラフ名と、デプロイメントがインデックスを作成しているネットワークで照合できます。
デプロイメントルールの設定例:
[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"]
設定ファイルに以下を記述することで、ノードを明示的にクエリノードに設定することができます。
[general]query = "<regular expression>"
正規表現に --node-id がマッチするノードは、クエリーにのみ応答するように設定されます。
ほとんどの場合、1つのPostgresデータベースでグラフノードインスタンスをサポートするのに十分です。グラフノードインスタンスが1つのPostgresデータベースを使い切った場合、グラフノードデータを複数のPostgresデータベースに分割して保存することが可能です。全てのデータベースが一緒になってグラフノードインスタンスのストアを形成します。個々のデータベースはシャード(shard)と呼ばれます。
シャードは、サブグラフを複数のデータベースに分割するために使用することができ、また、データベース間でクエリの負荷を分散するためにレプリカを使用することができます。これには、各 graph-node
が各データベースの接続プールに保持すべき、利用可能なデータベース接続の数の設定が含まれ、これはより多くのサブグラフがインデックス化されるにつれて、ますます重要になります。
グラフノードの負荷に既存のデータベースが追いつかず、これ以上データベースサイズを大きくすることができない場合に、シャーディングが有効になります。
一般的には、シャードを作成する前に、単一のデータベースを可能な限り大きくすることをお勧めします。例外は、クエリのトラフィックがサブグラフ間で非常に不均一に分割される場合です。このような状況では、ボリュームの大きいサブグラフを1つのシャードに、それ以外を別のシャードに保存すると劇的に効果があります。この設定により、ボリュームの大きいサブグラフのデータがdb内部キャッシュに残り、ボリュームの小さいサブグラフからそれほど必要とされていないデータに置き換えられる可能性が少なくなるためです。
接続の設定に関しては、まずpostgresql.confのmax_connectionsを400(あるいは200)に設定し、store_connection_wait_time_msとstore_connection_checkout_count Prometheusメトリクスを見てみてください。顕著な待ち時間(5ms以上)は、利用可能な接続が少なすぎることを示しています。高い待ち時間は、データベースが非常に忙しいこと(CPU負荷が高いなど)によっても引き起こされます。しかし、データベースが安定しているようであれば、待ち時間が長いのは接続数を増やす必要があることを示しています。設定上、各グラフノードインスタンスが使用できるコネクション数は上限であり、グラフノードは必要ないコネクションはオープンにしておきません。
ストアコンフィギュレーションについて詳しくはをご覧ください。
複数のノードが設定されている場合、設定されているすべてのインデックスノードがチェーンヘッドをポーリングしないように、新しいブロックの取り込みに責任を持つノードを1つ指定する必要があります。これは chains
名前空間の一部として行われ、ブロックの取り込みに使われる node_id
を指定します。
[chains]ingestor = "block_ingestor_node"
Graph Protocolは、インデックス作成報酬のためにサポートされるネットワークの数を増やしており、インデックス作成者が処理したいと思うサポートされていないネットワークをインデックスしたサブグラフが多く存在します。config.toml
ファイルは、表現力豊かで柔軟な設定を可能にします。
- 複数のネットワーク
- ネットワークごとに複数のプロバイダ(プロバイダ間で負荷を分割することができ、また、フルノードとアーカイブノードを構成することができ、作業負荷が許す限り、Graph Nodeはより安価なプロバイダを優先することができます)。
- 機能、認証、プロバイダの種類など、プロバイダの詳細(実験的なFirehoseのサポートのため)
[chains]
セクションは、graph-nodeが接続するイーサリアムプロバイダーと、各チェーンのブロックや他のメタデータが格納される場所を制御します。次の例では、mainnet と kovan の 2 つのチェーンを設定し、mainnet のブロックは vip シャードに、kovan のブロックは primary シャードに格納されます。mainnet チェーンでは 2 つの異なるプロバイダを使用できますが、kovan チェーンではプロバイダは 1 つだけです。
[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 = [] } ]
グラフ ノードは、機能を有効にしたり、グラフ ノードの動作を変更したりできるさまざまな環境変数をサポートしています。これらはに記載されています。
高度な構成でスケーリングされたインデックス作成セットアップを運用しているユーザーは、Kubernetes を使用してグラフ ノードを管理することでメリットが得られる場合があります。
- インデクサーリポジトリには、があります。
- は、GraphOps が管理する Kubernetes 上で Graph Protocol Indexer を動作させるためのツールキットです。グラフノードのデプロイを管理するためのHelmチャートのセットとCLIを提供します。
グラフノードが動作している場合、それらのノードに展開されたサブグラフを管理することが課題となります。グラフノードは、サブグラフを管理するための様々なツールを提供します。
グラフノードのログは、グラフノードと特定のサブグラフのデバッグと最適化に役立つ情報を提供します。グラフノードeは、GRAPH_LOG
環境変数によって、以下のレベルで、異なるログレベルをサポートします:エラー、警告、情報、デバッグ、トレース。
さらに、GRAPH_LOG_QUERY_TIMING
をgql
に設定すると、GraphQLクエリの実行方法についてより詳細に知ることができます(ただし、これは大量のログを生成することになります)。
グラフノードは、デフォルトで8040ポートのPrometheusエンドポイント経由でメトリクスを提供します。そして、Grafanaを使用してこれらのメトリクスを可視化することができます。
graphman
はグラフノードのメンテナンスツールで、日常的なタスクや例外的なタスクの診断と解決を支援します。
graphmanコマンドは公式コンテナに含まれており、グラフノードコンテナにdocker execすることで実行できます。その際、config.toml
ファイルが必要になります。
graphman
コマンドの完全なドキュメントは グラフノードリポジトリにあります。グラフノードの /docs
にある [/docs/graphman.md] () を参照してください。
デフォルトではポート8030/graphqlで利用可能なindexing status APIは、異なるサブグラフのindexing statusのチェック、indexing proofのチェック、サブグラフの特徴の検査など、様々なメソッドを公開しています。
インデックス作成は、3つのパートに分かれています:
- プロバイダから興味のあるイベントを取得
- 適切なハンドラで順番にイベントを処理する(これには、状態のためにチェーンを呼び出したり、ストアからデータを取得したりすることが含まれます)。
- 出来上がったデータをストアに書き込む
これらのステージはパイプライン化されていますが(つまり、並列に実行することができます)、互いに依存し合っています。サブグラフのインデックス作成に時間がかかる場合、その根本的な原因は、特定のサブグラフに依存します。
インデックス作成が遅くなる一般的な原因:
- チェーンから関連するイベントを見つけるのにかかる時間 (特にコール ハンドラーは
trace_filter
に依存しているため遅くなることがあります) - ハンドラの一部として大量の
eth_calls
を作成する - 実行中に大量のストアインタラクションが発生
- ストアに保存するデータ量が多い場合
- 処理するイベントの数が多い場合
- データベースへの接続時間が遅い(混雑したノード)
- プロバイダー自体がチェーンヘッドに遅れる場合
- チェーンヘッドでプロバイダーから新しいレシートを取得する際の遅延
サブグラフのインデックス作成指標は、インデックス作成の遅さの根本的な原因を診断するのに役立ちます。あるケースでは、問題はサブグラフ自体にありますが、他のケースでは、ネットワークプロバイダーの改善、データベースの競合の減少、その他の構成の改善により、インデックス作成性能を著しく向上させることができます。
インデックス作成中、サブグラフは予期しないデータに遭遇したり、あるコンポーネントが期待通りに動作しなかったり、イベントハンドラや設定に何らかのバグがあったりすると、失敗することがあります。失敗には一般に2つのタイプがあります。
- 決定論的失敗:再試行では解決できない失敗
- 非決定論的失敗:プロバイダの問題や、予期しないグラフノードのエラーに起因する可能性があります。非決定論的失敗が発生すると、グラフノードは失敗したハンドラを再試行し、時間をかけて後退させます。
いくつかのケースでは、失敗はインデクサーによって解決できるかもしれません(例えば、エラーが正しい種類のプロバイダを持っていない結果である場合、必要なプロバイダを追加することでインデックス作成を継続することが可能になります)。しかし、サブグラフのコードを変更する必要がある場合もあります。
決定論的な失敗は「最終」と見なされ、失敗したブロックに対して生成されたインデックス作成の証明が含まれますが、非決定論的な失敗はそうではありません。場合によっては、非決定論的ラベルが正しくなく、サブグラフがエラーを克服することはありません。このような障害は、グラフ ノード リポジトリの問題として報告する必要があります。
グラフノードは、プロバイダからのリフェッチを節約するために、ストア内の特定のデータをキャッシュします。ブロックは、eth_calls
の結果と同様にキャッシュされます(後者は特定のブロックのものとしてキャッシュされます)。このキャッシュは、わずかに変更されたサブグラフの "再同期" 時にインデックス作成速度を劇的に向上させることができます。
ただし、場合によっては、イーサリアム ノードが一定期間誤ったデータを提供した場合、それがキャッシュに入り、誤ったデータやサブグラフの失敗につながる可能性があります。この場合、インデクサーは graphman
を使用して汚染されたキャッシュをクリアし、影響を受けたサブグラフを巻き戻して、(できれば) 正常なプロバイダーから新しいデータをフェッチします。
TX受信欠落イベントなど、ブロックキャッシュの不整合が疑われる場合。
graphman chain list
でチェーン名を検索します。graphman chain check-blocks <CHAIN> by-number <NUMBER>
は、キャッシュされたブロックがプロバイダにマッチするかをチェックし、マッチしない場合はキャッシュからブロックを削除します。- もし違いがあれば、
graphman chain truncate <CHAIN>
でキャッシュ全体を切り捨てた方が安全かもしれません。 - ブロックがプロバイダに一致する場合、問題はプロバイダに対して直接デバッグすることができます。
サブグラフがインデックス化されると、インデクサはサブグラフの専用クエリエントポイントを介してクエリを提供することが期待できます。もしインデクサがかなりの量のクエリを提供することを望むなら、専用のクエリノードを推奨します。また、クエリ量が非常に多い場合、インデクサーはレプリカシャードを構成して、クエリがインデックス作成プロセスに影響を与えないようにしたいと思うかもしれません。
ただし、専用のクエリ ノードとレプリカを使用しても、特定のクエリの実行に時間がかかる場合があり、場合によってはメモリ使用量が増加し、他のユーザーのクエリ時間に悪影響を及ぼします。
「銀の弾丸」は一つではなく、遅いクエリを防止、診断、対処するための様々なツールがあります。
グラフノードはデフォルトで GraphQL クエリをキャッシュし、データベースの負荷を大幅に軽減することができます。これは、GRAPH_QUERY_CACHE_BLOCKS
と GRAPH_QUERY_CACHE_MAX_MEM
設定でさらに設定することができます - 詳しくは を参照してください。
問題のあるクエリが表面化するのは、ほとんどの場合、次の2つの方法のどちらかです。あるケースでは、ユーザー自身があるクエリが遅いと報告します。この場合、一般的な問題なのか、そのサブグラフやクエリに固有の問題なのか、遅さの理由を診断することが課題となります。そしてもちろん、可能であればそれを解決することです。
また、クエリノードでメモリ使用量が多いことが引き金になる場合もあり、その場合は、まず問題の原因となっているクエリを特定することが課題となります。
インデクサは を使ってグラフノードのクエリログを処理したりまとめたりすることができます。GRAPH_LOG_QUERY_TIMING
は、遅いクエリを特定しデバッグするために有効です。
クエリが遅い場合、インデクサーにはいくつかのオプションがあります。もちろん、コスト モデルを変更して、問題のあるクエリを送信するコストを大幅に増やすことができます。これにより、そのクエリの頻度が減少する可能性があります。ただし、これでは問題の根本原因が解決しないことがよくあります。
エンティティを格納するデータベーステーブルには、一般に2つの種類があるようです。エンティティは一度作成されると更新されない「トランザクションライク」なもの、 つまり金融取引のリストのようなものを格納するものと、エンティティが頻繁に更新される「アカウント ライク」なもの、つまり取引が記録されるたびに変更される金融口座のようなものを格納するものである。口座のようなテーブルの特徴は、多くのバージョンの実体を含むが、異なる実体は 比較的少ないということである。このようなテーブルでは、別個のエンティティの数は行(エンティティバージョン)の総数の1%であることがよくある。
アカウントライクテーブルでは、graph-node
は、Postgresがどのように高い変更率でデータを保存することになるかの詳細、つまり、最近のブロックのすべてのバージョンがそのテーブルのストレージ全体の小さなサブセクションにあるということを利用したクエリを生成することができます。
コマンド graphman stats show <sgdNNNN
>展開内の各エンティティ タイプ/テーブルについて、個別のエンティティの数と、各テーブルに含まれるエンティティ バージョンの数を示します。このデータは Postgres 内部の見積もりに基づいているため、必然的に不正確であり、桁違いにずれている可能性があります。 entities
列の -1
は、すべての行に別個のエンティティが含まれていると Postgres が認識していることを意味します。
一般に、別個の実体の数が行/実体のバージョンの総数の1%未満であるテーブルは、アカウントのような最適化の良い候補になります。graphman stats show
の出力が、テーブルがこの最適化によって恩恵を受けるかもしれないことを示す場合、graphman stats show <sgdNNN> <table>
はテーブルをフルカウントし、遅いかもしれませんが、全体のエンティティバージョンに対するdistinct entitiesの比率を正確に測定することができます。
一旦テーブルがアカウントライクであると決定されると、graphman stats account-like <sgdNNN>.<table>
を実行すると、そのテーブルに対するクエリのためにアカウントライクの最適化がオンになります。最適化は、graphman stats account-like --clear <sgdNNN>.<table>
で再びオフにすることができます。最適化がオンまたはオフになったことにクエリノードが気付くまで、最大で5分間かかります。最適化をオンにした後、その変更によって実際にそのテーブルのクエリが遅くならないことを確認する必要があります。Postgres を監視するように Grafana を構成している場合、遅いクエリは pg_stat_activity
に大量に表示され、数秒かかることになるはずです。その場合、最適化を再度オフにする必要があります。
Uniswap のようなサブグラフの場合、pair
および token
テーブルがこの最適化の最有力候補であり、データベースの負荷に劇的な影響を与える可能性があります。
これは新しい機能で、Graph Node 0.29.xで利用可能になる予定です。
ある時点で、インデクサーは与えられたサブグラフを削除したいと思うかもしれません。これは、graphman drop
によって簡単に行えます。これは、配置とそのインデックスされたデータを全て削除します。配置はサブグラフ名、IPFS ハッシュ Qm..
またはデータベース名前空間 sgdNNN
として指定することができます。詳しいドキュメントは、にあります。