Skip to content

Commit 646b1e7

Browse files
replay: stop baseline self-Suspend; rely on eviction only
random/resource-evict no longer call Suspend after phases, matching semantic-score so CI is not killed by suspend timeouts under knockMu. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 732cde9 commit 646b1e7

1 file changed

Lines changed: 21 additions & 58 deletions

File tree

hack/replay-agent-semantic.py

Lines changed: 21 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
Uses dataset arrivals + phase_spans + task_profile only.
77
88
Baseline policies (random / resource-evict):
9-
Create → Resume once → hold Worker through all phases → Suspend
9+
Create → Resume once → hold Worker through all phases (never self-Suspend);
10+
unlocked peers may be stolen by knockers; leftovers cleaned up at policy end.
1011
1112
semantic-score (+ l1 ablation):
1213
Create (suspended) → llm_wait locally (heartbeat, no slot) →
@@ -30,8 +31,8 @@
3031
Ablation aliases: `semantic-score-l1` → PRIOR_MIX=0; `semantic-score` → PRIOR_MIX=0.3.
3132
3233
Concurrency: max_inflight defaults to workers+1 (slot contention). Concurrent
33-
Resume knockers are allowed so semantic-score can rank waiters; Suspend is
34-
serialized per sandbox id only (avoid double-checkpoint of the same id).
34+
Resume knockers are allowed so semantic-score can rank waiters. No client
35+
self-Suspend; only CP eviction archives running sandboxes.
3536
"""
3637

3738
from __future__ import annotations
@@ -692,48 +693,19 @@ def _phase_needs_worker(phase: str, lock: bool) -> bool:
692693
return lock or phase == "tool_loop"
693694

694695

695-
def _try_suspend(
696-
client: CPClient,
697-
sandbox_id: str,
698-
sandbox_locks: dict[str, threading.Lock],
699-
locks_mu: threading.Lock,
700-
) -> bool:
701-
"""Suspend if running. Returns False if already off-worker / not running."""
702-
with _sandbox_lock(sandbox_locks, locks_mu, sandbox_id):
703-
try:
704-
client.suspend(sandbox_id)
705-
return True
706-
except RuntimeError as e:
707-
msg = str(e).lower()
708-
if "suspended" in msg or "want running" in msg or "want suspended" in msg:
709-
return False
710-
raise
711-
712-
713-
def _sandbox_lock(
714-
sandbox_locks: dict[str, threading.Lock],
715-
locks_mu: threading.Lock,
716-
sandbox_id: str,
717-
) -> threading.Lock:
718-
with locks_mu:
719-
lk = sandbox_locks.get(sandbox_id)
720-
if lk is None:
721-
lk = threading.Lock()
722-
sandbox_locks[sandbox_id] = lk
723-
return lk
724-
725-
726696
def drive_phases_hold(
727697
client: CPClient,
728698
sandbox_id: str,
729699
spans: list[dict[str, Any]],
730700
speed: float,
731701
workflow_id: str,
732702
min_lock_sec: float,
733-
sandbox_locks: dict[str, threading.Lock],
734-
locks_mu: threading.Lock,
735703
) -> None:
736-
"""Baseline: already Running; hold the Worker through all spans, then Suspend."""
704+
"""Baseline: already Running; hold the Worker through all spans.
705+
706+
Never self-Suspend: leave unlocked idle so knockers can steal; leftovers
707+
are removed by policy-end cleanup_sandboxes.
708+
"""
737709
if not spans:
738710
spans = [{"phase": "idle", "lock": False, "t_start_ms": 0, "t_end_ms": 500}]
739711
for span in spans:
@@ -755,7 +727,15 @@ def drive_phases_hold(
755727
)
756728
if dur > 0:
757729
time.sleep(dur)
758-
_try_suspend(client, sandbox_id, sandbox_locks, locks_mu)
730+
client.post_semantic(
731+
sandbox_id,
732+
{
733+
"version": "v1",
734+
"phase": "idle",
735+
"lock": False,
736+
"workflowID": workflow_id,
737+
},
738+
)
759739

760740

761741
def drive_phases_semantic(
@@ -765,8 +745,6 @@ def drive_phases_semantic(
765745
speed: float,
766746
workflow_id: str,
767747
min_lock_sec: float,
768-
sandbox_locks: dict[str, threading.Lock],
769-
locks_mu: threading.Lock,
770748
l3: L3Tracker,
771749
) -> float:
772750
"""semantic-score path: llm_wait needs no slot until a Worker is held;
@@ -854,8 +832,6 @@ def run_one_session(
854832
index: int,
855833
total: int,
856834
inflight: threading.Semaphore,
857-
sandbox_locks: dict[str, threading.Lock],
858-
locks_mu: threading.Lock,
859835
l3: L3Tracker,
860836
semantic_slots: bool,
861837
) -> SessionJob:
@@ -906,8 +882,6 @@ def run_one_session(
906882
speed,
907883
sid_label,
908884
min_lock_sec,
909-
sandbox_locks,
910-
locks_mu,
911885
l3,
912886
)
913887
job.resumed = True
@@ -924,20 +898,13 @@ def run_one_session(
924898
speed,
925899
sid_label,
926900
min_lock_sec,
927-
sandbox_locks,
928-
locks_mu,
929901
)
930-
job.suspended = True
902+
# Eviction may have Suspended us; never self-Suspend.
903+
job.suspended = _sandbox_state(client, job.sandbox_id) == "suspended"
931904
log(f"[session {index+1}/{total}] done {sid_label}")
932905
except Exception as e: # noqa: BLE001 — per-session errors collected in report
933906
job.error = f"{sid_label}: {e}"
934907
log(f"[session] FAIL {job.error}")
935-
# Baseline may still try a best-effort Suspend; semantic never self-Suspends.
936-
if job.sandbox_id and not semantic_slots:
937-
try:
938-
_try_suspend(client, job.sandbox_id, sandbox_locks, locks_mu)
939-
except RuntimeError:
940-
pass
941908
finally:
942909
if held:
943910
inflight.release()
@@ -977,8 +944,6 @@ def run_policy(
977944
if max_inflight <= 0:
978945
max_inflight = max(2, worker_count + 1)
979946
inflight = threading.Semaphore(max_inflight)
980-
sandbox_locks: dict[str, threading.Lock] = {}
981-
locks_mu = threading.Lock()
982947
# Pool large enough that arrivals can block on the semaphore without starving.
983948
pool_n = min(total, max(max_inflight * 2, 8))
984949
mode = "semantic tool-only slots" if semantic_slots else "hold-all-phases"
@@ -1008,8 +973,6 @@ def run_policy(
1008973
i,
1009974
total,
1010975
inflight,
1011-
sandbox_locks,
1012-
locks_mu,
1013976
l3,
1014977
semantic_slots,
1015978
)
@@ -1154,7 +1117,7 @@ def write_compare(results: list[PolicyResult], path: Path) -> None:
11541117
"- `evict_llm_wait`: preferred interrupt window for semantic-score.",
11551118
"- `mid_tool_rate = mid_tool / suspend` (0 if no suspends).",
11561119
"- `vic_hard` / `vic_easy` / `hard_rate`: eviction victims attributed via "
1157-
"running→suspended peers at Resume time (excludes voluntary end Suspend).",
1120+
"running→suspended peers at Resume time (eviction only; no client self-Suspend).",
11581121
"- `hard_rate = vic_hard / (hard+mid+easy+inactive)`; lower favors semantic-score L3.",
11591122
"- `resume_hard_s` / `resume_easy_s`: mean client Resume RTT for that cohort "
11601123
"(proxy for wait+restore; not OTel resume_wait).",

0 commit comments

Comments
 (0)