Согласованность данных: где нужна строгая, а где достаточно «в итоге»

Статья архитектурно-аналитической серии на примере лунной базы: как выбирать уровень согласованности данных под реальные сценарии — строгая, eventual или пакетная — и почему «согласованность везде» ломает систему так же, как её отсутствие

«Данные должны быть согласованы» звучит как безусловно правильное требование. Но это одно из самых дорогих и самых недопонятых требований в проектировании. Строгая согласованность везде делает систему медленной и хрупкой к разрывам связи. Её отсутствие там, где она критична, приводит к решениям на основе противоречивых данных. Искусство — не в том, чтобы «сделать согласованно», а в том, чтобы выбрать правильный уровень для каждого участка.

В прошлой статье мы разбирали, как части системы общаются асинхронно и с задержкой. Как только появляется асинхронность, всплывает вопрос: что значит «данные согласованы», если они физически расходятся во времени? Эта статья — про то, как отвечать на него осознанно.

Согласованность данных между Луной и Землёй: строгая, eventual, пакетная

В статье

Почему «согласованность везде» — это миф

В распределённой системе при сетевом разделении (когда канал между независимыми узлами рвётся) нельзя одновременно сохранить и мгновенную согласованность, и доступность. Это не пессимизм, а фундаментальное ограничение: пока всё на связи, можно иметь и то и другое — но в момент разрыва (а в лунной базе он случается регулярно) приходится выбирать: либо отказать в операции ради согласованности, либо разрешить её ценой временного расхождения.

Поэтому «согласованность везде» обычно означает одно из двух: либо система медленная и падает при каждом разрыве, либо согласованность только декларируется, а на деле держится на удаче. Честный путь — признать, что разным данным нужны разные гарантии.

Три уровня согласованности на практике

Для рабочего разговора достаточно различать три уровня (мы уже наметили их в статье про доменную модель):

  • Строгая (strong). Все участники видят согласованное состояние по правилам системы (видимость и порядок операций гарантированы). Дорого, требует доступности участников. Иногда это near-real-time, но не обязательно: strong — это про гарантию, а не про скорость. Нужна там, где расхождение опасно для жизни или денег.
  • Eventual (в итоге). Значения сходятся через какое-то время. Дёшево и устойчиво к разрывам. Подходит там, где короткое расхождение допустимо.
  • Пакетная / запаздывающая (batch/lagging). Данные обновляются периодически и заведомо отстают. Нормально для отчётов, прогнозов и аналитики.

Ошибка — не в выборе одного из уровней, а в применении одного уровня ко всему сразу.

Часто аналитику понятнее не сам термин, а бюджет устаревания (staleness budget): какое отставание данных допустимо — 0 секунд, 5 минут, 40 минут, сутки? Практический вопрос звучит не «какая нужна консистентность?», а «насколько старые данные здесь ещё безопасны?». Ответ в секундах и минутах сам подсказывает уровень: 0 обычно ведёт к строгой, минуты — часто к eventual, часы и больше — часто к пакетной (границы условны: бывает и eventual с секундами, и пакетные обновления раз в минуты).

Где это решает аналитик, а не база данных

Уровень согласованности часто считают технической деталью «на уровне БД». Но на самом деле это доменное решение, и аналитик участвует в нём первым. Вопрос «допустимо ли, чтобы наземный центр 40 минут видел старое значение запаса кислорода?» — не про базу, а про риск и смысл. Ответ на него и определяет, какая нужна согласованность.

Аналитик приносит сюда то, чего не видно из технологии: цену расхождения. Для одних данных 40 минут задержки — пустяк, для других — катастрофа. Это различие нельзя вывести из схемы БД, его нужно принести из домена.

Лунная база: разные данные — разные правила

Применим это к сквозному сценарию серии: связь с Землёй пропала на 40 минут, а энергобюджет просел. Разные данные ведут себя совершенно по-разному:

Данные Уровень Почему Что при разрыве связи
Уровень кислорода, давление строгая, локально расхождение опасно для жизни решается на базе, Земля не нужна
Энергобюджет и приоритеты нагрузок строгая, локально решение нужно сейчас и точное пересчитывается локально
Запасы расходников eventual короткое расхождение допустимо сходится после восстановления связи
Прогноз поставок, аналитика пакетная заведомо отстаёт, это нормально просто обновится позже

У каждого набора данных полезно сразу назвать источник правды (кто владеет истиной): для кислорода и энергии — локальный контур базы; для запасов — локальный складской контур с последующей синхронизацией; для прогноза — наземная аналитика. Источник правды и допустимое отставание вместе и определяют уровень согласованности.

Ключевой вывод: критичные для выживания данные не должны зависеть от связи с Землёй — их согласованность обеспечивается локально. А логистике и аналитике eventual/batch не только допустимы, но и желательны: они делают систему устойчивой к разрывам.

flowchart LR A["Данные базы"] --> B["Жизнеобеспечение и энергия"] A --> C["Запасы и логистика"] A --> D["Прогноз и аналитика"] B --> E["Строгая, локальная
(не зависит от Земли)"] C --> F["Eventual
(сходится после связи)"] D --> G["Пакетная / запаздывающая
(обновится позже)"]

flowchart LR
  A["Данные базы"] --> B["Жизнеобеспечение и энергия"]
  A --> C["Запасы и логистика"]
  A --> D["Прогноз и аналитика"]
  B --> E["Строгая, локальная
(не зависит от Земли)"] C --> F["Eventual
(сходится после связи)"] D --> G["Пакетная / запаздывающая
(обновится позже)"]
Уровни согласованности по типам данных лунной базы

Как держать согласованность без распределённых транзакций

Соблазн — обеспечить согласованность «двухфазным коммитом» через все контуры. В системе с ненадёжной связью это худший выбор: транзакция, которая ждёт всех участников, падает при первом же разрыве. На практике используют другие подходы (на уровне идеи, без погружения в реализацию):

  • Локальная согласованность + согласование позже. Критичный контур принимает решение на своих данных немедленно, а синхронизация с остальными происходит асинхронно, когда канал доступен.
  • Outbox / надёжные события. Изменение и факт о нём фиксируются вместе локально, а наружу уходят как событие с гарантией доставки — без распределённой транзакции.
  • Компенсация вместо отката. Если шаг распределённого процесса не удался, выполняется компенсирующее действие, а не глобальный rollback (это идея саги — об этом подробнее в отдельной статье про Saga).
  • Идемпотентность и версии. Повтор и приход данных не по порядку не ломают состояние, потому что операции можно безопасно применять повторно.

Главное правило: согласованность достигается не «общей транзакцией на всё», а явным выбором, что синхронизируется немедленно, что — позже, и как система ведёт себя в промежутке.

Короткий checklist по согласованности

Для каждого важного набора данных спросите:

  1. Какова цена того, что кто-то увидит устаревшее значение?
  2. Какой максимум устаревания допустим (0 секунд, 5 минут, 40 минут, сутки)?
  3. Что система делает, когда допустимый бюджет устаревания превышен?
  4. Должны ли эти данные оставаться доступными при разрыве связи?
  5. Кто «владеет истиной» по этим данным (источник правды)?
  6. Строгая, eventual или пакетная согласованность здесь уместна?
  7. Как данные сходятся после восстановления связи?
  8. Что защищает от дубликатов и нарушения порядка?
  9. Что показываем пользователю/оператору, если данные могут быть устаревшими (предупреждение, статус синхронизации, время последнего обновления)?

Если на всё отвечают «строгая везде» — это сигнал, что цену согласованности ещё не обсудили честно.

Что дальше

Мы выбрали уровни согласованности и взаимодействий. Но любая, даже идеально спроектированная, система живёт ровно настолько, насколько её можно эксплуатировать. Следующая статья серии — эксплуатация в архитектуре: как инженеры возвращают схему в реальность — про наблюдаемость, деградацию и то, кто будет разбираться в инциденте в три часа ночи.

См. также

Обсуждение в Telegram

Присоединиться →

Комментарии