test: harden scheduling review boundaries

This commit is contained in:
lda
2026-09-10 02:17:27 +07:00 Verified
parent ed70223c1b
commit f8ed8e2192
10 changed files with 231 additions and 25 deletions
+42 -3
View File
@@ -1,7 +1,10 @@
from __future__ import annotations
import json
import threading
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from typing import Any
import pytest
@@ -69,7 +72,7 @@ def test_file_store_round_trips_deployment(tmp_path) -> None:
def test_concurrent_deployment_saves_advance_revision_without_lost_updates(
tmp_path,
tmp_path, monkeypatch
) -> None:
store = FileWorkflowArtifactStore(tmp_path)
store.save_deployment(
@@ -88,9 +91,45 @@ def test_concurrent_deployment_saves_advance_revision_without_lost_updates(
for version in range(2, 10)
]
with ThreadPoolExecutor(max_workers=len(updates)) as executor:
list(executor.map(store.save_deployment, updates))
start_gate = threading.Barrier(len(updates))
read_gate = threading.Barrier(len(updates))
read_state_lock = threading.Lock()
active_reads = 0
max_active_reads = 0
deployment_path = (store.deployments_dir / "concurrent.personal.json").resolve()
original_read_text = Path.read_text
def coordinated_read(path: Path, *args: Any, **kwargs: Any) -> str:
nonlocal active_reads, max_active_reads
if path.resolve() != deployment_path:
return original_read_text(path, *args, **kwargs)
with read_state_lock:
active_reads += 1
max_active_reads = max(max_active_reads, active_reads)
try:
# A broken implementation reaches this gate from every worker
# after reading the same revision. The real store's lock lets
# only one worker enter the read/modify/write window, so the
# gate times out once and later workers proceed immediately.
try:
read_gate.wait(timeout=0.5)
except threading.BrokenBarrierError:
pass
return original_read_text(path, *args, **kwargs)
finally:
with read_state_lock:
active_reads -= 1
monkeypatch.setattr(Path, "read_text", coordinated_read)
def save(deployment: WorkflowDeployment) -> None:
start_gate.wait(timeout=10)
store.save_deployment(deployment)
with ThreadPoolExecutor(max_workers=len(updates)) as executor:
list(executor.map(save, updates))
assert max_active_reads == 1
assert store.get_deployment("concurrent.personal").revision == 9