Skip to content

Commit 84d1cb1

Browse files
committed
sbft: limit backlog
Issue: FAB-603 Change-Id: If6ac606018c5ab82eaaf8f16e075417ef2f7e243 Signed-off-by: Simon Schubert <[email protected]>
1 parent 31b7572 commit 84d1cb1

File tree

4 files changed

+77
-20
lines changed

4 files changed

+77
-20
lines changed

consensus/simplebft/backlog.go

+9-10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ limitations under the License.
1616

1717
package simplebft
1818

19+
const maxBacklogSeq = 4
20+
const msgPerSeq = 3 // (pre)prepare, commit, checkpoint
21+
1922
func (s *SBFT) testBacklog(m *Msg, src uint64) bool {
2023
if len(s.replicaState[src].backLog) > 0 {
2124
return true
@@ -52,17 +55,13 @@ func (s *SBFT) recordBacklogMsg(m *Msg, src uint64) {
5255
if src == s.id {
5356
panic("should never have to backlog my own message")
5457
}
55-
// TODO
56-
//
57-
// Prevent DoS by limiting the number of messages per replica.
58-
//
59-
// If the backlog limit is exceeded, re-establish the
60-
// connection.
61-
//
62-
// After the connection has been re-established, we will
63-
// receive a hello, which will advance our state and discard
64-
// old messages.
58+
6559
s.replicaState[src].backLog = append(s.replicaState[src].backLog, m)
60+
61+
if len(s.replicaState[src].backLog) > maxBacklogSeq*msgPerSeq {
62+
s.discardBacklog(src)
63+
s.sys.Reconnect(src)
64+
}
6665
}
6766

6867
func (s *SBFT) discardBacklog(src uint64) {

consensus/simplebft/simplebft.go

+4-9
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type System interface {
4343
LastBatch() *Batch
4444
Sign(data []byte) []byte
4545
CheckSig(data []byte, src uint64, sig []byte) error
46+
Reconnect(replica uint64)
4647
}
4748

4849
// Canceller allows cancelling of a scheduled timer event.
@@ -192,12 +193,6 @@ func (s *SBFT) broadcast(m *Msg) {
192193
func (s *SBFT) Receive(m *Msg, src uint64) {
193194
log.Debugf("received message from %d: %s", src, m)
194195

195-
if s.testBacklog(m, src) {
196-
log.Debugf("message for future seq, storing for later")
197-
s.recordBacklogMsg(m, src)
198-
return
199-
}
200-
201196
if h := m.GetHello(); h != nil {
202197
s.handleHello(h, src)
203198
return
@@ -212,9 +207,9 @@ func (s *SBFT) Receive(m *Msg, src uint64) {
212207
return
213208
}
214209

215-
if !s.activeView {
216-
log.Infof("we are not active in view %d, discarding message from %d",
217-
s.seq.View, src)
210+
if s.testBacklog(m, src) {
211+
log.Debugf("message for future seq, storing for later")
212+
s.recordBacklogMsg(m, src)
218213
return
219214
}
220215

consensus/simplebft/simplebft_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -746,3 +746,44 @@ func TestRestartMissedViewChange(t *testing.T) {
746746
}
747747
}
748748
}
749+
750+
func TestFullBacklog(t *testing.T) {
751+
N := uint64(4)
752+
sys := newTestSystem(N)
753+
var repls []*SBFT
754+
var adapters []*testSystemAdapter
755+
for i := uint64(0); i < N; i++ {
756+
a := sys.NewAdapter(i)
757+
s, err := New(i, &Config{N: N, F: 1, BatchDurationNsec: 2000000000, BatchSizeBytes: 1, RequestTimeoutNsec: 20000000000}, a)
758+
if err != nil {
759+
t.Fatal(err)
760+
}
761+
repls = append(repls, s)
762+
adapters = append(adapters, a)
763+
}
764+
765+
r1 := []byte{1, 2, 3}
766+
767+
connectAll(sys)
768+
sys.enqueue(200*time.Millisecond, &testTimer{id: 999, tf: func() {
769+
repls[0].sys.Send(&Msg{&Msg_Prepare{&Subject{Seq: &SeqView{Seq: 100}}}}, 1)
770+
}})
771+
for i := 0; i < 10; i++ {
772+
sys.enqueue(time.Duration(i)*100*time.Millisecond, &testTimer{id: 999, tf: func() {
773+
repls[0].Request(r1)
774+
}})
775+
}
776+
sys.Run()
777+
if len(repls[1].replicaState[2].backLog) > 4*3 {
778+
t.Errorf("backlog too long: %d", len(repls[1].replicaState[0].backLog))
779+
}
780+
for _, a := range adapters {
781+
if len(a.batches) == 0 {
782+
t.Fatalf("expected execution of batches on %d", a.id)
783+
}
784+
bh := a.batches[len(a.batches)-1].DecodeHeader()
785+
if bh.Seq != 10 {
786+
t.Errorf("wrong request executed on %d: %v", a.id, bh)
787+
}
788+
}
789+
}

consensus/simplebft/testsys_test.go

+23-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func (t *testSystemAdapter) SetReceiver(recv Receiver) {
6565
t.receiver = recv
6666
}
6767

68-
func (t *testSystemAdapter) Send(msg *Msg, dest uint64) {
68+
func (t *testSystemAdapter) getArrival(dest uint64) time.Duration {
6969
// XXX for now, define fixed variance per destination
7070
arr, ok := t.arrivals[dest]
7171
if !ok {
@@ -78,7 +78,11 @@ func (t *testSystemAdapter) Send(msg *Msg, dest uint64) {
7878
arr = inflight + variance
7979
t.arrivals[dest] = arr
8080
}
81+
return arr
82+
}
8183

84+
func (t *testSystemAdapter) Send(msg *Msg, dest uint64) {
85+
arr := t.getArrival(dest)
8286
ev := &testMsgEvent{
8387
inflight: arr,
8488
src: t.id,
@@ -202,6 +206,24 @@ func (t *testSystemAdapter) CheckSig(data []byte, src uint64, sig []byte) error
202206
return nil
203207
}
204208

209+
func (t *testSystemAdapter) Reconnect(replica uint64) {
210+
testLog.Infof("dropping connection from %d to %d", replica, t.id)
211+
t.sys.queue.filter(func(e testElem) bool {
212+
switch e := e.ev.(type) {
213+
case *testMsgEvent:
214+
if e.dst == t.id && e.src == replica {
215+
return false
216+
}
217+
}
218+
return true
219+
})
220+
arr := t.sys.adapters[replica].arrivals[t.id] * 10
221+
t.sys.enqueue(arr, &testTimer{id: t.id, tf: func() {
222+
testLog.Infof("reconnecting %d to %d", replica, t.id)
223+
t.sys.adapters[replica].receiver.Connection(t.id)
224+
}})
225+
}
226+
205227
// ==============================================
206228

207229
type testEvent interface {

0 commit comments

Comments
 (0)