Обновить

«Два стула» для данных: как мы боремся с рассинхроном в Rust-сервисе между Solana и PostgreSQL

Уровень сложностиСредний
Время на прочтение12 мин
Охват и читатели8.6K
Всего голосов 2: ↑1 и ↓10
Комментарии15

Комментарии 15

А что такое " "два стула" "?

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

В нашем случае, эти «два стула» - это два наших хранилища данных, каждое со своей, уникальной задачей:
Первый стул - блокчейн Solana. Это наш «стул» неизменности и гарантий.
Второй стул - PostgreSQL. Это наш «стул» скорости и удобства.

Вся статья по сути о том как опасно сидеть «между» этими стульями. Что делать, если мы успешно «сели» на первый стул (записали транзакцию в Solana), но «пошатнулись» на втором (запись в PostgreSQL сорвалась из-за ошибки)? В этот самый момент и возникает рассинхрон — главная проблема, с которой мы боремся.

А есть и другие два стула...

А что если запись в блокчейн пройдет успешно, а в момент коммита транзакции получим ошибку? Ведь изначальная проблема остается

Cбой на этапе коммита это реальный риск для любой базы данных.

К счастью, здесь нам на помощь приходят гарантии транзакционной системы самой PostgreSQL.

  1. Если COMMIT не прошел (из-за сбоя сети, падения БД, проблем с диском), база данных сама откатит всю транзакцию целиком. Это означает, что ни запись о дипломе, ни событие в outbox_events не будут сохранены. С точки зрения системы, операция просто не удалась. Пользователь получит ошибку, попробует еще раз, и мы не получим рассинхрона.

  2. Если COMMIT прошел, СУБД гарантирует, что данные записаны на диск (согласно уровню durability). Даже если сервис упадет через миллисекунду после этого, обе записи (диплом и событие в outbox) уже будут в базе.

Таким образом, благодаря атомарности транзакции в PostgreSQL, мы избегаем состояния "частичной записи". У нас либо обе записи успешно сохранены, либо ни одной. Изначальная проблема "запись в блокчейн есть, а в БД нет" решена, так как запись в блокчейн теперь происходит асинхронно и только после успешного коммита в БД.

Статья ллм'кой написана

И даже в комментариях отвечает ллм

Сколько будет два стула плюс два стула ?

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

Не комплимент

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

Аргумент - типовой текст от ллм не несёт ценности

Вы всё ещё не привели ни одного технического замечания.
Пока что единственная “экспертиза”, которую вы демонстрируете - это умение заметить слово “LLM” и начать на нём ехать, вместо анализа сути.

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

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

Вам действительно интересно подискутировать на тему статьи ?

Если реально интересно и если это ваш реальный опыт

Аутбокс - норм тема, проверять его и все что дальше это оверхэд

Аутбокс просто повторит попытку записи через какое то время

В какую базу писать первой наверное нет разницы, но я бы писал в постгрю, там acid

Мониторинг можно добавить, это гуд

Очередь добавлять-оверхэд

Сага - вообще антипаттерн, означает что система неправильно декомпозирована, из за чего и пришлось сагу писать

Звучит как высосанная из пальца проблема. Кто мешает писать в постгреc, а потом в блокчейн и юзать обычный сага паттерн? Тогда все прекрасно откатывается

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

Флоу "Сначала Postgres, потом Solana" (Saga):

  1. Начинаем локальную транзакцию в Postgres, сохраняем диплом со статусом PENDING. Коммитим.

  2. Отправляем транзакцию в Solana.

Теперь рассмотрим точки отказа:

  • Сценарий 1 (простой): Отправка в Solana не удалась. Что мы делаем? Компенсирующую транзакцию: обновляем статус в Postgres на FAILED. Пока все выглядит неплохо и действительно откатывается.

  • Сценарий 2 (коварный): Транзакция в Solana прошла успешно, но наш сервис падает до того, как он успел обновить статус в Postgres на CONFIRMED.

И вот здесь мы получаем тот же самый рассинхрон, только в обратную сторону: в блокчейне запись есть, а в нашей базе она навсегда осталась в статусе PENDING. Для исправления этого нам понадобится тот самый Reconciliation Job, который будет сверять записи и исправлять такие "подвисшие" состояния.

Но ключевая проблема в вашем тезисе - «все прекрасно откатывается» - не работает с блокчейном. Если транзакция в Solana прошла (сценарий 2), ее невозможно откатить. Можно только сделать новую транзакцию, которая логически ее аннулирует, но это:
а) сложно (нужно хранить состояние, чтобы знать, что аннулировать),
б) усложняет модель данных (появляются отмененные записи),
в) стоит реальных денег (газ).

Именно поэтому Transactional Outbox оказывается надежнее в этом контексте. Он атомарно гарантирует, что задача "отправить в Solana" будет создана. А отдельный воркер потом надежно, с повторными попытками, выполнит эту задачу, защищая нас от сбоев самого сервиса-отправителя.

Так что проблема не высосана из пальца, а является фундаментальным компромиссом при работе с необратимыми внешними системами.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации