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
@@ -105,6 +105,56 @@ class DurableRecoveryOperation:
session.commit()
self.closed = True
def commit_atomic_success(
self,
session: Session,
*,
evidence: dict[str, Any],
) -> None:
"""Commit domain writes and verified success in one DB transaction."""
self._commit_atomic_terminal(
session,
status=RecoveryStatus.SUCCEEDED,
summary="Operation effects and authoritative state were verified",
kind="verified-success",
evidence=evidence,
)
def commit_atomic_failure(
self,
session: Session,
*,
summary: str,
evidence: dict[str, Any],
) -> None:
"""Commit domain failure evidence and the terminal state atomically."""
self._commit_atomic_terminal(
session,
status=RecoveryStatus.FAILED,
summary=summary,
kind="verified-failure",
evidence=evidence,
)
def commit_atomic_rejection(
self,
session: Session,
*,
summary: str,
evidence: dict[str, Any],
) -> None:
"""Commit a definitive rejection and its domain evidence atomically."""
self._commit_atomic_terminal(
session,
status=RecoveryStatus.REJECTED,
summary=summary,
kind="verified-rejection",
evidence=evidence,
)
def fail(self, *, summary: str, evidence: dict[str, Any]) -> None:
"""Finish a verified, ordinary failure that needs no recovery."""
@@ -325,6 +375,49 @@ class DurableRecoveryOperation:
self.lease_claim = claim
return operation, claim
def _commit_atomic_terminal(
self,
session: Session,
*,
status: RecoveryStatus,
summary: str,
kind: str,
evidence: dict[str, Any],
) -> None:
if status not in {
RecoveryStatus.SUCCEEDED,
RecoveryStatus.FAILED,
RecoveryStatus.REJECTED,
}:
raise ValueError("Unsupported atomic terminal recovery status")
try:
operation, claim = self._locked_and_renewed(session)
if operation.mode != RecoveryMode.ATOMIC.value:
raise RecoveryGuaranteeError(
"Atomic terminal commits require an atomic recovery plan"
)
transition_recovery_operation(
session,
operation,
status=status,
kind=kind,
summary=summary,
evidence=evidence,
failure_summary=(
summary
if status in {RecoveryStatus.FAILED, RecoveryStatus.REJECTED}
else None
),
lease_claim=claim,
)
self._verify_chain(session)
release_lease(session, claim)
session.commit()
except Exception:
session.rollback()
raise
self.closed = True
def _verify_chain(self, session: Session) -> None:
if not verify_recovery_evidence_chain(session, self.operation_id):
raise RecoveryGuaranteeError(