sched: require proven ownership at poll/admin/recovery entries (F5)

This commit is contained in:
lda
2026-09-08 11:24:40 +07:00 Verified
parent 15bd472bdc
commit 993ed07fd3
7 changed files with 293 additions and 22 deletions
+9
View File
@@ -15,6 +15,7 @@ from wf_artifacts.runs.models import StoredRunStatus
from wf_artifacts.runs.store import FileRunStore
from wf_scheduling.calendar import OneShotSource
from wf_scheduling.models import Schedule
from wf_scheduling.ownership import SchedulerOwnership
from wf_scheduling.poll import SCAN_CAP, Scheduler
from wf_scheduling.prepare import SchedulePreparer
from wf_scheduling.store import FileScheduleStore
@@ -87,6 +88,7 @@ def _harness(
capacity=capacity,
preparer=preparer,
dispatcher=ScriptedDispatcher(script),
ownership=SchedulerOwnership(tmp_path / "sched", owner="test").acquire(),
)
return sched, sched_store, run_store, sources
@@ -135,6 +137,7 @@ def test_overlap_skip_blocks_and_late_drops() -> None:
sched.poll(t0 + timedelta(minutes=30))
kinds = [(r["kind"], r["resolved_at"]) for r in _history(store, "a")]
assert any(k == "skipped-misfire" for k, _ in kinds)
sched.ownership.release()
def test_latest_coalesces_to_one_candidate_and_no_double_admit() -> None:
@@ -167,6 +170,7 @@ def test_latest_coalesces_to_one_candidate_and_no_double_admit() -> None:
2026, 9, 8, 13, 0
)
assert store.get_candidate("h") is None
sched.ownership.release()
def test_parallel_limits_and_interrupted_slots() -> None:
@@ -206,6 +210,7 @@ def test_parallel_limits_and_interrupted_slots() -> None:
r["kind"] == "skipped-overlap" and "12:15" in str(r["resolved_at"])
for r in _history(store, "p")
)
sched.ownership.release()
def test_pause_is_not_downtime() -> None:
@@ -238,6 +243,7 @@ def test_pause_is_not_downtime() -> None:
]
assert "2026-09-08T10:00:00+00:00" not in admitted
assert "2026-09-08T11:00:00+00:00" not in admitted
sched.ownership.release()
def test_long_downtime_is_bounded(tmp_path: Path) -> None:
@@ -254,6 +260,7 @@ def test_long_downtime_is_bounded(tmp_path: Path) -> None:
assert (
len([r for r in _history(store, "m") if r["kind"] == "interval-summary"]) == 1
)
sched.ownership.release()
def test_fairness_slow_schedule_not_starved(tmp_path: Path) -> None:
@@ -277,6 +284,7 @@ def test_fairness_slow_schedule_not_starved(tmp_path: Path) -> None:
runs.save_run(run.model_copy(update={"status": StoredRunStatus.COMPLETED}))
sched.poll(t0 + timedelta(seconds=30))
assert any(r["kind"] == "admitted" for r in _history(store, "slow"))
sched.ownership.release()
def test_capacity_wait_then_expire_for_skip(tmp_path: Path) -> None:
@@ -289,3 +297,4 @@ def test_capacity_wait_then_expire_for_skip(tmp_path: Path) -> None:
cast(ScriptedDispatcher, sched.dispatcher).script = {"*": "complete"}
sched.poll(t0 + timedelta(seconds=30))
assert len([r for r in _history(store, "a") if r["kind"] == "admitted"]) == 1
sched.ownership.release()