fix: finish cancelled resume cleanup

This commit is contained in:
lda
2026-09-09 23:26:18 +07:00 Verified
parent 6a87711f89
commit 5d364f005a
3 changed files with 95 additions and 19 deletions
+52
View File
@@ -36,6 +36,7 @@ from wf_scheduling.lifecycle import (
from wf_scheduling.models import Schedule
from wf_scheduling.ownership import SchedulerOwnership, SecondOwnerError
from wf_scheduling.poll import Scheduler
from wf_scheduling.resume_gate import SchedulerResumeGate
from wf_scheduling.store import FileScheduleStore
@@ -405,6 +406,57 @@ asyncio.run(main())
assert "registration-stop-complete" in output
async def test_cancelled_resume_release_waits_for_lock_cleanup(
tmp_path: Path,
) -> None:
"""Cancellation cannot skip scheduled-resume slot cleanup."""
intended = ts(2026, 9, 8, 12, 0)
service = _service(tmp_path, ScriptedRuntime("interrupt"))
lock_held = threading.Event()
release_lock = threading.Event()
holder: threading.Thread | None = None
release_task: asyncio.Task[Any] | None = None
try:
await service.start()
service.schedule_store.create_schedule(_sched_model("a", intended))
await service.poll_once(intended + timedelta(seconds=1))
run_id = _only_run_id(service.run_store)
await _wait_for(lambda: service.live_executions == 0)
gate = SchedulerResumeGate(service)
assert await gate.acquire(run_id, owner_task=asyncio.current_task())
def hold_service_lock() -> None:
service._lock.acquire()
lock_held.set()
release_lock.wait(5)
service._lock.release()
holder = threading.Thread(target=hold_service_lock, daemon=True)
holder.start()
assert await asyncio.to_thread(lock_held.wait, 5)
release_task = asyncio.create_task(gate.release(run_id))
await asyncio.sleep(0.05)
assert not release_task.done()
release_task.cancel()
release_lock.set()
with pytest.raises(asyncio.CancelledError):
await asyncio.wait_for(release_task, 5)
assert not service.run_store.is_executing(run_id)
assert run_id not in service._live_resumes
assert run_id not in service._resume_tasks
finally:
release_lock.set()
if release_task is not None and not release_task.done():
release_task.cancel()
await asyncio.gather(release_task, return_exceptions=True)
if holder is not None:
holder.join(timeout=5)
await service.stop()
async def test_scheduled_interrupt_stays_resumable(tmp_path: Path) -> None:
intended = ts(2026, 9, 8, 12, 0)
service = _service(tmp_path, ScriptedRuntime("interrupt"))