Создаём папку сделки
Сначала журнал сохраняет намерение. Модель диска создаёт папку, затем журнал подтверждает результат.
event_id: delivery-1
deal_id: deal-101
revision: 1 · phase: activeОдно создание. Папка находится в «Дела в работе».
Сервис уже переместил папку, а подтверждение не сохранилось. Посмотрите, как проверка фактического состояния помогает завершить обработку без второго перемещения.
Учебная симуляция. Здесь показан сохранённый прогон Python-механизма с моделью внешнего диска. Кнопки переключают его состояния. Реальные CRM и API не подключены; клиентские данные не читаются и ничего не отправляется. Это технический образец, не выполненный заказ.
Сначала журнал сохраняет намерение. Модель диска создаёт папку, затем журнал подтверждает результат.
event_id: delivery-1
deal_id: deal-101
revision: 1 · phase: activeОдно создание. Папка находится в «Дела в работе».
Идентификатор доставки уже есть в журнале. Повтор возвращает статус duplicate; нового создания нет.
event_id: delivery-1
deal_id: deal-101
revision: 1 · phase: activeПапка и число действий не меняются.
Папка уже перемещена в «Завершенные дела». Симулированный сбой происходит до записи подтверждения в SQLite.
event_id: delivery-2
deal_id: deal-101
revision: 2 · phase: completedНамерение — pending. Сохранённый подтверждённый путь ещё старый; он не доказывает текущее состояние диска.
recover() находит папку по устойчивому deal_id. Она уже в нужном месте: обработчик подтверждает намерение без повторного перемещения.
recover()
pending → applied
effect: noneИтог прогона: одно создание и одно перемещение.
Новая доставка содержит версию 1, а сохранённая версия сделки — 2. Обработчик возвращает stale.
event_id: late-delivery
deal_id: deal-101
revision: 1 · phase: activeЗавершённая папка остаётся на месте. Версии должны приходить из доверенного источника; порядок доставки их не заменяет.
Другая сделка пытается занять уже закреплённое название. Статус conflict останавливает операцию: слияния и перезаписи нет.
event_id: delivery-other
deal_id: deal-202
revision: 1 · phase: activeПапка первой сделки сохранена. Причину конфликта нужно устранить отдельно.
В модели папка ищется по ID сделки. Настоящий API должен дать устойчивый ID, метаданные владения или подходящий ключ идемпотентности. Одного названия недостаточно.
Нужны доверенное состояние сделки и правило порядка версий. Переименования и повторное открытие сделки в этот образец не входят.
Отдельно проверяются хранение журнала, реальный рестарт, таймауты, повторы и ограничения API. Общей транзакции между БД и диском нет.
Оригинальный локальный Python-образец проверен 17 тестами: повторы, одновременные доставки, ошибки до и после эффекта, старые версии и конфликты. Страница показывает шесть состояний из его demo.py. Python-код в браузере не исполняется.
SQLite хранит намерения; внешний диск существует только как объект в памяти. При восстановлении в прогоне объект диска сохраняется, а обработчик создаётся заново. Завершение Python-процесса теряет модель диска. Симулированный сбой не проверяет аварийное выключение ОС и не доказывает работу реального коннектора.
Статус duplicate не выполняет постоянный аудит диска. Доставка «ровно один раз» через границу БД/API не гарантируется; результат после потерянного ответа требует проверки.