sched: sibling-distinct lock identity; reject shared-store overlaps (R4 wave 4)

This commit is contained in:
lda
2026-09-09 10:20:03 +07:00 Verified
parent d2c0867f76
commit c0d36d6b61
4 changed files with 168 additions and 32 deletions
+87
View File
@@ -205,3 +205,90 @@ def test_canonical_windows_path_handling_retained(tmp_path: Path) -> None:
assert canonical_store_path("C:\\Temp\\SCHED") == canonical_store_path(
"c:/temp/sched"
)
def test_non_sibling_store_pairs_have_no_lock_identity(tmp_path: Path) -> None:
"""Only distinct siblings under one composition root share a lock file.
Cross pairs that reuse one protected store (shared schedule store or
shared run store), nested pairs, and same-directory dual use would map
to different lock files while covering the same store files, so they
have no lock identity at all: guards must reject them outright.
"""
overlap = tmp_path / "overlap"
sched_store, run_store = _stores(overlap)
other_sched, other_runs = _stores(tmp_path / "other")
# Shared schedule store, different run store.
assert canonical_lock_root(sched_store.root, other_runs.root) is None
# Shared run store, different schedule store.
assert canonical_lock_root(other_sched.root, run_store.root) is None
# Nested pairs: one store inside the other.
assert canonical_lock_root(overlap, sched_store.root) is None
assert canonical_lock_root(sched_store.root, overlap) is None
# Same directory serving as both stores.
assert canonical_lock_root(sched_store.root, sched_store.root) is None
def test_shared_schedule_store_cannot_gain_independent_authority(
tmp_path: Path,
) -> None:
overlap = tmp_path / "overlap"
sched_store, run_store = _stores(overlap)
_, other_runs = _stores(tmp_path / "other")
owner = SchedulerOwnership(overlap, owner="owner").acquire()
intruder = SchedulerOwnership(tmp_path, owner="intruder").acquire()
try:
assert owner.covers(sched_store.root, run_store.root) is True
# The cross pair reuses the protected schedule store but maps to
# no lock identity, so no held lock can authorize it.
assert intruder.covers(sched_store.root, other_runs.root) is False
assert owner.covers(sched_store.root, other_runs.root) is False
now = datetime.now(UTC)
sched_recovery.recover(
schedule_store=sched_store,
run_store=run_store,
now=now,
ownership=owner,
)
before_runs = FileRunStore(overlap / "runs").list_runs()
with pytest.raises(SecondOwnerError):
sched_recovery.recover(
schedule_store=FileScheduleStore(overlap / "sched"),
run_store=FileRunStore(tmp_path / "other" / "runs"),
now=now,
ownership=intruder,
)
# Rejection happens before any write to either store.
assert FileRunStore(overlap / "runs").list_runs() == before_runs
assert FileRunStore(tmp_path / "other" / "runs").list_runs() == []
assert FileRunStore(tmp_path / "other" / "runs").list_admissions() == []
finally:
owner.release()
intruder.release()
def test_shared_run_store_cannot_gain_independent_authority(
tmp_path: Path,
) -> None:
overlap = tmp_path / "overlap"
sched_store, run_store = _stores(overlap)
other_sched, _ = _stores(tmp_path / "other")
owner = SchedulerOwnership(overlap, owner="owner").acquire()
intruder = SchedulerOwnership(tmp_path, owner="intruder").acquire()
try:
assert owner.covers(sched_store.root, run_store.root) is True
assert intruder.covers(other_sched.root, run_store.root) is False
assert owner.covers(other_sched.root, run_store.root) is False
now = datetime.now(UTC)
with pytest.raises(SecondOwnerError):
sched_recovery.recover(
schedule_store=FileScheduleStore(tmp_path / "other" / "sched"),
run_store=FileRunStore(overlap / "runs"),
now=now,
ownership=intruder,
)
assert FileRunStore(overlap / "runs").list_runs() == []
assert FileRunStore(overlap / "runs").list_admissions() == []
finally:
owner.release()
intruder.release()