Fix uds `get_random` reaper-regex break (+2 nits)
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<name>.+)@(?P<pid>\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
test_cpu_throttling
parent
63498a80c2
commit
51326f4b7a
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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<name>.+)@(?P<pid>\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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue