test(issue-18): rec 5 - acceptance5 uses a fresh session and secret per run, widens the idle window, and skips only on proven broker non-delivery
The remote runner subscribes to every job's retained dispatch topic on the public broker and serves the first dispatch signed for its session. With the fixed session name `sim-worker` and a fixed secret, a stale retained dispatch left by an earlier run verified fine and was served instead of this run's job, which then timed out: in the observed failure the runner logged `published started/completed ... job=5eb1abcf` and `DONE rc=0` while the orchestrator waited on job 5ee3fbcf with nothing received. Each run now uses a random session name and secret, so stale dispatches cannot match. Also, per the plan: the no-event window is 10 x 2 s instead of 3 x 2 s, and a failure skips only when the runner provably published `started` for the orchestrator's own job, the orchestrator subscribed to that job's topic, and received and dropped nothing for it (broker non-delivery). Every other failure still fails. The runner's own `started --heartbeat` already covers the window. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017R3Eg7MEpSWnm1oB9ZPP6G
This commit is contained in:
1 parent
e57663ac4c
commit
7bc3e1a288
1 file changed
+39
-4
@@ -24,6 +24,7 @@ sandbox); each stays under a minute.
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
@@ -1160,17 +1161,49 @@ def test_acceptance3_lost_heartbeats_close_the_job_and_a_late_completed_is_refus
|
||||
# 6. remote transport (broker): no shared disk, no pane, only the wire
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _skip_if_broker_did_not_deliver(reg: Path, runner_log: Path) -> None:
|
||||
"""acceptance5 runs over a public broker. The remote runner already covers
|
||||
the idle window with `started --heartbeat` (remote.py handle_dispatch). The
|
||||
observed failures were of one kind: the runner published started and
|
||||
completed successfully, the orchestrator had subscribed to the right topic,
|
||||
and it received nothing at all for the job - the broker did not deliver.
|
||||
Only that proven non-delivery skips; a drop (HMAC, identity), a wrong
|
||||
topic, or a runner that failed to publish still fails."""
|
||||
log = runner_log.read_text(errors="replace")
|
||||
m = re.search(r"published started seq=1 job=(\w+)", log)
|
||||
if not m:
|
||||
return
|
||||
jid = m.group(1)
|
||||
rec = reg / f"{jid}.json"
|
||||
if not rec.exists():
|
||||
return
|
||||
topic = json.loads(rec.read_text()).get("topic_prefix") or ""
|
||||
out = reg / f"{jid}.subscriber.out"
|
||||
sub = out.read_text(errors="replace") if out.exists() else ""
|
||||
if (topic and f"SUBSCRIBED {topic}/events" in sub
|
||||
and f"job={jid}" not in sub and f"drop event for job {jid}" not in sub):
|
||||
pytest.skip(f"public broker did not deliver the runner's events to the orchestrator "
|
||||
f"(runner published started for {jid}; subscriber received nothing for it)")
|
||||
|
||||
|
||||
def test_acceptance5_remote_runner_completes_a_job_over_the_broker_only(tmp_path):
|
||||
orch = tmp_path / "orchestrator"
|
||||
remote = tmp_path / "remote-host"
|
||||
for d in (orch, remote):
|
||||
(d / "jobs").mkdir(parents=True)
|
||||
secret = "issue18-shared-secret"
|
||||
# A fresh session name and secret per run: dispatches are retained on the
|
||||
# (public) broker and the runner subscribes to every job's dispatch topic,
|
||||
# so with fixed values a stale dispatch left by an earlier run was served
|
||||
# instead of this run's job, which then timed out (observed: runner DONE
|
||||
# for one job id, the orchestrator waiting on another).
|
||||
tag = os.urandom(4).hex()
|
||||
session = f"sim-worker-{tag}"
|
||||
secret = f"issue18-shared-secret-{tag}"
|
||||
runner_env = _env(remote, {"MAM_DISPATCH_SECRET": secret, "MAM_JOBS_DIR": str(remote / "jobs")})
|
||||
runner_log = remote / "runner.log"
|
||||
with open(runner_log, "wb") as log:
|
||||
runner = subprocess.Popen(
|
||||
[sys.executable, str(REMOTE_PY), "run", "--session", "sim-worker", "--registry-dir", str(remote / "jobs"),
|
||||
[sys.executable, str(REMOTE_PY), "run", "--session", session, "--registry-dir", str(remote / "jobs"),
|
||||
"--exec", 'echo "remote work done for $JOB_ID"', "--once", "--timeout", "150"],
|
||||
cwd=str(remote), env=runner_env, stdout=log, stderr=subprocess.STDOUT)
|
||||
try:
|
||||
@@ -1181,11 +1214,13 @@ def test_acceptance5_remote_runner_completes_a_job_over_the_broker_only(tmp_path
|
||||
pytest.skip("broker unreachable: " + runner_log.read_text(errors="replace")[-300:])
|
||||
|
||||
reg = orch / "jobs"
|
||||
proc, env = _submit(orch, reg, "remote:sim-worker", "remote",
|
||||
["--heartbeat", "2", "--heartbeat-misses", "3", "--ack-timeout", "60"],
|
||||
proc, env = _submit(orch, reg, f"remote:{session}", "remote",
|
||||
["--heartbeat", "2", "--heartbeat-misses", "10", "--ack-timeout", "60"],
|
||||
env_extra={"MAM_DISPATCH_SECRET": secret}, timeout_s=120)
|
||||
stdout, stderr = proc.communicate(timeout=180)
|
||||
jid = _wait_job_id(reg)
|
||||
if proc.returncode != 0:
|
||||
_skip_if_broker_did_not_deliver(reg, runner_log)
|
||||
assert proc.returncode == 0, f"rc={proc.returncode}\n{stdout}\n{stderr}\nrunner:\n{runner_log.read_text()}"
|
||||
assert "subscriber received started" in stderr or "advanced to 'running'" in stderr
|
||||
assert _job(reg, jid)["status"] == "completed", \
|
||||
|
||||
Reference in new issue
Block a user