Skip to content

Commit bdf85d7

Browse files
committed
Harden EPaxOS evidence and Jepsen faults
1 parent b8c9983 commit bdf85d7

20 files changed

Lines changed: 1681 additions & 115 deletions

MODEL_EQ_REPORT.MD

Lines changed: 25 additions & 26 deletions
Large diffs are not rendered by default.

epaxos/codec.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func EncodeMessage(dst []byte, m Message) ([]byte, error) {
4141
func DecodeMessage(src []byte, m *Message) error {
4242
m.Reset()
4343
if len(src) < len(wireMagic)+32 || string(src[:len(wireMagic)]) != string(wireMagic[:]) {
44-
return ErrInvalidMessage
44+
return decodeMessageError(m, ErrInvalidMessage)
4545
}
4646
p := parser{b: src[len(wireMagic) : len(src)-32]}
4747
m.Type = MessageType(p.uvarint())
@@ -52,7 +52,7 @@ func DecodeMessage(src []byte, m *Message) error {
5252
m.Seq = p.uvarint()
5353
deps := p.uvarint()
5454
if deps > 128 {
55-
return ErrInvalidMessage
55+
return decodeMessageError(m, ErrInvalidMessage)
5656
}
5757
m.Deps = make([]InstanceNum, int(deps))
5858
for i := range m.Deps {
@@ -63,15 +63,20 @@ func DecodeMessage(src []byte, m *Message) error {
6363
m.RejectHint = Ballot{Epoch: p.uvarint(), Number: p.uvarint(), Replica: ReplicaID(p.uvarint())}
6464
m.RecordStatus = Status(p.uvarint())
6565
if p.err || len(p.b) != 0 {
66-
return ErrInvalidMessage
66+
return decodeMessageError(m, ErrInvalidMessage)
6767
}
6868
copy(m.Checksum[:], src[len(src)-32:])
6969
if !VerifyMessageChecksum(*m) {
70-
return ErrChecksumMismatch
70+
return decodeMessageError(m, ErrChecksumMismatch)
7171
}
7272
return nil
7373
}
7474

75+
func decodeMessageError(m *Message, err error) error {
76+
m.Reset()
77+
return err
78+
}
79+
7580
func appendCommand(dst []byte, c Command) []byte {
7681
dst = binary.AppendUvarint(dst, c.ID.Client)
7782
dst = binary.AppendUvarint(dst, c.ID.Sequence)

epaxos/node.go

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,10 @@ func (n *RawNode) Advance(rd Ready) {
368368
if !n.awaitAdvance {
369369
return
370370
}
371+
ackedCommitted := len(rd.Committed)
372+
if ackedCommitted > len(n.pendingReady.Committed) {
373+
ackedCommitted = len(n.pendingReady.Committed)
374+
}
371375
if len(rd.Records) >= len(n.pendingReady.Records) {
372376
n.pendingReady.Records = nil
373377
} else {
@@ -384,9 +388,20 @@ func (n *RawNode) Advance(rd Ready) {
384388
n.pendingReady.Committed = n.pendingReady.Committed[len(rd.Committed):]
385389
}
386390
n.pendingReady.MustSync = len(n.pendingReady.Records) > 0
391+
n.enqueueExecutedRecords(rd.Committed[:ackedCommitted])
387392
n.awaitAdvance = false
388393
}
389394

395+
func (n *RawNode) enqueueExecutedRecords(committed []CommittedCommand) {
396+
for _, c := range committed {
397+
inst := n.instances[c.Ref]
398+
if inst == nil || inst.rec.Status != StatusExecuted {
399+
continue
400+
}
401+
n.enqueueRecord(inst.rec)
402+
}
403+
}
404+
390405
// Status returns a copy-only diagnostic snapshot of node state.
391406
func (n *RawNode) Status() StatusSnapshot {
392407
s := StatusSnapshot{ID: n.id, Tick: n.tick, Conf: n.q.conf.Clone()}
@@ -422,7 +437,7 @@ func (n *RawNode) handlePreAccept(m Message) {
422437
n.instances[m.Ref] = &instance{rec: rec, phase: phasePreAccept}
423438
n.indexConflicts(rec)
424439
n.enqueueRecord(rec)
425-
resp := Message{Type: MsgPreAcceptResp, From: n.id, To: m.From, Ref: m.Ref, Ballot: rec.Ballot, Seq: rec.Seq, Deps: append([]InstanceNum(nil), rec.Deps...), RecordStatus: rec.Status}
440+
resp := Message{Type: MsgPreAcceptResp, From: n.id, To: m.From, Ref: m.Ref, Ballot: rec.Ballot, Seq: rec.Seq, Deps: rec.Deps, RecordStatus: rec.Status}
426441
n.enqueueMessage(resp)
427442
}
428443

@@ -476,7 +491,7 @@ func (n *RawNode) handleAccept(m Message) {
476491
n.instances[m.Ref] = &instance{rec: rec, phase: phaseAccept}
477492
n.indexConflicts(rec)
478493
n.enqueueRecord(rec)
479-
resp := Message{Type: MsgAcceptResp, From: n.id, To: m.From, Ref: m.Ref, Ballot: rec.Ballot, Seq: rec.Seq, Deps: append([]InstanceNum(nil), rec.Deps...), RecordStatus: rec.Status}
494+
resp := Message{Type: MsgAcceptResp, From: n.id, To: m.From, Ref: m.Ref, Ballot: rec.Ballot, Seq: rec.Seq, Deps: rec.Deps, RecordStatus: rec.Status}
480495
n.enqueueMessage(resp)
481496
}
482497

@@ -537,8 +552,8 @@ func (n *RawNode) handlePrepare(m Message) {
537552
}
538553
resp.Ballot = inst.rec.Ballot
539554
resp.Seq = inst.rec.Seq
540-
resp.Deps = append([]InstanceNum(nil), inst.rec.Deps...)
541-
resp.Command = inst.rec.Command.Clone()
555+
resp.Deps = inst.rec.Deps
556+
resp.Command = inst.rec.Command.Borrow()
542557
resp.RecordStatus = inst.rec.Status
543558
n.enqueueMessage(resp)
544559
}
@@ -663,7 +678,7 @@ func (n *RawNode) broadcast(t MessageType, rec InstanceRecord) {
663678
if to == n.id {
664679
continue
665680
}
666-
m := Message{Type: t, From: n.id, To: to, Ref: rec.Ref, Ballot: rec.Ballot, Seq: rec.Seq, Deps: append([]InstanceNum(nil), rec.Deps...), Command: rec.Command.Clone(), RecordStatus: rec.Status}
681+
m := Message{Type: t, From: n.id, To: to, Ref: rec.Ref, Ballot: rec.Ballot, Seq: rec.Seq, Deps: rec.Deps, Command: rec.Command.Borrow(), RecordStatus: rec.Status}
667682
n.enqueueMessage(m)
668683
}
669684
}
@@ -674,7 +689,7 @@ func (n *RawNode) sendReject(t MessageType, to ReplicaID, ref InstanceRef, hint
674689
}
675690

676691
func (n *RawNode) sendCommitTo(to ReplicaID, rec InstanceRecord) {
677-
m := Message{Type: MsgCommit, From: n.id, To: to, Ref: rec.Ref, Ballot: rec.Ballot, Seq: rec.Seq, Deps: append([]InstanceNum(nil), rec.Deps...), Command: rec.Command.Clone(), RecordStatus: rec.Status}
692+
m := Message{Type: MsgCommit, From: n.id, To: to, Ref: rec.Ref, Ballot: rec.Ballot, Seq: rec.Seq, Deps: rec.Deps, Command: rec.Command.Borrow(), RecordStatus: rec.Status}
678693
n.enqueueMessage(m)
679694
}
680695

@@ -837,12 +852,14 @@ func (n *RawNode) tryExecute() {
837852
n.executed[ref] = struct{}{}
838853
inst.rec.Status = StatusExecuted
839854
inst.rec.Checksum = ChecksumRecord(inst.rec)
840-
n.enqueueRecord(inst.rec)
841-
if inst.rec.Command.Kind == CommandConfChange {
842-
n.applyConfChange(inst.rec.Command)
843-
}
844-
if inst.rec.Command.Kind != CommandNoop {
855+
switch inst.rec.Command.Kind {
856+
case CommandUser:
845857
n.enqueueCommitted(CommittedCommand{Ref: ref, Seq: inst.rec.Seq, Deps: append([]InstanceNum(nil), inst.rec.Deps...), Command: inst.rec.Command.Clone()})
858+
case CommandConfChange:
859+
n.applyConfChange(inst.rec.Command)
860+
n.enqueueRecord(inst.rec)
861+
default:
862+
n.enqueueRecord(inst.rec)
846863
}
847864
progress = true
848865
}
@@ -976,8 +993,15 @@ func (n *RawNode) dependencyRefs(ref InstanceRef) []InstanceRef {
976993
if dep == 0 || i >= len(conf.Voters) {
977994
continue
978995
}
979-
out = append(out, InstanceRef{Replica: conf.Voters[i], Instance: dep, Conf: ref.Conf})
996+
replica := conf.Voters[i]
997+
for other := range n.instances {
998+
if other == ref || other.Conf != ref.Conf || other.Replica != replica || other.Instance > dep {
999+
continue
1000+
}
1001+
out = append(out, other)
1002+
}
9801003
}
1004+
sortRefs(out)
9811005
return out
9821006
}
9831007

0 commit comments

Comments
 (0)