GH GambleHub

Репликация и eventual consistency

Репликация и eventual consistency

1) Зачем eventual consistency

Когда система распределена по зонам/регионам, синхронная запись везде дает высокую латентность и низкую доступность при сетевых сбоях. Eventual consistency (EC) допускает временную рассинхронизацию реплик ради:
  • низкой задержки записи (локальный прием),
  • лучшей доступности при разделениях сети,
  • горизонтального масштабирования.

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


2) Модели согласованности — что обещаем клиенту

Strong: чтение сразу видит последнюю запись.
Bounded stale / read-not-older-than (RNOT): чтение не старее отметки (LSN/версия/время).
Causal: сохраняются «причинно-следственные» отношения (A до B).
Read-Your-Writes: клиент видит свои недавние записи.
Monotonic Reads: каждое следующее чтение не «откатывается» назад.
Session: набор гарантий в рамках одной сессии.
Eventual: при отсутствии новых записей все реплики сходятся.

Практика: комбинируйте Session + RNOT на критичных путях и Eventual на витринах/кэшах.


3) Репликация: механики и anti-entropy

Синхронная (кворум/RAFT): запись считается успешной после подтверждения N узлами; минимальный RPO, выше p99.
Асинхронная: лидер коммитит локально, раздает журнал позже; низкая латентность, RPO > 0.
Физическая (WAL/binlog): быстрая, гомогенная.
Логическая/CDC: поток изменений на уровне строк/событий, гибкая маршрутизация, фильтры.
Anti-entropy: периодические сверки и починка (Merkle-деревья, сравнение хэшей, фоновый re-sync).


4) Идентификаторы версии и заказы причинности

Монотонные версии: increment/LSN/epoch; просто, но не кодируют параллелизм.
Lamport timestamp: частичный порядок по логическим часам.
Vector clock: фиксирует параллельные ветви и позволяет детектировать конфликтные апдейты (concurrent).
Hybrid/TrueTime/Clock-SI: логика «не раньше T» для глобального порядка.

Рекомендация: для CRDT/конфликтных апдейтов — vector clock; для «не старее» — LSN/GTID.


5) Конфликты: обнаружение и разрешение

Типовые ситуации: запись из двух регионов в один и тот же объект.

Стратегии:

1. Last-Write-Wins (LWW) по часовому/логическому штампу — просто, но может «потерять» апдейты.

2. Merge-функции по доменной логике:
  • поля-счетчики складываются (G-Counter/PN-Counter),
  • множества объединяются с «add-wins/remove-wins»,
  • суммы/балансы — только через транзакционные журналы, не через простое LWW.
  • 3. CRDT (конвергентные типы): G-Counter, OR-Set, LWW-Register, RGA для списков.
  • 4. Операционные трансформации (редко для БД, чаще для редакторов).
  • 5. Manual resolution: конфликт в «inbox», пользователь выбирает верную версию.

Правило: инварианты домена диктуют стратегию. Для денег/остатков — избегайте LWW; используйте транзакции/события с компенсацией.


6) Гарантии записей и идемпотентность

Идемпотентные ключи на командах (payment, withdraw, create) → повтор безопасен.
Дедупликация на «входе» (inbox) и «выходе» (outbox) по ключу идемпотентности/серийному номеру.
Exactly-once недостижимо без сильных предпосылок; практикуйте at-least-once + идемпотентность.
Outbox/Inbox-паттерн: запись в БД и публикация события атомарны (локальная транзакция), получатель обрабатывает по idempotency-key.


7) Чтения «не старее X» (RNOT)

Техники:
  • LSN/GTID-гейт: клиент передает минимальную версию (из ответа записи), роутер/прокси направляет на реплику, догнавшую LSN ≥ X, иначе — на лидер.
  • Time-bound: «не старее 2 сек» — простой SLA без версий.
  • Session pinning: после записи N секунд читаем только лидера (Read-Your-Writes).

8) Потоки изменений и согласование кэшей

CDC → шина событий (Kafka/Pulsar) → потребители (кэши, индексы, витрины).
Инвалидация кэшей: топики `invalidate:{ns}:{id}`; idempotent обработка.
Rebuild/Backfill: при рассинхроне пересоберите проекции из журнала событий.


9) Саги и компенсации (межсервисные транзакции)

В EC-мире долгоживущие операции разбиваются на шаги с компенсирующими действиями:
  • Оркестрация: координатор вызывает шаги и их компенсации.
  • Хореография: шаги реагируют на события и сами публикуют следующие.

Инварианты (пример): «баланс ≥ 0» — проверка на границах шага + компенсация при отклонении.


10) Мульти-регион и сетевые разделения

Local-write, async-replicate: запись в локальном регионе + доставка в другие (EC).
Geo-fencing: данные «приклеены» к региону (низкая латентность, меньше конфликтов).
Кворумные БД (Raft) для CP-данных; кэши/витрины — AP/EC.
Split-brain план: при потере связи регионы продолжают работать в рамках доменных лимитов (write fencing, квоты), затем — reconcile.


11) Наблюдаемость и SLO

Метрики:
  • Replica lag: время/LSN-дистанция/offset (p50/p95/p99).
  • Staleness: доля ответов старше порога (например, >2s или LSN
  • Conflict rate: частота конфликтов и успешных merge.
  • Convergence time: время схождения реплик после пика.
  • Reconcile backlog: объем/время отстающих партиций.
  • RPO/RTO по категориям данных (CP/AP).
Алерты:
  • Лаг > целевого, рост конфликтов, «длинные» окна несходимости.

12) Проектирование схемы данных под EC

Явная версия/вектор в каждой записи (колонки `version`, `vc`).
Append-only журналы для критичных инвариантов (балансы, начисления).
Идентификаторы событий (snowflake/ULID) для порядка и дедупа.
Поля с коммутативной природой (счетчики, множества) → кандидаты на CRDT.
Дизайн API: PUT с if-match/etag, PATCH с precondition.


13) Паттерны хранения и чтения

Read model / CQRS: запись в «источник», чтение из проекций (могут отставать → отображайте «обновляется…»).
Stale-OK маршруты (каталог/лента) vs Strict (кошелек/лимиты).
Sticky/Bounded-stale флаги в запросе (заголовок `x-read-consistency`).


14) Чек-лист внедрения (0–45 дней)

0–10 дней

Категоризировать данные: CP-критичные (деньги, заказы) vs EC/стале-OK (каталоги, поисковые индексы).
Определить SLO стейла (например, «не старее 2s»), целевые лаги.
Включить версионирование объектов и idempotency-keys в API.

11–25 дней

Внедрить CDC и outbox/inbox, маршруты инвалидации кэша.
Добавить RNOT (LSN-гейт) и session pinning на запись-критичных путях.
Реализовать минимум одну merge-стратегию (LWW/CRDT/доменная) и журнал конфликтов.

26–45 дней

Автоматизировать anti-entropy (сверки/починка) и отчеты по стейлу.
Провести game-day: разделение сети, всплеск конфликтов, восстановление.
Визуализировать на дашбордах: lag, staleness, conflict rate, convergence.


15) Анти-паттерны

Слепой LWW для критичных инвариантов (потеря денег/баллов).
Отсутствие idempotency → дубли операций при ретраях.
«Сильная» модель на всем → чрезмерные хвосты p99 и хрупкость при сбоях.
Нет RNOT/Session гарантий → UX «мигает», пользователи «не видят» свои изменения.
Скрытая рассинхронизация кэша и источника (нет CDC/инвалидации).
Отсутствие инструмента reconcile/anti-entropy — данные «на века» расходятся.


16) Метрики зрелости

Replica lag p95 ≤ целевого (например, ≤ 500 ms внутри региона, ≤ 2 s межрегионы).
Staleness SLO выполняется ≥ 99% запросов на «строгих» маршрутах.
Conflict resolution success ≥ 99.9%, среднее время разрешения ≤ 1 мин.
Convergence time после пиков — минуты, не часы.
100% «денежных» операций покрыты idempotency-ключами и outbox/inbox.


17) Рецепты (сниппеты)

If-Match/ETag (HTTP)


PUT /profile/42
If-Match: "v17"
Body: { "email": "new@example.com" }

Если версия изменилась — `412 Precondition Failed` → клиент решает конфликт.

Запрос «не старее LSN» (псевдо)


x-min-lsn: 16/B373F8D8

Роутер выбирает реплику с `replay_lsn ≥ x-min-lsn`, иначе — лидер.

CRDT G-Counter (идея)

Каждый регион хранит свой счетчик; итог — сумма всех компонент; репликация — операция коммутативна.


18) Заключение

Eventual consistency — не компромисс качества, а осознанный контракт: где-то мы платим свежестью ради скорости и доступности, но защищаем критичные инварианты доменными стратегиями и инструментами. Введите версии, idempotency, RNOT/Session гарантии, CDC и anti-entropy, измеряйте lag/staleness/conflicts — и ваша распределенная система будет быстрой, устойчивой и предсказуемо сходящейся даже под сбоями и пиковыми нагрузками.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

Нажимая кнопку, вы соглашаетесь на обработку данных.