From 51326f4b7a6f89a7bb039e2cc88bece9de4596d3 Mon Sep 17 00:00:00 2001 From: goodboy Date: Mon, 29 Jun 2026 18:51:26 -0400 Subject: [PATCH] Fix uds `get_random` reaper-regex break (+2 nits) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot's 2nd-pass review (PR #468) caught a regression I landed in the uds fix: `UDSAddress.get_random()` put the per-call token AFTER `@{pid}` (`no_runtime_*@{pid}.{token}.sock`), which breaks the `tractor._testing._reap` matcher `^(?P.+)@(?P\d+)\.sock$` — so no-runtime orphan socks stopped matching and never got reaped/attributed. Move the token INTO the name (`{prefix}.{token}@{pid}.sock`) so the canonical `@{pid}.sock` suffix stays intact for both that regex and the `spawn._reap` reconstruction; also bump the token to 8 hex chars. Also two robustness nits from the same review, - `tests.conftest._measure_sustained_headroom()`: guard `frac <= 0` before `1./frac` — a 0/parked-core freq read would `ZeroDivisionError`, get swallowed by the broad `except` into a 1.0 (no-throttle), defeating the probe on the exact broken box it should flag; read 0 as max throttle. - `scripts/cpu-perf-check`: mark the burn procs `daemon=True` and wrap sampling in `try/finally` so a Ctrl-C / error reaps them instead of leaving stray CPU hogs. Regressed-by: 09c50f49 (uds no-runtime token placed after `@pid`) Found-via: Copilot review #4595803812 (`_testing._reap` regex) Review: PR #468 (Copilot) https://github.com/goodboy/tractor/pull/468#pullrequestreview-4595803812 (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- scripts/cpu-perf-check | 25 ++++++++++++++++--------- tests/conftest.py | 6 ++++++ tractor/ipc/_uds.py | 22 ++++++++++++---------- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/scripts/cpu-perf-check b/scripts/cpu-perf-check index 35622e92..2a15a6d0 100755 --- a/scripts/cpu-perf-check +++ b/scripts/cpu-perf-check @@ -112,21 +112,28 @@ def main( print(f'\n=== sustained {ncpu}-core load ({secs:.0f}s) — the real test ===') stop: float = time.perf_counter() + secs procs = [ - mp.Process(target=_burn, args=(stop,)) + mp.Process(target=_burn, args=(stop,), daemon=True) for _ in range(ncpu) ] for p in procs: p.start() - # skip the initial ~0.6s ramp, then sample steady-state + # skip the initial ~0.6s ramp, then sample steady-state. + # `try/finally` so a Ctrl-C / sampling error still reaps the + # burners instead of leaving stray CPU hogs (daemon=True is the + # backstop should we exit abnormally before the join). samples: list[int] = [] - time.sleep(0.6) - while time.perf_counter() < stop - 0.2: - if (fr := _cur_freqs_mhz()): - samples.append(sum(fr) // len(fr)) - time.sleep(0.3) - for p in procs: - p.join() + try: + time.sleep(0.6) + while time.perf_counter() < stop - 0.2: + if (fr := _cur_freqs_mhz()): + samples.append(sum(fr) // len(fr)) + time.sleep(0.3) + finally: + for p in procs: + p.terminate() + for p in procs: + p.join() if not (samples and pkg_max): print(' could not sample cur freq — assume OK') diff --git a/tests/conftest.py b/tests/conftest.py index 4acf9379..db2528f6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -236,6 +236,12 @@ def _measure_sustained_headroom( # under-shoots and still trips the marginal cases). if frac >= throttle_gate: return 1. + # a 0/parked-core freq read would `ZeroDivisionError` the + # inverse below — silently swallowed by the outer `except` + # into a 1.0 (no-throttle), defeating the probe on exactly + # the broken box it should flag; read 0 as max throttle. + if frac <= 0: + return max_headroom return min(max_headroom, 1. / frac) except Exception: diff --git a/tractor/ipc/_uds.py b/tractor/ipc/_uds.py index 4c07a200..d1e9d2f6 100644 --- a/tractor/ipc/_uds.py +++ b/tractor/ipc/_uds.py @@ -205,16 +205,18 @@ class UDSAddress( else: prefix: str = 'no_runtime_actor' - # XXX, no live actor -> no `Aid` to key off, so append a - # per-CALL token: w/o a runtime the sockname is otherwise - # a pure fn of `(prefix, pid)`, so two `get_random()` - # calls in one proc alias to the SAME sockpath and the - # 2nd `.bind()` trips `EADDRINUSE`. The token makes each - # call a distinct file. Safe vs `._reap` cleanup which - # only reconstructs the runtime `{name}@{pid}` convention - # (above), never these `no_runtime_*` socks. - token: str = uuid4().hex[:6] - sockname: str = f'{prefix}@{pid}.{token}' + # XXX, no live actor -> no `Aid` to key off, so mix a + # per-CALL token into the NAME part for uniqueness: + # w/o a runtime the sockname is otherwise a pure fn of + # `(prefix, pid)`, so two `get_random()` calls in one + # proc alias to the SAME sockpath and the 2nd `.bind()` + # trips `EADDRINUSE`. Token goes BEFORE `@{pid}` so the + # canonical `...@{pid}.sock` suffix stays intact for the + # reapers keyed off it: `._testing._reap`'s + # `(?P.+)@(?P\d+)\.sock` regex, and the + # `spawn._reap` `{name}@{pid}.sock` reconstruction. + token: str = uuid4().hex[:8] + sockname: str = f'{prefix}.{token}@{pid}' sockpath: Path = Path(f'{sockname}.sock') return UDSAddress(