macula_hyparview_view (macula v11.4.0)
View SourceHyParView active + passive partial-view data structure (Leitão, Pereira, Rodrigues 2007 — implementation of Part 3 §7.1).
Each realm member holds two bounded views of other members:
- Active view — symmetric, TCP-like. Default cap 5 (per Part 3 §7.1:
max(5, ceil(log₂(N)))capped at 15). Plumtree gossip eager-pushes along active-view edges. - Passive view — candidate pool. Default cap is
4 × active. Promoted into the active view on active-peer failure; refreshed via periodic shuffles with active neighbours.
This module is pure. Mutating ops return a new view; the wire-protocol orchestration (JOIN / FORWARD_JOIN / NEIGHBOR / SHUFFLE / SHUFFLE_REPLY) lands in Session 5.2 on top of this.
Eviction policy
Whenever a add_active/2 or promote/2 would push the active view past active_cap, a uniformly-random current active peer is demoted to the passive view to make room. Symmetric removal on the other side is the protocol layer's responsibility.
Whenever add_passive/2 or merge_shuffle/2 would push the passive view past passive_cap, a uniformly-random current passive peer is dropped to make room.
Reference: plans/PLAN_MACULA_V2_PART3_DISCOVERY.md §7.1; plans/PLAN_PHASE_5_BREAKDOWN.md Session 5.1.
Summary
Functions
Move a peer from active to passive (e.g. on graceful peer disconnect that should be retained as a candidate).
Place the node_ids new to the view that a frame from From brings, as far as that neighbour's allowance goes at Now. exceeded when some were left out.
Move a peer from passive to active. The peer must already be in the passive view; otherwise this is equivalent to add_active/2.
Record that this node sent a SHUFFLE at Now.
Take the record of the oldest SHUFFLE sent in the 30 seconds before Now that has no reply yet, forgetting older ones. none when there is no such SHUFFLE.
Types
-type counts() :: #{active := non_neg_integer(), passive := non_neg_integer()}.
-type eligibility() :: self | already_active | was_passive | fresh.
-type opts() :: #{active_cap => pos_integer(), passive_cap => pos_integer()}.
-type peer() :: <<_:256>>.
-type view() :: #{self_id := peer(), active_cap := pos_integer(), passive_cap := pos_integer(), active := sets:set(peer()), passive := sets:set(peer()), placements := #{peer() => {non_neg_integer(), integer()}}, shuffles_sent := [integer()]}.
Functions
Move a peer from active to passive (e.g. on graceful peer disconnect that should be retained as a candidate).
Place the node_ids new to the view that a frame from From brings, as far as that neighbour's allowance goes at Now. exceeded when some were left out.
Move a peer from passive to active. The peer must already be in the passive view; otherwise this is equivalent to add_active/2.
-spec random_active_subset(view(), non_neg_integer()) -> [peer()].
-spec random_passive_subset(view(), non_neg_integer()) -> [peer()].
Record that this node sent a SHUFFLE at Now.
Take the record of the oldest SHUFFLE sent in the 30 seconds before Now that has no reply yet, forgetting older ones. none when there is no such SHUFFLE.