Fix `_testing.addr.get_rando_addr` cross-process collisions
Previously the random port was a default-arg expression (`_rando_port: str = random.randint(1000, 9999)`) — evaluated ONCE at module import time, making it a per-process singleton. Two parallel pytest sessions had a 1/9000 birthday-pair chance of picking the same port; when it hit, every `reg_addr`-using test in BOTH runs would cascade-fail with "Address already in use". Switch to per-call `random.randint()` salted with `os.getpid()` so: - within one session: two calls return distinct ports — e.g. `test_tpt_bind_addrs::bind-subset-reg` now actually gets two different reg addrs on the TCP backend (it was silently duplicating before), - across parallel sessions: pid salt biases each process's port choices apart, making cross-run collisions vanishingly rare. Drop the bogus `: str` annotation (was always `int`). UDS already gets per-process isolation via `UDSAddress.get_random()`'s `@<pid>` socket-path suffix, so no change needed there. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-codesubint_forkserver_backend
parent
cbdf1eb6db
commit
7c5dd4d033
|
|
@ -22,6 +22,7 @@ Might be eventually useful to expose as a util set from
|
|||
our `tractor.discovery` subsys?
|
||||
|
||||
'''
|
||||
import os
|
||||
import random
|
||||
from typing import (
|
||||
Type,
|
||||
|
|
@ -31,17 +32,28 @@ from tractor.discovery import _addr
|
|||
|
||||
def get_rando_addr(
|
||||
tpt_proto: str,
|
||||
*,
|
||||
|
||||
# choose random port at import time
|
||||
_rando_port: str = random.randint(1000, 9999)
|
||||
|
||||
) -> tuple[str, str|int]:
|
||||
'''
|
||||
Used to globally override the runtime to the
|
||||
per-test-session-dynamic addr so that all tests never conflict
|
||||
with any other actor tree using the default.
|
||||
|
||||
Cross-process isolation: TCP-port picks salt
|
||||
`random.randint()` with `os.getpid()` so two parallel
|
||||
pytest sessions (e.g. one running `--tpt-proto=tcp` and
|
||||
another `--tpt-proto=uds` concurrently) almost-never
|
||||
collide on the same port. Without the salt, the prior
|
||||
impl's import-time `random.randint(1000, 9999)` default
|
||||
arg was effectively a process-singleton with a 1/9000
|
||||
chance of cross-run collision per pair — and when it
|
||||
happened EVERY `reg_addr`-using test in BOTH runs would
|
||||
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.
|
||||
|
||||
'''
|
||||
addr_type: Type[_addr.Addres] = _addr._address_types[tpt_proto]
|
||||
def_reg_addr: tuple[str, int] = _addr._default_lo_addrs[tpt_proto]
|
||||
|
|
@ -51,9 +63,21 @@ def get_rando_addr(
|
|||
testrun_reg_addr: tuple[str, int|str]
|
||||
match tpt_proto:
|
||||
case 'tcp':
|
||||
# Per-call randomness mixed with `os.getpid()` —
|
||||
# see the docstring above for the cross-process
|
||||
# isolation rationale. The mix means:
|
||||
# - within one pytest session, two calls return
|
||||
# distinct ports (good for tests that need a
|
||||
# second-different-reg-addr in one fn body, e.g.
|
||||
# `test_tpt_bind_addrs::bind-subset-reg`),
|
||||
# - across parallel pytest sessions, the pid bias
|
||||
# makes coincident port choices unlikely.
|
||||
port: int = 1000 + (
|
||||
random.randint(0, 8999) + os.getpid()
|
||||
) % 9000
|
||||
testrun_reg_addr = (
|
||||
addr_type.def_bindspace,
|
||||
_rando_port,
|
||||
port,
|
||||
)
|
||||
|
||||
# NOTE, file-name uniqueness (no-collisions) will be based on
|
||||
|
|
|
|||
Loading…
Reference in New Issue