«Данные должны быть согласованы» звучит как безусловно правильное требование. Но это одно из самых дорогих и самых недопонятых требований в проектировании. Строгая согласованность везде делает систему медленной и хрупкой к разрывам связи. Её отсутствие там, где она критична, приводит к решениям на основе противоречивых данных. Искусство — не в том, чтобы «сделать согласованно», а в том, чтобы выбрать правильный уровень для каждого участка.
В прошлой статье мы разбирали, как части системы общаются асинхронно и с задержкой. Как только появляется асинхронность, всплывает вопрос: что значит «данные согласованы», если они физически расходятся во времени? Эта статья — про то, как отвечать на него осознанно.
В статье
- Почему «согласованность везде» — это миф
- Три уровня согласованности на практике
- Где это решает аналитик, а не база данных
- Лунная база: разные данные — разные правила
- Как держать согласованность без распределённых транзакций
- Короткий checklist по согласованности
- Что дальше
Почему «согласованность везде» — это миф
В распределённой системе при сетевом разделении (когда канал между независимыми узлами рвётся) нельзя одновременно сохранить и мгновенную согласованность, и доступность. Это не пессимизм, а фундаментальное ограничение: пока всё на связи, можно иметь и то и другое — но в момент разрыва (а в лунной базе он случается регулярно) приходится выбирать: либо отказать в операции ради согласованности, либо разрешить её ценой временного расхождения.
Поэтому «согласованность везде» обычно означает одно из двух: либо система медленная и падает при каждом разрыве, либо согласованность только декларируется, а на деле держится на удаче. Честный путь — признать, что разным данным нужны разные гарантии.
Три уровня согласованности на практике
Для рабочего разговора достаточно различать три уровня (мы уже наметили их в статье про доменную модель):
- Строгая (strong). Все участники видят согласованное состояние по правилам системы (видимость и порядок операций гарантированы). Дорого, требует доступности участников. Иногда это near-real-time, но не обязательно: strong — это про гарантию, а не про скорость. Нужна там, где расхождение опасно для жизни или денег.
- Eventual (в итоге). Значения сходятся через какое-то время. Дёшево и устойчиво к разрывам. Подходит там, где короткое расхождение допустимо.
- Пакетная / запаздывающая (batch/lagging). Данные обновляются периодически и заведомо отстают. Нормально для отчётов, прогнозов и аналитики.
Ошибка — не в выборе одного из уровней, а в применении одного уровня ко всему сразу.
Часто аналитику понятнее не сам термин, а бюджет устаревания (staleness budget): какое отставание данных допустимо — 0 секунд, 5 минут, 40 минут, сутки? Практический вопрос звучит не «какая нужна консистентность?», а «насколько старые данные здесь ещё безопасны?». Ответ в секундах и минутах сам подсказывает уровень: 0 обычно ведёт к строгой, минуты — часто к eventual, часы и больше — часто к пакетной (границы условны: бывает и eventual с секундами, и пакетные обновления раз в минуты).
Где это решает аналитик, а не база данных
Уровень согласованности часто считают технической деталью «на уровне БД». Но на самом деле это доменное решение, и аналитик участвует в нём первым. Вопрос «допустимо ли, чтобы наземный центр 40 минут видел старое значение запаса кислорода?» — не про базу, а про риск и смысл. Ответ на него и определяет, какая нужна согласованность.
Аналитик приносит сюда то, чего не видно из технологии: цену расхождения. Для одних данных 40 минут задержки — пустяк, для других — катастрофа. Это различие нельзя вывести из схемы БД, его нужно принести из домена.
Лунная база: разные данные — разные правила
Применим это к сквозному сценарию серии: связь с Землёй пропала на 40 минут, а энергобюджет просел. Разные данные ведут себя совершенно по-разному:
| Данные | Уровень | Почему | Что при разрыве связи |
|---|---|---|---|
| Уровень кислорода, давление | строгая, локально | расхождение опасно для жизни | решается на базе, Земля не нужна |
| Энергобюджет и приоритеты нагрузок | строгая, локально | решение нужно сейчас и точное | пересчитывается локально |
| Запасы расходников | eventual | короткое расхождение допустимо | сходится после восстановления связи |
| Прогноз поставок, аналитика | пакетная | заведомо отстаёт, это нормально | просто обновится позже |
У каждого набора данных полезно сразу назвать источник правды (кто владеет истиной): для кислорода и энергии — локальный контур базы; для запасов — локальный складской контур с последующей синхронизацией; для прогноза — наземная аналитика. Источник правды и допустимое отставание вместе и определяют уровень согласованности.
Ключевой вывод: критичные для выживания данные не должны зависеть от связи с Землёй — их согласованность обеспечивается локально. А логистике и аналитике eventual/batch не только допустимы, но и желательны: они делают систему устойчивой к разрывам.
(не зависит от Земли)"] C --> F["Eventual
(сходится после связи)"] D --> G["Пакетная / запаздывающая
(обновится позже)"]
flowchart LR
A["Данные базы"] --> B["Жизнеобеспечение и энергия"]
A --> C["Запасы и логистика"]
A --> D["Прогноз и аналитика"]
B --> E["Строгая, локальная
(не зависит от Земли)"]
C --> F["Eventual
(сходится после связи)"]
D --> G["Пакетная / запаздывающая
(обновится позже)"]
Как держать согласованность без распределённых транзакций
Соблазн — обеспечить согласованность «двухфазным коммитом» через все контуры. В системе с ненадёжной связью это худший выбор: транзакция, которая ждёт всех участников, падает при первом же разрыве. На практике используют другие подходы (на уровне идеи, без погружения в реализацию):
- Локальная согласованность + согласование позже. Критичный контур принимает решение на своих данных немедленно, а синхронизация с остальными происходит асинхронно, когда канал доступен.
- Outbox / надёжные события. Изменение и факт о нём фиксируются вместе локально, а наружу уходят как событие с гарантией доставки — без распределённой транзакции.
- Компенсация вместо отката. Если шаг распределённого процесса не удался, выполняется компенсирующее действие, а не глобальный rollback (это идея саги — об этом подробнее в отдельной статье про Saga).
- Идемпотентность и версии. Повтор и приход данных не по порядку не ломают состояние, потому что операции можно безопасно применять повторно.
Главное правило: согласованность достигается не «общей транзакцией на всё», а явным выбором, что синхронизируется немедленно, что — позже, и как система ведёт себя в промежутке.
Короткий checklist по согласованности
Для каждого важного набора данных спросите:
- Какова цена того, что кто-то увидит устаревшее значение?
- Какой максимум устаревания допустим (0 секунд, 5 минут, 40 минут, сутки)?
- Что система делает, когда допустимый бюджет устаревания превышен?
- Должны ли эти данные оставаться доступными при разрыве связи?
- Кто «владеет истиной» по этим данным (источник правды)?
- Строгая, eventual или пакетная согласованность здесь уместна?
- Как данные сходятся после восстановления связи?
- Что защищает от дубликатов и нарушения порядка?
- Что показываем пользователю/оператору, если данные могут быть устаревшими (предупреждение, статус синхронизации, время последнего обновления)?
Если на всё отвечают «строгая везде» — это сигнал, что цену согласованности ещё не обсудили честно.
Что дальше
Мы выбрали уровни согласованности и взаимодействий. Но любая, даже идеально спроектированная, система живёт ровно настолько, насколько её можно эксплуатировать. Следующая статья серии — эксплуатация в архитектуре: как инженеры возвращают схему в реальность — про наблюдаемость, деградацию и то, кто будет разбираться в инциденте в три часа ночи.
Комментарии