Skip to content

Add MultiPaxos Leader Leases spec - #224

Open
josehu07 wants to merge 1 commit into
tlaplus:masterfrom
josehu07:master
Open

Add MultiPaxos Leader Leases spec#224
josehu07 wants to merge 1 commit into
tlaplus:masterfrom
josehu07:master

Conversation

@josehu07

@josehu07 josehu07 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Adds a MultiPaxos Leader Leases spec. This spec builds on top of the earlier MultiPaxos-SMR spec. Please see specifications/MultiPaxos-LeaderLeases/README.md for more information on what it models.

Due to the nature of the leasing algorithm, checking the default config is a rather heavy task and runs for ~20 hours on a powerful EC2 instance. I included a _short variant of the config that completes in ~1 minute for validation purposes.

Signed-off-by: Guanzhou Hu <josehgz@amazon.com>

@muenchnerkindl muenchnerkindl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your submission. I added a few comments and observations about your spec, hope you find them useful.

(*************************)
(* Type check invariant. *)
(*************************)
TypeOK == /\ \A m \in msgs: m \in Messages

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why doesn't this go to MultiPaxos.tla? It is not just relevant for model checking.

(* Type check invariant. *)
(*************************)
TypeOK == /\ \A m \in msgs: m \in Messages
/\ \A r \in Replicas: node[r] \in NodeStates

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A typing invariant should have conjuncts such as
node \in [Replicas -> NodeStates]
that fixes the type of each variable. Note that in TLA+ you cannot deduce the predicate above from the one that you write.

/\ \A r \in Replicas: node[r] \in NodeStates
/\ \A r \in Replicas: time[r] \in Times
/\ Len(pending) =< NumCommands
/\ Cardinality(Range(pending)) = Len(pending)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a predicate such as IsInjective from module Functions.tla in the CommunityModules.

/\ \A r \in Replicas: time[r] \in Times
/\ Len(pending) =< NumCommands
/\ Cardinality(Range(pending)) = Len(pending)
/\ \A c \in Range(pending): c \in Commands

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pending \in Seq(Commands)

(*************************************)
(* Lease expiration safety property. *)
(*************************************)
LeaseExpirationSafety ==

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this is a correctness property that should go to the main module rather than to the _MC module.

ReadsAssumption == /\ IsFiniteSet(Reads)
/\ Cardinality(Reads) >= 0
/\ "nil" \notin Writes

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, should the sets Reads and Writes be disjoint? Consider introducing an operator ReadsWritesAssumption that lists all assumptions on commands.

AckEvent(c, v) == [type |-> "Ack", cmd |-> c, val |-> v]
\* for a write command, val is the old value

InitPending == (CHOOSE ws \in [1..Cardinality(Writes) -> Writes]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes an arbitrary but fixed (injective) sequence of Writes. Alternatively, you could set up your model so that all possible such sequences are verified – certainly at the cost of even heavier state space explosion.

end with;
end macro;

\* Replica node crashes itself under promised conditions.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "promised conditions"?

\* if fewer than (N - MajorityNum) number of replicas have failed
await /\ MajorityNum + numCrashed < Cardinality(Replicas)
/\ ~crashed[r]
/\ node[r].balMaxKnown < MaxBallot;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition is not explained in the comment.

end macro;

\* Advances time by one tick globally, and garbage-collects expired lease
\* state. Per-pair seq counters are preserved across GC.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If time is modeled globally (i.e., all replicas share the same time at every moment), why model it as a local (pre-replica) variable that is updated simultaneously for all replicas? A global variable would show more clearly how time is represented in this spec.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants