Індексація даних Solana потрібна там, де стандартні RPC-методи перестають працювати ефективно: складні запити за кількома акаунтами, агрегація історичних транзакцій, побудова аналітичних панелей або формування подій для сторонніх систем. Нижче — практичний погляд на архітектуру індексатора, підключення через Geyser plugin, обробку реорганізацій блоків та моніторинг стабільності.
Архітектура індексатора
Індексатор Solana — це конвеєр, який приймає оновлення стану блокчейна, десеріалізує їх у типізовані структури та зберігає в цільову базу даних. Його архітектура визначається джерелом даних, моделлю зберігання та стратегією нормалізації.
Джерела даних: RPC, geyser, webhooks
Кожне джерело має власну межу застосування:
- RPC-підписки (WebSocket) — підходять для невеликих застосунків, які слухають події одного або кількох акаунтів через
accountSubscribeтаlogsSubscribe. Обмеження: кількість одночасних підписок на публічних нодах обмежена, історичні дані недоступні, при розриві з'єднання доводиться відновлювати стан вручну. - Geyser plugin — підключається безпосередньо до валідатора та отримує всі оновлення акаунтів і слотів у реальному часі без обмежень RPC. Єдиний спосіб індексувати повний обсяг даних або дані конкретних програм з мінімальною затримкою. Вимагає доступу до валідатора або виділеної інфраструктури.
- Webhooks від сторонніх провайдерів — готові події, які надсилає зовнішній сервіс. Зручні для швидкого старту, але дають обмежений контроль над форматом даних, селекцією акаунтів та порядком обробки.
Для production-індексатора, який має обробляти значні обсяги даних, Geyser plugin є єдиним рішенням, що масштабується.
Моделі даних та нормалізація
Сирі дані від Geyser або RPC — це серіалізовані байти акаунтів та логи транзакцій. Перед збереженням їх треба перетворити на типізовані моделі:
- Десеріалізація акаунтів — розпакування Borsh- або Anchor-структур у внутрішні схеми (PostgreSQL, ClickHouse тощо). Зверніть увагу на дискримінатори Anchor (перші 8 байтів) та версії структури: якщо програма оновлювалася, старі акаунти можуть мати інший формат.
- Нормалізація транзакцій — розбір інструкцій, визначення програм-викликів, витягнення PDA-адрес та параметрів. Транзакцію варто зберігати разом із метаданими слота, статусом (processed, confirmed, finalized) та хешем.
- Зв'язки між сутностями — акаунти часто посилаються одне на одного (власник, mint, авторизований користувач). Нормалізація ці зв'язки розкриває, але вимагає чіткої схеми бази даних та індексів під типові запити.
Передумова: визначте мінімальний набір сутностей та запитів до них до початку реалізації. Індексатор, який намагається зберегти все без фільтрації, швидко стає нев керованим.
Налаштування geyser plugin
Geyser plugin працює як спільна бібліотека, яку завантажує валідатор під час запуску. Конфігурація визначає, які дані передавати, у якому форматі та куди.
Середовище: валідатор Solana з увімкненою підтримкою плагінів (збірка з прапорцем --features=geyser-plugin-interface). Перевірте актуальну версію валідатора та сумісність з версією Geyser plugin у документації репозиторію solana-labs/solana.
Передумови: доступ до конфігураційного файлу валідатора, встановлена цільова база даних (наприклад, PostgreSQL), скомпільований плагін (наприклад, geyser-plugin-postgres або власна реалізація на Rust).
Очікуваний результат: валідатор стартує з підключеним плагіном, оновлення акаунтів обраної програми потрапляють у базу даних у реальному часі.
Базова конфігурація (TOML-формат) містить три ключові секції:
- libpath — абсолютний шлях до скомпільованої бібліотеки плагіна (
.soдля Linux). - accounts_selector — фільтр за програмами або конкретними акаунтами. Без цього фільтра плагін отримуватиме оновлення всіх акаунтів мережі, що створить непомірне навантаження.
- message_format — формат передачі даних (JSON або Protobuf). Protobuf значно ефективніший за обсягом трафіку та швидкістю парсингу.
Приклад фільтрації за програмою в конфігурації:
У секції
accounts_selectorвкажіть масив Pubkey програм, дані яких потрібно індексувати. Це різко зменшує обсяг вхідних даних та навантаження на базу.
Плагін підключається у конфігурації валідатора через параметр --geyser-plugin-config /шлях/до/config.toml. Після перезапуску валідатора перевірте логи: плагін має підтвердити успішне підключення та початок отримання оновлень.
Ризики: плагін працює в адресному просторі валідатора. Помилка в плагіні (panic, memory leak) може зупинити весь валідатор. Тому у production використовуйте лише перевірені плагіни або власні реалізації після ретельного тестування на testnet.
План відкату: видаліть параметр --geyser-plugin-config з конфігурації валідатора та перезапустіть його. Валідатор працюватиме без плагіна, індексація призупиниться, але консенсус не постраждає.
Обробка реорганізацій блоків
Solana використовує механізм фіналізації: слот вважається фінальним після підтвердження супермажоритарем стейку. До цього моменту слот може бути відкинутий унаслідок форку. Індексатор має коректно обробляти такі ситуації, інакше в базі з'являться дані, яких немає в ланцюжку.
Фактична причина помилок при реорганізаціях: індексатор зберігає дані з нефінальних слотів як остаточні, а при відкаті не видаляє або не позначає їх як скасовані. Це не припущення — це найпоширеніший дефект у самописних індексаторах.
Стратегія обробки:
- Зберігайте статус слота. Кожен запис у базі даних має містити номер слота та його статус на момент обробки (processed, confirmed, finalized). Не позначайте дані як фінальні, поки слот не досягне відповідного рівня.
- Буферизуйте нефінальні дані. Зберігайте їх у окремій таблиці або з прапорцем
is_finalized = false. Після досягнення фіналізації переміщуйте в основну таблицю. - Реагуйте на повідомлення про відкат слотів. Geyser plugin надсилає повідомлення типу
SlotStatusзі значеннямDeadабоFirstShredReceivedдля слотів, що відкидаються. При отриманні такого повідомлення видаліть або інвалідуйте всі записи, пов'язані з цим слотом. - Використовуйте атомарні операції. Оновлення статусу слота та пов'язаних даних має відбуватися в межах однієї транзакції бази даних, щоб уникнути часткових станів.
Перевірка: після запуску індексатора на testnet штучно створіть умови для форку (наприклад, запустіть дві ноди з різними початковими точками) та перевірте, чи коректно індексатор видаляє дані з відкинутих слотів у логах та базі.
Межа застосування: якщо ваш застосунок працює лише з фінальними даними (наприклад, розрахунок балансових агрегатів), найпростіший підхід — індексувати лише слоти зі статусом finalized. Це додає затримку (зазвичай кілька секунд), але повністю усуває проблему реорганізацій.
Моніторинг стабільності індексації
Індексатор, за яким ніхто не стежить, зупиняється непомітно. Моніторинг має фіксувати три ключові метрики та сповіщати про відхилення.
1. Відставання за слотами (slot lag) — різниця між найвищим відомим слотом мережі та останнім проіндексованим слотом. Це головна метрика здоров'я індексатора.
- Отримуйте найвищий слот через періодичний RPC-виклик
getSlotабо з повідомлень Geyser. - Порівнюйте з останнім збереженим слотом у базі даних.
- Поріг сповіщення: залежить від вимог застосунку. Для більшості випадків відставання понад 50–100 слотів є причиною для перевірки.
2. Частота помилок парсингу — кількість акаунтів або транзакцій, які не вдалося десеріалізувати.
- Логуйте кожен випадок з хешем транзакції, адресою акаунта та описом помилки (наприклад, невідомий дискримінатор, невідповідність довжини даних).
- Разове зростання може свідчити про оновлення програми на ланцюжку зі зміною структури даних.
- Постійні помилки парсингу одного й того ж акаунта — ознака некоректної обробки закритих акаунтів (ліквідація).
3. Швидкість запису в базу даних — кількість записів за секунду та затримка (latency) вставки.
- Якщо швидкість надходження даних від Geyser перевищує швидкість запису, буфер плагіна або черга індексатора зростатимуть, що призведе до збільшення споживання пам'яті та eventual відставання за слотами.
- Вимірюйте час виконання batch-вставок та моніторьте розмір черги повідомлень.
Додаткові сигнали:
- Зупинка отримання повідомлень від Geyser (перевірте статус з'єднання плагіна з валідатором).
- Ріст обсягу бази даних, що не відповідає очікуваній кількості нових записів (можливий витік через дублікати при реорганізаціях).
- Збільшення часу відповіді цільової бази даних (деградація індексів, необхідність партиціювання).
Практична рекомендація: реалізуйте простий health-check ендпоінт, який повертає поточний проіндексований слот, розмір черги та статус останньої помилки. Це дозволяє інтегрувати індексатор у стандартні системи моніторингу без складних налаштувань.
Наступний крок: після стабільної роботи індексатора варто налаштувати сповіщення про події з проіндексованих даних. Як це зробити за допомогою webhooks, описано в окремому матеріалі.