Эксплуатация Graph Node
Reading time: 15 min
Graph Node — это компонент, который индексирует подграфы и делает полученные данные доступными для запроса через GraphQL API. Таким образом, он занимает центральное место в стеке индексатора, а правильная работа Graph Node имеет решающее значение для успешного запуска индексатора.
Здесь представлен контекстуальный обзор Graph Node и некоторые более продвинутые параметры, доступные индексаторам. Подробную документацию и инструкции можно найти в .
— это эталонная реализация для индексации подграфов в The Graph Network, подключения к клиентам блокчейна, индексирования подграфов и предоставления индексированных данных для запроса.
Graph Node (и весь стек индексаторов) можно запускать на «голом железе» или в облачной среде. Эта гибкость центрального компонента индексации имеет решающее значение для надежности The Graph Protocol. Точно так же Graph Node может быть , или индексаторы могут использовать один из .
Основное хранилище для Graph Node, это место, где хранятся данные подграфа, а также метаданные о подграфах и сетевые данные, не зависящие от подграфа, такие как кэш блоков и кэш eth_call.
Для индексации сети Graph Node требуется доступ к сетевому клиенту через EVM-совместимый JSON-RPC API. Этот RPC может подключаться к одному клиенту или может представлять собой более сложную настройку, которая распределяет нагрузку между несколькими.
В то время как для некоторых субграфов может потребоваться полная нода, другие могут иметь функции индексации, для которых требуются дополнительные функции RPC. В частности, для субграфов, которые выполняют eth_calls
как часть индексации, потребуется нода архива, поддерживающая , а субграфы с 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 .
Метаданные о развертывании подграфа хранятся в сети IPFS. The Graph Node в первую очередь обращается к ноде IPFS во время развертывания подграфа, чтобы получить манифест подграфа и все связанные файлы. Сетевым индексаторам не требуется запускать собственную ноду IPFS. Нода IPFS для сети находиться по адресу .
Чтобы включить мониторинг и отчетность, Graph Node может дополнительно регистрировать метрики на сервере метрик Prometheus.
-
Rust
-
PostgreSQL
-
IPFS
-
Дополнительные требования для пользователей Ubuntu. Для запуска Graph Node в 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 build
Теперь, когда все зависимости настроены, запустите 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 можно найти в .
Во время работы Graph Node предоставляет следующие порты:
Порт | Назначение | Расположение | CLI-аргумент | Переменная среды |
---|---|---|---|---|
8000 | HTTP-сервер GraphQL (для запросов подграфов) | /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 может работать с одним экземпляром Graph Node, одной базой данных PostgreSQL, нодой IPFS и сетевыми клиентами в соответствии с требованиями субграфов, подлежащих индексированию.
Эту настройку можно масштабировать горизонтально, добавляя несколько Graph Node и несколько баз данных для поддержки этих Graph Node. Опытные пользователи могут воспользоваться некоторыми возможностями горизонтального масштабирования Graph Node, а также некоторыми более продвинутыми параметрами конфигурации через файл config.toml
и переменные среды Graph Node.
Файл конфигурации можно использовать для установки более сложных конфигураций, чем те, которые представлены в интерфейсе командной строки. Местоположение файла передается с помощью параметра командной строки --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 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 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 соответствует регулярному выражению, будет настроена так, чтобы отвечать только на запросы.
В большинстве случаев одной базы данных 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.
- В репозитории индексатора есть
- – это набор инструментов для запуска Graph Protocol индексатора в Kubernetes, поддерживаемый GraphOps. Он предоставляет набор диаграмм Helm и интерфейс командной строки для управления развертыванием 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 для визуализации этих метрик.
В репозитории индексатора представлен .
graphman
– это инструмент обслуживания Graph Node, помогающий диагностировать и решать различные повседневные и исключительные задачи.
Команда graphman включена в официальные контейнеры, и Вы можете выполнить docker exec в контейнере graph-node, чтобы запустить ее. Для этого требуется файл config.toml
.
Полная документация по командам graphman
доступна в репозитории Graph Node. См. [/docs/graphman.md] () в Graph Node /docs
Доступный по умолчанию на порту 8030/graphql, API статуса индексирования предоставляет ряд методов для проверки статуса индексирования для различных подграфов, проверки доказательств индексирования, проверки функций подграфов и многого другого.
Процесс индексирования состоит из трех отдельных частей:
- Получение интересующих событий от провайдера
- Обработка событий по порядку с помощью соответствующих обработчиков (это может включать вызов чейна для состояния и выборку данных из хранилища)
- Запись полученных данных в хранилище
Эти этапы конвейерные (т.е. могут выполняться параллельно), но они зависят друг от друга. Там, где подграфы индексируются медленно, основная причина будет зависеть от конкретного подграфа.
Распространенные причины низкой скорости индексации:
- Время, затрачиваемое на поиск соответствующих событий в чейне (в частности, обработчики вызовов могут работать медленно, учитывая зависимость от
trace_filter
) - Создание большого количества
eth_call
в составе обработчиков - Большое количество операций с хранилищем во время выполнения
- Большой объем данных для сохранения в хранилище
- Большое количество событий для обработки
- Медленное время подключения к базе данных для переполненных нод
- Сам провайдер отстает от головного чейна
- Задержка получения новых поступлений от провайдера в головном чейне
Метрики индексации подграфов могут помочь диагностировать основную причину замедления индексации. В некоторых случаях проблема связана с самим подграфом, но в других случаях усовершенствованные сетевые провайдеры, снижение конкуренции за базу данных и другие улучшения конфигурации могут заметно повысить производительность индексирования.
Во время индексации подграфов может произойти сбой, если они столкнутся с неожиданными данными, какой-то компонент не будет работать должным образом или если в обработчиках событий или конфигурации появится ошибка. Есть два основных типа отказа:
- Детерминированные сбои: это сбои, которые не будут устранены при повторных попытках
- Недетерминированные сбои: они могут быть связаны с проблемами с провайдером или какой-либо неожиданной ошибкой Graph Node. Когда происходит недетерминированный сбой, Graph Node повторяет попытки обработчиков сбоя, со временем отказываясь от них.
В некоторых случаях сбой может быть устранен индексатором (например, если ошибка вызвана отсутствием нужного поставщика, добавление необходимого поставщика позволит продолжить индексирование). Однако в других случаях требуется изменить код подграфа.
Детерминированные сбои считаются «окончательными», при этом для отказавшего блока генерируется Доказательство индексации, а недетерминированные сбои — нет, поскольку подграфу может удаться «отменить сбой» и продолжить индексирование. В некоторых случаях недетерминированная метка неверна, и подграф никогда не преодолеет ошибку; о таких сбоях следует сообщать как о проблемах в репозитории Graph Node.
Graph Node кэширует определенные данные в хранилище, чтобы избежать повторной загрузки от провайдера. Блоки кэшируются, как и результаты eth_calls
(последние кэшируются для определенного блока). Такое кэширование может резко увеличить скорость индексации при «повторной синхронизации» слегка измененного подграфа.
Однако, в некоторых случаях, если нода Ethereum предоставила неверные данные за какой-то период, они могут попасть в кеш, что приведет к некорректным данным или повреждённым субграфам. В этом случае индексаторы могут использовать graphman
для очистки испорченного кеша, а затем перематывать затронутые субграфы, которые затем будут получать свежие данные от (мы надеемся на это) исправного поставщика.
Если есть подозрение на несогласованность кэша блоков, например, событие отсутствия квитанции tx:
graphman chain list
, чтобы найти название чейна.graphman chain check-blocks <CHAIN> by-number <NUMBER>
проверит, соответствует ли кэшированный блок провайдеру, и удалит блок из кэша, если это не так.- Если есть разница, может быть безопаснее усечь весь кеш с помощью
graphman chain truncate <CHAIN>
. - Если блок соответствует провайдеру, то проблема может быть отлажена непосредственно провайдером.
После индексации подграфа индексаторы могут рассчитывать на обслуживание запросов через выделенную конечную точку запроса подграфа. Если индексатор планирует обслуживать значительный объем запросов, рекомендуется выделенная нода запросов, а в случае очень больших объемов запросов индексаторы могут настроить сегменты копий так, чтобы запросы не влияли на процесс индексирования.
Однако, даже с выделенной нодой запросов и копиями выполнение некоторых запросов может занять много времени, а в некоторых случаях увеличить использование памяти и негативно повлиять на время выполнения запросов другими пользователями.
Существует не одна «серебряная пуля» (панацея), а целый ряд инструментов для предотвращения, диагностики и обработки медленных запросов.
Graph Node по умолчанию кэширует запросы GraphQL, что может значительно снизить нагрузку на базу данных. Это можно дополнительно настроить с помощью параметров GRAPH_QUERY_CACHE_BLOCKS
и GRAPH_QUERY_CACHE_MAX_MEM
— подробнее [здесь](. /docs/environment-variables.md#graphql-caching).
Проблемные запросы чаще всего выявляются одним из двух способов. В некоторых случаях пользователи сами сообщают, что данный запрос выполняется медленно. В этом случае задача состоит в том, чтобы диагностировать причину замедленности — является ли это общей проблемой или специфичной для этого подграфа или запроса. А затем, конечно же, решить ее, если это возможно.
В других случаях триггером может быть высокий уровень использования памяти на ноде запроса, и в этом случае сначала нужно определить запрос, вызвавший проблему.
Индексаторы могут использовать для обработки и обобщения логов запросов 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
. Дополнительную документацию можно найти .