Kind-3 term markers in the WAL stream, STREAM-relative vote frontiers, heartbeat-only divergence detection + quarantine, and fenced promote. Elections converge in 0.6–1.0s; zero acked-write loss across all kill points. Closes G5 (leaderless recovery) from the v0.9 wave.
183 lines
7.0 KiB
Protocol Buffer
183 lines
7.0 KiB
Protocol Buffer
syntax = "proto3";
|
|
package tidal.replication.v1;
|
|
|
|
// Globally unique identifier for a WAL segment.
|
|
message WalSegmentId {
|
|
uint32 region_id = 1;
|
|
uint32 shard_id = 2;
|
|
uint64 seqno = 3;
|
|
}
|
|
|
|
// A WAL segment ready for shipping to a peer shard.
|
|
message ShipSegmentRequest {
|
|
WalSegmentId id = 1;
|
|
bytes payload = 2;
|
|
uint64 event_count = 3;
|
|
// The segment's authoritative last WAL sequence number, computed by the
|
|
// leader from the ORIGINAL (pre-community-overlay-filter) bytes. Lets the
|
|
// receiver advance its replication-lag leader high-water-mark even for an
|
|
// all-local segment that filters to an empty payload (obs-REPL-1). A 0 value
|
|
// (e.g. from an older sender that omits this field) means "unknown" and the
|
|
// receiver falls back to the per-batch boundaries it decodes.
|
|
uint64 leader_last_seq = 4;
|
|
// The shipping stream's baseline (m11p2): the WAL seqno at which this
|
|
// leader's stream STARTED. Non-zero only on catch-up stream chunks from a
|
|
// promoted leader; seqnos at or below it are pre-stream history the
|
|
// receiver jumps its frontier past instead of treating as a gap. 0 (the
|
|
// default, and what every live unary ship carries) means "stream from the
|
|
// beginning".
|
|
uint64 stream_baseline = 5;
|
|
// The sender's leadership term (m11p4 fencing). 0 = the topology era or a
|
|
// pre-m11p4 sender; a receiver at term >= 1 rejects term-0 traffic, and any
|
|
// receiver rejects a term below its own (FAILED_PRECONDITION) — a deposed
|
|
// leader's ships can never apply.
|
|
uint64 term = 6;
|
|
// The region claiming leadership of `term` (m11p4).
|
|
uint32 leader_region = 7;
|
|
}
|
|
|
|
// Response to a segment shipment.
|
|
message ShipSegmentResponse {
|
|
bool accepted = 1;
|
|
// The receiver's contiguous applied seqno for the request's source shard at
|
|
// acceptance time (m11p2): a monotonic hint the sender folds into its acked
|
|
// frontier so retries of already-applied data prune and heal needs no
|
|
// separate status fetch. 0 = unknown (older peer / no applied source wired).
|
|
uint64 applied_seqno = 2;
|
|
// The receiver's current term (m11p4): a value above the sender's term is
|
|
// the sender's step-down signal.
|
|
uint64 term = 3;
|
|
}
|
|
|
|
// Request to stream segments from a given sequence number.
|
|
message StreamRequest {
|
|
uint32 shard_id = 1;
|
|
uint64 from_seqno = 2;
|
|
// The puller's current term (m11p4). The source serves only when the terms
|
|
// match: a stale puller must rejoin first, and a stale SOURCE must step
|
|
// down rather than serve its possibly-divergent tail.
|
|
uint64 term = 3;
|
|
}
|
|
|
|
// Heartbeat request matching ControlPlane's ShardStats.
|
|
message HeartbeatRequest {
|
|
uint32 shard_id = 1;
|
|
uint32 region_id = 2;
|
|
uint64 entity_count = 3;
|
|
double signal_throughput_eps = 4;
|
|
uint64 disk_bytes = 5;
|
|
// Replication lag per peer region (region_id -> lag in events).
|
|
map<uint32, uint64> replication_lag = 6;
|
|
uint64 last_heartbeat_ns = 7;
|
|
// The sender's leadership term (m11p4): a leader heartbeat is the lease
|
|
// assertion + failure-detector input. 0 = the topology era / a non-leader
|
|
// health probe.
|
|
uint64 term = 8;
|
|
// The region asserting leadership of `term` (m11p4).
|
|
uint32 leader_region = 9;
|
|
// The asserted term's activation stream baseline (m11p4): immutable for
|
|
// the term; a joining follower jumps its applied frontier for the leader's
|
|
// stream to it (seqnos at or below are pre-stream history).
|
|
uint64 stream_baseline = 10;
|
|
// The leader's ELECTION-TIME log position (m11p4): the term and frontier
|
|
// of its log in the PREVIOUS stream's numbering — the same pair the vote
|
|
// restriction compares. A joining node is DIVERGENT iff its own position
|
|
// exceeds this lexicographically (it holds entries the new leadership's
|
|
// history does not subsume).
|
|
uint64 prev_log_term = 11;
|
|
uint64 prev_log_seq = 12;
|
|
}
|
|
|
|
// Heartbeat acknowledgement.
|
|
message HeartbeatResponse {
|
|
bool acknowledged = 1;
|
|
// The responder's current term (m11p4): above the sender's term = the
|
|
// sender's step-down signal.
|
|
uint64 term = 2;
|
|
// Whether the responder accepted the sender's leadership assertion
|
|
// (false = the sender's term is stale).
|
|
bool accepted = 3;
|
|
}
|
|
|
|
// A follower's self-report of its durable frontier (m11p3).
|
|
//
|
|
// Pushed by the receiver once per apply round — fully decoupled from ship
|
|
// acks, so the leader's quorum commit index stays fresh even when its
|
|
// outbound ships stall (gap-parked follower, quiet leader, pull catch-up).
|
|
message AppliedReport {
|
|
// The reporting node's shard id.
|
|
uint32 reporter_shard = 1;
|
|
// The stream's source shard (the leader being reported to).
|
|
uint32 source_shard = 2;
|
|
// The reporter's contiguous durably-applied seqno for that stream.
|
|
uint64 applied_seqno = 3;
|
|
// The reporter's current term (m11p4): the leader folds a report into its
|
|
// quorum commit index ONLY when this matches the index's activation term —
|
|
// a stale or cross-leadership report can never advance commitment.
|
|
uint64 reporter_term = 4;
|
|
}
|
|
|
|
// Applied-report acknowledgement.
|
|
message AppliedReportAck {
|
|
bool acknowledged = 1;
|
|
}
|
|
|
|
// A pre-vote or vote request (m11p4 leader election).
|
|
message VoteRequest {
|
|
// The term votes are requested for. For a pre-vote this is the PROPOSED
|
|
// term (candidate's current + 1) — nothing has been bumped.
|
|
uint64 term = 1;
|
|
uint32 candidate_region = 2;
|
|
// The candidate's log position for the up-to-date restriction,
|
|
// compared lexicographically: (last_log_term, last_log_seq).
|
|
uint64 last_log_term = 3;
|
|
uint64 last_log_seq = 4;
|
|
// Pre-vote probe: changes no voter state, never inflates terms.
|
|
bool prevote = 5;
|
|
// Leadership-transfer election (`TimeoutNow`): voters skip the
|
|
// leader-freshness refusal — the current leader sanctioned this.
|
|
bool transfer = 6;
|
|
}
|
|
|
|
// A vote (or pre-vote) reply.
|
|
message VoteResponse {
|
|
// The voter's current term (above the candidate's = step-down signal).
|
|
uint64 term = 1;
|
|
bool granted = 2;
|
|
}
|
|
|
|
// The current leader tells `target` to start an immediate transfer election
|
|
// (m11p4 fenced promote).
|
|
message TimeoutNowRequest {
|
|
// The sanctioning leader's current term.
|
|
uint64 term = 1;
|
|
uint32 leader_region = 2;
|
|
}
|
|
|
|
message TimeoutNowResponse {
|
|
// Whether the target started an election.
|
|
bool accepted = 1;
|
|
}
|
|
|
|
// WAL segment shipping service between tidalDB shards.
|
|
service WalShipping {
|
|
// Ship a single WAL segment to a peer shard (unary).
|
|
rpc ShipSegment(ShipSegmentRequest) returns (ShipSegmentResponse);
|
|
|
|
// Stream WAL segments from a given sequence number (server-streaming).
|
|
rpc StreamSegments(StreamRequest) returns (stream ShipSegmentRequest);
|
|
|
|
// Periodic health check for the ControlPlane; with m11p4, the leader's
|
|
// lease assertion and the failure detector's input.
|
|
rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse);
|
|
|
|
// Follower -> leader durable-frontier report (m11p3 quorum acks).
|
|
rpc ReportApplied(AppliedReport) returns (AppliedReportAck);
|
|
|
|
// Pre-vote / vote (m11p4 leader election).
|
|
rpc RequestVote(VoteRequest) returns (VoteResponse);
|
|
|
|
// Fenced leadership transfer: start an immediate election (m11p4).
|
|
rpc TimeoutNow(TimeoutNowRequest) returns (TimeoutNowResponse);
|
|
}
|