Key `TIPCAddress` instances by actor UUID
TIPC service names span the cluster while PIDs remain host-local. Hashing only `(name, pid)` could therefore make same-named actors on different hosts silently share one round-robin service name. Derive the live-runtime seed from `Aid.uid` so the actor UUID separates those names while keeping each identity reproducible. Pin both properties with a deterministic regression test. Review: PR #493 (copilot-pull-request-reviewer[bot],goodboy) https://github.com/goodboy/tractor/pull/493 (this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))wkt/pr493_review
parent
a19a639ddf
commit
d52c78c106
|
|
@ -232,6 +232,43 @@ def test_get_random_collision_resistance():
|
||||||
assert all(addr.is_valid for addr in addrs)
|
assert all(addr.is_valid for addr in addrs)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_random_keys_live_actors_by_uuid(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
):
|
||||||
|
'''
|
||||||
|
TIPC names are cluster-wide while PIDs are host-local. Hashing
|
||||||
|
only `(actor name, pid)` therefore made same-named actors with
|
||||||
|
equal PIDs on different hosts publish one service name, where
|
||||||
|
TIPC silently round-robins connects between them.
|
||||||
|
|
||||||
|
Hold the actor name and PID fixed while changing only its UUID;
|
||||||
|
distinct instances prove the globally unique identity field is
|
||||||
|
now part of the derivation.
|
||||||
|
|
||||||
|
'''
|
||||||
|
monkeypatch.setattr(_tipc.os, 'getpid', lambda: 1616)
|
||||||
|
|
||||||
|
def get_addr(uuid: str) -> TIPCAddress:
|
||||||
|
actor = SimpleNamespace(
|
||||||
|
aid=Aid(
|
||||||
|
name='worker',
|
||||||
|
uuid=uuid,
|
||||||
|
pid=1616,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
_tipc,
|
||||||
|
'current_actor',
|
||||||
|
lambda **kwargs: actor,
|
||||||
|
)
|
||||||
|
return TIPCAddress.get_random()
|
||||||
|
|
||||||
|
first: TIPCAddress = get_addr('actor-uuid-a')
|
||||||
|
second: TIPCAddress = get_addr('actor-uuid-b')
|
||||||
|
assert first._instance != second._instance
|
||||||
|
assert get_addr('actor-uuid-a')._instance == first._instance
|
||||||
|
|
||||||
|
|
||||||
def test_get_random_honors_bindspace():
|
def test_get_random_honors_bindspace():
|
||||||
addr: TIPCAddress = TIPCAddress.get_random(
|
addr: TIPCAddress = TIPCAddress.get_random(
|
||||||
bindspace=TIPC_NODE_SCOPE,
|
bindspace=TIPC_NODE_SCOPE,
|
||||||
|
|
|
||||||
|
|
@ -369,10 +369,11 @@ class TIPCAddress(
|
||||||
between them (verified). I.e. a collision manifests as
|
between them (verified). I.e. a collision manifests as
|
||||||
*silent crosstalk*, not an error.
|
*silent crosstalk*, not an error.
|
||||||
|
|
||||||
So the instance is a `blake2b` digest of a per-call-unique
|
So the instance is a `blake2b` digest of the actor UUID, or
|
||||||
seed, giving a well-spread 32b value. Being a pure fn of the
|
a per-call token outside a live runtime, giving a well-spread
|
||||||
seed it is also *reproducible*, which the (follow-up)
|
32b value. Being a pure fn of the seed it is also
|
||||||
registrar-less discovery fast-path wants.
|
*reproducible*, which the (follow-up) registrar-less
|
||||||
|
discovery fast-path wants.
|
||||||
|
|
||||||
NOTE the residual risk is birthday-bounded: ~1.2e-2 for 10k
|
NOTE the residual risk is birthday-bounded: ~1.2e-2 for 10k
|
||||||
names sharing one `_stype`. See plan 01 §9 for the
|
names sharing one `_stype`. See plan 01 §9 for the
|
||||||
|
|
@ -384,7 +385,7 @@ class TIPCAddress(
|
||||||
err_on_no_runtime=False,
|
err_on_no_runtime=False,
|
||||||
)
|
)
|
||||||
if actor:
|
if actor:
|
||||||
seed: str = f'{actor.aid.name}@{pid}'
|
seed: str = '.'.join(actor.aid.uid)
|
||||||
else:
|
else:
|
||||||
if is_root_process():
|
if is_root_process():
|
||||||
prefix: str = 'no_runtime_root'
|
prefix: str = 'no_runtime_root'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue