Expand description
Logged uniform total-order broadcast.
Status: transcription. Space: unbounded — unordered, delivered and the family of consensus
instances all grow with the number of entries handled, and delivered and proposals grow in
stable storage as well. That is the page. See docs/bounded-space.md.
Cachin, Guerraoui & Rodrigues, Module LoggedUniformTotalOrderBroadcast and Algorithm 6.12,
p. 327, quoted from the book:
Algorithm 6.12: Logged Uniform Total-Order Broadcast
Implements: LoggedUniformTotalOrderBroadcast, instance lutob.
Uses:
LoggedUniformReliableBroadcast, instance lurb;
LoggedUniformConsensus (multiple instances).
upon event ⟨ lutob, Init ⟩ do
unordered := ∅;
delivered := [];
round := 1;
recovering := FALSE;
wait := FALSE;
forall r > 0 do proposals[r] := ⊥;
upon event ⟨ Recovery ⟩ do
unordered := ∅;
delivered := [];
round := 1;
recovering := TRUE;
wait := FALSE;
retrieve(proposals);
if proposals[1] ≠ ⊥ then
trigger ⟨ luc.1, Propose | proposals[1] ⟩;
upon event ⟨ lutob, Broadcast | m ⟩ do
trigger ⟨ lurb, Broadcast | m ⟩;
upon event ⟨ lurb, Deliver | lurbdelivered ⟩ do
unordered := unordered ∪ lurbdelivered;
upon unordered \ delivered ≠ ∅ ∧ wait = FALSE ∧ recovering = FALSE do
wait := TRUE;
Initialize a new instance luc.round of logged uniform consensus;
proposals[round] := unordered \ delivered;
store(proposals);
trigger ⟨ luc.round, Propose | proposals[round] ⟩;
upon event ⟨ luc.r, Decide | decided ⟩ such that r = round do
forall (s, m) ∈ sort(decided) do // by the order in the resulting sorted list
append(delivered, (s, m));
store(delivered);
round := round + 1;
if recovering = TRUE then
if proposals[round] ≠ ⊥ then
trigger ⟨ luc.round, Propose | proposals[round] ⟩;
else
recovering := FALSE;
else
wait := FALSE;
trigger ⟨ lutob, Deliver | delivered ⟩;The pair to crate::consensus_based_total_order_broadcast, and held to the same suite. What
differs is exactly one thing: the ordered sequence survives a restart. Everything else — one
consensus instance per round, propose the unordered set, sort what is decided — is the same
shape, which is what makes the comparison worth having.
Why the recovery is not simply “read it back”. A process that proposed for a round and then
forgot would, on recovering, propose something different for the same round — and a uniform
consensus that has already decided cannot accommodate it. So proposals[r] is durable before the
proposal is visible to anyone, and recovery re-proposes what was recorded, round by round, until
it reaches one it never proposed for. That is what recovering is counting through.
§Departures from the page
-
A read, as the port requires. See
crate::total_order_log. -
Consensus instances are a family, and the conditional event handler is discharged here. Both as in the crash-stop member, and for the same reasons; that module’s header states them. Unlike that member, this one runs over a consensus assuming no synchrony, so processes can genuinely drift and the family is doing work rather than standing on faithfulness alone.
-
deliveredandproposalsare appended, not rewritten. The page writesappend(delivered, (s, m)); store(delivered)andstore(proposals)— rewriting a whole growing structure on every change, which costsO(n²)bytes over a run. This repository’s storage interface splits the two cases so the choice is visible in the types, anddocs/bounded-space.mdrecords both logged modules having had exactly this defect and losing it. So both go into the appended sequence, one entry each, and recovery replays them. -
Consensus instances are re-created here, not by a runtime. The page says so outright: “During the recovery operation after a crash, the total-order algorithm runs again through all rounds executed before the crash and executes the same consensus instances once more. (We assume that the runtime environment re-instantiates all instances of consensus that had been dynamically initialized before the crash.)” There is no such runtime here — a crash rebuilds a process from its constructor and nothing else survives but storage — so
on_recoveryre-creates every instance the durable record names and runs each one’s own recovery, all of them before any decision is acted on: a decided instance announces its decision again from its record, and an undecided one must have read its state back before recovery re-proposes into it. The same shape of departure as the conditional event handler: a facility the book assumes, discharged in the module. -
The decided prefix is replayed from the record, not re-decided. The page rebuilds
deliveredby running every round again, which is what its runtime’s re-instantiated instances are for. The appendedRecord::Orderedentries already hold the sequence in order, so recovery replays them directly and the walk over re-announced decisions advancesroundwithout appending or announcing anything twice — the same guard that makes duplicate decisions harmless in a live run makes the replay idempotent. Each replayed entry is announced again, once, for the reasoncrate::logged_leader_driven_consensusgives for re-announcing a decision: the layer above may have crashed with this process and never seen the first indication. Positions make the re-announcement idempotent for a client. -
The child that appends is composed through a sequence slot.
logged_uniform_reliable_ broadcastkeeps an appended record of its own, and until this module nothing composed over a child that appends —store.rssaid so, and said what the missing half would be. This is its second consumer, and [recon_core::SeqSlot] is what that paragraph described. Parent and child append into one sequence, so the order between their entries is real rather than invented at recovery.
Structs§
- Durable
- This protocol’s rewritten metadata, and its children’s inside it.
- Logged
Uniform Total Order Broadcast - A totally ordered log whose sequence survives a restart.
Enums§
- Cmd
- Requests from the layer above.
- Ind
- Indications to the layer above.
- Record
- What this protocol appends. One sequence, carrying its own entries and its child’s.
- Wire
- This layer’s messages: the broadcast’s, and a consensus instance’s stamped with its round.
Type Aliases§
- Consensus
- The consensus one round runs. Not a type parameter, for the reason the crash-stop member gives.