Home POD 0010: Rotating Slot OwnershipDownload PDF

POD 0010: Rotating Slot Ownership

Abstract

paxos-odin can run its log with every voting member proposing at once. Under the Node_Options.rotating_ownership option, slot 𝑠 belongs to the member at index (𝑠−1)mod𝑁 of the membership, the owner proposes there at a reserved round-zero ballot with no phase one, an idle owner fills its slots with the host's no-op, a stalled prefix is repaired by a bounded, per-decree phase one that fences the suspected owner out of the stalled slots only, and a suggestion that lost to such a revocation is queued for best-effort resubmission in a later own slot. This record specifies the rules, the messages and ledger records they touch (Prepare_Scope, Nack_Message.slot, Write_Promise_At), the state added to Node, each procedure in src/ownership.odin and its hooks in src/consensus.odin and src/election.odin, the safety argument (B1 per decree), and what tests/test_ownership.odin and the simulator's --ownership mode check. The design follows Mencius (Mao, Junqueira, Marzullo, 2008).

Status and Implementation Boundary

Rotating slot ownership is implemented in src/ownership.odin and enabled via Node_Options.rotating_ownership. The subsystem reuses the core acceptor, learner, and recovery machinery while establishing a round-zero suggestion channel, bounded range revocations, and best-effort resubmissions.

Execution safety is verified through unit tests in tests/test_ownership.odin, batch regression coverage in tests/test_batch_review.odin, and randomized schedules in paxos-sim --ownership. Multi-decree safety conditions hold per slot; clock-based leases and dynamic ownership reweighting remain future extensions.

Introduction

The single stable leader of Multi-Paxos makes every decision one round trip, but every proposal passes through one node. A member that receives a client request forwards it to the leader before the round trip begins, and the leader's link, disk, and CPU bound the group's throughput. When members are spread across sites, the forwarding hop is a wide-area latency on every request that did not originate at the leader.

Mencius observed that Paxos does not require one proposer per log, only one proposer per ballot per decree. If the decrees are dealt out round-robin and each coordinator uses a ballot that is, in its own decrees, the lowest possible, the coordinator can propose without a prepare, and the group's proposing capacity scales with its size. The cost is that the log now depends on every member: an idle member must skip its instances and a crashed one must be revoked. This record adopts that design on the existing Node, keeping the acceptor, the learner, and the phase-one machinery intact and adding a second ballot regime alongside campaigns.

Terminology and Scope

In scope: everything in src/ownership.odin and the branches on node.ownership in src/node.odin, src/consensus.odin, src/election.odin, and src/effects.odin. Out of scope: reads, leases, and weighted or adaptive ownership, listed under open questions.

Design Overview

Six rules define the mode.

  1. Ownership. Slot 𝑠 is owned by membership_get(membership, (s - 1) mod N). Ownership is a pure function of the membership and the slot; no message carries it.
  2. Ballot partition. Round zero of the 40-bit round field is reserved. ownership_ballot(owner) is the only round-zero ballot an acceptor votes at in the owner's slots; start_campaign and start_revocation always choose a round of one or more. Within each decree the owner's ballot is therefore unique and the least ballot any acceptor votes at, so B1 holds per decree and B3 leaves the owner free to choose any value.
  3. Suggest. An owner proposes in its next usable own slot through send_accept at its ownership ballot, with no prepare. Decided or revoked own slots are stepped over.
  4. Skip. On every tick an owner suggests the no-op in each own slot at or below highest_seen, at most min(CHUNK_SLOTS, SKIP_BURST) per tick.
  5. Revoke. A member whose delivered_through stays below highest_seen for election_timeout_ticks runs a bounded phase one over [delivered_through + 1, min(chunk end, highest_seen)], promises are recorded per decree, every slot in the range is driven (a recovered vote by B3, otherwise the no-op), and the revoker returns to .Follower when the range has been driven; individual decisions may still be pending.
  6. Resubmit. When a decision arrives for a slot this node suggested in at its ownership ballot and the decided value differs, the suggested value is queued for best-effort proposal in a later own slot.

campaign is refused with .Campaign_Disabled, and proposal_gate admits any voting member regardless of role.

Detailed Design

Messages and Records

Node State

Ownership uses these fields: ownership: bool (copied from Node_Options.rotating_ownership), own_next: Slot (the next own slot to consider; seeded by own_slot_from(node, 1) in node_init and recomputed from next_slot in node_resume_at), highest_seen: Slot (the greatest slot suggested, accepted, or decided at this node, raised in propose_owned, on_accept after the ownership check, record_commit, and node_resume_at), stall_ticks: u32, resubmit: small_array.Small_Array(CHUNK_SLOTS, Value), and the saturating resubmits_dropped counter. The existing recover_base, recover_last, lead_slot, lead_ballot, and acknowledgements are reused: recover_last bounds a revocation, and lead_ballot keys acknowledgement counting and retransmission to the ballot this node is driving in each slot, which under ownership is either its ownership ballot or its revocation ballot.

Procedures

Security and Correctness Considerations

B1 per decree. Lamport's B1 requires distinct ballots within one decree. The ownership ballot (0, 0, owner) recurs across the owner's decrees, which B1 permits, and within each decree it is the only round-zero ballot any acceptor votes at, because on_accept drops every other round-zero accept before consulting its promise. send_accept refuses to reuse a ballot with a second value in a slot this node is driving (.Conflicting_Value), and on_accept refuses to vote a second value at a ballot it has already voted (.Conflicting_Value). Campaign and revocation ballots are compared as integers exactly as before, and their round is always above zero, so the owner's ballot is the least ballot in each of its decrees and B3 constrains it to nothing. The Synod proof therefore applies unchanged.

Why revocation is phase one. A revoker proposes in another member's slot, at round one or above. Doing so without a prepare would let the owner's round-zero vote and the revoker's vote form two quorums with different values. A bounded prepare collects a read quorum of per-decree promises, which fences the owner's ballot out of those decrees and reports every vote in them, so resolve_chunk can apply B3. Promising per decree rather than globally is what keeps the owner usable in every slot outside the range; promising and driving the whole range is what keeps a promised slot from being fenced without ever being decided. promise_bounded fails closed rather than partially, so a candidate never counts a promise an acceptor did not record.

The pre-durable exception. A campaign accept may leave before the sender's writes are durable because a restarted proposer campaigns at a fresh round. An owner's suggestion has no fresh round: after a crash before Write_Vote is durable, node_resume_at finds the slot unused and the owner would suggest a different value at the same ballot in the same decree. pre_durable_next therefore never yields a round-zero accept; the host sends suggestions only after confirm_writes_durable. The simulator's persist_sim_write oracle fails a run when two members hold different values under one ballot in one slot, which is the observable form of this violation.

Operational Considerations

Validation and Acceptance Gates

Verification coverage. tests/test_ownership.odin, on Node(u64, 3, 16, 4) with an in-process queue and a silent member whose traffic is dropped: ownership_three_owners_propose_concurrently (slots 1 to 4 decided from three owners, owner_of values, campaign refused); ownership_idle_owners_skip (slots 2 and 3 decide the no-op after three ticks); ownership_revokes_a_crashed_owner (slot 3 of the silent owner decides the no-op after thirty ticks and both survivors are .Follower); ownership_revocation_keeps_a_seen_vote (a suggestion heard by one survivor is re-proposed by the revoker and decided); ownership_revoked_suggestion_is_resubmitted (a suggestion heard by nobody is revoked to the no-op and, once the owner is reconnected, decided in a later own slot).

Simulator verification. paxos-sim --ownership initialises every node with rotating_ownership = true, skips the bootstrap campaign, proposes from any live node, and applies every oracle of the single-leader mode: agreement and validity of every durable decision against the golden log, the durable-vote quorum detector and the one-value-per-ballot check, promise regression for the global promise, contiguity of released entries, convergence of every node, and a liveness probe after healing. Crashes land before writes, after a prefix of writes, or after a prefix of messages, and restarts go through restore with the same option. The oracle does not cross-check Write_Promise_At against later votes; that check is left to the ledger's own .Promise_Regression on apply.

Alternatives Considered

Open Questions

  1. Piggybacked skips. Mencius carries skips inside other messages; here each skip is a full round-zero suggestion with its own quorum. Piggybacking a "skipped through" mark on Accept_Message or Accepted_Message would cut idle-owner traffic but adds fields to every message.
  2. Learning skips without a quorum. In Mencius a skip needs no consensus, since only the coordinator can suggest in its instance and a skip is the coordinator's word. The library decides skips like values so that the learner never trusts a single member; whether an acceptor may treat an owner's no-op suggestion as decided on receipt, safely, is open.
  3. Resubmission is best effort and can duplicate commands. A differing replacement queues the lost suggestion; an equal replacement does not. A resubmission is a new proposal, not a deduplicated application operation; the API supplies no exactly-once command guarantee. Overflow of the one-chunk queue is counted in resubmits_dropped; host retries and application ids remain necessary. The SDK must expose this limitation before enabling ownership.
  4. Adaptive ownership shares. Ownership is uniform round-robin. A busy member cannot take more slots and a quiet one cannot give them up, so a workload concentrated on one member pays 𝑁−1 skips per decision. Weighted ownership would need the weights agreed through the log, for example in a stop sign's metadata.

References