Skip to content

Commit

Permalink
ccfraft!AdvanceCommitIndex advance to the smalles possible index, whe…
Browse files Browse the repository at this point in the history
…reas raft.h advances to the largest possible one.

Originally found in #5798.
  • Loading branch information
lemmy committed Jan 5, 2024
1 parent d19bf19 commit 2740995
Showing 1 changed file with 22 additions and 12 deletions.
34 changes: 22 additions & 12 deletions tla/consensus/ccfraft.tla
Original file line number Diff line number Diff line change
Expand Up @@ -678,24 +678,34 @@ AdvanceCommitIndex(i) ==
required_quorum == Quorums[config_servers]
agree_servers == {k \in config_servers : matchIndex[i][k] >= idx}
IN (IF i \in config_servers THEN {i} ELSE {}) \cup agree_servers \in required_quorum
\* We want to get the smallest such index forward that is a signature
\* This index must be from the current term,
\* as explained by Figure 8 and Section 5.4.2 of https://raft.github.io/raft.pdf
\* See find_highest_possible_committable_index in raft.h
new_index == SelectInSubSeq(log[i], commitIndex[i]+1, Len(log[i]),
LAMBDA e : e.contentType = TypeSignature /\ e.term = currentTerm[i])
\* The function find_highest_possible_committable_index in raft.h returns the largest committable index
\* in the current term (Figure 8 and Section 5.4.2 of https://raft.github.io/raft.pdf explains why it has
\* to be the *current* term). Finding the largest index is an (mplementation-level) optimization that
\* reduces the number of AdvanceCommitIndex calls, but this optimization also shrinks this spec's state-space.
\*
\* Theoretically, any committable index in the current term works, i.e., highestCommittableIndex could be
\* defined non-deterministically as:
\* \E idx \in { j \in (commitIndex[i]+1)..Len(log[i]) : /\ log[i][j].term = currentTerm[i]
\* /\ log[i][j].contentType = TypeSignature }
\* /\ HasConsensusWatermark(idx)
\* /\ ...
\*
\* Max({0} \cup {...}) to default to 0 if no committable index is found.
highestCommittableIndex == Max({0} \cup { j \in (commitIndex[i]+1)..Len(log[i]) :
/\ log[i][j].term = currentTerm[i]
/\ log[i][j].contentType = TypeSignature
/\ HasConsensusWatermark(j) })
IN
/\ HasConsensusWatermark(new_index)
\* only advance if necessary (this is basically a sanity check after the Min above)
/\ commitIndex[i] < new_index
/\ commitIndex' = [commitIndex EXCEPT ![i] = new_index]
\* only advance if necessary (this is basically a sanity)
/\ commitIndex[i] < highestCommittableIndex
/\ commitIndex' = [commitIndex EXCEPT ![i] = highestCommittableIndex]
/\ committableIndices' = [ committableIndices EXCEPT ![i] = @ \ 0..commitIndex'[i] ]
\* If commit index surpasses the next configuration, pop configs, and retire as leader if removed
/\ IF /\ Cardinality(DOMAIN configurations[i]) > 1
/\ new_index >= NextConfigurationIndex(i)
/\ highestCommittableIndex >= NextConfigurationIndex(i)
THEN
LET new_configurations == RestrictDomain(configurations[i],
LAMBDA c : c >= LastConfigurationToIndex(i, new_index))
LAMBDA c : c >= LastConfigurationToIndex(i, highestCommittableIndex))
IN
/\ configurations' = [configurations EXCEPT ![i] = new_configurations]
\* Retire if i is not in active configuration anymore
Expand Down

0 comments on commit 2740995

Please sign in to comment.