OpenSearch cluster coordination in a stable period: the steps and executions of a stable election and of a stable publication
DefinitionOpenSearchProgressStable periods of the cluster model of the OpenSearchSafety mission (OpenSearch revision b2a54ba5c0defba3d1d20423e911d4bf920bab19), whose Params, Cluster, Message, Step, RestartState, Reachable and Committed are used as they are, over the per-node CoordinationState of the OpenSearchCoordination mission. A stable period is a window in which one node runs for election (or publishes) and the steps excluded below do not occur, while every other step of the cluster remains possible. Each stable step is one of the nine kinds of Step (bootstrap, startJoin, join, clientValue, publish, respond, commit, restart, restartAny), with the same hypotheses and the same effect, and possibly an extra hypothesis.
ElectionStep P c t Q kt kv W W' — a step of a stable election period for the candidate c in term t with voters Q (a list of node ids), where (kt, kv) stands for the term and version of c's accepted state when the period starts:
bootstrapat a nodenonly ifn ≠ candn ∉ Q;startJoin(any node handling a request from a cluster-manager-eligiblesrcfor a termt') only ift' < t, orsrc = candt' = t: no start-join request in a term at leasttother thanc's own request fort;join,respondandcommitas inStep;publish(a nodenhandling a sent publication of a states) only if, whenn = corn ∈ Q,sis no fresher than(kt, kv):s.term < kt, ors.term = ktands.version ≤ kv(cand the voters do not accept a stale publication of a former cluster-manager that is fresher thanc's state);restartof a cluster-manager-eligible nodenonly ifn ≠ candn ∉ Q;restartAny(a node that is not cluster-manager-eligible) as inStep;- no
clientValuestep: no new publication.
ElectionExec P c t Q kt kv W W' — W' is reached from W by finitely many, possibly zero, ElectionSteps.
PublicationStep P c t Q W W' — a step of a stable publication period for the cluster-manager c of term t with acknowledging nodes Q:
startJoinat a nodenwithn = corn ∈ Qonly for termst' ≤ t(at other nodes, any term);clientValueonly at nodesn ≠ c:cdoes not publish again;restartof a cluster-manager-eligible nodenonly ifn ≠ c;bootstrap,join,publish,respond,commitandrestartAnyas inStep.
PublicationExec P c t Q W W' — W' is reached from W by finitely many, possibly zero, PublicationSteps.
Formalization Note. Every stable step is a Step of the safety model, so a stable execution from a reachable cluster stays reachable and the safety theorems apply along it. Messages are still never removed: within a stable period a sent message may be handled any number of times, in any order, or never; the four universal theorems (election_vote_accepted, election_won, publication_acks_accepted, publication_committed) hold for every finite stable execution, that is, every prefix of a longer one, and the two existence theorems (election_possible, publication_possible) exhibit one; there is no fairness assumption. A stable period is a hypothesis of the theorems, not a property of the Java system: nothing here says that a real cluster reaches such a period, for how long it lasts, or how long its steps take. The parameters (kt, kv) of the election period are instantiated in the theorems with c's last accepted term and version at the start. A voter may be the candidate itself, and Q may contain c in a publication period. Only the OpenSearchSafety definitions are imported (Lean core).
import Definitions.Def_OpenSearchSafety
/-!
# OpenSearch cluster coordination: progress under stability
Safety holds in every execution. Progress cannot: in an asynchronous network a message may never be delivered, and
competing candidates can keep raising terms (this is what pre-voting and the randomized election scheduler mitigate).
The theorems of this mission state progress for the executions of a *stable period*: a window in which one node runs
for election (or publishes) and none of the steps that the relations below exclude occurs, while every other step of
the cluster remains possible — deliveries in any order, duplicates, other nodes' joins, acceptances and commits,
restarts of other nodes. Under their hypotheses, they show that in every such execution the candidate, once in the
election term, accepts and counts every voter's join for that term, and every acknowledging node in the
cluster-manager's term has acknowledged its publication or accepts it, while the cluster-manager accepts and counts
every acknowledgement of it sent by an acknowledging node; that once all of a quorum's votes (acknowledgements) are
counted, the election is won (the publication committed); and that such executions exist. No theorem states that the
start-joins an election or a publication needs stay enabled.
-/
namespace OpenSearchProgress
open OpenSearchCoordination OpenSearchSafety
variable {V : Type}
/-- A step of a stable election period for candidate `c` in term `t` with voters `Q`, `(kt, kv)` being the term and
version of `c`'s accepted state when the period starts: any step of the cluster except a start-join in a term at least
`t` other than `c`'s own request for `t`, any new publication, a bootstrap of `c` or of a voter, a restart of `c` or of
a voter that is cluster-manager-eligible (`restartAny`, the restart of a node that is not eligible, is not restricted),
and the acceptance by `c` or a voter of a publication fresher than `(kt, kv)` (a stale publication of a former
cluster-manager arriving during the election). -/
inductive ElectionStep (P : Params) (c : NodeId) (t : Nat) (Q : List NodeId) (kt : Nat) (kv : Int) :
Cluster V → Cluster V → Prop where
| bootstrap (W : Cluster V) (n : NodeId) (X : VotingConfiguration) (s : ClusterState V)
(cs' : CoordinationState V) (hX : P.initConfs X) (hnq : n ≠ c ∧ n ∉ Q)
(hcontract : (W.node n).initialStateContract s)
(hconf : s.lastCommittedConfiguration = X ∧ s.lastAcceptedConfiguration = X)
(h : (W.node n).setInitialState s = .ok cs') :
ElectionStep P c t Q kt kv W (W.set n cs')
| startJoin (W : Cluster V) (n src : NodeId) (t' : Nat) (cs' : CoordinationState V) (j : Join)
(hsrc : P.eligible src = true) (hstable : t' < t ∨ (src = c ∧ t' = t))
(h : (W.node n).handleStartJoin ⟨P.dn src, t'⟩ = .ok (cs', j)) :
ElectionStep P c t Q kt kv W ((W.set n cs').send (.join j))
| join (W : Cluster V) (n : NodeId) (j : Join) (cs' : CoordinationState V) (b : Bool)
(hsent : W.sent (.join j)) (hdest : j.targetNode.id = n)
(h : (W.node n).handleJoin P.strategy j = .ok (cs', b)) :
ElectionStep P c t Q kt kv W (W.set n cs')
| publish (W : Cluster V) (n src : NodeId) (s : ClusterState V) (pt : Nat) (pv : Int)
(cs' : CoordinationState V) (r : PublishResponse) (hsent : W.sent (.publishRequest src s pt pv))
(hfresh : n = c ∨ n ∈ Q → s.term < kt ∨ (s.term = kt ∧ s.version ≤ kv))
(h : (W.node n).handlePublishRequest ⟨s⟩ = .ok (cs', r)) :
ElectionStep P c t Q kt kv W ((W.set n cs').send (.publishResponse n src r))
| respond (W : Cluster V) (n src : NodeId) (r : PublishResponse) (cs' : CoordinationState V)
(o : Option ApplyCommitRequest) (hsent : W.sent (.publishResponse src n r))
(h : (W.node n).handlePublishResponse (P.dn src) r = .ok (cs', o)) :
ElectionStep P c t Q kt kv W (match o with
| none => W.set n cs'
| some a => (W.set n cs').send (.applyCommit a))
| commit (W : Cluster V) (n : NodeId) (a : ApplyCommitRequest) (cs' : CoordinationState V)
(hsent : W.sent (.applyCommit a)) (h : (W.node n).handleCommit a = .ok cs') :
ElectionStep P c t Q kt kv W (W.set n cs')
| restart (W : Cluster V) (n : NodeId) (s' : ClusterState V) (hn : P.eligible n = true)
(hnq : n ≠ c ∧ n ∉ Q) (hs : RestartState (W.node n).lastAcceptedState s') :
ElectionStep P c t Q kt kv W (W.set n (CoordinationState.init (W.node n).localNode (W.node n).currentTerm s'))
| restartAny (W : Cluster V) (n : NodeId) (t' : Nat) (s' : ClusterState V) (hn : P.eligible n = false) :
ElectionStep P c t Q kt kv W (W.set n (CoordinationState.init (W.node n).localNode t' s'))
end OpenSearchProgress
namespace OpenSearchProgress
open OpenSearchCoordination OpenSearchSafety
variable {V : Type}
/-- The executions of a stable election period: finite sequences of `ElectionStep`s. -/
inductive ElectionExec (P : Params) (c : NodeId) (t : Nat) (Q : List NodeId) (kt : Nat) (kv : Int) :
Cluster V → Cluster V → Prop where
| refl (W : Cluster V) : ElectionExec P c t Q kt kv W W
| step (W W' W'' : Cluster V) : ElectionExec P c t Q kt kv W W' → ElectionStep P c t Q kt kv W' W'' →
ElectionExec P c t Q kt kv W W''
/-- A step of a stable publication period for the cluster-manager `c` of term `t` with acknowledging nodes `Q`: any step
of the cluster except a start-join in a term above `t` at `c` or at a node of `Q`, a further publication by `c`, and a
restart of `c` if it is cluster-manager-eligible (`restartAny`, the restart of a node that is not eligible, is not
restricted). -/
inductive PublicationStep (P : Params) (c : NodeId) (t : Nat) (Q : List NodeId) : Cluster V → Cluster V → Prop where
| bootstrap (W : Cluster V) (n : NodeId) (X : VotingConfiguration) (s : ClusterState V)
(cs' : CoordinationState V) (hX : P.initConfs X)
(hcontract : (W.node n).initialStateContract s)
(hconf : s.lastCommittedConfiguration = X ∧ s.lastAcceptedConfiguration = X)
(h : (W.node n).setInitialState s = .ok cs') :
PublicationStep P c t Q W (W.set n cs')
| startJoin (W : Cluster V) (n src : NodeId) (t' : Nat) (cs' : CoordinationState V) (j : Join)
(hsrc : P.eligible src = true) (hstable : (n = c ∨ n ∈ Q) → t' ≤ t)
(h : (W.node n).handleStartJoin ⟨P.dn src, t'⟩ = .ok (cs', j)) :
PublicationStep P c t Q W ((W.set n cs').send (.join j))
| join (W : Cluster V) (n : NodeId) (j : Join) (cs' : CoordinationState V) (b : Bool)
(hsent : W.sent (.join j)) (hdest : j.targetNode.id = n)
(h : (W.node n).handleJoin P.strategy j = .ok (cs', b)) :
PublicationStep P c t Q W (W.set n cs')
| clientValue (W : Cluster V) (n : NodeId) (s : ClusterState V) (cs' : CoordinationState V)
(p : PublishRequest V) (hn : n ≠ c) (hcontract : (W.node n).clientValueContract s)
(h : (W.node n).handleClientValue s = .ok (cs', p)) :
PublicationStep P c t Q W ((W.set n cs').send (.publishRequest n p.acceptedState (W.node n).lastAcceptedTerm
(W.node n).lastAcceptedVersion))
| publish (W : Cluster V) (n src : NodeId) (s : ClusterState V) (pt : Nat) (pv : Int)
(cs' : CoordinationState V) (r : PublishResponse) (hsent : W.sent (.publishRequest src s pt pv))
(h : (W.node n).handlePublishRequest ⟨s⟩ = .ok (cs', r)) :
PublicationStep P c t Q W ((W.set n cs').send (.publishResponse n src r))
| respond (W : Cluster V) (n src : NodeId) (r : PublishResponse) (cs' : CoordinationState V)
(o : Option ApplyCommitRequest) (hsent : W.sent (.publishResponse src n r))
(h : (W.node n).handlePublishResponse (P.dn src) r = .ok (cs', o)) :
PublicationStep P c t Q W (match o with
| none => W.set n cs'
| some a => (W.set n cs').send (.applyCommit a))
| commit (W : Cluster V) (n : NodeId) (a : ApplyCommitRequest) (cs' : CoordinationState V)
(hsent : W.sent (.applyCommit a)) (h : (W.node n).handleCommit a = .ok cs') :
PublicationStep P c t Q W (W.set n cs')
| restart (W : Cluster V) (n : NodeId) (s' : ClusterState V) (hn : P.eligible n = true) (hnc : n ≠ c)
(hs : RestartState (W.node n).lastAcceptedState s') :
PublicationStep P c t Q W (W.set n (CoordinationState.init (W.node n).localNode (W.node n).currentTerm s'))
| restartAny (W : Cluster V) (n : NodeId) (t' : Nat) (s' : ClusterState V) (hn : P.eligible n = false) :
PublicationStep P c t Q W (W.set n (CoordinationState.init (W.node n).localNode t' s'))
/-- The executions of a stable publication period. -/
inductive PublicationExec (P : Params) (c : NodeId) (t : Nat) (Q : List NodeId) : Cluster V → Cluster V → Prop where
| refl (W : Cluster V) : PublicationExec P c t Q W W
| step (W W' W'' : Cluster V) : PublicationExec P c t Q W W' → PublicationStep P c t Q W' W'' →
PublicationExec P c t Q W W''
end OpenSearchProgress
Read-back
What the Lean code literally says, in plain math · claude-opus-5-5 (independent sub-agent, blind: Lean code + auditor instructions only)
Read-back: Def_OpenSearchProgress.lean
The file declares four inductive relations in the namespace OpenSearchProgress and nothing else: ElectionStep, ElectionExec, PublicationStep, PublicationExec. Each relates two clusters (Cluster V → Cluster V → Prop) and is indexed by fixed parameters. The payload type V is an implicit parameter throughout. The file imports Def_OpenSearchSafety, which imports Def_OpenSearchCoordination. The imported notions are expanded first, so that each declaration can be read on its own. The module docstring describes "stable periods" for an election and for a publication; the last section compares it with the terms.
Imported notions, expanded
Notation used in this read-back
For a node state (a CoordinationState V), write:
- for
localNode, forcurrentTerm(a natural number) and forlastAcceptedState(a cluster state); - and (an integer). is
lastAcceptedVersionOrMetadataVersion: it equals if , and otherwise; - and for the last committed and last accepted configuration of ;
- and for the lists of node ids of
joinVotesandpublishVotes; - for
startedJoinSinceLastRebootand forelectionWon(both Booleans), forlastPublishedVersion(an integer) and forlastPublishedConfiguration.
" with " means with field replaced and every other field unchanged. For keys, meaning pairs of a natural-number term and an integer version, means . This is the lexicographic order; the Safety file names it KeyLE, but the Progress file writes it out inline.
Node ids, discovery nodes, parameters, strategies
-
A node id is a natural number. Only equality is used. A discovery node is a pair of an id and a Boolean .
-
The parameters consist of:
- a Boolean function on node ids (cluster-manager eligibility);
- an arbitrary predicate on voting configurations (the configurations a node may bootstrap with);
- an election strategy .
We write .
-
An election strategy is a single Boolean function
satisfiesAdditionalQuorumConstraintsof seven arguments: the local node, current term, accepted term, accepted version, last committed configuration, last accepted configuration and vote collection.ElectionStrategy.DEFAULT_INSTANCEreturnstruefor all arguments. -
is used as a hypothesis by every theorem. It says: for all configurations with and , and all lists of ids whose members are all eligible, if and then some belongs to both and . It holds vacuously if admits nothing. It also holds, for example, if admits exactly one configuration, because any two majorities of one set intersect.
Voting configurations, quorums, vote collections
- A voting configuration is a strictly increasing list of ids . It is therefore duplicate-free, and equality of configurations is equality of the id lists, i.e. of the id sets. has no ids. " is empty" (
isEmpty) means . - For a list of ids, (Lean
X.hasQuorum L = true) means
Properties:
- it is false whenever is empty, and false whenever ;
- it is monotone in : if every member of is in , then ;
- duplicates in , and members of outside , are irrelevant.
- A vote collection is a list of ids. returns if and , and otherwise. So after , the id is in the collection exactly when it already was, or is flagged cluster-manager. .
Cluster states and messages
-
A cluster state has the following fields:
- , and ;
- two configurations, (last committed) and (last accepted);
- an optional natural number ;
- two Booleans,
clusterUUIDKnownandclusterUUIDCommitted; - a payload in .
-
works in two steps:
- if , it sets ;
- then, if
clusterUUIDKnownholds andclusterUUIDCommitteddoes not, it setsclusterUUIDCommitted := true.
Term, version, metadata version, and payload are unchanged.
-
The message payloads are:
- A join has a source and a target discovery node (, ), a term , and the voter's last accepted term and version . The version is filled by
handleStartJoinwith , so for a term-0 accepted state it is the metadata version. - A publish request is , a single cluster state.
- A publish response is , a term and a version.
- An apply-commit request is : a source discovery node, a term and a version.
- A join has a source and a target discovery node (, ), a term , and the voter's last accepted term and version . The version is filled by
-
A message is one of four kinds:
- ;
- : a request from node carrying state and a "ghost" parent key ;
- : a response from to ;
- .
Publish requests and commits carry no addressee. A join's addressee is .
Node states and handlers
A node state has the fields listed in the notation. is the node state with localNode , current term , accepted state , join votes , , , , and publish votes .
Each handler returns either an error or . Its guards are tested in the order listed, and the first guard that holds yields an error.
-
setInitialState is an error if is non-empty. Otherwise it is .
- initialStateContract is a separate proposition that the handler does not check. It requires ; empty; ; empty; ; ; ; ; ; and and non-empty.
-
handleStartJoin is an error if . Otherwise it is , where:
- is with , , , , , and ;
- , whose source is this node's localNode and whose target is the requester .
-
handleJoin:
- error if ;
- error if ;
- error if ;
- error if and ;
- error if is empty;
- otherwise:
- let and ;
- let be with and ;
- let be with if , and otherwise;
- the result is .
Here . For the default strategy this is just the two quorums. The configurations are 's, which the handler does not change. The target node of is not examined. Whether the handler succeeds, and the new vote list, do not depend on the strategy: only and do.
wonis recomputed on every successful join, and the handler itself does not prevent it from going from true to false. Java asserts that this never happens; see the citation check. Whether it can happen in a reachable cluster is not addressed by any definition. -
handleClientValue returns an error, in this order:
- if ;
- if ;
- if ;
- if ;
- if and ;
- if .
Otherwise it returns . Its contract clientValueContract is .
-
handlePublishRequest:
- error if ;
- else, if and :
- if (the "Zen1" branch): error if is none; accept if it is some with ; error otherwise;
- if : error;
- otherwise, accept.
"Accept" means .
-
handlePublishResponse returns an error if , if , or if . Otherwise, let and let be with .
- If (this conjunction is
isPublishQuorum), the result is . - Otherwise it is .
- If (this conjunction is
-
handleCommit returns an error if , if , or if . Otherwise it returns . The source is not examined.
Clusters, steps, reachability
-
A cluster has , a node state for every id, and , a set of messages given as a predicate.
- replaces node 's state by and leaves the other nodes and
sentunchanged. - adds to
sentand leaves the nodes unchanged.
No operation removes a message.
- replaces node 's state by and leaves the other nodes and
-
RestartState relates a node's old accepted state to its new one . It holds in one of two cases:
- First case: has the same term, version, , ,
clusterUUIDKnownandclusterUUIDCommittedas . Metadata version, cluster-manager node and payload are free. - Second case: has term 0, both configurations empty and
clusterUUIDKnown = false, and has term 0 and both configurations empty. Everything else about is free.
- First case: has the same term, version, , ,
-
Step holds in exactly the following nine cases, where for the acting node :
-
bootstrap :
- guards: ; ; ; ;
- result: .
-
startJoin :
- guards: ; ;
- result: .
No start-join message is required. Any node may act on a request "from" any eligible node in any term.
-
join :
- guards: ; ; ;
- result: .
-
clientValue :
- guards: ; ;
- result: . The state in is .
-
publish :
- guards: ; ;
- result: .
-
respond :
- guards: ; ;
- result: if , and if .
-
commit :
- guards: ; ;
- result: .
-
restart :
- guards: ; ;
- result: . The current term is kept.
-
restartAny :
- guard: ;
- result: , with an arbitrary term and an arbitrary state .
-
-
Initial: no message is sent, and every node has for some state with term 0 and both configurations empty. Version, metadata version, payload, cluster-manager node and UUID flags are free and may differ per node.
-
Reachable is the least set of clusters that contains every initial cluster and is closed under .
-
Committed: some apply-commit request , with any source , is in .
The Safety file also defines BootstrapConfig, Parent, Descends, KeyLE, Below, Published, Realizes, JoinVoters, BaseVoters, RespVoters, ElectionRecord, Won and the invariant Inv. None of them occurs in Def_OpenSearchProgress.lean or in the six theorem statements of this mission.
Java citation check
The Progress file cites no Java. Its four relations reuse the handlers of Def_OpenSearchCoordination (cited as CS: = CoordinationState.java and so on, at OpenSearch revision b2a54ba5…). The local checkout /home/user/opensearch-project/OpenSearch is at exactly that revision. I compared every handler and helper that the progress definitions and theorems rely on.
| Lean | Cited Java | Verdict |
|---|---|---|
CoordinationState.init | CS:84-106 | Matches. The remote-state flag is omitted. |
isElectionQuorum | CS:144-154; ES:68-88 | Matches: committed quorum ∧ accepted quorum ∧ additional constraint. |
isPublishQuorum | CS:156-158 | Matches. |
setInitialState, initialStateContract | CS:180-204 (asserts CS:190-201) | Matches. The 11 assertions are the 11 conjuncts of the contract. |
handleStartJoin | CS:213-255 | Matches. The logging branch is omitted. |
handleJoin | CS:264-339 | Matches. Java's addJoinVote(join) is addVote(join.getSourceNode()) plus recording the join, CS:759-765. Two assertions are not checked: CS:265 (target matches local node) and CS:323, "we cannot go from won to not won". |
handleClientValue, clientValueContract | CS:348-407 (assert CS:397-398) | Matches. |
handlePublishRequest | CS:416-460 | Matches, including the Zen1 branch: a null incoming cluster-manager throws in Java and is an error here. The remote write is omitted. |
handlePublishResponse | CS:471-515 | Matches. handlePreCommit() (remote state, CS:510) is omitted. |
handleCommit, markCommitted | CS:523-573; CS:703-740 | Matches. The remote commit and the assertions CS:572 and CS:708-710 are omitted. |
VoteCollection.addVote, isQuorum | CS:755-757; CS:772-774 | Matches. The Java map of discovery nodes keyed by id becomes a list of ids. |
VotingConfiguration.hasQuorum | CM:405-409 | Matches. The ids are sorted and duplicate-free, so the filter length equals the size of the intersection. |
ElectionStrategy.DEFAULT_INSTANCE | ES:46-59 | Matches: always true. |
versionOrMetadataVersion | CLS:239-244 | Matches. |
ZEN1_BWC_TERM | Coordinator.java:134 | Matches: 0. |
I also spot-checked the restart description in the Safety file (GatewayMetaState.java:141-256), and it is consistent at the level I checked:
- the on-disk term, version and metadata are loaded;
- remote restore happens only when the local cluster UUID is unknown;
- the role-change caveat at 232-255 is the branch for nodes that are neither cluster-manager nor data nodes, which persists term 0.
I did not check the Safety file's Coordinator.java (CO:1101-1161) or ClusterBootstrapService citations. No transcription mismatch was found.
One item in the table bears on this mission. Java asserts at CS:323 that electionWon never goes from true to false in handleJoin. In Java production builds assertions are disabled, and in the Lean model nothing enforces this. In a publication period the cluster-manager may still handle joins. Some publication theorems implicitly require that stays elected: conjunct (B) of publication_acks_accepted, and publication_committed. They therefore rely on this being a consequence of reachability, under the default strategy they assume.
Declaration 1: ElectionStep P c t Q kt kv
Parameters.
- : parameters.
- : a node id, the "candidate".
- : the "term".
- : a list of node ids, the "voters".
- and .
The definition does not relate these to each other:
- need not be eligible, and may belong to ;
- may be empty or contain duplicates;
- is an arbitrary key. The docstring reads it as "the term and version of 's accepted state when the period starts", but only the theorems' instantiation supplies that meaning.
Cases. holds in exactly the following eight cases, where :
- bootstrap : all hypotheses of Step case 1, plus . Result: .
- startJoin : all hypotheses of Step case 2 with request term , plus
Result: . The guard applies to every acting node , not only to and . 3. join : exactly Step case 3, with no extra guard. 4. publish : all hypotheses of Step case 5, plus
that is, if the receiver is or a voter, then . Result: . Other receivers are unrestricted. 5. respond : exactly Step case 6. 6. commit : exactly Step case 7. 7. restart : all hypotheses of Step case 8, plus . Result: . 8. restartAny : exactly Step case 9. There is no condition relating to or ; the only guard is .
There is no case for Step's clientValue: no node sends a new publication request during an election period.
What the guards amount to. I derived these from the handlers; the file does not state them.
-
Start-joins.
handleStartJoinrequires and the guard requires . So a node whose current term is cannot start-join at all during the period. A node in a term can move, through start-joins:- to any term strictly between its term and , on a request from any eligible requester;
- to itself only on a request whose requester is .
No node's term passes through a start-join. A non-eligible node can still receive any term through
restartAny. -
Publications. Only publication requests already in at the start of the period can be handled. Responses and commits can still be produced from them.
-
Freshness bound at . Take the theorems' instantiation, of at the start, in a reachable cluster with eligible. Then can accept no publication during the period. The reasons:
- Acceptance requires , so the guard forces and .
- The handler rejects that case unless .
- Sent publications have term (observation G3 below).
So 's accepted term, version and stay fixed during the period, and only a commit can change , to . The voters' accepted keys can grow, but only to keys .
-
Version measure. The bound is compared with .
handleJoininstead compares joins against , the metadata version at term 0. When , the guard admits only term-0 publications, and none exist in a reachable cluster (observation G3). So and the voters accept nothing in that case, and the two version measures never interact.
Declaration 2: ElectionExec P c t Q kt kv
is the reflexive-transitive closure of ElectionStep, built by appending steps at the end:
- holds for every ;
- if and , then .
Equivalently, there are and clusters such that each consecutive pair is an election step for the same parameters . The empty execution (, ) is included.
Declaration 3: PublicationStep P c t Q
Parameters.
- : parameters.
- : a node id, "the cluster-manager of term ".
- .
- : a list of node ids, "acknowledging nodes".
None of them is constrained by the definition. In particular, is not required to have won or to be in term .
Cases. holds in exactly the following nine cases, where :
- bootstrap: exactly Step case 1. There is no condition on , so and members of may bootstrap if the contract holds.
- startJoin : all hypotheses of Step case 2, plus . Nodes outside may start-join to any term.
- join: exactly Step case 3.
- clientValue : all hypotheses of Step case 4, plus . Result: . Every node other than may still publish.
- publish: exactly Step case 5. There is no freshness restriction.
- respond: exactly Step case 6.
- commit: exactly Step case 7.
- restart : all hypotheses of Step case 8, plus . Members of may restart.
- restartAny: exactly Step case 9, with no condition relating to .
What the guards amount to. These are derived, not stated.
- Suppose at the start, as in all publication theorems. Then 's term stays throughout: a start-join at would need and ;
restartis excluded for ; andrestartAnyneeds non-eligible. In a reachable cluster only eligible nodes ever have (observation G3), so a with is eligible. - cannot publish again, but it can still handle joins, publications, responses and commits.
- A member of in a term can move to any term , on a request from any eligible requester. A member of in a term cannot start-join.
Declaration 4: PublicationExec P c t Q
PublicationExec is the reflexive-transitive closure of PublicationStep, with the same shape as ElectionExec: zero or more publication steps for fixed .
Relation to Step, and general observations
These observations are derived by the auditor; the file states none of them.
- G1. Each constructor of
ElectionStepandPublicationStepcarries exactly the hypotheses of the same-namedStepconstructor plus the extra guards listed above, and produces the same resulting cluster. Hence every election or publication step is aStep, and anElectionExecorPublicationExecstarting from a reachable cluster ends in a reachable cluster. - G2. Along every step, and hence every execution, . A message present at the start of a period remains available, and can be handled any number of times.
- G3. In a reachable cluster the following hold, read off the step relation directly and not through
Inv:- every node's localNode is .
Initialsets it,setandinitkeep it, and no handler changes it; - every sent join has for the node that produced it, and for an eligible . So only eligible nodes ever handle a join;
- becomes true only in
handleJoin, so only eligible nodes ever have ; - for eligible nodes, , and never decreases;
- every sent publication has term . Its term is the publisher's current term while the publisher is elected. Being elected requires , which requires a start-join since the last restart, which makes the term .
- every node's localNode is .
- G4. Executions are finite, and no fairness or eventual delivery is part of either relation. Every message stays available, but no step is ever forced to occur.
- G5. The Coordination file states its own abstraction for a failed remote write. Java throws after the local effects; the transcription returns the result instead. For safety this only adds behaviours: a result that is sent but never delivered is equivalent to the Java throw. For the progress statements it has a consequence. In the model, an acceptance always yields a publish response that stays deliverable, and a counted quorum always yields a commit. Java can accept a publication locally and then throw, sending no response; a re-delivery of the same publication is then rejected (same version). So statements of the form "a voter in term has either acknowledged or can still accept" (
publication_acks_accepted), or "once the votes are counted, a commit was sent" (publication_committed), hold for the model. In OpenSearch they presuppose that remote writes succeed.
Docstrings versus terms
-
Module docstring. It says a stable period is one in which "one node runs for election (or publishes) and nothing disrupts it, while every other step of the cluster remains possible — deliveries in any order, duplicates, other nodes' joins, acceptances and commits, restarts of other nodes". In the terms:
- The election period also forbids every new publication by any node. It forbids any start-join anywhere to a term other than handling 's request for ; this applies to nodes outside too, so no third node can start an election at a term . It forbids bootstraps of and , and acceptance by or of publications fresher than .
- The publication period forbids start-joins above only at and , further publications by , and restarts of .
These exclusions are the ones listed in the step docstrings, so "every other step" must be read relative to them. The docstring also says that the theorems show "the steps the protocol needs stay enabled". The theorems state enabledness only for three steps:
- handling a voter's term- join;
- a voter that is already in term handling the publication;
- handling a voter's acknowledgement.
No theorem states that the start-joins needed along the way stay enabled: 's own start-join to , a voter handling 's request, and a member of moving up to term in a publication period. The period definitions allow these steps, and the handler's only guard is the term comparison, so this is a gap in what is stated rather than a contradiction.
-
ElectionStepdocstring. It is consistent with the term: start-joins in a term other than 's request for are excluded; new publications are excluded; bootstrap and restart of or a voter are excluded; and acceptance by or a voter of a publication with a key above is excluded. There is one nuance. "Restart of or of a voter" is excluded only for therestartconstructor (eligible nodes);restartAny(non-eligible nodes) is not guarded by node identity. This matters only if or a voter is non-eligible, which the election theorems rule out withhcandhQ. -
ElectionExecdocstring ("finite sequences ofElectionSteps"). Consistent, and it includes the empty sequence. -
PublicationStepdocstring. Consistent: start-joins above at or , a further publication by , and a restart of are excluded. The samerestartAnynuance applies to . The publication theorems do not assume eligible, but in a reachable cluster implies it (G3). -
PublicationExecdocstring. Consistent.
Confirmed by the mission captain (proposal self-audit).
Confirmed by the moderator at approval.