From b6184ab74e76c7a3a2f2b1486b7cb231ff3cdeed Mon Sep 17 00:00:00 2001 From: goodboy Date: Mon, 29 Jun 2026 17:22:41 -0400 Subject: [PATCH] Uniquify no-runtime UDS `get_random()` paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit W/o a live runtime `get_random()` named UDS socks purely by `(prefix, pid)`, so two calls in one proc returned the SAME `no_runtime_*@{pid}.sock` — the 2nd `.bind()` then tripped `EADDRINUSE`. Append a per-call `uuid4().hex[:6]` token so each call yields a distinct sockpath. This fixes the 3 `tests.discovery.test_tpt_bind_addrs` uds failures (one registrar + disjoint-bind, two non-registrar binds) where `reg_addr` and a "random" bind addr aliased — the uds CI job's only red, surfaced when this branch rebased onto the newer base that carries those tests. Scoped to the no-runtime branch ON PURPOSE: the runtime `{name}@{pid}` convention stays deterministic so `spawn._reap.unlink_uds_bind_addrs()` can still reconstruct + unlink a SIGKILL'd subactor's sock (#454). Deats, - `_uds.py`: add `uuid4` import + token in the no-runtime branch - `_testing.addr`: tighten `get_rando_addr()` docstring re the intra-proc per-call token (not just pid-keyed namespacing) (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tractor/_testing/addr.py | 11 ++++++++--- tractor/ipc/_uds.py | 12 +++++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/tractor/_testing/addr.py b/tractor/_testing/addr.py index db95889d..58acfd1a 100644 --- a/tractor/_testing/addr.py +++ b/tractor/_testing/addr.py @@ -50,9 +50,14 @@ def get_rando_addr( fight over the bind, cascading into a chain of "Address already in use" failures. - For UDS this concern doesn't apply: `UDSAddress.get_random()` - already builds socket paths from `os.getpid()` so each - pytest process gets its own socket-path namespace. + For UDS the *cross*-process concern is handled by keying + socket paths off `os.getpid()` (each pytest process gets its + own namespace). *Within* a process — where `get_random()` has + no live runtime to name from — it also appends a per-call + `uuid4` token, so two calls (e.g. a `reg_addr` + a distinct + bind addr in one test body) yield distinct sockpaths rather + than aliasing to the same `name@pid.sock` and tripping + `EADDRINUSE`. ''' addr_type: Type[_addr.Addres] = _addr._address_types[tpt_proto] diff --git a/tractor/ipc/_uds.py b/tractor/ipc/_uds.py index 8c57664d..4c07a200 100644 --- a/tractor/ipc/_uds.py +++ b/tractor/ipc/_uds.py @@ -36,6 +36,7 @@ from typing import ( TYPE_CHECKING, ClassVar, ) +from uuid import uuid4 import msgspec import trio @@ -204,7 +205,16 @@ class UDSAddress( else: prefix: str = 'no_runtime_actor' - sockname: str = f'{prefix}@{pid}' + # 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}' sockpath: Path = Path(f'{sockname}.sock') return UDSAddress(