[Bug] Восстановить terminal hourly lifecycle после persistence failure #56

Closed
opened 2026-10-11 21:41:56 +03:00 by codex-bot · 0 comments
Owner

Проблема

Почасовая синхронизация T-Bank может завершиться в неконсистентном состоянии после ошибки сохранения.

  • Внешний catch повторно вызывает settle со старым списком ошибок. Из-за этого незавершённый обход может быть записан как succeeded.
  • Ошибка в финализаторе enrichment/operations либо подавленная ошибка settle могут оставить в SQLite попытку со статусом running.
  • Такой running-обход не имеет ограниченного восстановления: polling может ждать бесконечно, а локальная reservation не позволит запустить следующий обход в том же процессе.

Это статически подтверждённый P1-механизм. Частота его возникновения в production пока не установлена.

Ожидаемое поведение

Каждая попытка синхронизации должна завершаться достоверным terminal-статусом:

  • невыполненный обязательный шаг приводит к failed, partial или interrupted, но не к succeeded;
  • успешные результаты дочерних шагов не теряются при частичном сбое;
  • после сбоя сохранения состояние в SQLite восстанавливается в диагностируемый и повторно допустимый для запуска вид;
  • восстановление не создаёт overlap и не допускает поздней публикации устаревших данных.

Границы задачи

Входит в задачу

  • Логика итогового статуса coordinator и обработка ошибок в дочерних финализаторах.
  • Ограниченное ожидание и восстановление после ошибок finish, publishSource и settle.
  • Проверка реальной reservation/coordinator-логики на SQLite, а не только scheduler stubs.
  • Контролируемый сценарий app.close() / restart и его влияние на незавершённую попытку.

Не входит в задачу

  • Распределённая очередь, lease или поддержка нескольких реплик.
  • Удаление данных.
  • Ослабление существующих CAS-ограничений attempt / revision / source.
  • Изменение топологии deployment.

Подход к реализации

Исправить существующих владельцев строк синхронизации и логирования; не вводить параллельный scheduler.

Важно: timeout ожидания не отменяет RPC. Повторная reservation должна оставаться защищённой fence, а поздняя публикация от старой попытки обязана быть отклонена. Graceful drain добавлять только если сценарий контролируемой остановки действительно покажет такую необходимость.

Критерии готовности

  • Исключение до membership increment никогда не сохраняет попытку как succeeded; уже полученные частичные успехи сохраняются корректно.
  • После однократной ошибки finish, publishSource или settle и последующего восстановления SQLite ожидание завершается, а следующая допустимая попытка может стартовать.
  • Устаревший дочерний процесс наблюдается относительно актуальных attempt / successor; поздняя публикация старого процесса отклоняется.
  • Coordinator различает состояния first, not-due, running, restart и partial; итог проверяется после terminal barrier.
  • Сценарий app.close() / restart доказан тестами: нет бесконечного drain и после остановки не создаются новые reservations.

Проверка

Добавить или расширить изолированные тесты SQLite с управляемыми часами и upstream:

  • инъекция ошибок отдельно в дочерние шаги и в родительский coordinator;
  • восстановление после ошибки сохранения;
  • защита от overlap и от поздней публикации;
  • controlled stop/restart.

Сохранить существующие наборы auth/money/publication и выполнить полный backend verify на финальном diff. Scheduler stub в upcoming-events не считается достаточным доказательством.

Зависимости

Блокирующих задач нет. Проверка cadence (T03) выполняется независимо; cleanup workflow не является prerequisite.

Основания и затронутый код

<!-- wealthledger-audit:2026-10-11:hourly-lifecycle --> ## Проблема Почасовая синхронизация T-Bank может завершиться в неконсистентном состоянии после ошибки сохранения. - Внешний `catch` повторно вызывает `settle` со старым списком ошибок. Из-за этого незавершённый обход может быть записан как `succeeded`. - Ошибка в финализаторе enrichment/operations либо подавленная ошибка `settle` могут оставить в SQLite попытку со статусом `running`. - Такой `running`-обход не имеет ограниченного восстановления: polling может ждать бесконечно, а локальная reservation не позволит запустить следующий обход в том же процессе. Это статически подтверждённый P1-механизм. Частота его возникновения в production пока не установлена. ## Ожидаемое поведение Каждая попытка синхронизации должна завершаться достоверным terminal-статусом: - невыполненный обязательный шаг приводит к `failed`, `partial` или `interrupted`, но не к `succeeded`; - успешные результаты дочерних шагов не теряются при частичном сбое; - после сбоя сохранения состояние в SQLite восстанавливается в диагностируемый и повторно допустимый для запуска вид; - восстановление не создаёт overlap и не допускает поздней публикации устаревших данных. ## Границы задачи ### Входит в задачу - Логика итогового статуса coordinator и обработка ошибок в дочерних финализаторах. - Ограниченное ожидание и восстановление после ошибок `finish`, `publishSource` и `settle`. - Проверка реальной reservation/coordinator-логики на SQLite, а не только scheduler stubs. - Контролируемый сценарий `app.close()` / restart и его влияние на незавершённую попытку. ### Не входит в задачу - Распределённая очередь, lease или поддержка нескольких реплик. - Удаление данных. - Ослабление существующих CAS-ограничений `attempt` / `revision` / `source`. - Изменение топологии deployment. ## Подход к реализации Исправить существующих владельцев строк синхронизации и логирования; не вводить параллельный scheduler. Важно: timeout ожидания **не отменяет RPC**. Повторная reservation должна оставаться защищённой fence, а поздняя публикация от старой попытки обязана быть отклонена. Graceful drain добавлять только если сценарий контролируемой остановки действительно покажет такую необходимость. ## Критерии готовности - [ ] Исключение до `membership increment` никогда не сохраняет попытку как `succeeded`; уже полученные частичные успехи сохраняются корректно. - [ ] После однократной ошибки `finish`, `publishSource` или `settle` и последующего восстановления SQLite ожидание завершается, а следующая допустимая попытка может стартовать. - [ ] Устаревший дочерний процесс наблюдается относительно актуальных `attempt` / successor; поздняя публикация старого процесса отклоняется. - [ ] Coordinator различает состояния `first`, `not-due`, `running`, `restart` и `partial`; итог проверяется после terminal barrier. - [ ] Сценарий `app.close()` / restart доказан тестами: нет бесконечного drain и после остановки не создаются новые reservations. ## Проверка Добавить или расширить изолированные тесты SQLite с управляемыми часами и upstream: - инъекция ошибок отдельно в дочерние шаги и в родительский coordinator; - восстановление после ошибки сохранения; - защита от overlap и от поздней публикации; - controlled stop/restart. Сохранить существующие наборы auth/money/publication и выполнить полный `backend verify` на финальном diff. Scheduler stub в `upcoming-events` не считается достаточным доказательством. ## Зависимости Блокирующих задач нет. Проверка cadence (T03) выполняется независимо; cleanup workflow не является prerequisite. ## Основания и затронутый код - Кампания: [main #23](https://git.personal.ksv741.ru/WealthLedger/main/issues/23). - Находки аудита: `BE-002`, `BE-015`, `RISK-001`, `RISK-002`, `TQ-003`, `TA-F02`. - Проверенная ревизия: `0ad84ff4f0b4712abc7e69dc299856f14af6eadd`. - Уверенность: высокая для lifecycle, средняя для shutdown; shutdown пока не подтверждён runtime-проверкой. - Отчёты: [архитектура](https://git.personal.ksv741.ru/WealthLedger/main/src/commit/d07fbfabb1295780952a554185989d2d5b5c8349/audits/2026-10-11/reports/06-backend-architecture-report.md), [надёжность и безопасность](https://git.personal.ksv741.ru/WealthLedger/main/src/commit/d07fbfabb1295780952a554185989d2d5b5c8349/audits/2026-10-11/reports/13-reliability-and-security-report.md), [качество тестов](https://git.personal.ksv741.ru/WealthLedger/main/src/commit/d07fbfabb1295780952a554185989d2d5b5c8349/audits/2026-10-11/reports/08-test-quality-and-effectiveness-report.md). - Затронутый код: [scheduler, строки 93–218](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/0ad84ff4f0b4712abc7e69dc299856f14af6eadd/src/modules/investments/tbank-sync/tbank-hourly.scheduler.ts#L93-L218), `tbank-sync.repository.ts:189–242`, `catalog-enrichment.service.ts:563–590`, `brokerage-operations-sync.service.ts:195–230, 296–301`.
codex-bot added this to the Technical Audit Remediation — 2026-10-11 milestone 2026-10-11 21:41:56 +03:00
codex-bot added the Kind/Bug
Priority
High
2
audit/2026-10-11
labels 2026-10-11 21:41:56 +03:00
codex-bot was assigned by hermes-agent 2026-10-12 00:04:25 +03:00
hermes-agent added this to the WealthLedger — Delivery Board project 2026-10-12 00:04:25 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: WealthLedger/backend#56