15 минуты
Graph Node
Graph Node is the component which indexes Subgraphs, and makes the resulting data available to query via a GraphQL API. As such it is central to the indexer stack, and correct operation of Graph Node is crucial to running a successful indexer.
Здесь представлен контекстуальный обзор Graph Node и некоторые более продвинутые параметры, доступные индексаторам. Подробную документацию и инструкции можно найти в репозитории Graph Node.
Graph Node
Graph Node is the reference implementation for indexing Subgraphs on The Graph Network, connecting to blockchain clients, indexing Subgraphs and making indexed data available to query.
Graph Node (и весь стек Индексаторов) можно запускать на «голом железе» или в облачной среде. Эта гибкость центрального компонента индексирования имеет решающее значение для надежности The Graph Protocol. Точно так же Graph Node может быть создана из исходного кода, или Индексаторы могут использовать один из предусмотренных Docker Images.
База данных PostgreSQL
The main store for the Graph Node, this is where Subgraph data is stored, as well as metadata about Subgraphs, and Subgraph-agnostic network data such as the block cache, and eth_call cache.
Клиенты сети
Для индексации сети Graph Node требуется доступ к сетевому клиенту через EVM-совместимый JSON-RPC API. Этот RPC может подключаться к одному клиенту или может представлять собой более сложную настройку, которая распределяет нагрузку между несколькими.
While some Subgraphs may just require a full node, some may have indexing features which require additional RPC functionality. Specifically Subgraphs which make eth_calls
as part of indexing will require an archive node which supports EIP-1898, and Subgraphs with callHandlers
, or blockHandlers
with a call
filter, require trace_filter
support (see trace module documentation here).
Network Firehoses. Firehose — это служба gRPC, предоставляющая упорядоченный, но учитывающий форк поток блоков, разработанная разработчиками ядра The Graph для лучшей поддержки крупномасштабного высокопроизводительного индексирования. В настоящее время это не является обязательным требованием для Индексаторов, но Индексаторам рекомендуется ознакомиться с технологией до начала полной поддержки сети. Подробнее о Firehose можно узнать [здесь(https://firehose.streamingfast.io/).
Ноды IPFS
Subgraph deployment metadata is stored on the IPFS network. The Graph Node primarily accesses the IPFS node during Subgraph deployment to fetch the Subgraph manifest and all linked files. Network indexers do not need to host their own IPFS node. An IPFS node for the network is hosted at https://ipfs.thegraph.com.
Сервер метрик Prometheus
Чтобы включить мониторинг и отчетность, Graph Node может дополнительно регистрировать метрики на сервере метрик Prometheus.
Начало работы с исходным кодом
Установка необходимых компонентов
-
Rust
-
PostgreSQL
-
IPFS
-
Дополнительные требования для пользователей Ubuntu. Для запуска Graph Node на Ubuntu может потребоваться несколько дополнительных пакетов.
1sudo apt-get install -y clang libpq-dev libssl-dev pkg-config
Настройка
- Запустите сервер базы данных PostgreSQL
1initdb -D .postgres2pg_ctl -D .postgres -l logfile start3createdb graph-node
-
Клонируйте репозиторий Graph Node и соберите исходный код, запустив
cargo build
-
Теперь, когда все зависимости настроены, запустите Graph Node:
1cargo run -p graph-node --release -- \2 --postgres-url postgresql://[USERNAME]:[PASSWORD]@localhost:5432/graph-node \3 --ethereum-rpc [NETWORK_NAME]:[URL] \4 --ipfs https://ipfs.thegraph.com
Начало работы с Kubernetes
Полный пример конфигурации Kubernetes можно найти в репозитории индексатора.
Порты
Во время работы Graph Node предоставляет следующие порты:
Порт | Назначение | Маршруты | Аргумент CLI | Переменная среды |
---|---|---|---|---|
8000 | GraphQL HTTP-сервер (для запросов к Субграфу) | /subgraphs/id/… /subgraphs/name/…/… | —http-port | - |
8001 | GraphQL WS (для подписок на Субграф) | /subgraphs/id/… /subgraphs/name/…/… | —ws-port | - |
8020 | JSON-RPC (для управления развертываниями) | / | —admin-port | - |
8030 | API статуса индексирования Субграфа | /graphql | —index-node-port | - |
8040 | Метрики Prometheus | /metrics | —metrics-port | - |
Важно. Будьте осторожны, открывая порты для общего доступа — порты администрирования должны оставаться закрытыми. Это касается конечных точек Graph Node JSON-RPC.
Расширенная настройка Graph Node
At its simplest, Graph Node can be operated with a single instance of Graph Node, a single PostgreSQL database, an IPFS node, and the network clients as required by the Subgraphs to be indexed.
Эту настройку можно масштабировать горизонтально, добавляя несколько Graph Node и несколько баз данных для поддержки этих Graph Node. Опытные пользователи могут воспользоваться некоторыми возможностями горизонтального масштабирования Graph Node, а также некоторыми более продвинутыми параметрами конфигурации через файл config.toml
l и переменные среды Graph Node.
config.toml
Файл конфигурации TOML можно использовать для установки более сложных конфигураций, чем те, которые представлены в интерфейсе командной строки. Местоположение файла передается с помощью параметра командной строки —config.
При использовании файла конфигурации невозможно использовать параметры —postgres-url, —postgres-secondary-hosts и —postgres-host-weights.
Можно предоставить минимальный файл config.toml
, следующий файл эквивалентен использованию опции командной строки —postgres-url:
1[store]2[store.primary]3connection="<.. postgres-url argument ..>"4[deployment]5[[deployment.rule]]6indexers = [ "<.. list of all indexing nodes ..>" ]
Полную документацию по config.toml
можно найти в документации Graph Node.
Множественные Graph Node
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 dedicated query nodes, block ingestors, and splitting Subgraphs across nodes with deployment rules.
Обратите внимание, что несколько Graph Nodes могут быть настроены для использования одной и той же базы данных, которая сама по себе может масштабироваться по горизонтали с помощью сегментирования.
Правила развертывания
Given multiple Graph Nodes, it is necessary to manage deployment of new Subgraphs so that the same Subgraph isn’t being indexed by two different nodes, which would lead to collisions. This can be done by using deployment rules, which can also specify which shard
a Subgraph’s data should be stored in, if database sharding is being used. Deployment rules can match on the Subgraph name and the network that the deployment is indexing in order to make a decision.
Пример настройки правил развертывания:
1[deployment]2[[deployment.rule]]3match = { name = "(vip|important)/.*" }4shard = "vip"5indexers = [ "index_node_vip_0", "index_node_vip_1" ]6[[deployment.rule]]7match = { network = "kovan" }8# No shard, so we use the default shard called 'primary'9indexers = [ "index_node_kovan_0" ]10[[deployment.rule]]11match = { network = [ "xdai", "poa-core" ] }12indexers = [ "index_node_other_0" ]13[[deployment.rule]]14# There's no 'match', so any Subgraph matches15shards = [ "sharda", "shardb" ]16indexers = [17 "index_node_community_0",18 "index_node_community_1",19 "index_node_community_2",20 "index_node_community_3",21 "index_node_community_4",22 "index_node_community_5"23 ]
Подробную информацию о правилах развертывания можно найти здесь.
Выделенные ноды запросов
Ноды можно настроить так, чтобы они были нодами запросов, включив в файл конфигурации следующее:
1[general]2query = "<regular expression>"
Любая нода, чей —node-id соответствует регулярному выражению, будет настроена так, чтобы отвечать только на запросы.
Масштабирование базы данных с помощью шардинга (сегментирования)
В большинстве случаев одной базы данных Postgres достаточно для поддержки отдельной Graph Node. Когда отдельная Graph Node перерастает одну базу данных Postgres, можно разделить хранилище данных Graph Node между несколькими базами данных Postgres. Все базы данных вместе образуют хранилище отдельной Graph Node. Каждая отдельная база данных называется шардом (сегментом).
Shards can be used to split Subgraph deployments across multiple databases, and can also be used to use replicas to spread query load across databases. This includes configuring the number of available database connections each graph-node
should keep in its connection pool for each database, which becomes increasingly important as more Subgraphs are being indexed.
Сегментирование становится полезным, когда Ваша существующая база данных не может справиться с нагрузкой, которую на нее возлагает Graph Node, и когда больше невозможно увеличить размер базы данных.
It is generally better make a single database as big as possible, before starting with shards. One exception is where query traffic is split very unevenly between Subgraphs; in those situations it can help dramatically if the high-volume Subgraphs are kept in one shard and everything else in another because that setup makes it more likely that the data for the high-volume Subgraphs stays in the db-internal cache and doesn’t get replaced by data that’s not needed as much from low-volume Subgraphs.
Что касается настройки соединений, начните с max_connections в postgresql.conf, установленного на 400 (или, может быть, даже на 200), и посмотрите на метрики store_connection_wait_time_ms и store_connection_checkout_count Prometheus. Длительное время ожидания (все, что превышает 5 мс) является признаком того, что доступных соединений слишком мало; большое время ожидания также будет вызвано тем, что база данных очень загружена (например, высокая загрузка ЦП). Однако, если в остальном база данных кажется стабильной, большое время ожидания указывает на необходимость увеличения количества подключений. В конфигурации количество подключений, которое может использовать каждая отдельная Graph Node, является верхним пределом, и Graph Node не будет держать соединения открытыми, если они ей не нужны.
Подробную информацию о настройке хранилища можно найти здесь.
Прием выделенного блока
Если настроено несколько нод, необходимо выделить одну, которая будет отвечать за прием новых блоков, чтобы все сконфигурированные ноды индекса не опрашивали заголовок чейна. Это настраивается в рамках пространства имен chains
, в котором node_id
, используемый для приема блоков:
1[chains]2ingestor = "block_ingestor_node"
Поддержка нескольких сетей
The Graph Protocol is increasing the number of networks supported for indexing rewards, and there exist many Subgraphs indexing unsupported networks which an indexer would like to process. The config.toml
file allows for expressive and flexible configuration of:
- Несколько сетей
- Несколько провайдеров на сеть (это может позволить разделить нагрузку между провайдерами, а также может позволить настроить полные ноды, а также архивные ноды, при этом Graph Node предпочитает более дешевых поставщиков, если позволяет данная рабочая нагрузка).
- Дополнительные сведения о провайдере, такие как функции, аутентификация и тип провайдера (для экспериментальной поддержки Firehose)
Раздел [chains]
управляет провайдерами Ethereum, к которым подключается graph-node, и где хранятся блоки и другие метаданные для каждого чейна. В следующем примере настраиваются два чейна, mainnet и kovan, где блоки для mainnet хранятся в сегменте vip, а блоки для kovan — в основном сегменте. Чейн mainnet может использовать двух разных провайдеров, тогда как у kovan есть только один провайдер.
1[chains]2ingestor = "block_ingestor_node"3[chains.mainnet]4shard = "vip"5provider = [6 { label = "mainnet1", url = "http://..", features = [], headers = { Authorization = "Bearer foo" } },7 { label = "mainnet2", url = "http://..", features = [ "archive", "traces" ] }8]9[chains.kovan]10shard = "primary"11provider = [ { label = "kovan", url = "http://..", features = [] } ]
Подробную информацию о настройке провайдера можно найти здесь.
Переменные среды
Graph Node поддерживает ряд переменных среды, которые могут включать функции или изменять поведение Graph Node. Они описаны [здесь] (https://github.com/graphprotocol/graph-node/blob/master/docs/environment-variables.md).
Непрерывное развертывание
Пользователи, использующие масштабируемую настройку индексирования с расширенной конфигурацией, могут получить преимущество от управления своими узлами Graph с помощью Kubernetes.
- В репозитории индексатора имеется пример ссылки на Kubernetes
- Launchpad – это набор инструментов для запуска Индексатора Graph Protocol в Kubernetes, поддерживаемый GraphOps. Он предоставляет набор диаграмм Helm и интерфейс командной строки для управления развертыванием Graph Node.
Управление Graph Node
Given a running Graph Node (or Graph Nodes!), the challenge is then to manage deployed Subgraphs across those nodes. Graph Node surfaces a range of tools to help with managing Subgraphs.
Логирование (ведение журналов)
Graph Node’s logs can provide useful information for debugging and optimisation of Graph Node and specific Subgraphs. Graph Node supports different log levels via the GRAPH_LOG
environment variable, with the following levels: error, warn, info, debug or trace.
Кроме того, установка для GRAPH_LOG_QUERY_TIMINGзначения
gql` предоставляет дополнительные сведения о том, как выполняются запросы GraphQL (хотя это приводит к созданию большого объема логов).
Мониторинг и оповещения
Graph Node предоставляет метрики через конечную точку Prometheus на порту 8040 по умолчанию. Затем можно использовать Grafana для визуализации этих метрик.
В репозитории индексатора имеется [пример конфигурации Grafana] (https://github.com/graphprotocol/indexer/blob/main/k8s/base/grafana.yaml).
Graphman
graphman
– это инструмент обслуживания Graph Node, помогающий диагностировать и решать различные повседневные и исключительные задачи.
Команда graphman включена в официальные контейнеры, и Вы можете выполнить docker exec в контейнере graph-node, чтобы запустить ее. Для этого требуется файл config.toml
.
Полная документация по командам graphman
доступна в репозитории Graph Node. См. [/docs/graphman.md] (https://github.com/graphprotocol/graph-node/blob/master/docs/graphman.md) в Graph Node /docs
Working with Subgraphs
API статуса индексирования
Available on port 8030/graphql by default, the indexing status API exposes a range of methods for checking indexing status for different Subgraphs, checking proofs of indexing, inspecting Subgraph features and more.
Полная схема доступна здесь.
Производительность индексирования
Процесс индексирования состоит из трех отдельных частей:
- Получение интересующих событий от провайдера
- Обработка событий по порядку с помощью соответствующих обработчиков (это может включать вызов чейна для состояния и выборку данных из хранилища)
- Запись полученных данных в хранилище
These stages are pipelined (i.e. they can be executed in parallel), but they are dependent on one another. Where Subgraphs are slow to index, the underlying cause will depend on the specific Subgraph.
Распространенные причины низкой скорости индексации:
- Время, затрачиваемое на поиск соответствующих событий в чейне (в частности, обработчики вызовов могут работать медленно, учитывая зависимость от
trace_filter
) - Создание большого количества
eth_calls
в составе обработчиков - Большое количество операций с хранилищем во время выполнения
- Большой объем данных для сохранения в хранилище
- Большое количество событий для обработки
- Медленное время подключения к базе данных для переполненных нод
- Сам провайдер отстает от головного чейна
- Задержка получения новых поступлений от провайдера в головном чейне
Subgraph indexing metrics can help diagnose the root cause of indexing slowness. In some cases, the problem lies with the Subgraph itself, but in others, improved network providers, reduced database contention and other configuration improvements can markedly improve indexing performance.
Failed Subgraphs
During indexing Subgraphs might fail, if they encounter data that is unexpected, some component not working as expected, or if there is some bug in the event handlers or configuration. There are two general types of failure:
- Детерминированные сбои: это сбои, которые не будут устранены при повторных попытках
- Недетерминированные сбои: они могут быть связаны с проблемами с провайдером или какой-либо неожиданной ошибкой Graph Node. Когда происходит недетерминированный сбой, Graph Node повторяет попытки обработчиков сбоя, со временем отказываясь от них.
In some cases a failure might be resolvable by the indexer (for example if the error is a result of not having the right kind of provider, adding the required provider will allow indexing to continue). However in others, a change in the Subgraph code is required.
Deterministic failures are considered “final”, with a Proof of Indexing generated for the failing block, while non-deterministic failures are not, as the Subgraph may manage to “unfail” and continue indexing. In some cases, the non-deterministic label is incorrect, and the Subgraph will never overcome the error; such failures should be reported as issues on the Graph Node repository.
Кэш блокировки и вызова
Graph Node caches certain data in the store in order to save refetching from the provider. Blocks are cached, as are the results of eth_calls
(the latter being cached as of a specific block). This caching can dramatically increase indexing speed during “resyncing” of a slightly altered Subgraph.
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.
Если есть подозрение на несогласованность кэша блоков, например, событие отсутствия квитанции tx:
graphman chain list
, чтобы найти название чейна.graphman chain check-blocks <CHAIN> by-number <NUMBER>
проверит, соответствует ли кэшированный блок провайдеру, и удалит блок из кэша, если это не так.- Если есть разница, может быть безопаснее усечь весь кеш с помощью
graphman chain truncate <CHAIN>
. - Если блок соответствует провайдеру, то проблема может быть отлажена непосредственно провайдером.
- Если есть разница, может быть безопаснее усечь весь кеш с помощью
Запрос проблем и ошибок
Once a Subgraph has been indexed, indexers can expect to serve queries via the Subgraph’s dedicated query endpoint. If the indexer is hoping to serve significant query volume, a dedicated query node is recommended, and in case of very high query volumes, indexers may want to configure replica shards so that queries don’t impact the indexing process.
Однако, даже с выделенной нодой запросов и копиями выполнение некоторых запросов может занять много времени, а в некоторых случаях увеличить использование памяти и негативно повлиять на время выполнения запросов другими пользователями.
Существует не одна «серебряная пуля» (панацея), а целый ряд инструментов для предотвращения, диагностики и обработки медленных запросов.
Кэширование запросов
Graph Node по умолчанию кэширует запросы GraphQL, что может значительно снизить нагрузку на базу данных. Это можно дополнительно настроить с помощью параметров GRAPH_QUERY_CACHE_BLOCKS
и GRAPH_QUERY_CACHE_MAX_MEM
— подробнее читайте [здесь](https://github.com/graphprotocol/graph-node/blob/master. /docs/environment-variables.md#graphql-caching).
Анализ запросов
Problematic queries most often surface in one of two ways. In some cases, users themselves report that a given query is slow. In that case the challenge is to diagnose the reason for the slowness - whether it is a general issue, or specific to that Subgraph or query. And then of course to resolve it, if possible.
В других случаях триггером может быть высокий уровень использования памяти на ноде запроса, и в этом случае сначала нужно определить запрос, вызвавший проблему.
Индексаторы могут использовать qlog для обработки и суммирования логов запросов Graph Node. Также можно включить GRAPH_LOG_QUERY_TIMING
для выявления и отладки медленных запросов.
При медленном запросе у индексаторов есть несколько вариантов. Разумеется, они могут изменить свою модель затрат, чтобы значительно увеличить стоимость отправки проблемного запроса. Это может привести к снижению частоты этого запроса. Однако это часто не устраняет основной причины проблемы.
Аккаунт-подобная оптимизация
Таблицы базы данных, в которых хранятся объекты, как правило, бывают двух видов: «подобные транзакциям», когда объекты, однажды созданные, никогда не обновляются, т. е. они хранят что-то вроде списка финансовых транзакций и «подобные учетной записи», где объекты обновляются очень часто, т. е. они хранят что-то вроде финансовых счетов, которые изменяются каждый раз при записи транзакции. Таблицы, подобные учетным записям, характеризуются тем, что они содержат большое количество версий объектов, но относительно мало отдельных объектов. Часто в таких таблицах количество отдельных объектов составляет 1% от общего количества строк (версий объектов)
Для таблиц, подобных учетным записям, graph-node
может генерировать запросы, в которых используются детали того, как Postgres в конечном итоге сохраняет данные с такой высокой скоростью изменения, а именно, что все версии последних блоков находятся в небольшом подразделе общего хранилища для такой таблицы.
Команда graphman stats show <sgdNNNN>
показывает для каждого типа/таблицы объектов в развертывании, сколько различных объектов и сколько версий объектов содержит каждая таблица. Эти данные основаны на внутренних оценках Postgres и, следовательно, неточны и могут отличаться на порядок. -1
в столбце entities
означает, что Postgres считает, что все строки содержат отдельный объект.
В общем, таблицы, в которых количество отдельных объектов составляет менее 1 % от общего количества версий строк/объектов, являются хорошими кандидатами на оптимизацию по аналогии с учетными записями. Если выходные данные graphman stats show
указывают на то, что эта оптимизация может принести пользу таблице, запуск graphman stats show <sgdNNN> <table>
произведёт полный расчет таблицы. Этот процесс может быть медленным, но обеспечит точную степень соотношения отдельных объектов к общему количеству версий объекта.
Как только таблица будет определена как учетная запись, запуск graphman stats account-like <sgdNNN>.<table>
, включит оптимизацию, подобную учетной записи, для запросов к этой таблице. Оптимизацию можно снова отключить с помощью graphman stats account-like --clear <sgdNNN>.<table>
. Нодам запроса требуется до 5 минут, чтобы заметить, что оптимизация включена или выключена. После включения оптимизации необходимо убедиться, что изменение фактически не приводит к замедлению запросов к этой таблице. Если Вы настроили Grafana для мониторинга Postgres, медленные запросы будут отображаться в pg_stat_activity
в больших количествах, это займет несколько секунд. В этом случае оптимизацию необходимо снова отключить.
For Uniswap-like Subgraphs, the pair
and token
tables are prime candidates for this optimization, and can have a dramatic effect on database load.
Removing Subgraphs
Это новый функционал, который будет доступен в Graph Node 0.29.x
At some point an indexer might want to remove a given Subgraph. This can be easily done via graphman drop
, which deletes a deployment and all it’s indexed data. The deployment can be specified as either a Subgraph name, an IPFS hash Qm..
, or the database namespace sgdNNN
. Further documentation is available here.