Commit atomic domain and recovery state together

This commit is contained in:
2026-08-03 05:43:39 +02:00
parent 7e59a7f2b3
commit 842be5edb5
3 changed files with 189 additions and 1 deletions
+88 -1
View File
@@ -1,8 +1,9 @@
from __future__ import annotations
from datetime import datetime, timedelta, timezone
from unittest.mock import patch
from sqlalchemy import create_engine, select
from sqlalchemy import Column, MetaData, String, Table, create_engine, select
from sqlalchemy.orm import sessionmaker
import pytest
@@ -70,6 +71,24 @@ def _start(factory, identity, *, key: str = "build-1"):
)
def _start_atomic(factory, identity, *, key: str = "sync-1"):
return begin_durable_recovery_operation(
factory,
identity=identity,
module_id="connectors",
operation_type="read-snapshot",
idempotency_key=key,
request={"provider_id": "provider-1", "cursor": "revision-1"},
recovery_plan=RecoveryPlan(
mode=RecoveryMode.ATOMIC,
preconditions=("the provider read is non-mutating",),
verification_steps=("compare the committed projection",),
),
precondition_evidence={"provider_mutation": False},
lease_resource_key="connectors:provider-1",
)
def test_durable_operation_commits_before_caller_effect_and_replays_success() -> None:
engine, factory = _fixture()
try:
@@ -102,6 +121,74 @@ def test_durable_operation_commits_before_caller_effect_and_replays_success() ->
engine.dispose()
def test_atomic_terminal_commits_domain_rows_and_recovery_evidence_together() -> None:
engine, factory = _fixture()
metadata = MetaData()
projection = Table(
"test_recovery_projection",
metadata,
Column("id", String(36), primary_key=True),
)
metadata.create_all(engine)
try:
started = _start_atomic(factory, _identity("worker-1", "incarnation-1"))
assert started.operation is not None
with factory() as session:
session.execute(projection.insert().values(id="projection-1"))
started.operation.commit_atomic_success(
session,
evidence={
"verified": True,
"checks": {"projection_id": "projection-1"},
},
)
with factory() as session:
assert session.scalar(select(projection.c.id)) == "projection-1"
operation = session.get(RecoveryOperation, started.operation_id)
assert operation is not None
assert operation.status == RecoveryStatus.SUCCEEDED.value
assert verify_recovery_evidence_chain(session, operation.id)
finally:
engine.dispose()
def test_failed_atomic_commit_rolls_back_domain_and_terminal_checkpoint() -> None:
engine, factory = _fixture()
metadata = MetaData()
projection = Table(
"test_recovery_projection_rollback",
metadata,
Column("id", String(36), primary_key=True),
)
metadata.create_all(engine)
try:
started = _start_atomic(factory, _identity("worker-1", "incarnation-1"))
assert started.operation is not None
with factory() as session:
session.execute(projection.insert().values(id="rolled-back"))
with (
patch.object(session, "commit", side_effect=RuntimeError("commit failed")),
pytest.raises(RuntimeError, match="commit failed"),
):
started.operation.commit_atomic_success(
session,
evidence={
"verified": True,
"checks": {"projection_id": "rolled-back"},
},
)
with factory() as session:
assert session.execute(select(projection.c.id)).all() == []
operation = session.get(RecoveryOperation, started.operation_id)
assert operation is not None
assert operation.status == RecoveryStatus.RUNNING.value
assert verify_recovery_evidence_chain(session, operation.id)
finally:
engine.dispose()
def test_same_fence_cannot_start_duplicate_running_operation() -> None:
engine, factory = _fixture()
try: