Expand description
Per-subscriber fan-out of the frames arriving on a pooled peer’s session.
§Why the pool needs this at all
A pooled session’s inbound mpsc::Receiver<Message> is consumed by ONE task. Before this
module that task folded NewPeakWallet into an atomic and discarded everything else, so a
consumer that needs the frames themselves — a wallet replica following CoinStateUpdate —
could not be served from a pooled session and had to dial its own. That is the reason the node
ran several independent peer stacks (dig_ecosystem#2761).
§Every frame names the session it came from
The pool holds many peers at once and fans all of their frames into one subscription, so a
frame that does not say who sent it is a claim from the pool, which no peer in it is entitled
to make. CoinStateUpdate is an UNSOLICITED push carrying no request id, so an unattributed
fan-out lets any held peer inject fabricated coin states that a subscriber cannot tell from the
peer it deliberately followed — and cannot eject, because nothing knows who sent them.
Attribution lives on SourcedFrame, the envelope, rather than on the individual
PoolFrame variants. A variant added later cannot forget to carry it, and a subscriber
cannot read a frame without having its source in hand.
§Sessions, not a pool-wide generation
A SessionId identifies ONE peer connection for the life of the pool. It is allocated when
that connection is admitted and never changes, so the identity a frame carries stays true
whatever else the pool does afterwards.
This is deliberately not a pool-wide counter. A pool of N independent sessions has no single “current” generation to be in: a counter bumped by every reconnect makes every OTHER session’s frames look stale, so a consumer honouring it discards N-1 peers’ frames on every ordinary refill. Staleness is a property of one peer’s stream, and it is signalled on that stream.
PoolFrame::Reset opens a session, PoolFrame::SessionEnded closes it, and between them
everything a subscriber sees from that source belongs to one continuous connection.
§Overflow terminates the subscriber; it never skips a frame
Each subscriber gets a BOUNDED channel, because an unbounded one turns a slow consumer into unbounded memory. When that channel is full the subscription is DROPPED and its receiver observes the stream end.
The tempting alternative — drop the frame, keep the subscription — is the failure this ordering
exists to prevent. A missed CoinStateUpdate is a coin whose spend the replica never learns
about, so the replica goes on reporting Synced while reading spent money as present. A
terminated stream is a fact the consumer can act on; a gap is indistinguishable from quiet.
Structs§
- Frame
Fanout - The pool’s fan-out: many subscribers, each with its own bounded queue.
- Frame
Source - WHO a frame came from.
- Frame
Subscription - The receiving half of a subscription.
- Session
Id - One peer connection, for the life of the pool.
- Sourced
Frame - A frame together with the session that produced it.
Enums§
- Pool
Frame - One frame from a pooled peer session, as a subscriber sees it.
- Session
EndReason - Why a session stopped producing frames.