-
Notifications
You must be signed in to change notification settings - Fork 200
/
ACP_SB.tla
402 lines (314 loc) · 14.3 KB
/
ACP_SB.tla
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
--------------------------------- MODULE ACP_SB --------------------------------
\* Time-stamp: <10 Jun 2002 at 12:39:50 by charpov on berlioz.cs.unh.edu>
\* `^Atomic Committment Protocol^' with Simple Broadcast primitive (ACP-SB)
\* From:
\* `^Sape Mullender^', editor. Distributed Systems.
\* Chapter 6: Non-Blocking Atomic Commitment, by `^\"O. Babao\u{g}lu and S. Toueg.^'
\* 1993.
\*******************************************************************************
\* Synchronous communication has been replaced with (implicit) asynchronous communication.
\* Failures are detected "magically" instead or relying on timeouts.
\*
\* This version of the protocol uses a "simple broadcast" where a broadcast is simply a
\* series of messages sent, possibly interrupted by a failure. Consequently, this algorithm
\* is "non terminating" and property AC5 does not hold.
\*******************************************************************************
CONSTANTS
participants, \* set of participants
yes, no, \* vote
undecided, commit, abort, \* decision
waiting, \* coordinator state wrt a participant
notsent \* broadcast state wrt a participant
VARIABLES
participant, \* participants (N)
coordinator \* coordinator (1)
--------------------------------------------------------------------------------
TypeInvParticipant == participant \in [
participants -> [
vote : {yes, no},
alive : BOOLEAN,
decision : {undecided, commit, abort},
faulty : BOOLEAN,
voteSent : BOOLEAN
]
]
TypeInvCoordinator == coordinator \in [
request : [participants -> BOOLEAN],
vote : [participants -> {waiting, yes, no}],
broadcast : [participants -> {commit, abort, notsent}],
decision : {commit, abort, undecided},
alive : BOOLEAN,
faulty : BOOLEAN
]
TypeInv == TypeInvParticipant /\ TypeInvCoordinator
--------------------------------------------------------------------------------
\* Initially:
\* All the participants:
\* have a yes/no vote
\* are alive and not faulty
\* have not sent in their votes yet
\* are undecided about final decision
\* The coordinator:
\* has not sent vote requests yet
\* has not recieved votes from any participant
\* is alive and not faulty
\* has not sent broadcast messages to any participant
\* is undecided about final decision
InitParticipant == participant \in [
participants -> [
vote : {yes, no},
alive : {TRUE},
decision : {undecided},
faulty : {FALSE},
voteSent : {FALSE}
]
]
InitCoordinator == coordinator \in [
request : [participants -> {FALSE}],
vote : [participants -> {waiting}],
alive : {TRUE},
broadcast : [participants -> {notsent}],
decision : {undecided},
faulty : {FALSE}
]
Init == InitParticipant /\ InitCoordinator
--------------------------------------------------------------------------------
\* COORDINATOR STATEMENTS
\* request(i):
\* IF
\* coordinator is alive
\* request for vote has not been sent to participant i
\* THEN `~ why isn't THEN left-justified? ~'
\* request for vote is sent to participant i
request(i) == /\ coordinator.alive
/\ ~coordinator.request[i]
/\ coordinator' = [coordinator EXCEPT !.request =
[@ EXCEPT ![i] = TRUE]
]
/\ UNCHANGED<<participant>>
\* getVote(i):
\* IF
\* coordinator is alive
\* coordinator is still undecided
\* coordinator has sent request for votes to all participants
\* coordinator is waiting to receive a vote from participant i
\* participant i has sent the vote message
\* THEN
\* the coordinator can record the vote of participant i
getVote(i) == /\ coordinator.alive
/\ coordinator.decision = undecided
/\ \A j \in participants : coordinator.request[j]
/\ coordinator.vote[i] = waiting
/\ participant[i].voteSent
/\ coordinator' = [coordinator EXCEPT !.vote =
[@ EXCEPT ![i] = participant[i].vote]
]
/\ UNCHANGED<<participant>>
\* detectFault(i):
\* IF
\* coordinator is alive
\* coordinator is still undecided
\* coordinator has sent request for votes to all participants
\* coordinator is waiting for vote from participant i
\* participant i has died without sending its vote
\* THEN
\* coordinator times out on participant i and decides to abort
detectFault(i) == /\ coordinator.alive
/\ coordinator.decision = undecided
/\ \A j \in participants : coordinator.request[j]
/\ coordinator.vote[i] = waiting
/\ ~participant[i].alive
/\ ~participant[i].voteSent
/\ coordinator' = [coordinator EXCEPT !.decision = abort]
/\ UNCHANGED<<participant>>
\* makeDecision:
\* IF
\* coordinator is alive
\* coordinator is undecided
\* coordinator has received votes from all participants
\* THEN
\* IF
\* all votes are yes
\* THEN
\* coordinator decides commit
\* ELSE
\* coordinator decides abort
makeDecision == /\ coordinator.alive
/\ coordinator.decision = undecided
/\ \A j \in participants : coordinator.vote[j] \in {yes, no}
/\ \/ /\ \A j \in participants : coordinator.vote[j] = yes
/\ coordinator' = [coordinator EXCEPT !.decision = commit]
\/ /\ \E j \in participants : coordinator.vote[j] = no
/\ coordinator' = [coordinator EXCEPT !.decision = abort]
/\ UNCHANGED<<participant>>
\* coordBroadcast(i) (simple broadcast):
\* IF
\* coordinator is alive
\* coordinator has made a decision
\* coordinator has not sent the decision to participant i
\* THEN
\* coordinator sends its decision to participant i
coordBroadcast(i) == /\ coordinator.alive
/\ coordinator.decision # undecided
/\ coordinator.broadcast[i] = notsent
/\ coordinator' = [coordinator EXCEPT !.broadcast =
[@ EXCEPT ![i] = coordinator.decision]
]
/\ UNCHANGED<<participant>>
\* coordDie:
\* IF
\* coordinator is alive
\* THEN
\* coordinator dies
\* coordinator is now faulty
coordDie == /\ coordinator.alive
/\ coordinator' = [coordinator EXCEPT !.alive = FALSE, !.faulty = TRUE]
/\ UNCHANGED<<participant>>
------------------------------------------------------------------------------
\* PARTICIPANT STATEMENTS
\* sendVote(i):
\* IF
\* participant is alive
\* participant has received a request for vote
\* THEN
\* participant sends vote
sendVote(i) == /\ participant[i].alive
/\ coordinator.request[i]
/\ participant' = [participant EXCEPT ![i] =
[@ EXCEPT !.voteSent = TRUE]
]
/\ UNCHANGED<<coordinator>>
\* abortOnVote(i):
\* IF
\* participant is alive
\* participant is undecided
\* participant has sent its vote to the coordinator
\* participant's vote is no
\* THEN
\* participant decides (unilaterally) to abort
abortOnVote(i) == /\ participant[i].alive
/\ participant[i].decision = undecided
/\ participant[i].voteSent
/\ participant[i].vote = no
/\ participant' = [participant EXCEPT ![i] =
[@ EXCEPT !.decision = abort]
]
/\ UNCHANGED<<coordinator>>
\* abortOnTimeoutRequest(i):
\* IF
\* participant is alive
\* participant is still undecided
\* coordinator has died without sending request for vote
\* THEN
\* participant decides (unilaterally) to abort
abortOnTimeoutRequest(i) == /\ participant[i].alive
/\ participant[i].decision = undecided
/\ ~coordinator.alive
/\ ~coordinator.request[i]
/\ participant' = [participant EXCEPT ![i] =
[@ EXCEPT !.decision = abort]
]
/\ UNCHANGED<<coordinator>>
\* decide(i):
\* IF
\* participant is alive
\* participant is undecided
\* participant has recieved decision from the coordinator
\* THEN
\* participant decides according to decision from coordinator
\*
decide(i) == /\ participant[i].alive
/\ participant[i].decision = undecided
/\ coordinator.broadcast[i] # notsent
/\ participant' = [participant EXCEPT ![i] =
[@ EXCEPT !.decision = coordinator.broadcast[i]]
]
/\ UNCHANGED<<coordinator>>
\* parDie(i):
\* IF
\* participant is alive
\* THEN
\* participant dies and is now faulty
parDie(i) == /\ participant[i].alive
/\ participant' = [participant EXCEPT ![i] =
[@ EXCEPT !.alive = FALSE, !.faulty = TRUE]
]
/\ UNCHANGED<<coordinator>>
-------------------------------------------------------------------------------
\* FOR N PARTICIPANTS
parProg(i) == sendVote(i) \/ abortOnVote(i) \/ abortOnTimeoutRequest(i) \/ decide(i)
parProgN == \E i \in participants : parDie(i) \/ parProg(i)
coordProgA(i) == request(i) \/ getVote(i) \/ detectFault(i) \/ coordBroadcast(i)
coordProgB == makeDecision \/ \E i \in participants : coordProgA(i)
coordProgN == coordDie \/ coordProgB
progN == parProgN \/ coordProgN
\* Death transitions are left outside of fairness
fairness == /\ \A i \in participants : WF_<<coordinator, participant>>(parProg(i))
/\ WF_<<coordinator, participant>>(coordProgB)
Spec == Init /\ [][progN]_<<coordinator, participant>> /\ fairness
--------------------------------------------------------------------------------
\* CORRECTNESS SPECIFICATION
\*******************************************************************************
\* This specification follows the original paper, except that AC3 is stronger:
\* It forces participants to abort if one vote at least is NO (in the absence
\* of failure).
\*
\* The specification is split between safety and liveness.
\*******************************************************************************
--------------------------------------------------------------------------------
\* SAFETY
\* All participants that decide reach the same decision
AC1 == [] \A i, j \in participants :
\/ participant[i].decision # commit
\/ participant[j].decision # abort
\* If any participant decides commit, then all participants must have votes YES
AC2 == [] ( (\E i \in participants : participant[i].decision = commit)
=> (\A j \in participants : participant[j].vote = yes))
\* If any participant decides abort, then:
\* at least one participant voted NO, or
\* at least one participant is faulty, or
\* coordinator is faulty
AC3_1 == [] ( (\E i \in participants : participant[i].decision = abort)
=> \/ (\E j \in participants : participant[j].vote = no)
\/ (\E j \in participants : participant[j].faulty)
\/ coordinator.faulty)
\* Each participant decides at most once
AC4 == [] /\ (\A i \in participants : participant[i].decision = commit
=> [](participant[i].decision = commit))
/\ (\A j \in participants : participant[j].decision = abort
=> [](participant[j].decision = abort))
--------------------------------------------------------------------------------
\* LIVENESS
\* (stronger for AC3 than in the original paper)
AC3_2 == <> \/ \A i \in participants : participant[i].decision \in {abort, commit}
\/ \E j \in participants : participant[j].faulty
\/ coordinator.faulty
--------------------------------------------------------------------------------
\* (SOME) INTERMEDIATE PROPERTIES USED IN PROOFS
FaultyStable == /\ \A i \in participants : [](participant[i].faulty => []participant[i].faulty)
/\ [](coordinator.faulty => [] coordinator.faulty)
VoteStable == \A i \in participants :
\/ [](participant[i].vote = yes)
\/ [](participant[i].vote = no)
StrongerAC2 == [] ( (\E i \in participants : participant[i].decision = commit)
=> /\ (\A j \in participants : participant[j].vote = yes)
/\ coordinator.decision = commit)
StrongerAC3_1 == [] ( (\E i \in participants : participant[i].decision = abort)
=> \/ (\E j \in participants : participant[j].vote = no)
\/ /\ \E j \in participants : participant[j].faulty
/\ coordinator.decision = abort
\/ /\ coordinator.faulty
/\ coordinator.decision = undecided)
\* (AC1 follows from StrongerAC2 /\ StrongerAC3_1)
NoRecovery == [] /\ \A i \in participants : participant[i].alive <=> ~participant[i].faulty
/\ coordinator.alive <=> ~coordinator.faulty
--------------------------------------------------------------------------------
\* (SOME) INVALID PROPERTIES
DecisionReachedNoFault == (\A i \in participants : participant[i].alive)
~> (\A k \in participants : participant[k].decision # undecided)
AbortImpliesNoVote == [] ( (\E i \in participants : participant[i].decision = abort)
=> (\E j \in participants : participant[j].vote = no))
\* The following is the termination property that this SB algorithm doesn't have
AC5 == <> \A i \in participants : \/ participant[i].decision \in {abort, commit}
\/ participant[i].faulty
================================================================================