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?
|
our `tractor.discovery` subsys?
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
import os
|
||||||
import random
|
import random
|
||||||
from typing import (
|
from typing import (
|
||||||
Type,
|
Type,
|
||||||
|
|
@ -31,17 +32,28 @@ from tractor.discovery import _addr
|
||||||
|
|
||||||
def get_rando_addr(
|
def get_rando_addr(
|
||||||
tpt_proto: str,
|
tpt_proto: str,
|
||||||
*,
|
|
||||||
|
|
||||||
# choose random port at import time
|
|
||||||
_rando_port: str = random.randint(1000, 9999)
|
|
||||||
|
|
||||||
) -> tuple[str, str|int]:
|
) -> tuple[str, str|int]:
|
||||||
'''
|
'''
|
||||||
Used to globally override the runtime to the
|
Used to globally override the runtime to the
|
||||||
per-test-session-dynamic addr so that all tests never conflict
|
per-test-session-dynamic addr so that all tests never conflict
|
||||||
with any other actor tree using the default.
|
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]
|
addr_type: Type[_addr.Addres] = _addr._address_types[tpt_proto]
|
||||||
def_reg_addr: tuple[str, int] = _addr._default_lo_addrs[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]
|
testrun_reg_addr: tuple[str, int|str]
|
||||||
match tpt_proto:
|
match tpt_proto:
|
||||||
case 'tcp':
|
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 = (
|
testrun_reg_addr = (
|
||||||
addr_type.def_bindspace,
|
addr_type.def_bindspace,
|
||||||
_rando_port,
|
port,
|
||||||
)
|
)
|
||||||
|
|
||||||
# NOTE, file-name uniqueness (no-collisions) will be based on
|
# NOTE, file-name uniqueness (no-collisions) will be based on
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue