Эксплуатация Graph Node

Reading time: 15 min

Graph Node — это компонент, который индексирует подграфы и делает полученные данные доступными для запроса через GraphQL API. Таким образом, он занимает центральное место в стеке индексатора, а правильная работа Graph Node имеет решающее значение для успешного запуска индексатора.

Здесь представлен контекстуальный обзор Graph Node и некоторые более продвинутые параметры, доступные индексаторам. Подробную документацию и инструкции можно найти в репозитории Graph Node.

Graph Node

Ссылка на этот раздел

Graph Node — это эталонная реализация для индексации подграфов в The Graph Network, подключения к клиентам блокчейна, индексирования подграфов и предоставления индексированных данных для запроса.

Graph Node (и весь стек индексаторов) можно запускать на «голом железе» или в облачной среде. Эта гибкость центрального компонента индексации имеет решающее значение для надежности The Graph Protocol. Точно так же Graph Node может быть создана из исходного кода, или индексаторы могут использовать один из , предусмотренных Docker Images.

База данных PostgreSQL

Ссылка на этот раздел

Основное хранилище для Graph Node, это место, где хранятся данные подграфа, а также метаданные о подграфах и сетевые данные, не зависящие от подграфа, такие как кэш блоков и кэш eth_call.

Для индексации сети Graph Node требуется доступ к сетевому клиенту через EVM-совместимый JSON-RPC API. Этот RPC может подключаться к одному клиенту или может представлять собой более сложную настройку, которая распределяет нагрузку между несколькими.

В то время как для некоторых субграфов может потребоваться полная нода, другие могут иметь функции индексации, для которых требуются дополнительные функции RPC. В частности, для субграфов, которые выполняют eth_calls как часть индексации, потребуется нода архива, поддерживающая EIP-1898, а субграфы с callHandlers или blockHandlers с фильтром call требуют поддержки trace_filter (см. документацию по модулю трассировки здесь).

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 here.

Метаданные о развертывании подграфа хранятся в сети IPFS. The Graph Node в первую очередь обращается к ноде IPFS во время развертывания подграфа, чтобы получить манифест подграфа и все связанные файлы. Сетевым индексаторам не требуется запускать собственную ноду IPFS. Нода IPFS для сети находиться по адресу https://ipfs.network.thegraph.com.

Сервер метрик Prometheus

Ссылка на этот раздел

Чтобы включить мониторинг и отчетность, Graph Node может дополнительно регистрировать метрики на сервере метрик Prometheus.

Начало работы с исходным кодом

Ссылка на этот раздел

Установка предварительного обеспечения

Ссылка на этот раздел
  • Rust

  • PostgreSQL

  • IPFS

  • Дополнительные требования для пользователей Ubuntu. Для запуска Graph Node в Ubuntu может потребоваться несколько дополнительных пакетов.

sudo apt-get install -y clang libpq-dev libssl-dev pkg-config
  1. Запустите сервер базы данных PostgreSQL
initdb -D .postgres
pg_ctl -D .postgres -l logfile start
createdb graph-node
  1. Клонируйте репозиторий Graph Node и соберите исходный код, запустив cargo build

  2. Теперь, когда все зависимости настроены, запустите 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

Ссылка на этот раздел

Полный пример конфигурации Kubernetes можно найти в репозитории индексатора.

Во время работы Graph Node предоставляет следующие порты:

ПортНазначениеРасположениеCLI-аргументПеременная среды
8000HTTP-сервер GraphQL
(для запросов подграфов)
/subgraphs/id/...
/subgraphs/name/.../...
--http-port-
8001GraphQL WS
(для подписок на подграфы)
/subgraphs/id/...
/subgraphs/name/.../...
--ws-port-
8020JSON-RPC
(для управления процессом развертывания)
/--admin-port-
8030API для определения статуса индексирования подграфов/graphql--index-node-port-
8040Показатели Prometheus/metrics--metrics-port-

Важно. Будьте осторожны, открывая порты для общего доступа — порты администрирования должны быть заблокированы. Это касается конечных точек Graph Node JSON-RPC.

Расширенная настройка Graph Node

Ссылка на этот раздел

На простейшем уровне Graph Node может работать с одним экземпляром Graph Node, одной базой данных PostgreSQL, нодой IPFS и сетевыми клиентами в соответствии с требованиями субграфов, подлежащих индексированию.

Эту настройку можно масштабировать горизонтально, добавляя несколько Graph Node и несколько баз данных для поддержки этих Graph Node. Опытные пользователи могут воспользоваться некоторыми возможностями горизонтального масштабирования Graph Node, а также некоторыми более продвинутыми параметрами конфигурации через файл config.toml и переменные среды Graph Node.

Файл конфигурации TOML можно использовать для установки более сложных конфигураций, чем те, которые представлены в интерфейсе командной строки. Местоположение файла передается с помощью параметра командной строки --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.

Множественные 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 могут быть настроены для использования одной и той же базы данных, которая сама по себе может масштабироваться по горизонтали с помощью сегментирования.

Правила развертывания

Ссылка на этот раздел

При наличии нескольких Graph нод необходимо управлять развертыванием новых подграфов таким образом, чтобы один и тот же подграф не индексировался двумя разными нодами, что могло бы привести к конфликтам. Это можно сделать с помощью правил развертывания, которые также могут указывать, в каком 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 matches
shards = [ "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 соответствует регулярному выражению, будет настроена так, чтобы отвечать только на запросы.

Масштабирование базы данных с помощью шардинга (сегментирования)

Ссылка на этот раздел

В большинстве случаев одной базы данных Postgres достаточно для поддержки отдельной Graph Node. Когда отдельная Graph Node перерастает одну базу данных Postgres, можно разделить хранилище данных Graph Node между несколькими базами данных Postgres. Все базы данных вместе образуют хранилище отдельной Graph Node. Каждая отдельная база данных называется шардом (сегментом).

Сегменты можно использовать для разделения развертываний подграфов между несколькими базами данных, а также для использования копий с целью распределения нагрузки запросов между базами данных. Это включает в себя настройку количества доступных подключений к базе данных, которые каждый graph-node должен хранить в своем пуле подключений для каждой базы данных. Это становится все более важным по мере индексации большего количества подграфов.

Сегментирование становится полезным, когда Ваша существующая база данных не может справиться с нагрузкой, которую на нее возлагает Graph Node, и когда больше невозможно увеличить размер базы данных.

Обычно лучше сделать одну базу данных максимально большой, прежде чем начинать с шардов (сегментов). Единственным исключением является случай, когда трафик запросов распределяется между подграфами очень неравномерно; в таких ситуациях может существенно помочь, если подграфы большого объема хранятся в одном сегменте, а все остальное — в другом, потому что такая настройка повышает вероятность того, что данные для подграфов большого объема останутся во внутреннем кеше базы данных и не будут заменяться данными, которые не очень нужны, из подграфов с небольшим объемом.

Что касается настройки соединений, начните с max_connections в postgresql.conf, установленного на 400 (или, может быть, даже на 200), и посмотрите на метрики store_connection_wait_time_ms и store_connection_checkout_count Prometheus. Длительное время ожидания (все, что превышает 5 мс) является признаком того, что доступных соединений слишком мало; большое время ожидания также будет вызвано тем, что база данных очень загружена (например, высокая загрузка ЦП). Однако, если в остальном база данных кажется стабильной, большое время ожидания указывает на необходимость увеличения количества подключений. В конфигурации количество подключений, которое может использовать каждая отдельная Graph Node, является верхним пределом, и Graph Node не будет держать соединения открытыми, если они ей не нужны.

Подробнее о настройке хранилища читайте здесь.

Прием выделенного блока

Ссылка на этот раздел

Если настроено несколько нод, необходимо выделить одну, которая будет отвечать за прием новых блоков, чтобы все сконфигурированные ноды индекса не опрашивали головную часть чейна. Это делается как часть пространства имен chains, в котором node_id, будет использоваться для приема блоков:

[chains]
ingestor = "block_ingestor_node"

Поддержка нескольких сетей

Ссылка на этот раздел

Протокол The Graph увеличивает количество сетей, поддерживаемых для индексации вознаграждений, и существует множество подграфов, индексирующих неподдерживаемые сети, которые индексатор хотел бы обработать. Файл config.toml обеспечивает ярко выраженную и гибкую настройку:

  • Несколько сетей
  • Несколько провайдеров на сеть (это может позволить разделить нагрузку между провайдерами, а также может позволить настроить полные ноды, а также архивные ноды, при этом Graph Node предпочитает более дешевых поставщиков, если позволяет данная рабочая нагрузка).
  • Дополнительные сведения о провайдере, такие как функции, аутентификация и тип провайдера (для экспериментальной поддержки Firehose)

Раздел [chains] управляет провайдерами Ethereum, к которым подключается graph-node, и где хранятся блоки и другие метаданные для каждого чейна. В следующем примере настраиваются два чейна, mainnet и kovan, где блоки для mainnet хранятся в сегменте vip, а блоки для kovan — в основном сегменте. Чейн основной сети может использовать двух разных провайдеров, тогда как у kovan есть только один провайдер.

[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 = [] } ]

Подробнее о настройке провайдера читайте здесь.

Переменные среды

Ссылка на этот раздел

Graph Node поддерживает ряд переменных среды, которые могут включать функции или изменять поведение Graph Node. Они описаны здесь.

Непрерывное развертывание

Ссылка на этот раздел

Пользователи, использующие масштабируемую настройку индексирования с расширенной конфигурацией, могут получить преимущество от управления своими узлами Graph с помощью Kubernetes.

  • В репозитории индексатора есть пример справочника по Kubernetes
  • Launchpad – это набор инструментов для запуска Graph Protocol индексатора в Kubernetes, поддерживаемый GraphOps. Он предоставляет набор диаграмм Helm и интерфейс командной строки для управления развертыванием Graph Node.

Управление Graph Node

Ссылка на этот раздел

При наличии работающей Graph Node (или Graph Nodes!), задача состоит в том, чтобы управлять развернутыми подграфами на этих нодах. Graph Node предлагает ряд инструментов для управления подграфами.

Логирование (ведение журналов)

Ссылка на этот раздел

Логи Graph Node могут предоставить полезную информацию для отладки и оптимизации Graph Node и конкретных подграфов. Graph Node поддерживает различные уровни логов с помощью переменной среды GRAPH_LOG со следующими уровнями: ошибка, предупреждение, информация, отладка или трассировка.

Кроме того, установка для GRAPH_LOG_QUERY_TIMING значения gql предоставляет дополнительные сведения о том, как выполняются запросы GraphQL (хотя это приводит к созданию большого объема логов).

Мониторинг и оповещения

Ссылка на этот раздел

Graph Node предоставляет метрики через конечную точку Prometheus на порту 8040 по умолчанию. Затем можно использовать Grafana для визуализации этих метрик.

В репозитории индексатора представлен пример конфигурации Grafana.

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

Работа с подграфами

Ссылка на этот раздел

API статуса индексирования

Ссылка на этот раздел

Доступный по умолчанию на порту 8030/graphql, API статуса индексирования предоставляет ряд методов для проверки статуса индексирования для различных подграфов, проверки доказательств индексирования, проверки функций подграфов и многого другого.

Полная схема доступна здесь.

Производительность индексирования

Ссылка на этот раздел

Процесс индексирования состоит из трех отдельных частей:

  • Получение интересующих событий от провайдера
  • Обработка событий по порядку с помощью соответствующих обработчиков (это может включать вызов чейна для состояния и выборку данных из хранилища)
  • Запись полученных данных в хранилище

Эти этапы конвейерные (т.е. могут выполняться параллельно), но они зависят друг от друга. Там, где подграфы индексируются медленно, основная причина будет зависеть от конкретного подграфа.

Распространенные причины низкой скорости индексации:

  • Время, затрачиваемое на поиск соответствующих событий в чейне (в частности, обработчики вызовов могут работать медленно, учитывая зависимость от trace_filter)
  • Создание большого количества eth_call в составе обработчиков
  • Большое количество операций с хранилищем во время выполнения
  • Большой объем данных для сохранения в хранилище
  • Большое количество событий для обработки
  • Медленное время подключения к базе данных для переполненных нод
  • Сам провайдер отстает от головного чейна
  • Задержка получения новых поступлений от провайдера в головном чейне

Метрики индексации подграфов могут помочь диагностировать основную причину замедления индексации. В некоторых случаях проблема связана с самим подграфом, но в других случаях усовершенствованные сетевые провайдеры, снижение конкуренции за базу данных и другие улучшения конфигурации могут заметно повысить производительность индексирования.

Повреждённые подграфы

Ссылка на этот раздел

Во время индексации подграфов может произойти сбой, если они столкнутся с неожиданными данными, какой-то компонент не будет работать должным образом или если в обработчиках событий или конфигурации появится ошибка. Есть два основных типа отказа:

  • Детерминированные сбои: это сбои, которые не будут устранены при повторных попытках
  • Недетерминированные сбои: они могут быть связаны с проблемами с провайдером или какой-либо неожиданной ошибкой Graph Node. Когда происходит недетерминированный сбой, Graph Node повторяет попытки обработчиков сбоя, со временем отказываясь от них.

В некоторых случаях сбой может быть устранен индексатором (например, если ошибка вызвана отсутствием нужного поставщика, добавление необходимого поставщика позволит продолжить индексирование). Однако в других случаях требуется изменить код подграфа.

Детерминированные сбои считаются «окончательными», при этом для отказавшего блока генерируется Доказательство индексации, а недетерминированные сбои — нет, поскольку подграфу может удаться «отменить сбой» и продолжить индексирование. В некоторых случаях недетерминированная метка неверна, и подграф никогда не преодолеет ошибку; о таких сбоях следует сообщать как о проблемах в репозитории Graph Node.

Кэш блокировки и вызова

Ссылка на этот раздел

Graph Node кэширует определенные данные в хранилище, чтобы избежать повторной загрузки от провайдера. Блоки кэшируются, как и результаты eth_calls (последние кэшируются для определенного блока). Такое кэширование может резко увеличить скорость индексации при «повторной синхронизации» слегка измененного подграфа.

Однако, в некоторых случаях, если нода Ethereum предоставила неверные данные за какой-то период, они могут попасть в кеш, что приведет к некорректным данным или повреждённым субграфам. В этом случае индексаторы могут использовать graphman для очистки испорченного кеша, а затем перематывать затронутые субграфы, которые затем будут получать свежие данные от (мы надеемся на это) исправного поставщика.

Если есть подозрение на несогласованность кэша блоков, например, событие отсутствия квитанции tx:

  1. graphman chain list, чтобы найти название чейна.
  2. graphman chain check-blocks <CHAIN> by-number <NUMBER> проверит, соответствует ли кэшированный блок провайдеру, и удалит блок из кэша, если это не так.
    1. Если есть разница, может быть безопаснее усечь весь кеш с помощью graphman chain truncate <CHAIN>.
    2. Если блок соответствует провайдеру, то проблема может быть отлажена непосредственно провайдером.

Запрос проблем и ошибок

Ссылка на этот раздел

После индексации подграфа индексаторы могут рассчитывать на обслуживание запросов через выделенную конечную точку запроса подграфа. Если индексатор планирует обслуживать значительный объем запросов, рекомендуется выделенная нода запросов, а в случае очень больших объемов запросов индексаторы могут настроить сегменты копий так, чтобы запросы не влияли на процесс индексирования.

Однако, даже с выделенной нодой запросов и копиями выполнение некоторых запросов может занять много времени, а в некоторых случаях увеличить использование памяти и негативно повлиять на время выполнения запросов другими пользователями.

Существует не одна «серебряная пуля» (панацея), а целый ряд инструментов для предотвращения, диагностики и обработки медленных запросов.

Кэширование запросов
Ссылка на этот раздел

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).

Анализ запросов
Ссылка на этот раздел

Проблемные запросы чаще всего выявляются одним из двух способов. В некоторых случаях пользователи сами сообщают, что данный запрос выполняется медленно. В этом случае задача состоит в том, чтобы диагностировать причину замедленности — является ли это общей проблемой или специфичной для этого подграфа или запроса. А затем, конечно же, решить ее, если это возможно.

В других случаях триггером может быть высокий уровень использования памяти на ноде запроса, и в этом случае сначала нужно определить запрос, вызвавший проблему.

Индексаторы могут использовать 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-like account --clear <sgdNNN>.<table>. Нодам запроса требуется до 5 минут, чтобы заметить, что оптимизация включена или выключена. После включения оптимизации необходимо убедиться, что изменение фактически не приводит к замедлению запросов к этой таблице. Если Вы настроили Grafana для мониторинга Postgres, медленные запросы будут отображаться в pg_stat_activity в больших количествах, это займет несколько секунд. В этом случае оптимизацию необходимо снова отключить.

Для подграфов, подобных Uniswap, таблицы pair и token являются первыми кандидатами на эту оптимизацию и могут существенно повлиять на нагрузку базы данных.

Удаление подграфов

Ссылка на этот раздел

Это новый функционал, который будет доступен в Graph Node 0.29.x

В какой-то момент индексатору может потребоваться удалить данный подграф. Это можно легко сделать с помощью graphman drop, который удаляет развертывание и все его проиндексированные данные. Развертывание может быть задано как имя подграфа, хэш IPFS Qm.. или пространство имен базы данных sgdNNN. Дополнительную документацию можно найти здесь.

Редактировать страницу

Предыдущий
Tutorial
Следующий
TAP Migration Guide
Редактировать страницу