FastLeaderElection.tla 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. ------------------------- MODULE FastLeaderElection -------------------------
  2. \* This is the formal specification for Fast Leader Election in Zab protocol.
  3. (* Reference:
  4. FastLeaderElection.java, Vote.java, QuorumPeer.java in https://github.com/apache/zookeeper.
  5. Medeiros A. ZooKeeper's atomic broadcast protocol: Theory and practice[J]. Aalto University School of Science, 2012.
  6. *)
  7. EXTENDS Integers, FiniteSets, Sequences, Naturals, TLC
  8. -----------------------------------------------------------------------------
  9. \* The set of server identifiers
  10. CONSTANT Server
  11. \* Server states
  12. CONSTANTS LOOKING, FOLLOWING, LEADING
  13. (* NOTE: In spec, we do not discuss servers whose ServerState is OBSERVING.
  14. *)
  15. \* Message types
  16. CONSTANTS NOTIFICATION
  17. \* Timeout signal
  18. CONSTANT NONE
  19. -----------------------------------------------------------------------------
  20. Quorums == {Q \in SUBSET Server: Cardinality(Q)*2 > Cardinality(Server)}
  21. NullPoint == CHOOSE p: p \notin Server
  22. -----------------------------------------------------------------------------
  23. \* Server's state(LOOKING, FOLLOWING, LEADING).
  24. VARIABLE state
  25. VARIABLE history
  26. \* The epoch number of the last NEWLEADER packet accepted, used for comparing.
  27. VARIABLE currentEpoch
  28. \* The index and zxid of the last processed transaction in history.
  29. VARIABLE lastProcessed
  30. \* currentVote[i]: The server who i thinks is the current leader(id,zxid,peerEpoch,...).
  31. VARIABLE currentVote
  32. \* Election instance.(logicalClock in code)
  33. VARIABLE logicalClock
  34. \* The votes from the current leader election are stored in ReceiveVotes.
  35. VARIABLE receiveVotes
  36. (* The votes from previous leader elections, as well as the votes from the current leader election are
  37. stored in outofelection. Note that notifications in a LOOKING state are not stored in outofelection.
  38. Only FOLLOWING or LEADING notifications are stored in outofelection. *)
  39. VARIABLE outOfElection
  40. \* recvQueue[i]: The queue of received notifications or timeout signals in server i.
  41. VARIABLE recvQueue
  42. \* A veriable to wait for new notifications, corresponding to line 1050 in FastLeaderElection.java.
  43. VARIABLE waitNotmsg
  44. \* leadingVoteSet[i]: The set of voters that follow i.
  45. VARIABLE leadingVoteSet
  46. (* The messages about election sent from one server to another.
  47. electionMsgs[i][j] means the input buffer of server j from server i. *)
  48. VARIABLE electionMsgs
  49. \* Set used for mapping Server to Integers, to compare ids from different servers.
  50. \* VARIABLE idTable
  51. serverVarsL == <<state, currentEpoch, lastProcessed, history>>
  52. electionVarsL == <<currentVote, logicalClock, receiveVotes, outOfElection, recvQueue, waitNotmsg>>
  53. leaderVarsL == <<leadingVoteSet>>
  54. varsL == <<serverVarsL, electionVarsL, leaderVarsL, electionMsgs>>
  55. -----------------------------------------------------------------------------
  56. \* Processing of electionMsgs
  57. BroadcastNotmsg(i, m) == electionMsgs' = [electionMsgs EXCEPT ![i] = [v \in Server |-> IF v /= i
  58. THEN Append(electionMsgs[i][v], m)
  59. ELSE electionMsgs[i][v]]]
  60. DiscardNotmsg(i, j) == electionMsgs' = [electionMsgs EXCEPT ![i][j] = IF electionMsgs[i][j] /= << >>
  61. THEN Tail(electionMsgs[i][j])
  62. ELSE << >>]
  63. ReplyNotmsg(i, j, m) == electionMsgs' = [electionMsgs EXCEPT ![i][j] = Append(electionMsgs[i][j], m),
  64. ![j][i] = Tail(electionMsgs[j][i])]
  65. -----------------------------------------------------------------------------
  66. \* Processing of recvQueue
  67. RECURSIVE RemoveNone(_)
  68. RemoveNone(seq) == CASE seq = << >> -> << >>
  69. [] seq /= << >> -> IF Head(seq).mtype = NONE THEN RemoveNone(Tail(seq))
  70. ELSE <<Head(seq)>> \o RemoveNone(Tail(seq))
  71. \* Processing of idTable and order comparing
  72. RECURSIVE InitializeIdTable(_)
  73. InitializeIdTable(Remaining) == IF Remaining = {} THEN {}
  74. ELSE LET chosen == CHOOSE i \in Remaining: TRUE
  75. re == Remaining \ {chosen}
  76. IN {<<chosen, Cardinality(Remaining)>>} \union InitializeIdTable(re)
  77. IdTable == InitializeIdTable(Server)
  78. \* FALSE: id1 < id2; TRUE: id1 > id2
  79. IdCompare(id1,id2) == LET item1 == CHOOSE item \in IdTable: item[1] = id1
  80. item2 == CHOOSE item \in IdTable: item[1] = id2
  81. IN item1[2] > item2[2]
  82. \* FALSE: zxid1 <= zxid2; TRUE: zxid1 > zxid2
  83. ZxidCompare(zxid1, zxid2) == \/ zxid1[1] > zxid2[1]
  84. \/ /\ zxid1[1] = zxid2[1]
  85. /\ zxid1[2] > zxid2[2]
  86. ZxidEqual(zxid1, zxid2) == zxid1[1] = zxid2[1] /\ zxid1[2] = zxid2[2]
  87. \* FALSE: vote1 <= vote2; TRUE: vote1 > vote2
  88. TotalOrderPredicate(vote1, vote2) == \/ vote1.proposedEpoch > vote2.proposedEpoch
  89. \/ /\ vote1.proposedEpoch = vote2.proposedEpoch
  90. /\ \/ ZxidCompare(vote1.proposedZxid, vote2.proposedZxid)
  91. \/ /\ ZxidEqual(vote1.proposedZxid, vote2.proposedZxid)
  92. /\ IdCompare(vote1.proposedLeader, vote2.proposedLeader)
  93. VoteEqual(vote1, round1, vote2, round2) == /\ vote1.proposedLeader = vote2.proposedLeader
  94. /\ ZxidEqual(vote1.proposedZxid, vote2.proposedZxid)
  95. /\ vote1.proposedEpoch = vote2.proposedEpoch
  96. /\ round1 = round2
  97. InitLastProcessed(i) == IF Len(history[i]) = 0 THEN [ index |-> 0,
  98. zxid |-> <<0, 0>> ]
  99. ELSE
  100. LET lastIndex == Len(history[i])
  101. entry == history[i][lastIndex]
  102. IN [ index |-> lastIndex,
  103. zxid |-> entry.zxid ]
  104. RECURSIVE InitAcksidInTxns(_,_)
  105. InitAcksidInTxns(txns, src) == IF Len(txns) = 0 THEN << >>
  106. ELSE LET newTxn == [ zxid |-> txns[1].zxid,
  107. value |-> txns[1].value,
  108. ackSid |-> {src},
  109. epoch |-> txns[1].epoch ]
  110. IN <<newTxn>> \o InitAcksidInTxns( Tail(txns), src)
  111. InitHistory(i) == LET newState == state'[i] IN
  112. IF newState = LEADING THEN InitAcksidInTxns(history[i], i)
  113. ELSE history[i]
  114. -----------------------------------------------------------------------------
  115. \* Processing of currentVote
  116. InitialVote == [proposedLeader |-> NullPoint,
  117. proposedZxid |-> <<0, 0>>,
  118. proposedEpoch |-> 0]
  119. SelfVote(i) == [proposedLeader |-> i,
  120. proposedZxid |-> lastProcessed[i].zxid,
  121. proposedEpoch |-> currentEpoch[i]]
  122. UpdateProposal(i, nid, nzxid, nepoch) == currentVote' = [currentVote EXCEPT ![i].proposedLeader = nid, \* no need to record state in LOOKING
  123. ![i].proposedZxid = nzxid,
  124. ![i].proposedEpoch = nepoch]
  125. -----------------------------------------------------------------------------
  126. \* Processing of receiveVotes and outOfElection
  127. RvClear(i) == receiveVotes' = [receiveVotes EXCEPT ![i] = [v \in Server |-> [vote |-> InitialVote,
  128. round |-> 0,
  129. state |-> LOOKING,
  130. version |-> 0]]]
  131. RvPut(i, id, mvote, mround, mstate) == receiveVotes' = CASE receiveVotes[i][id].round < mround -> [receiveVotes EXCEPT ![i][id].vote = mvote,
  132. ![i][id].round = mround,
  133. ![i][id].state = mstate,
  134. ![i][id].version = 1]
  135. [] receiveVotes[i][id].round = mround -> [receiveVotes EXCEPT ![i][id].vote = mvote,
  136. ![i][id].state = mstate,
  137. ![i][id].version = @ + 1]
  138. [] receiveVotes[i][id].round > mround -> receiveVotes
  139. Put(i, id, rcvset, mvote, mround, mstate) == CASE rcvset[id].round < mround -> [rcvset EXCEPT ![id].vote = mvote,
  140. ![id].round = mround,
  141. ![id].state = mstate,
  142. ![id].version = 1]
  143. [] rcvset[id].round = mround -> [rcvset EXCEPT ![id].vote = mvote,
  144. ![id].state = mstate,
  145. ![id].version = @ + 1]
  146. [] rcvset[id].round > mround -> rcvset
  147. RvClearAndPut(i, id, vote, round) == receiveVotes' = LET oneVote == [vote |-> vote,
  148. round |-> round,
  149. state |-> LOOKING,
  150. version |-> 1]
  151. IN [receiveVotes EXCEPT ![i] = [v \in Server |-> IF v = id THEN oneVote
  152. ELSE [vote |-> InitialVote,
  153. round |-> 0,
  154. state |-> LOOKING,
  155. version |-> 0]]]
  156. VoteSet(i, msource, rcvset, thisvote, thisround) == {msource} \union {s \in (Server \ {msource}): VoteEqual(rcvset[s].vote,
  157. rcvset[s].round,
  158. thisvote,
  159. thisround)}
  160. HasQuorums(i, msource, rcvset, thisvote, thisround) == LET Q == VoteSet(i, msource, rcvset, thisvote, thisround)
  161. IN IF Q \in Quorums THEN TRUE ELSE FALSE
  162. CheckLeader(i, votes, thisleader, thisround) == IF thisleader = i THEN (IF thisround = logicalClock[i] THEN TRUE ELSE FALSE)
  163. ELSE (IF votes[thisleader].vote.proposedLeader = NullPoint THEN FALSE
  164. ELSE (IF votes[thisleader].state = LEADING THEN TRUE
  165. ELSE FALSE))
  166. OoeClear(i) == outOfElection' = [outOfElection EXCEPT ![i] = [v \in Server |-> [vote |-> InitialVote,
  167. round |-> 0,
  168. state |-> LOOKING,
  169. version |-> 0]]]
  170. OoePut(i, id, mvote, mround, mstate) == outOfElection' = CASE outOfElection[i][id].round < mround -> [outOfElection EXCEPT ![i][id].vote = mvote,
  171. ![i][id].round = mround,
  172. ![i][id].state = mstate,
  173. ![i][id].version = 1]
  174. [] outOfElection[i][id].round = mround -> [outOfElection EXCEPT ![i][id].vote = mvote,
  175. ![i][id].state = mstate,
  176. ![i][id].version = @ + 1]
  177. [] outOfElection[i][id].round > mround -> outOfElection
  178. -----------------------------------------------------------------------------
  179. InitServerVarsL == /\ state = [s \in Server |-> LOOKING]
  180. /\ currentEpoch = [s \in Server |-> 0]
  181. /\ lastProcessed = [s \in Server |-> [index |-> 0,
  182. zxid |-> <<0, 0>>] ]
  183. /\ history = [s \in Server |-> << >>]
  184. InitElectionVarsL == /\ currentVote = [s \in Server |-> SelfVote(s)]
  185. /\ logicalClock = [s \in Server |-> 0]
  186. /\ receiveVotes = [s \in Server |-> [v \in Server |-> [vote |-> InitialVote,
  187. round |-> 0,
  188. state |-> LOOKING,
  189. version |-> 0]]]
  190. /\ outOfElection = [s \in Server |-> [v \in Server |-> [vote |-> InitialVote,
  191. round |-> 0,
  192. state |-> LOOKING,
  193. version |-> 0]]]
  194. /\ recvQueue = [s \in Server |-> << >>]
  195. /\ waitNotmsg = [s \in Server |-> FALSE]
  196. InitLeaderVarsL == leadingVoteSet = [s \in Server |-> {}]
  197. InitL == /\ InitServerVarsL
  198. /\ InitElectionVarsL
  199. /\ InitLeaderVarsL
  200. /\ electionMsgs = [s \in Server |-> [v \in Server |-> << >>]]
  201. \* /\ idTable = InitializeIdTable(Server)
  202. -----------------------------------------------------------------------------
  203. (* The beginning part of FLE's main function lookForLeader() *)
  204. ZabTimeout(i) ==
  205. /\ state[i] \in {LEADING, FOLLOWING}
  206. /\ state' = [state EXCEPT ![i] = LOOKING]
  207. /\ lastProcessed' = [lastProcessed EXCEPT ![i] = InitLastProcessed(i)]
  208. /\ logicalClock' = [logicalClock EXCEPT ![i] = logicalClock[i] + 1]
  209. /\ currentVote' = [currentVote EXCEPT ![i] = [proposedLeader |-> i,
  210. proposedZxid |-> lastProcessed'[i].zxid,
  211. proposedEpoch |-> currentEpoch[i]]]
  212. /\ receiveVotes' = [receiveVotes EXCEPT ![i] = [v \in Server |-> [vote |-> InitialVote,
  213. round |-> 0,
  214. state |-> LOOKING,
  215. version |-> 0]]]
  216. /\ outOfElection' = [outOfElection EXCEPT ![i] = [v \in Server |-> [vote |-> InitialVote,
  217. round |-> 0,
  218. state |-> LOOKING,
  219. version |-> 0]]]
  220. /\ recvQueue' = [recvQueue EXCEPT ![i] = << >>]
  221. /\ waitNotmsg' = [waitNotmsg EXCEPT ![i] = FALSE]
  222. /\ leadingVoteSet' = [leadingVoteSet EXCEPT ![i] = {}]
  223. /\ BroadcastNotmsg(i, [mtype |-> NOTIFICATION,
  224. msource |-> i,
  225. mstate |-> LOOKING,
  226. mround |-> logicalClock'[i],
  227. mvote |-> currentVote'[i]])
  228. /\ UNCHANGED <<currentEpoch, history>>
  229. (* Abstraction of WorkerReceiver.run() *)
  230. ReceiveNotmsg(i, j) ==
  231. /\ electionMsgs[j][i] /= << >>
  232. /\ LET notmsg == electionMsgs[j][i][1]
  233. toSend == [mtype |-> NOTIFICATION,
  234. msource |-> i,
  235. mstate |-> state[i],
  236. mround |-> logicalClock[i],
  237. mvote |-> currentVote[i]]
  238. IN \/ /\ state[i] = LOOKING
  239. /\ recvQueue' = [recvQueue EXCEPT ![i] = Append(RemoveNone(recvQueue[i]), notmsg)]
  240. /\ LET replyOk == /\ notmsg.mstate = LOOKING
  241. /\ notmsg.mround < logicalClock[i]
  242. IN
  243. \/ /\ replyOk
  244. /\ ReplyNotmsg(i, j, toSend)
  245. \/ /\ ~replyOk
  246. /\ DiscardNotmsg(j, i)
  247. \/ /\ state[i] \in {LEADING, FOLLOWING}
  248. /\ \/ \* Only reply when sender's state is LOOKING
  249. /\ notmsg.mstate = LOOKING
  250. /\ ReplyNotmsg(i, j, toSend)
  251. \/ \* sender's state and mine are both not LOOKING, just discard
  252. /\ notmsg.mstate /= LOOKING
  253. /\ DiscardNotmsg(j, i)
  254. /\ UNCHANGED recvQueue
  255. /\ UNCHANGED <<serverVarsL, currentVote, logicalClock, receiveVotes, outOfElection, waitNotmsg, leaderVarsL>>
  256. NotmsgTimeout(i) ==
  257. /\ state[i] = LOOKING
  258. /\ \A j \in Server: electionMsgs[j][i] = << >>
  259. /\ recvQueue[i] = << >>
  260. /\ recvQueue' = [recvQueue EXCEPT ![i] = Append(recvQueue[i], [mtype |-> NONE])]
  261. /\ UNCHANGED <<serverVarsL, currentVote, logicalClock, receiveVotes, outOfElection, waitNotmsg, leaderVarsL, electionMsgs>>
  262. -----------------------------------------------------------------------------
  263. \* Sub-action in HandleNotmsg
  264. ReceivedFollowingAndLeadingNotification(i, n) ==
  265. LET newVotes == Put(i, n.msource, receiveVotes[i], n.mvote, n.mround, n.mstate)
  266. voteSet1 == VoteSet(i, n.msource, newVotes, n.mvote, n.mround)
  267. hasQuorums1 == voteSet1 \in Quorums
  268. check1 == CheckLeader(i, newVotes, n.mvote.proposedLeader, n.mround)
  269. leaveOk1 == /\ n.mround = logicalClock[i]
  270. /\ hasQuorums1
  271. /\ check1 \* state and leadingVoteSet cannot be changed twice in the first '/\' and second '/\'.
  272. IN
  273. /\ \/ /\ n.mround = logicalClock[i]
  274. /\ receiveVotes' = [receiveVotes EXCEPT ![i] = newVotes]
  275. \/ /\ n.mround /= logicalClock[i]
  276. /\ UNCHANGED receiveVotes
  277. /\ \/ /\ leaveOk1
  278. \* /\ PrintT("leave with condition 1")
  279. /\ state' = [state EXCEPT ![i] = IF n.mvote.proposedLeader = i THEN LEADING ELSE FOLLOWING]
  280. /\ leadingVoteSet' = [leadingVoteSet EXCEPT ![i] = IF n.mvote.proposedLeader = i THEN voteSet1 ELSE @]
  281. /\ UpdateProposal(i, n.mvote.proposedLeader, n.mvote.proposedZxid, n.mvote.proposedEpoch)
  282. /\ UNCHANGED <<logicalClock, outOfElection>>
  283. \/ /\ ~leaveOk1
  284. /\ outOfElection' = [outOfElection EXCEPT ![i] = Put(i, n.msource, outOfElection[i], n.mvote,n.mround, n.mstate)]
  285. /\ LET voteSet2 == VoteSet(i, n.msource, outOfElection'[i], n.mvote, n.mround)
  286. hasQuorums2 == voteSet2 \in Quorums
  287. check2 == CheckLeader(i, outOfElection'[i], n.mvote.proposedLeader, n.mround)
  288. leaveOk2 == /\ hasQuorums2
  289. /\ check2
  290. IN
  291. \/ /\ leaveOk2
  292. \* /\ PrintT("leave with condition 2")
  293. /\ logicalClock' = [logicalClock EXCEPT ![i] = n.mround]
  294. /\ state' = [state EXCEPT ![i] = IF n.mvote.proposedLeader = i THEN LEADING ELSE FOLLOWING]
  295. /\ leadingVoteSet' = [leadingVoteSet EXCEPT ![i] = IF n.mvote.proposedLeader = i THEN voteSet2 ELSE @]
  296. /\ UpdateProposal(i, n.mvote.proposedLeader, n.mvote.proposedZxid, n.mvote.proposedEpoch)
  297. \/ /\ ~leaveOk2
  298. /\ LET leaveOk3 == /\ n.mstate = LEADING
  299. /\ n.mround = logicalClock[i]
  300. IN
  301. \/ /\ leaveOk3
  302. \* /\ PrintT("leave with condition 3")
  303. /\ state' = [state EXCEPT ![i] = IF n.mvote.proposedLeader = i THEN LEADING ELSE FOLLOWING]
  304. /\ UpdateProposal(i, n.mvote.proposedLeader, n.mvote.proposedZxid, n.mvote.proposedEpoch)
  305. \/ /\ ~leaveOk3
  306. /\ UNCHANGED <<state, currentVote>>
  307. /\ UNCHANGED <<logicalClock, leadingVoteSet>>
  308. (* Main part of lookForLeader() *)
  309. HandleNotmsg(i) ==
  310. /\ state[i] = LOOKING
  311. /\ \lnot waitNotmsg[i]
  312. /\ recvQueue[i] /= << >>
  313. /\ LET n == recvQueue[i][1]
  314. rawToSend == [mtype |-> NOTIFICATION,
  315. msource |-> i,
  316. mstate |-> LOOKING,
  317. mround |-> logicalClock[i],
  318. mvote |-> currentVote[i]]
  319. IN \/ /\ n.mtype = NONE
  320. /\ BroadcastNotmsg(i, rawToSend)
  321. /\ UNCHANGED <<history, logicalClock, currentVote, receiveVotes, waitNotmsg, outOfElection, state, leadingVoteSet>>
  322. \/ /\ n.mtype = NOTIFICATION
  323. /\ \/ /\ n.mstate = LOOKING
  324. /\ \/ \* n.round >= my round, then update data and receiveVotes.
  325. /\ n.mround >= logicalClock[i]
  326. /\ \/ \* n.round > my round, update round and decide new proposed leader.
  327. /\ n.mround > logicalClock[i]
  328. /\ logicalClock' = [logicalClock EXCEPT ![i] = n.mround] \* There should be RvClear, we will handle it in the following.
  329. /\ LET selfinfo == [proposedLeader |-> i,
  330. proposedZxid |-> lastProcessed[i].zxid,
  331. proposedEpoch |-> currentEpoch[i]]
  332. peerOk == TotalOrderPredicate(n.mvote, selfinfo)
  333. IN \/ /\ peerOk
  334. /\ UpdateProposal(i, n.mvote.proposedLeader, n.mvote.proposedZxid, n.mvote.proposedEpoch)
  335. \/ /\ ~peerOk
  336. /\ UpdateProposal(i, i, lastProcessed[i].zxid, currentEpoch[i])
  337. /\ BroadcastNotmsg(i, [mtype |-> NOTIFICATION,
  338. msource |-> i,
  339. mstate |-> LOOKING,
  340. mround |-> n.mround,
  341. mvote |-> currentVote'[i]])
  342. \/ \* n.round = my round & n.vote > my vote
  343. /\ n.mround = logicalClock[i]
  344. /\ LET peerOk == TotalOrderPredicate(n.mvote, currentVote[i])
  345. IN \/ /\ peerOk
  346. /\ UpdateProposal(i, n.mvote.proposedLeader, n.mvote.proposedZxid, n.mvote.proposedEpoch)
  347. /\ BroadcastNotmsg(i, [mtype |-> NOTIFICATION,
  348. msource |-> i,
  349. mstate |-> LOOKING,
  350. mround |-> logicalClock[i],
  351. mvote |-> n.mvote])
  352. \/ /\ ~peerOk
  353. /\ UNCHANGED <<currentVote, electionMsgs>>
  354. /\ UNCHANGED logicalClock
  355. /\ LET rcvsetModifiedTwice == n.mround > logicalClock[i]
  356. IN \/ /\ rcvsetModifiedTwice \* Since a variable cannot be changed more than once in one action, we handle receiveVotes here.
  357. /\ RvClearAndPut(i, n.msource, n.mvote, n.mround) \* clear + put
  358. \/ /\ ~rcvsetModifiedTwice
  359. /\ RvPut(i, n.msource, n.mvote, n.mround, n.mstate) \* put
  360. /\ LET hasQuorums == HasQuorums(i, i, receiveVotes'[i], currentVote'[i], n.mround)
  361. IN \/ /\ hasQuorums \* If hasQuorums, see action WaitNewNotmsg and WaitNewNotmsgEnd.
  362. /\ waitNotmsg' = [waitNotmsg EXCEPT ![i] = TRUE]
  363. \/ /\ ~hasQuorums
  364. /\ UNCHANGED waitNotmsg
  365. \/ \* n.round < my round, just discard it.
  366. /\ n.mround < logicalClock[i]
  367. /\ UNCHANGED <<logicalClock, currentVote, electionMsgs, receiveVotes, waitNotmsg>>
  368. /\ UNCHANGED <<state, history, outOfElection, leadingVoteSet>>
  369. \/ \* mainly contains receivedFollowingNotification(line 1146), receivedLeadingNotification(line 1185).
  370. /\ n.mstate \in {LEADING, FOLLOWING}
  371. /\ ReceivedFollowingAndLeadingNotification(i, n)
  372. /\ history' = [history EXCEPT ![i] = InitHistory(i) ]
  373. /\ UNCHANGED <<electionMsgs, waitNotmsg>>
  374. /\ recvQueue' = [recvQueue EXCEPT ![i] = Tail(recvQueue[i])]
  375. /\ UNCHANGED <<currentEpoch, lastProcessed>>
  376. \* On the premise that ReceiveVotes.HasQuorums = TRUE, corresponding to logic in LFE.java.
  377. WaitNewNotmsg(i) ==
  378. /\ state[i] = LOOKING
  379. /\ waitNotmsg[i] = TRUE
  380. /\ \/ /\ recvQueue[i] /= << >>
  381. /\ recvQueue[i][1].mtype = NOTIFICATION
  382. /\ LET n == recvQueue[i][1]
  383. peerOk == TotalOrderPredicate(n.mvote, currentVote[i])
  384. IN \/ /\ peerOk
  385. /\ waitNotmsg' = [waitNotmsg EXCEPT ![i] = FALSE]
  386. /\ recvQueue' = [recvQueue EXCEPT ![i] = Append(Tail(@), n)]
  387. \/ /\ ~peerOk
  388. /\ recvQueue' = [recvQueue EXCEPT ![i] = Tail(@)]
  389. /\ UNCHANGED waitNotmsg
  390. /\ UNCHANGED <<serverVarsL, currentVote, logicalClock, receiveVotes, outOfElection,
  391. leaderVarsL, electionMsgs>>
  392. \/ /\ \/ recvQueue[i] = << >>
  393. \/ /\ recvQueue[i] /= << >>
  394. /\ recvQueue[i][1].mtype = NONE
  395. /\ state' = [state EXCEPT ![i] = IF currentVote[i].proposedLeader = i THEN LEADING
  396. ELSE FOLLOWING ]
  397. /\ leadingVoteSet' = [leadingVoteSet EXCEPT ![i] =
  398. IF currentVote[i].proposedLeader = i
  399. THEN VoteSet(i, i, receiveVotes[i], currentVote[i],
  400. logicalClock[i])
  401. ELSE @]
  402. /\ history' = [history EXCEPT ![i] = InitHistory(i)]
  403. /\ UNCHANGED <<currentEpoch, lastProcessed, electionVarsL, electionMsgs>>
  404. -----------------------------------------------------------------------------
  405. (*Test - simulate modifying currentEpoch and lastProcessed.
  406. We want to reach violations to achieve some traces and see whether the whole state of system is advancing.
  407. The actions below are completely not equal to implementation in real,
  408. just simulate a process of leader updates state and followers get it. *)
  409. LeaderAdvanceEpoch(i) ==
  410. /\ state[i] = LEADING
  411. /\ currentEpoch' = [currentEpoch EXCEPT ![i] = @ + 1]
  412. /\ UNCHANGED <<state, lastProcessed, history, electionVarsL, leaderVarsL, electionMsgs>>
  413. FollowerUpdateEpoch(i, j) ==
  414. /\ state[i] = FOLLOWING
  415. /\ currentVote[i].proposedLeader = j
  416. /\ state[j] = LEADING
  417. /\ currentEpoch[i] < currentEpoch[j]
  418. /\ currentEpoch' = [currentEpoch EXCEPT ![i] = currentEpoch[j]]
  419. /\ UNCHANGED <<state, lastProcessed, history, electionVarsL, leaderVarsL, electionMsgs>>
  420. LeaderAdvanceZxid(i) ==
  421. /\ state[i] = LEADING
  422. /\ lastProcessed' = [lastProcessed EXCEPT ![i] = IF lastProcessed[i].zxid[1] = currentEpoch[i]
  423. THEN [ index |-> lastProcessed[i].index + 1,
  424. zxid |-> <<currentEpoch[i], lastProcessed[i].zxid[2] + 1>> ]
  425. ELSE [ index |-> lastProcessed[i].index + 1,
  426. zxid |-> <<currentEpoch[i], 1>> ] ]
  427. /\ history' = [history EXCEPT ![i] = Append(@, [zxid |-> lastProcessed'[i].zxid,
  428. value |-> NONE,
  429. ackSid |-> {},
  430. epoch |-> 0])]
  431. /\ UNCHANGED <<state, currentEpoch, electionVarsL, leaderVarsL, electionMsgs>>
  432. FollowerUpdateZxid(i, j) ==
  433. /\ state[i] = FOLLOWING
  434. /\ currentVote[i].proposedLeader = j
  435. /\ state[j] = LEADING
  436. /\ LET precede == \/ lastProcessed[i].zxid[1] < lastProcessed[j].zxid[1]
  437. \/ /\ lastProcessed[i].zxid[1] = lastProcessed[j].zxid[1]
  438. /\ lastProcessed[i].zxid[2] < lastProcessed[j].zxid[2]
  439. IN /\ precede
  440. /\ lastProcessed' = [lastProcessed EXCEPT ![i] = lastProcessed[j]]
  441. /\ history' = [history EXCEPT ![i] = history[j]]
  442. /\ UNCHANGED <<state, currentEpoch, electionVarsL, leaderVarsL, electionMsgs>>
  443. NextL ==
  444. \/ \E i \in Server: ZabTimeout(i)
  445. \/ \E i, j \in Server: ReceiveNotmsg(i, j)
  446. \/ \E i \in Server: NotmsgTimeout(i)
  447. \/ \E i \in Server: HandleNotmsg(i)
  448. \/ \E i \in Server: WaitNewNotmsg(i)
  449. \/ \E i \in Server: LeaderAdvanceEpoch(i)
  450. \/ \E i, j \in Server: FollowerUpdateEpoch(i, j)
  451. \/ \E i \in Server: LeaderAdvanceZxid(i)
  452. \/ \E i, j \in Server: FollowerUpdateZxid(i, j)
  453. SpecL == InitL /\ [][NextL]_varsL
  454. \* These invariants should be violated after running for minutes.
  455. ShouldBeTriggered1 == ~\E Q \in Quorums: /\ \A i \in Q: /\ state[i] \in {FOLLOWING, LEADING}
  456. /\ currentEpoch[i] > 3
  457. /\ logicalClock[i] > 2
  458. /\ currentVote[i].proposedLeader \in Q
  459. /\ \A i, j \in Q: currentVote[i].proposedLeader = currentVote[j].proposedLeader
  460. (*
  461. ShouldBeTriggered2 == ~\E Q \in Quorums: /\ \A i \in Q: /\ state[i] \in {FOLLOWING, LEADING}
  462. /\ currentEpoch[i] > 3
  463. /\ currentVote[i].proposedLeader \in Q
  464. /\ \A i, j \in Q: currentVote[i].proposedLeader = currentVote[j].proposedLeader*)
  465. =============================================================================
  466. \* Modification History
  467. \* Last modified Sat Jan 14 15:19:45 CST 2023 by huangbinyu
  468. \* Last modified Sun Nov 14 15:18:32 CST 2021 by Dell
  469. \* Created Fri Jun 18 20:23:47 CST 2021 by Dell