GossipView

class GossipView(selfId: PeerId, seam: Seam, roster: StateFlow<Set<PeerId>>, rawIncoming: SharedFlow<Swatch>, random: Random, clock: () -> Instant, config: HeartbeatConfig = HeartbeatConfig(), spareCount: Int = DEFAULT_SPARE_COUNT, jitter: ClosedRange<Duration> = DEFAULT_JITTER, topology: TopologyPolicy = RandomKRegular(random))

The live, self-healing partial view of the gossip overlay for one peer.

GossipView turns a roster StateFlow into a continuously-maintained active-neighbour set (the ~k peers this node gossips deltas with and GCs against) plus an ordered spares standby list. It is the "membership/view manager" of docs/gossip-mesh-design.md Phase 2 — the runtime counterpart of the pure partialView selection function.

Derivation. On every roster change the view is reconciled against the topology policy's target view — for the default RandomKRegular, a seeded random k-out sample (k = recommendedActiveViewSize(N)) — after a per-peer jitter drawn from jitter so peers don't recompute in lockstep and storm the overlay. The policy owns selection (which peers, and any randomness); this manager owns stability: recompute is churn-minimising — healthy active neighbours are retained across recomputes and only freed slots are filled (from spares first, then fresh policy picks) — so a single join/leave does not reshuffle the whole overlay.

Liveness. One HeartbeatPartitionDetector runs per active neighbour over a shared rawIncoming fan-out (the SeamRoom-style composer the design calls for; see PerPeerSeam). When a detector reports PartitionEvent.PeerUnresponsive or PartitionEvent.PeerLost the edge is treated as down: the neighbour is dropped, the next spare is promoted immediately (reactive healing), and the view is reconciled. A failed peer is excluded from re-selection until it leaves the roster; transient blips are healed by anti-entropy (Phase 1), not by this manager re-admitting the peer (a documented v1 simplification — see gossip-mesh-design.md).

Determinism (required). All scheduling runs on the start scope's dispatcher; all randomness draws from the injected random and the topology's own seeded RNG (the same instance, with the default policy); the clock is the injected clock. State is confined to a single command-processing coroutine (an actor over commands) so there are no locks and event ordering is deterministic. Tests drive virtual time with StandardTestDispatcher + bounded advanceTimeBy/runCurrent and never advanceUntilIdle (the heartbeat timers re-arm forever).

Parameters

selfId

this peer's id; never selected into the view.

seam

the base multi-peer seam; per-peer detectors send ping/pong through it.

roster

the live full-membership set (includes selfId); the view's source of truth.

rawIncoming

a fan-out of the base seam's inbound frames. The owner (e.g. GossipSeam) is the single collector of seam.incoming and re-publishes each Swatch here, so the per-peer detectors can subscribe without contending for the single-consumer seam.incoming channel (ADR-034).

random

a seeded RNG, seeded per-peer by the caller so peers choose independently. Drives the recompute jitter; with the default topology the same instance also seeds neighbour selection.

clock

injected time source for the per-peer detectors; never the wall clock.

topology

the overlay shape — which peers fill the active view. Owns all selection randomness; defaults to a RandomKRegular seeded from random.

Constructors

Link copied to clipboard
constructor(selfId: PeerId, seam: Seam, roster: StateFlow<Set<PeerId>>, rawIncoming: SharedFlow<Swatch>, random: Random, clock: () -> Instant, config: HeartbeatConfig = HeartbeatConfig(), spareCount: Int = DEFAULT_SPARE_COUNT, jitter: ClosedRange<Duration> = DEFAULT_JITTER, topology: TopologyPolicy = RandomKRegular(random))

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard
val active: StateFlow<Set<PeerId>>

The ~k active neighbours this peer gossips deltas with and GCs against.

Link copied to clipboard
val spares: StateFlow<List<PeerId>>

Ordered standby list; spares.first() is promoted on the next neighbour loss.

Functions

Link copied to clipboard
fun start(scope: CoroutineScope)

Starts the view manager on scope. Launches the command processor (sole mutator of view state) and the roster watcher (jittered recompute trigger). The view stays empty until the first jittered recompute completes.