Skip to content

Commit 2590cce

Browse files
author
Jason Yellick
committed
[FAB-4431] Orderer multichain api consenter errors
The orderer multichain consenter API currently does not provide a mechanism to provide feedback for a channel when the consenter is out of sync or is otherwise not producing data for that channel. This CR adds an Errored() method to the common consenter Chain interface, and consumes that API via the Deliver common code. It provides only a skeleton implementation for the consenter implementatinos (Kafka and Solo). The Kafka implementation will provide a more robust implementation of the API in the near future. Change-Id: I22863f2a8b37932a5ecbd5241372e2a5c512571d Signed-off-by: Jason Yellick <[email protected]>
1 parent dee53d0 commit 2590cce

File tree

6 files changed

+109
-2
lines changed

6 files changed

+109
-2
lines changed

orderer/common/deliver/deliver.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ type Support interface {
5050

5151
// Reader returns the chain Reader for the chain
5252
Reader() ledger.Reader
53+
54+
// Errored returns a channel which closes when the backing consenter has errored
55+
Errored() <-chan struct{}
5356
}
5457

5558
type deliverServer struct {
@@ -103,6 +106,15 @@ func (ds *deliverServer) Handle(srv ab.AtomicBroadcast_DeliverServer) error {
103106
return sendStatusReply(srv, cb.Status_NOT_FOUND)
104107
}
105108

109+
erroredChan := chain.Errored()
110+
select {
111+
case <-erroredChan:
112+
logger.Warningf("Rejecting deliver request because of consenter error")
113+
return sendStatusReply(srv, cb.Status_SERVICE_UNAVAILABLE)
114+
default:
115+
116+
}
117+
106118
sf := sigfilter.New(policies.ChannelReaders, chain.PolicyManager())
107119
result, _ := sf.Apply(envelope)
108120
if result != filter.Forward {
@@ -140,7 +152,12 @@ func (ds *deliverServer) Handle(srv ab.AtomicBroadcast_DeliverServer) error {
140152

141153
for {
142154
if seekInfo.Behavior == ab.SeekInfo_BLOCK_UNTIL_READY {
143-
<-cursor.ReadyChan()
155+
select {
156+
case <-erroredChan:
157+
logger.Warningf("Aborting deliver request because of consenter error")
158+
return sendStatusReply(srv, cb.Status_SERVICE_UNAVAILABLE)
159+
case <-cursor.ReadyChan():
160+
}
144161
} else {
145162
select {
146163
case <-cursor.ReadyChan():

orderer/common/deliver/deliver_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ func (mm *mockSupportManager) GetChain(chainID string) (Support, bool) {
112112
type mockSupport struct {
113113
ledger ledger.ReadWriter
114114
policyManager *mockpolicies.Manager
115+
erroredChan chan struct{}
116+
}
117+
118+
func (mcs *mockSupport) Errored() <-chan struct{} {
119+
return mcs.erroredChan
115120
}
116121

117122
func (mcs *mockSupport) PolicyManager() policies.Manager {
@@ -147,6 +152,7 @@ func newMockMultichainManager() *mockSupportManager {
147152
mm.chains[systemChainID] = &mockSupport{
148153
ledger: rl,
149154
policyManager: &mockpolicies.Manager{Policy: &mockpolicies.Policy{}},
155+
erroredChan: make(chan struct{}),
150156
}
151157
return mm
152158
}
@@ -382,6 +388,64 @@ func TestBlockingSeek(t *testing.T) {
382388
}
383389
}
384390

391+
func TestErroredSeek(t *testing.T) {
392+
mm := newMockMultichainManager()
393+
ms := mm.chains[systemChainID]
394+
l := ms.ledger
395+
close(ms.erroredChan)
396+
for i := 1; i < ledgerSize; i++ {
397+
l.Append(ledger.CreateNextBlock(l, []*cb.Envelope{&cb.Envelope{Payload: []byte(fmt.Sprintf("%d", i))}}))
398+
}
399+
400+
m := newMockD()
401+
defer close(m.recvChan)
402+
ds := NewHandlerImpl(mm)
403+
404+
go ds.Handle(m)
405+
406+
m.recvChan <- makeSeek(systemChainID, &ab.SeekInfo{Start: seekSpecified(uint64(ledgerSize - 1)), Stop: seekSpecified(ledgerSize), Behavior: ab.SeekInfo_BLOCK_UNTIL_READY})
407+
408+
select {
409+
case deliverReply := <-m.sendChan:
410+
assert.Equal(t, cb.Status_SERVICE_UNAVAILABLE, deliverReply.GetStatus(), "Mock support errored")
411+
case <-time.After(time.Second):
412+
t.Fatalf("Timed out waiting for error response")
413+
}
414+
}
415+
416+
func TestErroredBlockingSeek(t *testing.T) {
417+
mm := newMockMultichainManager()
418+
ms := mm.chains[systemChainID]
419+
l := ms.ledger
420+
for i := 1; i < ledgerSize; i++ {
421+
l.Append(ledger.CreateNextBlock(l, []*cb.Envelope{&cb.Envelope{Payload: []byte(fmt.Sprintf("%d", i))}}))
422+
}
423+
424+
m := newMockD()
425+
defer close(m.recvChan)
426+
ds := NewHandlerImpl(mm)
427+
428+
go ds.Handle(m)
429+
430+
m.recvChan <- makeSeek(systemChainID, &ab.SeekInfo{Start: seekSpecified(uint64(ledgerSize - 1)), Stop: seekSpecified(ledgerSize), Behavior: ab.SeekInfo_BLOCK_UNTIL_READY})
431+
432+
select {
433+
case deliverReply := <-m.sendChan:
434+
assert.NotNil(t, deliverReply.GetBlock(), "Expected first block")
435+
case <-time.After(time.Second):
436+
t.Fatalf("Timed out waiting to get first block")
437+
}
438+
439+
close(ms.erroredChan)
440+
441+
select {
442+
case deliverReply := <-m.sendChan:
443+
assert.Equal(t, cb.Status_SERVICE_UNAVAILABLE, deliverReply.GetStatus(), "Mock support errored")
444+
case <-time.After(time.Second):
445+
t.Fatalf("Timed out waiting for error response")
446+
}
447+
}
448+
385449
func TestSGracefulShutdown(t *testing.T) {
386450
m := newMockD()
387451
ds := NewHandlerImpl(nil)

orderer/kafka/chain.go

+5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ type chainImpl struct {
6868
startCompleted bool // For testing
6969
}
7070

71+
// Errored currently only closes on halt
72+
func (chain *chainImpl) Errored() <-chan struct{} {
73+
return chain.exitChan
74+
}
75+
7176
// Start allocates the necessary resources for staying up to date with this
7277
// Chain. Implements the multichain.Chain interface. Called by
7378
// multichain.NewManagerImpl() which is invoked when the ordering process is

orderer/multichain/chainsupport.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,14 @@ type Consenter interface {
5050
// 1. Messages are ordered into a stream, the stream is cut into blocks, the blocks are committed (solo, kafka)
5151
// 2. Messages are cut into blocks, the blocks are ordered, then the blocks are committed (sbft)
5252
type Chain interface {
53-
// Enqueue accepts a message and returns true on acceptance, or false on shutdown
53+
// Enqueue accepts a message and returns true on acceptance, or false on failure
5454
Enqueue(env *cb.Envelope) bool
5555

56+
// Errored returns a channel which will close when an error has occurred
57+
// This is especially useful for the Deliver client, who must terminate waiting
58+
// clients when the consenter is not up to date
59+
Errored() <-chan struct{}
60+
5661
// Start should allocate whatever resources are needed for staying up to date with the chain
5762
// Typically, this involves creating a thread which reads from the ordering source, passes those
5863
// messages to a block cutter, and writes the resulting blocks to the ledger
@@ -84,6 +89,9 @@ type ChainSupport interface {
8489
// Reader returns the chain Reader for the chain
8590
Reader() ledger.Reader
8691

92+
// Errored returns whether the backing consenter has errored
93+
Errored() <-chan struct{}
94+
8795
broadcast.Support
8896
ConsenterSupport
8997

@@ -205,6 +213,10 @@ func (cs *chainSupport) Enqueue(env *cb.Envelope) bool {
205213
return cs.chain.Enqueue(env)
206214
}
207215

216+
func (cs *chainSupport) Errored() <-chan struct{} {
217+
return cs.chain.Errored()
218+
}
219+
208220
func (cs *chainSupport) CreateNextBlock(messages []*cb.Envelope) *cb.Block {
209221
return ledger.CreateNextBlock(cs.ledger, messages)
210222
}

orderer/multichain/util_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ type mockChain struct {
4848
done chan struct{}
4949
}
5050

51+
func (mch *mockChain) Errored() <-chan struct{} {
52+
return nil
53+
}
54+
5155
func (mch *mockChain) Enqueue(env *cb.Envelope) bool {
5256
mch.queue <- env
5357
return true

orderer/solo/consensus.go

+5
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ func (ch *chain) Enqueue(env *cb.Envelope) bool {
7979
}
8080
}
8181

82+
// Errored only closes on exit
83+
func (ch *chain) Errored() <-chan struct{} {
84+
return ch.exitChan
85+
}
86+
8287
func (ch *chain) main() {
8388
var timer <-chan time.Time
8489

0 commit comments

Comments
 (0)