Sealevel (Solana): параллельное исполнение, рантайм и планировщик

Sealevel — это модель исполнения Solana, позволяющая запускать несколько транзакций одновременно, если они не конфликтуют по данным. Ключевая идея проста: каждая транзакция заранее объявляет все аккаунты (участки состояния), с которыми она собирается работать, и права доступа к ним (read/write, требуется ли подпись). Благодаря этому рантайм способен построить «сетку независимости» и распараллелить вызовы. В связке с моделью аккаунтов и источником времени Solana это даёт предсказуемую пропускную способность и низкие задержки.

Sealevel (Solana): параллельное исполнение, рантайм и планировщик

Эта страница — техническая опора для разработчиков и продвинутых пользователей. Здесь собраны принципы Sealevel, поведение под нагрузкой, анти-паттерны и практические приёмы проектирования dApp. За обзор протокола см. Архитектура Solana: Объяснение Высокой Производительности, PoH, Sealevel, терминологию виртуальной машины — SVM (Solana Virtual Machine): что это и как работает — BPF, CPI и отличие от EVM, устройство хранения — Solana: модель аккаунтов — PDAs, read/write-локи, rent-exempt, ALTs.

Коротко про Sealevel: параллельное исполнение в Solana (рантайм и планировщик)

  • Параллелизм идёт по данным, а не «по блокам»: независимые наборы аккаунтов исполняются одновременно.
  • Транзакция обязана заранее указать все аккаунты, которыми будет пользоваться (включая те, что понадобятся во внутренних вызовах — CPI).
  • Конфликт — это пересечение по аккаунту с правами записи (или по «горячим» системным ресурсам); чтение-чтение совместимо.
  • Планировщик Sealevel строит партии транзакций, минимизируя конфликты; внутри партии операции идут параллельно.

Базовые сущности Sealevel

Программа (program) Развёрнутый исполняемый код (аналог «контракта»), который вызывает пользовательская транзакция или другая программа (через CPI).

Аккаунт (account) Хранилище данных и/или объект-ресурс (включая токены, записи DEX, ордера и т. п.). В контексте вызова каждому аккаунту назначаются флаги: *is_signer*, *is_writable*, а также порядок.

Инструкция (instruction) Мини-вызов функции программы с явным списком «account metas» — именно он определяет доступ к конкретным аккаунтам. Транзакция содержит одну или несколько инструкций.

Compute Units (CU) Квота вычислений на транзакцию. Предел CU регулирует сложность работы; экономия важна для пропускной способности и UX.

Cross-Program Invocation (CPI) Вызов одной программы из другой внутри той же транзакции. Важное правило Sealevel: CPI не может «тайно» открыть новый аккаунт — все потенциальные аккаунты должны быть перечислены заранее.

Как именно достигается параллелизм

Sealevel реализует предварительное планирование по данным. Упрощённый жизненный цикл обработки партии:

  1. Узел-лидер принимает поток транзакций и «нарезает» их на партии.
  2. Для каждой транзакции строится множество аккаунтов с типами доступа (R/W).
  3. Рантайм пытается набрать в одну партию транзакции, для которых нет пересечений по write (RW-конфликты).
    • Чтение-чтение* совместимо; *чтение-запись* или *запись-запись* на одном аккаунте — конфликт.
  4. Партия отдаётся на параллельное исполнение (в пределах ядер/воркеров). Завершившиеся транзакции освобождают «замки» на аккаунтах, следующая волна стартует.

Если в процессе CPI возникает попытка доступа к аккаунту, не указанному в транзакции, или меняется режим доступа (нужна запись вместо чтения), рантайм прерывает выполнение — конфликты не утаить.

Типы конфликтов и их признаки

Тип конфликта Что это означает Как выявлять/лечить в dApp
write–write на одном аккаунте Две транзакции хотят одновременно изменить один и тот же аккаунт Шардируйте состояние: разбивайте глобальные «счётчики/пулы» на пользовательские или «по рынкам»
read–write на «горячем» аккаунте Одна транзакция пишет, другая читает тот же аккаунт (пока он «под замком») Используйте «снимки»/кэши, ETag-подобные стратегии и финальные проверки инвариантов
Скрытая зависимость через CPI Внутренний вызов пытается использовать неуказанный в транзакции аккаунт Явно перечисляйте «пороговые» аккаунты; проектируйте API программ так, чтобы их невозможно было вызвать без полного списка
Системные/арифметические лимиты CU Транзакция «съедает» квоту и блокирует воркер Профилируйте и оптимизируйте горячие пути, дробите инструкции

Модель аккаунтов и «шахматка независимости»

Solana хранит данные в аккаунтах, а не в «глобальном сторидже». Это задаёт геометрию параллелизма:

  • Если два действия затрагивают разные аккаунты, Sealevel может их запустить одновременно.
  • Если они делят один и тот же аккаунт на запись, возникнет блокировка — в партию попадёт что-то одно.
  • При разумном проектировании схемы хранения (PDA, «ключ-разделитель», per-user/per-market состояния) достигается почти линейный рост Throughput с числом независимых «островков данных».

Подробнее об устройстве см. Solana: модель аккаунтов — PDAs, read/write-локи, rent-exempt, ALTs.

CPI: мощный инструмент с жёсткими рамками

CPI позволяет строить композицию программ: AMM вызывает токен-программу, ордербук — клиринг, кошелёк — системные операции и т. д. Но у этого есть следствия:

  1. Внешняя транзакция обязана принести с собой все аккаунты, которые могут понадобиться глубже по стеку CPI.
  2. Набор прав (read/write, signer) не может «усугубляться» вглубь без явного разрешения на поверхности.
  3. Поэтому API ваших программ должен быть детерминирован в части перечня ресурсов: указывайте в документации ровно, какие аккаунты нужны.

Этот контракт защищает Sealevel от «динамического» конфликта, который нельзя было бы спланировать заранее.

Локальные рынки комиссий и приоритеты

Параллелизм Sealevel тесно связан с локальными рынками комиссий: там, где много транзакций бьются за один и тот же «горячий» аккаунт (пул ликвидности, минт коллекции и т. п.), образуется локальный аукцион приоритета. Транзакции, не затрагивающие этот очаг, не переплачивают. Смотрите базовые страницы Local Fee Market (Solana): локальные рынки комиссий и priority fees и Priority Fee (Solana): приоритетная комиссия за compute units (CU) и «чаевые» валидатору для пользовательского UX и ценообразования.

Для инфраструктуры и потоков заказов в моменты пиков используйте модели приватного/пакетного отправления; архитектурно это раскрывается в связке с Jito Block Engine: частные бандлы, аукционы и MEV на Solana — как это работает, где бандлеры и блок-энджины минимизируют вредные перестановки и фронт-раннинг (MEV (Maximal Extractable Value) в блокчейнах и на Solana — определение и примеры).

Проектные паттерны для высокой параллельности

  • Шардируйте по пользователю/рынку. Храните состояние так, чтобы каждая операция касалась минимального набора аккаунтов. Пример: не общий «супер-счётчик» для всей dApp, а счётчики на пользователя/рынок/ординарный ключ.
  • Разделяйте «горячее» и «холодное». Метаданные, конфигурации и редко меняющиеся записи держите в отдельных аккаунтах без записи в каждом вызове.
  • Фиксируйте инварианты «на входе». Пусть каждая инструкция валидирует пред-условия и завершает работу раньше, если конфликт неминуем.
  • Делайте «узкие» инструкции. Разбейте тяжёлый путь на несколько независимых инструкций, чтобы планировщик мог впитать их в разные партии.
  • Идентификаторы-разделители (PDA). Проектируйте адреса аккаунтов так, чтобы они естественно «раскладывались» по ключам, исключая ненужные пересечения.
  • Контролируйте CU. Используйте лимиты и профилирование, не допускайте «взрыва» вычислений одной транзакцией.

Анти-паттерны (и как их исправить)

  • Глобальные счётчики и «общие корзины». Любая запись в такой объект серийно блокирует всех — заменяйте на иерархию счётчиков/балансов.
  • Длинные цепочки CPI с «сюрпризами». Не вносите вглубь новые зависимости; публикуйте явные требования к аккаунтам в API.
  • Смешивание прав доступа. Если вам почти всегда нужна запись, объявляйте write сразу; излишне «оптимистичное» объявление read → повышает риск отказа.
  • Одна транзакция «за всё». Склеивание множества логических шагов в один вызов делает параллелизм невозможным и повышает риск лимитов CU.

Поведение под нагрузкой и диагностика

Симптом Возможная причина Что делать разработчику
Рост отказов без явных ошибок кода Транзакции сталкиваются по «горячим» аккаунтам, планировщик вынужден серийно ставить их в очередь Проведите профилирование конфликтов: какие аккаунты чаще всего в write; переразбейте хранение
«Флапающая» латентность (p95/p99) Перемежающаяся перегрузка конкретного рынка/пула Добавьте подсказки по Priority Fee (Solana): приоритетная комиссия за compute units (CU) и «чаевые» валидатору в UI, предложите режимы «обычно/быстро/срочно»
Высокий расход CU, деградация TPS Излишняя логика на стороне одной транзакции, мало переиспользуемых кэшей Декомпозируйте инструкции, оптимизируйте сериализацию/десериализацию
Отказы CPI «account not found/borrowed as immutable» Неполный перечень аккаунтов или неверный режим доступа Явно перечислите все аккаунты на поверхности, согласуйте флаги R/W и signer

Пример: почему два свопа могут идти параллельно

Представьте две транзакции A и B:

  • A меняет токен X↔Y в пуле «PX», трогая аккаунты: кошелёк пользователя A, два аккаунта токенов, записи пула «PX».
  • B меняет токен U↔V в пуле «PY», трогая кошелёк пользователя B, два аккаунта токенов, записи пула «PY».

Если PX ≠ PY и нет пересечений по кошелькам/посредникам на запись, то A и B независимы — Sealevel отправит их в одну партию, и обе завершатся без ожидания друг друга. Если же две операции касаются одного и того же пула «PX» и пишут в его состояние, они будут сериализованы.

Сравнение Sealevel и классической модели EVM

Аспект Sealevel (Solana) Классическая EVM (L1)
Единица состояния Аккаунты с явными правами в инструкциях Глобальный storage, адресуемый контрактами
Планирование Параллельно по несовпадающим наборам аккаунтов Последовательно (одна глобальная очередь)
Зависимости Видны до исполнения (по списку account metas) Выявляются во время исполнения
CPI / композиция Разрешена, но только поверх объявленных аккаунтов Вызовы свободны, зависимости — динамические
Ценообразование Локальные рынки вокруг «горячих» данных (Local Fee Market (Solana): локальные рынки комиссий и priority fees) Глобальный аукцион газа для всего блока
UX при пиках Стабильно вне «очагов»; внутри — локальный аукцион приоритета Растёт цена для всех, даже «непричастных» транзакций

Безопасность и инварианты

  • Подписи и владение. Любой аккаунт, требующий изменения, должен быть открыт с флагом *is_signer* у соответствующего владельца/программы.
  • Borrow-правила. Аккаунт не может одновременно быть изменяемым и заимствованным как «immutable» в том же контексте — это защищает от классов ошибок на уровне ссылок.
  • Детерминизм доступа. Поскольку все аккаунты указаны заранее, классы атак «скрытой подмены» через CPI существенно ограничены.
  • Ограничение CU. Предел вычислений защищает узел от «пылесоса CPU», а сеть — от «залипания» одной транзакции.

Практические рекомендации по API программ

  • Проектируйте плоские и предсказуемые интерфейсы: по названию инструкции и её параметрам должно быть ясно, какие аккаунты требуются.
  • Документируйте минимальные и достаточные наборы аккаунтов для каждого пути.
  • Валидируйте режимы доступа на старте: если нужен *write* — отклоняйте «read-транзакции» ранним кодом возврата.
  • Синхронизируйте версии программ с клиентскими SDK: несовместимости часто проявляются в неправильно собранных списках account metas.

Метрики и наблюдаемость

  • Доля конфликтов по аккаунтам. Сколько транзакций в партии пришлось сериализовать.
  • Распределение CU. Где «горит» вычисление — в сериализации, криптографии, логике приложения.
  • Латентность p95/p99 по типам операций, отдельно внутри и вне «горячих» рынков.
  • Ошибки CPI. Частота «account not found», «borrowed as immutable», «insufficient privileges» — прямые подсказки, куда смотреть в API.

Частые вопросы (FAQ)

Это «параллельный консенсус»? Нет. Параллелится исполнение транзакций. Консенсус и финализация описаны отдельно (см. Solana Proof of History (PoH): как работает «доказательство истории» и Solana Tower BFT: механизм финализации блоков и «лок-ауты» голосов).

Можно ли скрыть конфликт, подтянув аккаунт через CPI? Нельзя: все аккаунты должны быть перечислены в транзакции, иначе выполнение прервётся.

Почему две «простые» операции иногда ждут друг друга? Потому что они пишут в один и тот же «горячий» аккаунт. Перепроектируйте хранение, чтобы сократить пересечения.

Помогают ли высокие приоритетные комиссии «обогнать» конкурентов? Да, но только внутри локального рынка вокруг затронутых аккаунтов. См. Priority Fee (Solana): приоритетная комиссия за compute units (CU) и «чаевые» валидатору.

Влияет ли Jito/бандлинг на параллелизм? Да: грамотное упорядочивание и пакетирование потоков снижает вредные перестановки и улучшает использование «пустот» параллелизма (см. Jito Block Engine: частные бандлы, аукционы и MEV на Solana — как это работает).

Мини-глоссарий

  • Account metas — список аккаунтов с правами доступа, передаваемый в инструкцию.
  • CPI (Cross-Program Invocation) — вызов одной программы из другой в рамках одной транзакции.
  • Conflict set — пересечение транзакций по аккаунтам на запись; определяет сериализацию.
  • Compute Units (CU) — лимит вычислений на транзакцию.
  • PDA (Program-Derived Address) — адрес аккаунта, детерминированно получаемый программой по сид-параметрам.

См. также

Solana: модель аккаунтов — PDAs, read/write-локи, rent-exempt, ALTs Local Fee Market (Solana): локальные рынки комиссий и priority fees

Task Runner