From 8b172f584ef2d3c8bd52af6435089f80210a53e5 Mon Sep 17 00:00:00 2001 From: denyeart Date: Mon, 27 Feb 2017 04:04:49 -0500 Subject: [PATCH] [FAB-2498] Fix Panic upon Recover (CouchdB) If peers starts up and there is no CouchDB state database, there is a nil pointer panic due to nil savepoint. This changeset fixes it by returning nil height when there is nil savepont. Nil height for savepoint will trigger full state db recovery upon peer startup. Added test to ensure nil height is returned in these cases. Ensured that leveldb and couchdb behave consistently now. Tested recovery on both leveldb and couchdb. Change-Id: I0f2f2f89d7d3176fcf4a560d20665bd59c4d8a5d Signed-off-by: denyeart --- .../txmgmt/statedb/commontests/test_common.go | 7 ++++++- .../txmgmt/statedb/statecouchdb/statecouchdb.go | 13 +++++-------- .../statedb/statecouchdb/statecouchdb_test.go | 5 ++++- .../kvledger/txmgmt/txmgr/commontests/pkg_test.go | 3 ++- peer/core.yaml | 4 ++-- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/core/ledger/kvledger/txmgmt/statedb/commontests/test_common.go b/core/ledger/kvledger/txmgmt/statedb/commontests/test_common.go index 2049702b96d..93cd6f42fe1 100644 --- a/core/ledger/kvledger/txmgmt/statedb/commontests/test_common.go +++ b/core/ledger/kvledger/txmgmt/statedb/commontests/test_common.go @@ -30,6 +30,11 @@ func TestBasicRW(t *testing.T, dbProvider statedb.VersionedDBProvider) { db, err := dbProvider.GetDBHandle("testbasicrw") testutil.AssertNoError(t, err, "") + // Test that savepoint is nil for a new state db + sp, err := db.GetLatestSavePoint() + testutil.AssertNoError(t, err, "Error upon GetLatestSavePoint()") + testutil.AssertNil(t, sp) + // Test retrieval of non-existent key - returns nil rather than error // For more details see https://github.com/hyperledger-archives/fabric/issues/936. val, err := db.GetState("ns", "key1") @@ -54,7 +59,7 @@ func TestBasicRW(t *testing.T, dbProvider statedb.VersionedDBProvider) { vv, _ = db.GetState("ns2", "key4") testutil.AssertEquals(t, vv, &vv4) - sp, err := db.GetLatestSavePoint() + sp, err = db.GetLatestSavePoint() testutil.AssertNoError(t, err, "") testutil.AssertEquals(t, sp, savePoint) } diff --git a/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go b/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go index cc23b6e67e2..0ee432921be 100644 --- a/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go +++ b/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go @@ -277,9 +277,6 @@ func (vdb *VersionedDB) ApplyUpdates(batch *statedb.UpdateBatch, height *version attachment.AttachmentBytes = vv.Value attachment.ContentType = "application/octet-stream" attachment.Name = binaryWrapper - - attachments := []couchdb.Attachment{} - attachments = append(attachments, *attachment) couchDoc.Attachments = append(couchDoc.Attachments, *attachment) couchDoc.JSONValue = addVersionAndChainCodeID(nil, ns, vv.Version) } @@ -398,19 +395,19 @@ func (vdb *VersionedDB) GetLatestSavePoint() (*version.Height, error) { couchDoc, _, err := vdb.db.ReadDoc(savepointDocID) if err != nil { logger.Errorf("Failed to read savepoint data %s\n", err.Error()) - return &version.Height{BlockNum: 0, TxNum: 0}, err + return nil, err } - // ReadDoc() not found (404) will result in nil response, in these cases return height 0 - if couchDoc.JSONValue == nil { - return &version.Height{BlockNum: 0, TxNum: 0}, nil + // ReadDoc() not found (404) will result in nil response, in these cases return height nil + if couchDoc == nil || couchDoc.JSONValue == nil { + return nil, nil } savepointDoc := &couchSavepointData{} err = json.Unmarshal(couchDoc.JSONValue, &savepointDoc) if err != nil { logger.Errorf("Failed to unmarshal savepoint data %s\n", err.Error()) - return &version.Height{BlockNum: 0, TxNum: 0}, err + return nil, err } return &version.Height{BlockNum: savepointDoc.BlockNum, TxNum: savepointDoc.TxNum}, nil diff --git a/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb_test.go b/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb_test.go index 4b023232d64..3defdea2ab7 100644 --- a/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb_test.go +++ b/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb_test.go @@ -33,9 +33,12 @@ func TestMain(m *testing.M) { //call a helper method to load the core.yaml, will be used to detect if CouchDB is enabled ledgertestutil.SetupCoreYAMLConfig("./../../../../../../peer") + viper.Set("peer.fileSystemPath", "/tmp/fabric/ledgertests/kvledger/txmgmt/statedb/statecouchdb") viper.Set("ledger.state.stateDatabase", "CouchDB") + + // both vagrant and CI have couchdb configured at host "couchdb" viper.Set("ledger.state.couchDBConfig.couchDBAddress", "couchdb:5984") - viper.Set("peer.fileSystemPath", "/tmp/fabric/ledgertests/kvledger/txmgmt/statedb/statecouchdb") + result := m.Run() viper.Set("ledger.state.stateDatabase", "goleveldb") os.Exit(result) diff --git a/core/ledger/kvledger/txmgmt/txmgr/commontests/pkg_test.go b/core/ledger/kvledger/txmgmt/txmgr/commontests/pkg_test.go index e6d681176ed..beb7bf67f29 100644 --- a/core/ledger/kvledger/txmgmt/txmgr/commontests/pkg_test.go +++ b/core/ledger/kvledger/txmgmt/txmgr/commontests/pkg_test.go @@ -113,7 +113,8 @@ func (env *couchDBLockBasedEnv) getName() string { func (env *couchDBLockBasedEnv) init(t *testing.T) { viper.Set("peer.fileSystemPath", testFilesystemPath) - viper.Set("ledger.state.couchDBConfig.couchDBAddress", "127.0.0.1:5984") + // both vagrant and CI have couchdb configured at host "couchdb" + viper.Set("ledger.state.couchDBConfig.couchDBAddress", "couchdb:5984") testDBEnv := statecouchdb.NewTestVDBEnv(t) testDB, err := testDBEnv.DBProvider.GetDBHandle(couchTestChainID) testutil.AssertNoError(t, err, "") diff --git a/peer/core.yaml b/peer/core.yaml index 6b2a9ee18bf..c4121c948e0 100644 --- a/peer/core.yaml +++ b/peer/core.yaml @@ -211,9 +211,9 @@ peer: Hash: SHA2 Security: 256 # Location of Key Store, can be subdirectory of SbftLocal.DataDir - FileKeyStore: + FileKeyStore: # If "", defaults to 'mspConfigPath'/keystore - KeyStore: + KeyStore: # Path on the file system where peer will find MSP local configurations mspConfigPath: msp/sampleconfig