sched: add durable admission record and admitted run view (T05)
This commit is contained in:
@@ -11,6 +11,7 @@ from wf_artifacts import (
|
||||
DiagnosticSeverity,
|
||||
PinnedRunEnvironment,
|
||||
ResumeReadiness,
|
||||
RunAdmission,
|
||||
RunCheckpoint,
|
||||
RunStore,
|
||||
StoredRunStatus,
|
||||
@@ -43,6 +44,88 @@ def create_pinned_environment(
|
||||
)
|
||||
|
||||
|
||||
def persist_admission(
|
||||
*,
|
||||
store: RunStore,
|
||||
run_id: str,
|
||||
environment: PinnedRunEnvironment,
|
||||
resolved_input: dict[str, object],
|
||||
max_steps: int | None,
|
||||
scheduled_at: datetime | None = None,
|
||||
schedule_id: str | None = None,
|
||||
schedule_revision: int | None = None,
|
||||
deployment_revision: int | None = None,
|
||||
) -> RunAdmission:
|
||||
"""Persist the authoritative admission record before dispatch.
|
||||
|
||||
The occurrence is decided here: the caller must treat this persist as the
|
||||
single authority for the run identity and frozen invocation. A failed
|
||||
durable admission must never dispatch.
|
||||
"""
|
||||
from wf_core.models.json_values import validate_strict_json_value
|
||||
|
||||
frozen = validate_strict_json_value(dict(resolved_input))
|
||||
if not isinstance(frozen, dict):
|
||||
raise ValueError("resolved workflow input must be a JSON object")
|
||||
admission = RunAdmission(
|
||||
id=run_id,
|
||||
environment=environment,
|
||||
resolved_input=frozen,
|
||||
max_steps=max_steps,
|
||||
scheduled_at=scheduled_at,
|
||||
schedule_id=schedule_id,
|
||||
schedule_revision=schedule_revision,
|
||||
deployment_revision=deployment_revision,
|
||||
created_at=datetime.now(UTC),
|
||||
)
|
||||
store.save_admission(admission)
|
||||
return admission
|
||||
|
||||
|
||||
def materialize_admitted_view(
|
||||
*,
|
||||
store: RunStore,
|
||||
admission: RunAdmission,
|
||||
) -> WorkflowRunRecord:
|
||||
"""Materialize the run admission view using the admission identity.
|
||||
|
||||
The view carries no checkpoint, trace, output, or step counts: the outcome
|
||||
is unknown until dispatch completes and persists a stopped checkpoint.
|
||||
"""
|
||||
now = datetime.now(UTC)
|
||||
try:
|
||||
existing = store.get_run(admission.id)
|
||||
except KeyError:
|
||||
existing = None
|
||||
if existing is not None:
|
||||
return existing
|
||||
record = WorkflowRunRecord(
|
||||
id=admission.id,
|
||||
status=StoredRunStatus.ADMITTED,
|
||||
resume_readiness=ResumeReadiness.NOT_APPLICABLE,
|
||||
environment=admission.environment,
|
||||
latest_checkpoint_id=None,
|
||||
created_at=admission.created_at,
|
||||
updated_at=now,
|
||||
)
|
||||
store.save_run(record)
|
||||
return record
|
||||
|
||||
|
||||
def recover_admission_view(*, store: RunStore, run_id: str) -> WorkflowRunRecord:
|
||||
"""Reconcile a missing run view from its admission record.
|
||||
|
||||
Recovery never executes work: it only completes the missing view so a
|
||||
later poll can dispatch the captured invocation exactly once.
|
||||
"""
|
||||
try:
|
||||
return store.get_run(run_id)
|
||||
except KeyError:
|
||||
pass
|
||||
admission = store.get_admission(run_id)
|
||||
return materialize_admitted_view(store=store, admission=admission)
|
||||
|
||||
|
||||
def persist_stopped_run(
|
||||
*,
|
||||
store: RunStore,
|
||||
@@ -65,9 +148,16 @@ def persist_stopped_run(
|
||||
sequence = 1
|
||||
created_at = now
|
||||
if run_id is not None:
|
||||
existing = store.get_run(run_id)
|
||||
created_at = existing.created_at
|
||||
sequence = store.get_latest_checkpoint(run_id).sequence + 1
|
||||
try:
|
||||
existing = store.get_run(run_id)
|
||||
except KeyError:
|
||||
existing = None
|
||||
if existing is not None:
|
||||
created_at = existing.created_at
|
||||
try:
|
||||
sequence = store.get_latest_checkpoint(run_id).sequence + 1
|
||||
except KeyError:
|
||||
sequence = 1
|
||||
|
||||
status = StoredRunStatus(run.status.value)
|
||||
readiness = (
|
||||
@@ -130,6 +220,10 @@ def restore_interrupted_run(
|
||||
def load_stored_run(store: RunStore, run_id: str) -> tuple[WorkflowRunRecord, RunState]:
|
||||
"""Load any stopped run record together with its latest typed checkpoint."""
|
||||
record = store.get_run(run_id)
|
||||
if record.status is StoredRunStatus.ADMITTED:
|
||||
raise ValueError(
|
||||
f"workflow run {run_id!r} is admitted but has no stopped checkpoint yet"
|
||||
)
|
||||
checkpoint = store.get_latest_checkpoint(run_id)
|
||||
return record, load_run_state(checkpoint.state.model_dump(mode="json"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user