У нашего режима работы есть имя
Несколько сессий работают в общем дереве файлов, каждая правит свою копию, потом изменения сводятся инструментом слияния. Курс по распределённым системам называет конструкцию оптимистичной репликацией: координации перед правкой нет, согласие достигается после. Выбор верный — альтернатива означала бы блокировку на всё время работы сессии. Из той же лекции следует цена: сходимость в пределе обещает одинаковое конечное состояние копий и ничего — про сохранность содержимого. Без корректного слияния копии совпадут, а правки исчезнут.
Стёртая запись возвращается
Механика воскрешения от аккуратности не зависит. Есть две копии базы: в одной запись удалили физически, в другой она цела. При слиянии инструмент видит, что слева записи нет, а справа есть, и отличить «здесь её удалили» от «здесь её ещё не создали» нечем — данных о факте удаления не осталось. Разумное поведение — принять запись, и удалённый заказ возвращается в базу.
Лечение известно и стоит недорого: удаление записывается, а не выполняется. Запись помечается статусом и временем, физически остаётся на месте, и слияние видит намерение вместо пустоты.
Второй раз тот же вывод пришёл из главы про распределённые транзакции у Ричардсона. Наша цепочка «заявка → заказ → рейс → счёт → оплата → сверка» на каждом шаге может не случиться, и книга требует явных компенсаций вместо отката. Заявка, не ставшая заказом; отменённый до выезда рейс; аннулированный счёт — это статусы, а не удаление, уносящее историю сверки.
Три источника сошлись на одном. Лекция про слияние копий, глава про компенсирующие транзакции и наша собственная практика сверки требуют одного: запись не исчезает, у неё меняется состояние. Единственный случай в разборе, когда мы не искали четвёртого подтверждения.
«Побеждает поздний» — это тихая потеря
Вторая мина срабатывает без удалений вовсе. Две сессии правят один заказ: одна меняет адрес, другая — сумму. Если слияние выбирает целиком ту запись, что свежее, правка первой сессии исчезает молча — без конфликта, без предупреждения, без следа в отчёте.
Правильный режим описан в лекции про типы данных, устойчивые к слиянию, и разбит на два уровня. Конфликт разрешается по полю, а не по записи целиком: разные поля обеих сессий доживают до результата. При состязании за одно поле — сохранить оба значения на разбор, не выбирать молча.
Почему это неочевидно. Выбор по времени выглядит как разрешение конфликта и даёт зелёный результат: слияние прошло, ошибок нет. Заметить потерю можно только сверкой с тем, что сессия собиралась записать, — то есть способом, которым обычно не проверяют успешную операцию.
Журнал событий вместо перезаписи
Самая сходящаяся рекомендация разбора повторилась трижды: у Клеппмана в главе о потоковой обработке, у Ричардсона в главе про хранение по событиям и в лекции про слияние копий. Состояние не перезаписывается целиком, а собирается из журнала: создан, назначен, выполнен, оплачен.
Выгода складывается из четырёх пунктов сразу, и каждый у нас уже стрелял: затирание при одновременной записи, невозможность ответить «кто изменил», восстановление после сбоя, сверка по истории. Дописывание в конец файла почти не даёт гонок — писатели не трогают чужие строки.
Реалистичный первый шаг записан так: существующее хранилище не трогаем, а рядом заводим журнал, куда API дописывает каждое изменение. Приём проверенный — по такому же принципу у нас давно живёт журнал изменений по продвижению, где каждая строка связывает действие с результатом.
Концепции, рождённые в багах, становятся статусами
Приём из главы Эванса про неявные концепции звучит так: найти понятие, которое живёт в багах, и дать ему имя в модели. Нашлось три кандидата — все из расхождения языка бизнеса и кода.
- Долг. Модель допускает только долг, переплаты в ней нет как понятия. Пока соглашение жило в заметках, каждый новый расчёт открывал его заново.
- Ложный вызов с ожиданием. Отдельная строка расчёта со своим смыслом, а не скидка.
- Отмена после выезда. Компенсация с собственным правилом, а не удаление заказа.
Отдельный паттерн справочника довёл мысль до архитектурного решения: правила, описывающие поведение других данных, живут отдельным слоем, а не константами в коде. Наши правила тарификации — из этого слоя: класс определяется машиной, а не весом груза, у услуги есть минимальное время, отмена считается по правилу. Пока они размазаны по калькуляторам, коду и заметкам, случается то, что случалось: классификация по весу приписала тяжёлой машине чужой класс, а изменение ставки налога пришлось выискивать по всем сайтам.
Что из этого сделано
| Пункт | Состояние |
|---|---|
| Свойства функции слияния зафиксированы и проверены тестами | закрыто, разобрано отдельно |
| Единый словарь домена: долг, счёт, рейс, класс, идентичность | закрыто — термины в одном документе |
| Удаление как статус во всём контуре, а не в части кода | открыто — сценарий воскрешения не прогнан |
| Конфликт по полю и сохранение обеих версий при состязании | частично — не для всех полей |
| Журнал событий рядом с хранилищем | решение принято, спецификация готова, внедрения нет |
| Правила тарификации отдельным слоем данных | открыто — правила в нескольких местах |
Список сознательно оставлен неровным: половина пунктов — про понятое, но не построенное. Подавать это как сделанную работу значило бы повторить ошибку разбора — считать формулировку решением.