ORCH-114: durable transition-ownership lease + expected-stage CAS (fix double-effect/rollback↔done class) #138
Reference in New Issue
Block a user
Delete Branch "feature/ORCH-114-bug-pipeline-stage-transitions"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
ORCH-114 — Ownership-lease для side-effectful переходов стадий + умное восстановление при старте
Закрывает корневой класс инцидент-цепочки ORCH-110/111/112/113: у side-effectful переходов стадий не было единого владения.
advance_stageре-ентерабельна и писала стадию «голым»UPDATE … WHERE id=?(без CAS), а ≥5 акторов (монитор / Plane-webhook / reconciler F-1 / job-reaper / deploy-finalizer) входят в один переход независимо → конкурентный или после-рестартовый повторный вход дважды применял необратимые эффекты (merge_pr / coverage-ratchet / image-rebuild / инициация прод-деплоя) и давал противоречие rollback↔done (инцидент ORCH-111, job 1914 / PR #130).Решение — два комплементарных слоя (оба аддитивные, под единым kill-switch, never-raise)
src/transition_lease.py+ таблицаtransition_lease) — владение на ВХОДЕ в side-effectful регион: второй актор, увидев живого владельца, не стартует тяжёлые под-гейты вовсе (предотвращение, не починка постфактум).db.update_task_stage_cas) — атомарность на ЗАПИСИ стадии: проигравший гонку — аборт без побочных эффектов. Закрывает и 6 путей записи стадии в обходadvance_stage(gitea×5 + plane rollback).Liveness владельца =
owner_pid+owner_boot_id(НЕ heartbeat — блокирующий 900s merge re-test не может бить heartbeat, довод самого ORCH-113) → рестарт-recovery бесплатен (новый boot-id ⇒ прежние lease мгновенно устаревшие ⇒ реклеймятся). Lease без своего TTL: потолок возраста = Tier-3reaper_max_running_s(5400) → сквозной бюджет ORCH-065/109/110/113 не тронут.Интеграция
advance_stageзахватывает lease на side-effectful рёбрах (deploy-staging/deploy), пишет стадию через CAS, освобождает lease вtry/finally(на любом исходе, включая исключение/откат)._finalizer_ownsобобщён с процесс-локального ORCH-113 (Tier-2/deploy-staging) до durable cross-path lease (defer живого, реклейм мёртвого; Tier-3 backstop игнорирует маркер → bounded; реап force-освобождает lease)._try_advance_stage) делают defer при активном lease.main.lifespanзовётrecover_on_startup()послеrequeue_running_jobs.finalizer_liveness.pyне правится — остаётся поведением при выключенном ORCH-114.Инварианты
STAGE_TRANSITIONS/ реестрQG_CHECKS/ семантика и именаcheck_*/ machine-verdict-ключи / схемы существующих таблиц — байт-в-байт (одна аддитивная таблица, без epoch-колонки наtasks). Скоуп self-hosting (transition_lease_repos=""→ толькоorchestrator; enduro не затронут). Kill-switchORCH_TRANSITION_LEASE_ENABLED=false→ CAS вырождается в прежний безусловныйupdate_task_stage, lease инертен, reaper → ORCH-113 fallback → поведение байт-в-байт до ORCH-114.Наблюдаемость
Read-only блок
transition_leaseвGET /queue+ Telegram-алерт на форсированный/устаревший реклейм + опциональныйPOST /transition-lease/release?work_item=<id>.Тесты
tests/test_orch114_transition_ownership.py— TC-01 (обязательный регресс класса ORCH-111: красный до фикса, зелёный после) … TC-14. Полныйpytest tests/зелёный (2048 passed); 4 webhook-теста, шпионившие за удалённымgitea.update_task_stage, переведены на новый путь записиcommit_stage_cas.ADR
docs/work-items/ORCH-114/06-adr/ADR-001-transition-ownership-lease-and-stage-cas.mddocs/architecture/adr/adr-0045-transition-ownership-lease-and-stage-cas.mdRefs: ORCH-114
e4528d26b4to7490f4fac4