(We're researchers from NJU/MSR/UIUC/UBC working on automating system specification in TLA+. We've been using our tool on SwiftPaxos and wanted to contribute our findings back, if the team finds this useful! We understand this is research code and just wanted to confirm if our reports are valid.)
Invariant 8 of the paper (pg 8) states: if a replica sends `Ack` at ballot `b` and `NewLeaderAck` at ballot `b' > b`, it sends `Ack` before `NewLeaderAck`.
The implementation does not seem to order acks in the channel during recovery. For example:
- Before recovery, replica enqueues an old-ballot Ack into
batcher (e.g. here)
- Replica receives
NewLeader(b') and enters recovery:
handleNewLeader immediately sends NewLeaderAckN here
- It seems
batcher is not stopped/drained by recovery and continues running (e.g. goroutine loop here)
batcher can still emit an old-ballot Ack afterwards, since there's no filtering of stale ballots in the sender channel itself.
Because producer goroutines race to enqueue into sender, there is no guarantee the old `Ack`s are enqueued before the new ones. It seems that flushing batcher might also be necessary.
Also, when merging the new ballots, there is no equality or conflict check before cmdIds are overwritten here. Because the map iteration orders are unstable per local replica, it is possible commands with different phases across replicas could be overridden.
This seems to violate Invariant 1 (pg 4), which states: any two replicas commit a command with the same set of dependencies. On recovery, two replicas could end up with different dependency phases.
Can you please confirm whether or not this is a valid bug in the system as it is currently implemented?
(We're researchers from NJU/MSR/UIUC/UBC working on automating system specification in TLA+. We've been using our tool on SwiftPaxos and wanted to contribute our findings back, if the team finds this useful! We understand this is research code and just wanted to confirm if our reports are valid.)
Invariant 8 of the paper (pg 8) states: if a replica sends `Ack` at ballot `b` and `NewLeaderAck` at ballot `b' > b`, it sends `Ack` before `NewLeaderAck`.
The implementation does not seem to order acks in the channel during recovery. For example:
batcher(e.g. here)NewLeader(b')and enters recovery:handleNewLeaderimmediately sendsNewLeaderAckNherebatcheris not stopped/drained by recovery and continues running (e.g. goroutine loop here)batchercan still emit an old-ballotAckafterwards, since there's no filtering of stale ballots in the sender channel itself.Because producer goroutines race to enqueue into sender, there is no guarantee the old `Ack`s are enqueued before the new ones. It seems that flushing
batchermight also be necessary.Also, when merging the new ballots, there is no equality or conflict check before
cmdIds are overwritten here. Because the map iteration orders are unstable per local replica, it is possible commands with different phases across replicas could be overridden.This seems to violate Invariant 1 (pg 4), which states: any two replicas commit a command with the same set of dependencies. On recovery, two replicas could end up with different dependency phases.
Can you please confirm whether or not this is a valid bug in the system as it is currently implemented?