Skip to content

Sync main with release#311

Merged
wool-labs[bot] merged 3 commits into
mainfrom
release
Jul 14, 2026
Merged

Sync main with release#311
wool-labs[bot] merged 3 commits into
mainfrom
release

Conversation

@wool-labs

@wool-labs wool-labs Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Auto-generated by the sync branches workflow.

conradbzura and others added 3 commits July 14, 2026 12:38
WorkerMetadata hashes by uid alone while the generated dataclass
equality compares every field, so a pool keyed by the metadata record
could not address a worker whose record had changed. Every membership
test failed for exactly the events that mattered: the discovery
sentinel dropped worker-updated events carrying changed metadata, a
worker-dropped event whose record differed from the cached one missed
its entry, and a re-announcement inserted a duplicate that consumed a
second lease slot.

Key the pool by the worker's uid, which is what actually identifies a
worker: LoadBalancerContext maps uid to the worker's current record
and connection. Membership, lookup, and eviction all follow from the
key, so the mutators become single dict operations and a duplicate
entry is no longer possible to express. The lease cap counts distinct
uids, so a worker re-announcing itself refreshes its entry rather than
being turned away at capacity.

The upsert flag goes with the old key. Keying by uid gives add_worker
and update_worker distinct contracts — add admits or replaces, update
refreshes what is already admitted — so the boolean that told them
apart has nothing left to select.

Connections displaced by a refresh are deliberately left unclosed. A
connection is a lazy handle and the channel it names is owned by the
pooling layer, which reaps it by refcount and TTL; closing one here
would evict a channel the replacement may still share.

BREAKING CHANGE: LoadBalancerContextView.workers is now keyed by
uuid.UUID rather than WorkerMetadata, mapping each uid to a
(metadata, connection) pair. A custom balancer that iterates
context.workers.items() must iterate values() instead, and one that
tests membership with a metadata record must test with record.uid.
LoadBalancerContextLike.update_worker no longer accepts the upsert
keyword; call add_worker to admit a worker that may not be pooled yet.
A balancer yielded the (metadata, connection) pair it had read from
the pool, and the proxy dispatched through whatever pair it was handed.
But a balancer selects from a view it may have read before a discovery
refresh replaced a worker's entry: round-robin re-snapshots once per
wrap, and a custom balancer may snapshot once per call. The connection
it yielded could therefore name the worker's superseded address, which
left every balancer that does not check for it dispatching to a dead
endpoint — including the three this package documents as examples.

Yield the worker's uid instead. A selection names a worker; the proxy
resolves that name against the live pool at the moment it dispatches,
so a balancer cannot route a task to a superseded address however
stale the view it chose from. The staleness stops being something
balancer authors must defend against and becomes something they cannot
express.

The connection half of the yield was a round trip in any case: the
balancer took a connection out of the pool and handed it back to the
proxy, which owns the pool. It was also the half that went stale.
Balancers that need a record or a connection to decide — to match
tags, pin a version, weigh a connection — still read them from the
context; they simply no longer carry what they read back across the
boundary. The success echo narrows with the yield: asend now sends
back the uid.

Selecting a worker that has left the pool is no longer an error the
balancer must avoid. The proxy finds no entry, asks for the next
candidate, and reports nothing back, since a worker that has departed
has not failed a dispatch.

BREAKING CHANGE: LoadBalancerLike.delegate now yields uuid.UUID rather
than a (WorkerMetadata, WorkerConnection) pair, and receives a
uuid.UUID from asend. A custom balancer yields the uid it selected:
"yield metadata, connection" becomes "yield uid". Records and
connections remain available through context.workers, which maps each
uid to its current (metadata, connection) pair.
@wool-labs wool-labs Bot merged commit d2d8cf3 into main Jul 14, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant