Real-Time Backend
Конфликт-резолюция
Два человека одновременно нажали Save. Чьи данные правильные? Ответ на этот вопрос определяет, потеряет ли пользователь свою работу или нет - и это происходит миллионы раз в день в Google Docs, Figma и Notion.
- **Amazon DynamoDB** использует LWW по умолчанию и рекомендует conditional writes для критичных данных. Неправильно настроенный LWW в production приводил к silent data loss у крупных клиентов
- **CockroachDB** реализует HLC вместо wall clock для LWW: clock skew до 250ms не влияет на корректность conflict resolution
- **Notion** при block-level delete vs text-edit конфликте всегда выбирает удаление блока и уведомляет пользователя - это намеренное решение с понятным предсказуемым поведением
- **Figma** при конфликте свойств объекта использует field-level LWW: цвет и позиция объекта резолвятся независимо, можно потерять только одно свойство, не весь объект
Conflict Strategies
Конфликт в distributed системе - это не ошибка, это штатная ситуация. Два пользователя одновременно изменили одно и то же - кто прав? Ответ зависит от стратегии резолюции. В реальных системах их несколько, и выбор стратегии определяет user experience.
Три фундаментальные стратегии: **Last Write Wins (LWW)** - побеждает последняя запись по timestamp. **First Write Wins (FWW)** - побеждает первый зафиксировавший. **Merge** - оба изменения объединяются в результат, который содержит оба.
**LWW проблема:** wall clock timestamp не надёжен в distributed системах - часы на разных серверах расходятся. Amazon DynamoDB использует LWW по умолчанию и явно предупреждает: если точный порядок важен - используйте conditional writes или версионирование. Cassandra тоже LWW, но с configurable conflict resolution через `TIMESTAMP` колонку.
Два клиента одновременно установили значение поля: A=42 (timestamp=1000ms) и B=99 (timestamp=999ms). LWW с wall clock выберет A. В чём риск этого подхода?
Merge Policies
Merge policy - это правило, как именно объединяются конфликтующие версии. В тексте merge естественен: вставки от двух пользователей просто оба оказываются в документе (CRDT обеспечивает это автоматически). Для структурированных данных merge сложнее.
Ключевой принцип хорошей merge policy - **семантическая корректность**: результат должен быть валидным состоянием системы, а не просто технически не конфликтующим. Если Алиса переименовала файл в 'report.pdf', а Боб переместил его в другую папку - корректный merge сохранит оба изменения.
**MongoDB** использует field-level merge при репликации: если два документа изменили разные поля - конфликта нет, оба изменения применяются. **CouchDB** реализует document-level версионирование с явными конфликтами: приложение само разрешает конфликт и записывает победившую версию. **Automerge** (CRDT) делает field-level merge автоматически для JSON-документов.
Алиса изменила поле `price: 100`, Боб изменил поле `description: 'new text'` в одном и том же JSON-объекте. Как должна работать корректная merge policy?
User Intent
Техническая корректность merge - необходимое, но не достаточное условие. Алиса удалила параграф потому что он устарел; Боб исправил опечатку в этом параграфе. CRDT merge сохранит параграф с исправленной опечаткой - технически верно, но **intent Алисы потерян**. Это называется intent conflict.
Intent-aware resolution - подход, при котором система пытается понять намерение пользователя, а не только технический diff. Это сложная задача: намерение не передаётся в протоколе явно. Решения: semantic types для операций, пользовательские аннотации, ML-классификация намерений.
**Notion** использует block-level granularity: если Алиса удалила блок целиком, а Боб редактировал текст внутри - победит удаление блока (structural-delete). Notion показывает notification 'Your changes were lost because the block was deleted'. Это намеренное UX-решение, а не баг.
Алиса удалила раздел документа (intent: structural-delete). Боб одновременно отформатировал заголовок этого раздела (intent: format). Как должна сработать intent-aware резолюция?
Resolution Impl
Практическая реализация conflict resolution - это pipeline из нескольких слоёв. Сначала детекция конфликта (concurrent операции на пересекающихся данных), затем классификация (LWW / merge / intent-based), затем резолюция и опциональная нотификация пользователя.
**Hybrid Logical Clock (HLC)** - замена wall clock для LWW: HLC = max(localTime, receivedTime) + counter. Монотонно растёт и учитывает causality. Используется в CockroachDB, YugabyteDB. При использовании HLC LWW-конфликты резолвятся детерминировано и корректно даже при clock skew до сотен миллисекунд.
Конфликты в collaborative системах - признак плохого дизайна, их нужно полностью исключить через pessimistic locking
Конфликты неизбежны при concurrent работе; задача - разработать правильную стратегию резолюции, а не запрещать параллельное редактирование
Pessimistic locking убивает collaborative UX: нельзя редактировать пока кто-то другой держит lock. Google Docs, Figma, Notion работают optimistically - принимают конфликты и резолвят их. Пользователи получают плавный опыт, а конфликты разрешаются прозрачно.
Два insert-операции в одну и ту же позицию документа - это конфликт, который требует LWW/intent-резолюции?
Итоги
- LWW/FWW/Merge - три базовые стратегии; выбор зависит от семантики данных и допустимости потери
- Field-level merge (MongoDB, Automerge) - лучше document-level: независимые изменения разных полей не конфликтуют
- Intent-aware resolution добавляет семантику: structural-delete приоритетнее format; нотификация пользователю при потере изменений - обязательна
Связанные темы
Conflict resolution - центральная тема distributed систем с пересечениями в нескольких областях:
- CRDT структуры — CRDT автоматически разрешает конфликты для вставок/удалений через математически доказанный merge; conflict resolution нужен для LWW-семантики поверх CRDT
- Undo/Redo — Undo порождает особый класс конфликтов: инверсия операции конкурирует с последующими remote изменениями
- Eventual Consistency — Conflict resolution - механизм достижения eventual consistency: все реплики в итоге приходят к одному состоянию
Вопросы для размышления
- В spreadsheet Алиса установила формулу `=A1+B1` в ячейку C1, Боб одновременно записал туда значение `42`. Какая стратегия резолюции корректна и почему?
- Как уведомлять пользователя о потере изменений - без того чтобы засыпать его алертами при активной collaborative работе?
- Можно ли доверять wall clock timestamp для LWW в системе где клиенты - мобильные устройства с возможным clock drift?