Skip to content

Commit b314b09

Browse files
committed
[FAB-9683] Handle cc launch registration failures
Registry holds LaunchState to track launching instead of a bare channel. When launches complete, the embedded channel is closed. The error managed by LaunchState can be used to determine if the launch failed or succeeded. Change-Id: Ib069c5752915a8839cf9af50ab94224345e4ffc3 Signed-off-by: Matthew Sykes <sykesmat@us.ibm.com>
1 parent bebafec commit b314b09

11 files changed

+223
-74
lines changed

core/chaincode/chaincode_suite_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,6 @@ type ccpackage interface {
6868
ccprovider.CCPackage
6969
}
7070

71-
//go:generate counterfeiter -o mock/launch_registry.go --fake-name LaunchRegistry . launchRegistry
72-
type launchRegistry interface {
73-
chaincode.LaunchRegistry
74-
}
75-
7671
//go:generate counterfeiter -o mock/chaincode_stream.go --fake-name ChaincodeStream . chaincodeStream
7772
type chaincodeStream interface {
7873
ccintf.ChaincodeStream
@@ -115,6 +110,11 @@ type peerLedger interface {
115110

116111
// NOTE: These are getting generated into the "fake" package to avoid import cycles. We need to revisit this.
117112

113+
//go:generate counterfeiter -o fake/launch_registry.go --fake-name LaunchRegistry . launchRegistry
114+
type launchRegistry interface {
115+
chaincode.LaunchRegistry
116+
}
117+
118118
//go:generate counterfeiter -o fake/message_handler.go --fake-name MessageHandler . messageHandler
119119
type messageHandler interface {
120120
chaincode.MessageHandler

core/chaincode/chaincode_support.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,9 @@ func NewChaincodeSupport(
9393
return cs
9494
}
9595

96-
// Launch will launch the chaincode if not running (if running return nil) and will wait for handler of the chaincode to get into ready state.
96+
// Launch starts executing chaincode if it is not already running. This method
97+
// blocks until the peer side handler gets into ready state or encounters a fatal
98+
// error. If the chaincode is already running, it simply returns.
9799
func (cs *ChaincodeSupport) Launch(ctx context.Context, cccid *ccprovider.CCContext, spec ccprovider.ChaincodeSpecGetter) error {
98100
cname := cccid.GetCanonicalName()
99101
if cs.HandlerRegistry.Handler(cname) != nil {

core/chaincode/chaincode_support_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ func TestStartAndWaitSuccess(t *testing.T) {
10281028
cccid := ccprovider.NewCCContext("testchannel", "testcc", "0", "landwtimertest_txid", false, nil, nil)
10291029

10301030
//actual test - everythings good
1031-
err := launcher.startAndWaitForReady(context.Background(), cccid, cds)
1031+
err := launcher.start(context.Background(), cccid, cds)
10321032
if err != nil {
10331033
t.Fatalf("expected success but failed with error %s", err)
10341034
}
@@ -1053,7 +1053,7 @@ func TestStartAndWaitTimeout(t *testing.T) {
10531053
cccid := ccprovider.NewCCContext("testchannel", "testcc", "0", "landwtimertest_txid", false, nil, nil)
10541054

10551055
//the actual test - timeout 1000 > 500
1056-
err := launcher.startAndWaitForReady(context.Background(), cccid, cds)
1056+
err := launcher.start(context.Background(), cccid, cds)
10571057
if err == nil {
10581058
t.Fatalf("expected error but succeeded")
10591059
}
@@ -1077,7 +1077,7 @@ func TestStartAndWaitLaunchError(t *testing.T) {
10771077
cccid := ccprovider.NewCCContext("testchannel", "testcc", "0", "landwtimertest_txid", false, nil, nil)
10781078

10791079
//actual test - container launch gives error
1080-
err := launcher.startAndWaitForReady(context.Background(), cccid, cds)
1080+
err := launcher.start(context.Background(), cccid, cds)
10811081
if err == nil {
10821082
t.Fatalf("expected error but succeeded")
10831083
}

core/chaincode/mock/launch_registry.go core/chaincode/fake/launch_registry.go

+12-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/chaincode/fake/registry.go

+33
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/chaincode/handler.go

+16-14
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type ACLProvider interface {
3838
type Registry interface {
3939
Register(*Handler) error
4040
Ready(cname string)
41+
Failed(cname string, err error)
4142
Deregister(cname string) error
4243
}
4344

@@ -286,7 +287,7 @@ func (h *Handler) serialSend(msg *pb.ChaincodeMessage) error {
286287

287288
var err error
288289
if err = h.chatStream.Send(msg); err != nil {
289-
err = errors.WithMessage(err, fmt.Sprintf("[%s] Error sending %s", shorttxid(msg.Txid), msg.Type))
290+
err = errors.WithMessage(err, fmt.Sprintf("[%s] error sending %s", shorttxid(msg.Txid), msg.Type))
290291
chaincodeLogger.Errorf("%+v", err)
291292
}
292293
return err
@@ -421,25 +422,26 @@ func (h *Handler) sendReady() error {
421422
}
422423

423424
h.state = Ready
424-
h.Registry.Ready(h.chaincodeID.Name)
425425

426426
chaincodeLogger.Debugf("Changed to state ready for chaincode %+v", h.chaincodeID)
427427

428428
return nil
429429
}
430430

431-
// notifyDuringStartup will send ready on registration
432-
func (h *Handler) notifyDuringStartup(val bool) {
433-
// TODO: FAB-9683
434-
if !val {
435-
chaincodeLogger.Errorf("failed to start %s", h.chaincodeID)
436-
return
431+
// notifyRegistry will send ready on registration success and
432+
// update the launch state of the chaincode in the handler registry.
433+
func (h *Handler) notifyRegistry(err error) {
434+
if err == nil {
435+
err = h.sendReady()
437436
}
438437

439-
err := h.sendReady()
440438
if err != nil {
441-
chaincodeLogger.Debugf("sendReady failed: %s", err)
439+
h.Registry.Failed(h.chaincodeID.Name, err)
440+
chaincodeLogger.Errorf("failed to start %s", h.chaincodeID)
441+
return
442442
}
443+
444+
h.Registry.Ready(h.chaincodeID.Name)
443445
}
444446

445447
// handleRegister is invoked when chaincode tries to register.
@@ -456,7 +458,7 @@ func (h *Handler) HandleRegister(msg *pb.ChaincodeMessage) {
456458
h.chaincodeID = chaincodeID
457459
err = h.Registry.Register(h)
458460
if err != nil {
459-
h.notifyDuringStartup(false)
461+
h.notifyRegistry(err)
460462
return
461463
}
462464

@@ -466,8 +468,8 @@ func (h *Handler) HandleRegister(msg *pb.ChaincodeMessage) {
466468

467469
chaincodeLogger.Debugf("Got %s for chaincodeID = %s, sending back %s", pb.ChaincodeMessage_REGISTER, chaincodeID, pb.ChaincodeMessage_REGISTERED)
468470
if err := h.serialSend(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTERED}); err != nil {
469-
chaincodeLogger.Errorf("Error sending %s: %s", pb.ChaincodeMessage_REGISTERED, err)
470-
h.notifyDuringStartup(false)
471+
chaincodeLogger.Errorf("error sending %s: %s", pb.ChaincodeMessage_REGISTERED, err)
472+
h.notifyRegistry(err)
471473
return
472474
}
473475

@@ -476,7 +478,7 @@ func (h *Handler) HandleRegister(msg *pb.ChaincodeMessage) {
476478
chaincodeLogger.Debugf("Changed state to established for %+v", h.chaincodeID)
477479

478480
// for dev mode this will also move to ready automatically
479-
h.notifyDuringStartup(true)
481+
h.notifyRegistry(nil)
480482
}
481483

482484
func (h *Handler) Notify(msg *pb.ChaincodeMessage) {

core/chaincode/handler_registry.go

+44-14
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,34 @@ import (
1616
type HandlerRegistry struct {
1717
allowUnsolicitedRegistration bool // from cs.userRunsCC
1818

19-
mutex sync.Mutex // lock covering handlers and launching
20-
handlers map[string]*Handler // chaincode cname to associated handler
21-
launching map[string]chan struct{} // launching chaincodes to ready chan
19+
mutex sync.Mutex // lock covering handlers and launching
20+
handlers map[string]*Handler // chaincode cname to associated handler
21+
launching map[string]*LaunchState // launching chaincodes to LaunchState
22+
}
23+
24+
type LaunchState struct {
25+
done chan struct{}
26+
err error
27+
}
28+
29+
func NewLaunchState() *LaunchState {
30+
return &LaunchState{
31+
done: make(chan struct{}),
32+
}
33+
}
34+
35+
func (l *LaunchState) Done() <-chan struct{} { return l.done }
36+
func (l *LaunchState) Err() error { return l.err }
37+
func (l *LaunchState) Notify(err error) {
38+
l.err = err
39+
close(l.done)
2240
}
2341

2442
// NewHandlerRegistry constructs a HandlerRegistry.
2543
func NewHandlerRegistry(allowUnsolicitedRegistration bool) *HandlerRegistry {
2644
return &HandlerRegistry{
2745
handlers: map[string]*Handler{},
28-
launching: map[string]chan struct{}{},
46+
launching: map[string]*LaunchState{},
2947
allowUnsolicitedRegistration: allowUnsolicitedRegistration,
3048
}
3149
}
@@ -48,19 +66,20 @@ func (r *HandlerRegistry) hasLaunched(chaincode string) bool {
4866
return false
4967
}
5068

51-
// Launching indicates that chaincode is being launched. The channel that
52-
// is returned will be closed when registration completes. An error will be
53-
// returned if chaincode launch processing has already been initated.
54-
func (r *HandlerRegistry) Launching(cname string) (<-chan struct{}, error) {
69+
// Launching indicates that chaincode is being launched. The LaunchState that
70+
// is returned provides mechanisms to determine when the operation has completed
71+
// and whether or not it failed. An error will be returned if chaincode launch
72+
// processing has already been initated.
73+
func (r *HandlerRegistry) Launching(cname string) (*LaunchState, error) {
5574
r.mutex.Lock()
5675
defer r.mutex.Unlock()
5776
if r.hasLaunched(cname) {
5877
return nil, errors.Errorf("chaincode %s has already been launched", cname)
5978
}
6079

61-
registerCompleteCh := make(chan struct{})
62-
r.launching[cname] = registerCompleteCh
63-
return registerCompleteCh, nil
80+
launchState := NewLaunchState()
81+
r.launching[cname] = launchState
82+
return launchState, nil
6483
}
6584

6685
// Ready indicates that the chaincode registration has completed and the
@@ -69,10 +88,21 @@ func (r *HandlerRegistry) Ready(cname string) {
6988
r.mutex.Lock()
7089
defer r.mutex.Unlock()
7190

72-
registerCompleteCh := r.launching[cname]
73-
if registerCompleteCh != nil {
91+
launchStatus := r.launching[cname]
92+
if launchStatus != nil {
7493
delete(r.launching, cname)
75-
close(registerCompleteCh)
94+
launchStatus.Notify(nil)
95+
}
96+
}
97+
98+
// Failed indicates that registration of a launched chaincode has failed.
99+
func (r *HandlerRegistry) Failed(cname string, err error) {
100+
r.mutex.Lock()
101+
defer r.mutex.Unlock()
102+
103+
launchStatus := r.launching[cname]
104+
if launchStatus != nil {
105+
launchStatus.Notify(err)
76106
}
77107
}
78108

0 commit comments

Comments
 (0)