Proto-key the unwrapped-addr form in the plans
Shape-matching in `wrap_address()` doesn't survive 4 backends and
the plans were papering over it: TIPC's natural unwrapped form is
a `(str, int)`, indistinguishable from `TCPAddress`, and iroh's
is a `(str, str)`, which the *existing* UDS case
(`case (_, filename) if type(filename) is str`) already swallows.
So the contract doc (§1.1) now carries the conclusion as a
**recommended prerequisite for all three backends**: make the
unwrapped form carry an explicit proto-key spelled with the
`multiaddr` protocol name — `('tcp', host, port)`,
`('unix', path)`, `('tipc', stype, inst, scope)`. `wrap_address()`
then collapses from an order-sensitive `match` to
`_address_types[addr[0]]` and the whole collision class stops
existing, while the on-wire form finally agrees w/
`mk_maddr()`/`parse_maddr()` instead of being an independent
invention.
Two consequences spelled out: it's a wire-format change
(`SpawnSpec`, `_root_mailbox`, `_registry_addrs`) + every fixture
+ downstream config, so it wants its own migration commit landed
*before* any new backend; and it's the moment to stop handing raw
tuples to users at all — `Address` becomes the public currency
and `UnwrappedAddress` an internal serialization detail, the same
discipline `ipaddress` uses (you pass `IPv4Address`, never a
4-tuple).
Plan 01 §2.2 is rewritten to match and to explicitly **retract**
its own earlier `('tipc:<stype>:<scope>', instance)` self-tagging
prefix hack — it keeps `wrap_address()` order-sensitive and does
nothing for the iroh/UDS collision, so the doc says don't
resurrect it. Registration checklist item 4 likewise becomes "do
the migration first, then this is a one-line `_address_types`
entry".
Also seeds a `/tipc` multiaddr-spec submission as a follow-up,
mirroring the `wg` track (multiformats/py-multiaddr#107/#108 + gh
(this patch was generated in some part by `claude-code` using `claude-opus-5` (`anthropic`))
ng_tpts_planning
parent
f60bdc44c7
commit
208876cc3b
|
|
@ -70,6 +70,40 @@ Hard constraints learned from the existing two:
|
|||
`wrap_address()` (`_addr.py:230`), you have a bug that
|
||||
manifests as the wrong transport being loaded — the file's own
|
||||
`XXX NOTE` warns about precisely this.
|
||||
|
||||
⚠️ **and shape-matching does not survive 4 backends.** Adding
|
||||
TIPC and iroh breaks it outright: TIPC's natural form is a
|
||||
`(str, int)` — indistinguishable from `TCPAddress` — and
|
||||
iroh's is a `(str, str)`, which the *existing* UDS case
|
||||
(`case (_, filename) if type(filename) is str`) already
|
||||
swallows. Ordering hacks and prefix-tagging (an earlier
|
||||
revision of plan 01 proposed `('tipc:<stype>:<scope>', inst)`)
|
||||
paper over it at best.
|
||||
|
||||
**The fix, and the recommended prerequisite for all three
|
||||
backends: make the unwrapped form carry an explicit
|
||||
proto-key, using the `multiaddr` protocol name as the
|
||||
canonical spelling** — `('tcp', host, port)`,
|
||||
`('unix', path)`, `('udp', ...)`, `('tipc', stype, inst,
|
||||
scope)`. Then `wrap_address()` collapses from an
|
||||
order-sensitive `match` to `_address_types[addr[0]]`, and the
|
||||
whole collision class stops existing. Note this *also* aligns
|
||||
the on-wire form with `mk_maddr()`/`parse_maddr()`, so the two
|
||||
representations stop being independent inventions.
|
||||
|
||||
Two consequences to plan for:
|
||||
- it's a **wire-format change** (`SpawnSpec`,
|
||||
`_root_mailbox`, `_registry_addrs`) plus every test fixture
|
||||
and downstream config (`piker`'s `[network]` table). It
|
||||
wants its **own migration commit, landed before any new
|
||||
backend**, not smuggled into one.
|
||||
- it's the moment to **stop handing raw unwrapped tuples to
|
||||
users at all.** The long-term shape is: `Address` subtypes
|
||||
are the public currency and `UnwrappedAddress` becomes an
|
||||
internal serialization detail — the same discipline
|
||||
`ipaddress` uses (you pass `IPv4Address`, not a 4-tuple).
|
||||
Public API should accept `Address|maddr-str` and treat bare
|
||||
tuples as legacy-tolerated input, ideally deprecated.
|
||||
- **`.get_random()` must be collision-free without a live
|
||||
runtime.** See the `UDSAddress.get_random()` uuid-token
|
||||
comment (`_uds.py:207-220`): with no `current_actor()` the
|
||||
|
|
@ -214,8 +248,9 @@ Adding a backend touches these and only these:
|
|||
entry lazy — propose that refactor explicitly.
|
||||
4. `tractor/discovery/_addr.py:230` `wrap_address()` `match` —
|
||||
add a case iff your `unwrapped_type` isn't already uniquely
|
||||
matched. Prefer unwrapped forms that are *self-tagging*
|
||||
(see plan 01 §2.2 and plan 02 §2.2) so this stays cheap.
|
||||
matched. **Preferably do the proto-key migration in §1.1
|
||||
first**, after which this step becomes a one-line
|
||||
`_address_types` entry instead of an order-sensitive `case`.
|
||||
5. `tractor/ipc/_types.py` — `Address` union alias,
|
||||
`_msg_transports` list, `_key_to_transport[('msgpack', key)]`,
|
||||
`_addr_to_transport[<Proto>Address]`.
|
||||
|
|
|
|||
|
|
@ -98,34 +98,34 @@ class TIPCAddress(
|
|||
def_bindspace: ClassVar[int] = TIPC_CLUSTER_SCOPE
|
||||
```
|
||||
|
||||
**Unwrapped form** (the wire/`SpawnSpec` shape) — must be
|
||||
uniquely matchable in `wrap_address()`:
|
||||
**Unwrapped form** (the wire/`SpawnSpec` shape).
|
||||
|
||||
TIPC's natural form is `(stype, instance, scope)` — but a
|
||||
2-tuple squeeze of it is a `(str, int)`, i.e. *the same coarse
|
||||
shape as `TCPAddress`*, so `wrap_address()`'s
|
||||
`case (str(), int())` steals it. This backend is therefore the
|
||||
forcing function for the contract-doc's conclusion (§1.1):
|
||||
|
||||
> **make the unwrapped form carry an explicit proto-key, spelled
|
||||
> with the `multiaddr` protocol name.**
|
||||
|
||||
```python
|
||||
def unwrap(self) -> tuple[str, int]:
|
||||
# ('tipc:<stype>:<scope>', instance)
|
||||
return (f'tipc:{self._stype}:{self._scope}', self._instance)
|
||||
def unwrap(self) -> tuple[str, int, int, int]:
|
||||
return ('tipc', self._stype, self._instance, self._scope)
|
||||
```
|
||||
|
||||
i.e. `(str, int)` — *the same coarse shape as `TCPAddress`*, so
|
||||
`wrap_address()`'s `case (str(), int())` would steal it. Two
|
||||
options; **pick (a)**:
|
||||
`wrap_address()` then dispatches `_address_types[addr[0]]` and
|
||||
the collision class disappears. **This is a prerequisite
|
||||
migration commit, not part of this backend** — see contract §1.1
|
||||
for its blast radius (wire format + every fixture + `piker`
|
||||
config) and for the follow-on "stop handing raw tuples to users
|
||||
at all, à la `ipaddress`" direction.
|
||||
|
||||
- **(a) self-tagging prefix + a `case` ordered before the TCP
|
||||
one**, guarded on `addr[0].startswith('tipc:')`:
|
||||
```python
|
||||
case (str() as h, int()) if h.startswith('tipc:'):
|
||||
cls = TIPCAddress
|
||||
```
|
||||
Cheap, string-y, but honest about the fact that
|
||||
`UnwrappedAddress` is a deliberately-degenerate
|
||||
`tuple[str, int|str]` (see the `_addr.py:43-65` TODO block).
|
||||
It also keeps `TCPAddress.__post_init__`'s
|
||||
`ipaddress.ip_address()` validation from being reached with a
|
||||
non-IP host.
|
||||
- (b) widen `UnwrappedAddress` to a 3-tuple for tipc. Rejected:
|
||||
ripples into every `reg_addr` fixture, `_root_mailbox`, and
|
||||
downstream (`piker`) config.
|
||||
⚠️ an earlier revision of this plan proposed a self-tagging
|
||||
`('tipc:<stype>:<scope>', instance)` string-prefix hack with an
|
||||
ordered `case` guard. **Dropped** — it papers over the problem,
|
||||
keeps `wrap_address()` order-sensitive, and doesn't help iroh's
|
||||
`(str, str)`-vs-UDS collision at all. Do not resurrect it.
|
||||
|
||||
Note `TIPCAddress` is the first backend where `.unwrap()` is
|
||||
**not** a lossless view of the live socket — `maybe_node`/
|
||||
|
|
@ -689,6 +689,12 @@ single best demo this backend has; lead with it.
|
|||
|
||||
## 10. Follow-up issue seeds
|
||||
|
||||
- **register `/tipc` in the multiaddr spec**, mirroring the `wg`
|
||||
track (multiformats/py-multiaddr#107/#108 + gh #483). Same
|
||||
shape of work: propose the proto + code, land a codec in
|
||||
`py-multiaddr`, then drop our `str`-maddr fallback (§4). Worth
|
||||
filing *alongside* the `wg` spec-submission issue so both
|
||||
proposals go up together rather than as one-offs.
|
||||
- registrar-less discovery fast path via name derivation (§5.1)
|
||||
- `TIPC_TOP_SRV`-driven push registry in
|
||||
`discovery/_registry.py` (§5.2)
|
||||
|
|
|
|||
Loading…
Reference in New Issue