Обробка rate limits RPC-провайдерів — це не питання зручності, а умова стабільності застосунку в production. Коли ваш сервіс починає масштабуватися, неконтрольоване споживання квоти призводить до каскадних відмов: запити падають із помилками 429, черги повторних спроб перевантажують клієнт, а відновлення займає хвилини замість мілісекунд. Нижче — перевірені підходи до роботи з лімітами без залежності від конкретного провайдера.

Розуміння лімітів різних провайдерів

Кожен RPC-провайдер Solana реалізує лімітування по-різному, але більшість використовує одну з трьох моделей: обмеження за кількістю запитів у секунду (RPS), кредитну систему (credits) або комбіновану модель з додатковими обмеженнями на одночасні з'єднання.

Кредитна система працює так: різні методи мають різну вартість. Наприклад, getBalance може коштувати 1 кредит, getAccountInfo — 2, а getTransaction з повними метаданими — до 10. Ваш тарифний план визначає кількість кредитів за секунду або за добу. Це означає, що ви не можете оцінити запас міцності просто за кількістю HTTP-запитів — потрібно рахувати саме кредити.

Модель RPS простіша: фіксована кількість запитів незалежно від типу. Проте тут часто приховані нюанси — наприклад, окремі субліміти на важкі методи (getProgramAccounts, getBlock з повними транзакціями) або на кількість одночасних WebSocket-підключень.

Щоб зрозуміти реальні ліміти вашого плану, перевірте документацію провайдера за такими параметрами:

  • Методи з підвищеною вартістю або окремими сублімітами.
  • Чи диференційовані ліміти за методами HTTP та WebSocket.
  • Чи є burst-дозвіл (короткочасне перевищення ліміту з подальшим відновленням).
  • Як поводиться провайдер при перевищенні: миттєва відмова (429), чергування запиту або silent drop.

Типова помилка — розраховувати ліміти на основі середнього навантаження. У реальному трафіку спостерігаються спайки: індексація після пропущеного слота, масові підписки через WebSocket, batch-запити при ініціалізації. Якщо ваш середній RPS становить 50, але піковий — 400, а ліміт — 200, система буде падати періодично, хоча «на папері» ви вкладаєтесь у квоту.

Стратегії backoff та повторних спроб

Найгірше, що можна зробити при отриманні 429 — це негайно повторити запит. Це створює thundering herd: кілька клієнтів одночасно отримують відмову, одночасно повторюють і знову перевантажують провайдера.

Єдиний прийнятний підхід — exponential backoff з jitter. Суть: після кожної невдалиї інтервал очікування зростає експоненційно, а jitter (випадкове зміщення) розподіляє повторні спроби в часі, запобігаючи синхронізації.

Базова формула: delay = min(base_delay * 2^attempt + random_jitter, max_delay). Практичні значення для Solana RPC: base_delay — 500 мс, max_delay — 10–30 секунд, jitter — від 0 до 30% від розрахованого delay.

Критично важливий нюанс — ідемпотентність. Не всі RPC-методи безпечні для повторних спроб. sendTransaction при повторі може створити дублькат транзакції, якщо перший виклик фактично був оброблений мережею, але ви отримали таймаут замість відповіді. Перед повтором перевіряйте статус транзакції через getSignatureStatuses — це дешевий запит, який не створить дублікату.

Методи читання (getAccountInfo, getBalance, getBlock) ідемпотентні за своєю природою — повторний виклик поверне той самий результат або оновлений стан, не створюючи побічних ефектів.

Максимальна кількість повторних спроб залежить від контексту. Для фонової індексації — 5–10 спроб із повним backoff. Для синхронного API-запиту від користувача — 2–3 спроби, після чого повертайте помилку клієнту, інакше користувач чекатиме десятки секунд.

Ще одна помилка — єдиний глобальний лічильник спроб для всіх методів. Важкий запит getProgramAccounts з фільтрами майже гарантовано вдарить у ліміт, і повторювати його з тим самим набором фільтрів без змін безглуздо. У таких випадках краще зменшити обсяг запиту (пагінація, звуження фільтрів) або відкласти його на наступний слот.

Кешування відповідей на боці клієнта

Найефективніший спосіб не витрачати квоту — не робити запит. Кешування на боці клієнта різко зменшує навантаження на RPC, але вимагає чіткого розуміння, які дані можна кешувати, а які — ні.

Ключовий принцип: стан акаунта в Solana змінюється лише тоді, коли його змінює транзакція. Це означає, що між слотами, де акаунт не фігурує в жодній транзакції, його дані незмінні. Якщо ви читаєте getAccountInfo для PDA-акаунта, який змінюється рідко (наприклад, конфігураційний акаунт програми), ви можете кешувати результат на рівні слота.

Практична схема: при отриманні відповіді зберігайте дані разом із context.slot. При наступному запиті порівнюйте поточний слот (отриманий через дешевий виклик getSlot або підписку WebSocket) із кешованим. Якщо слот не змінився — повертайте кеш. Це один додатковий запит, але він економить усі подальші до зміни стану.

Для даних, що змінюються часто (баланси токен-акаунтів під час активної торгівлі), такий кеш малокорисний. Але для метаданих NFT, стану програм, конфігураційних PDA — економія квоти може сягати 80–90%.

Що кешувати безпечно:

  • Результати getProgramAccounts з фільтрами, якщо ви відстежуєте зміни через WebSocket-підписку на відповідні акаунти.
  • Результати getMultipleAccounts для пакетів акаунтів, що рідко змінюються.
  • Серіалізовані структури конфігураційних акаунтів програми.

Що кешувати небезпечно:

  • Результати getLatestBlockhash — вони мають короткий термін придатності (близько 60 секунд), і використання застарілого blockhash призведе до відхилення транзакції.
  • Стан акаунтів, які ви не відстежуєте через підписки — ви не дізнаєтесь про зміну без додаткового запиту.
  • Щось із залежністю від commitment «processed» — цей рівень надає попередній стан, який може бути відкочений.

Для інфраструктурних сервісів варто розглянути Redis або in-memory кеш (як-от lru у Node.js або moka у Rust) із TTL, що базується на очікуваній частоті зміни даних. WebSocket-підписки на зміну акаунтів слугують інвалідатором кешу — при отриманні оновлення ви примусово видаляєте або оновлюєте кешований запис.

Моніторинг споживання квоти

Без моніторингу ви дізнаєтесь про перевищення ліміту лише тоді, коли застосунок почне падати. Це неприйнятно для production. Моніторинг споживання квоти має бути вбудований у вашу інфраструктуру з першого дня роботи з зовнішнім RPC.

Мінімальний набір метрик, які треба збирати:

  • Загальна кількість запитів за період — розбита за методами. Це показує, які методи споживають найбільше квоти.
  • Кількість відповідей 429 — абсолютне значення та частка від загальної кількості запитів. Будь-яке значення більше нуля в production вимагає розслідування.
  • Латентність відповідей за методами — різке зростання латентності часто передує 429, бо провайдер починає чергувати запити замість миттєвої відмови.
  • Кількість повторних спроб — якщо відсоток повторів перевищує 5% від загального трафіку, ваша стратегія backoff масує проблему замість її вирішення.
  • Кеш-хітрейт — відсоток запитів, обслужених з кешу без звернення до RPC.

Деякі провайдери надають власні дашборди зі споживанням кредитів. Це корисно, але недостатньо — їхні дані затримуються, не розбиті за методами з вашого боку і не інтегровані у ваші алерти. Збирайте власні метрики на рівні HTTP-клієнта або middleware.

Практичний підхід до алертування: встановіть поріг на 70% від розрахованого ліміту за кредитами або RPS (з урахуванням пікового множника). Якщо ваш піковий множник — 4x від середнього, то алерт на 70% дасть вам час відреагувати до того, як спайк досягне 100%. Не чекайте на 429 — це вже відмова.

При досягненні порогу алерт має не просто сповіщати, а вказувати: який метод споживає найбільше, чи змінився патерн трафіку, чи не впав кеш-хітрейт. Без цієї контекстної інформації алерт перетворюється на шум.

Для діагностики в реальному часі корисно логувати кожен 429-відповідь із повним контекстом: метод, параметри, час доби, поточний RPS, розмір черги повторних спроб. Це дозволяє відрізнити структурне перевищення ліміту (зростання трафіку, що вимагає зміни тарифу) від тимчасового спайку (пов'язаного з конкретною подією в мережі).

Якщо моніторинг показує стійке наближення до ліміту незалежно від оптимізацій, це прямий сигнал до зміни тарифного плану або розподілу трафіку між кількома провайдерами. Розподіл (load balancing) між endpoints різних провайдерів — окреме інфраструктурне завдання, але без зібраних метрик ви не зможете обґрунтувати її необхідність чи оцінити ефективність.

Джерела