Skip to content

Commit c497883

Browse files
committed
FAB-1425: Record savepoint for historyDB
At time of commit() record a savepoint in historydb to store and help in recovery. Removed extra fsync. Returning err or SaveDoc error Change-Id: I93a2ab101c6bcde58b5e3050f86c621e9d13157d Signed-off-by: Balaji Viswanathan <[email protected]>
1 parent 01de0e4 commit c497883

File tree

2 files changed

+112
-1
lines changed

2 files changed

+112
-1
lines changed

core/ledger/history/couchdb_histmgr.go

+78-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package history
1818

1919
import (
2020
"bytes"
21+
"encoding/json"
22+
"fmt"
2123
"strconv"
2224

2325
"github.com/hyperledger/fabric/core/ledger"
@@ -28,6 +30,15 @@ import (
2830
logging "github.com/op/go-logging"
2931
)
3032

33+
// Savepoint docid (key) for couchdb
34+
const savepointDocID = "histdb_savepoint"
35+
36+
// Savepoint data for couchdb
37+
type couchSavepointData struct {
38+
BlockNum uint64 `json:"BlockNum"`
39+
UpdateSeq string `json:"UpdateSeq"`
40+
}
41+
3142
var logger = logging.MustGetLogger("history")
3243

3344
var compositeKeySep = []byte{0x00}
@@ -125,10 +136,55 @@ func (histmgr *CouchDBHistMgr) Commit(block *common.Block) error {
125136
if rev != "" {
126137
logger.Debugf("===HISTORYDB=== Saved document revision number: %s\n", rev)
127138
}
128-
129139
}
130140
}
141+
}
142+
143+
// Record a savepoint
144+
err := histmgr.recordSavepoint(blockNo)
145+
if err != nil {
146+
logger.Debugf("===COUCHDB=== Error during recordSavepoint: %s\n", err)
147+
return err
148+
}
149+
150+
return nil
151+
}
152+
153+
// recordSavepoint Record a savepoint in historydb.
154+
// Couch parallelizes writes in cluster or sharded setup and ordering is not guaranteed.
155+
// Hence we need to fence the savepoint with sync. So ensure_full_commit is called before AND after writing savepoint document
156+
// TODO: Optimization - merge 2nd ensure_full_commit with savepoint by using X-Couch-Full-Commit header
157+
func (txmgr *CouchDBHistMgr) recordSavepoint(blockNo uint64) error {
158+
var err error
159+
var savepointDoc couchSavepointData
160+
// ensure full commit to flush all changes until now to disk
161+
dbResponse, err := txmgr.couchDB.EnsureFullCommit()
162+
if err != nil || dbResponse.Ok != true {
163+
logger.Debugf("====COUCHDB==== Failed to perform full commit\n")
164+
return fmt.Errorf("Failed to perform full commit. Err: %s", err)
165+
}
166+
167+
// construct savepoint document
168+
// UpdateSeq would be useful if we want to get all db changes since a logical savepoint
169+
dbInfo, _, err := txmgr.couchDB.GetDatabaseInfo()
170+
if err != nil {
171+
logger.Debugf("====COUCHDB==== Failed to get DB info %s\n", err)
172+
return err
173+
}
174+
savepointDoc.BlockNum = blockNo
175+
savepointDoc.UpdateSeq = dbInfo.UpdateSeq
131176

177+
savepointDocJSON, err := json.Marshal(savepointDoc)
178+
if err != nil {
179+
logger.Debugf("====COUCHDB==== Failed to create savepoint data %s\n", err)
180+
return err
181+
}
182+
183+
// SaveDoc using couchdb client and use JSON format
184+
_, err = txmgr.couchDB.SaveDoc(savepointDocID, "", savepointDocJSON, nil)
185+
if err != nil {
186+
logger.Debugf("====CouchDB==== Failed to save the savepoint to DB %s\n", err)
187+
return err
132188
}
133189
return nil
134190
}
@@ -149,6 +205,27 @@ func (histmgr *CouchDBHistMgr) getTransactionsForNsKey(namespace string, key str
149205
return newHistScanner(compositeStartKey, *queryResult), nil
150206
}
151207

208+
// GetBlockNumFromSavepoint Reads the savepoint from database and returns the corresponding block number.
209+
// If no savepoint is found, it returns 0
210+
func (txmgr *CouchDBHistMgr) GetBlockNumFromSavepoint() (uint64, error) {
211+
var err error
212+
savepointJSON, _, err := txmgr.couchDB.ReadDoc(savepointDocID)
213+
if err != nil {
214+
// TODO: differentiate between 404 and some other error code
215+
logger.Debugf("====COUCHDB==== Failed to read savepoint data %s\n", err)
216+
return 0, err
217+
}
218+
219+
savepointDoc := &couchSavepointData{}
220+
err = json.Unmarshal(savepointJSON, &savepointDoc)
221+
if err != nil {
222+
logger.Debugf("====COUCHDB==== Failed to read savepoint data %s\n", err)
223+
return 0, err
224+
}
225+
226+
return savepointDoc.BlockNum, nil
227+
}
228+
152229
func constructCompositeKey(ns string, key string, blocknum uint64, trannum uint64) string {
153230
//History Key is: "namespace key blocknum trannum"", with namespace being the chaincode id
154231

core/ledger/history/couchdb_histmgr_test.go

+34
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,40 @@ func TestConstructCompositeKey(t *testing.T) {
8383
testutil.AssertEquals(t, compositeKey, "ns1"+strKeySep+"key1"+strKeySep+"1"+strKeySep+"1")
8484
}
8585

86+
//TestSavepoint tests the recordSavepoint and GetBlockNumfromSavepoint methods for recording and reading a savepoint document
87+
func TestSavepoint(t *testing.T) {
88+
89+
if ledgerconfig.IsHistoryDBEnabled() == true {
90+
91+
env := newTestEnvHistoryCouchDB(t, "history-test")
92+
env.cleanup() //cleanup at the beginning to ensure the database doesn't exist already
93+
defer env.cleanup() //and cleanup at the end
94+
95+
logger.Debugf("===HISTORYDB=== env.couchDBAddress: %v , env.couchDatabaseName: %v env.couchUsername: %v env.couchPassword: %v\n",
96+
env.couchDBAddress, env.couchDatabaseName, env.couchUsername, env.couchPassword)
97+
98+
histMgr := NewCouchDBHistMgr(
99+
env.couchDBAddress, //couchDB Address
100+
env.couchDatabaseName, //couchDB db name
101+
env.couchUsername, //enter couchDB id
102+
env.couchPassword) //enter couchDB pw
103+
104+
// read the savepoint
105+
blockNum, err := histMgr.GetBlockNumFromSavepoint()
106+
testutil.AssertEquals(t, blockNum, 0)
107+
108+
// record savepoint
109+
blockNo := uint64(5)
110+
err = histMgr.recordSavepoint(blockNo)
111+
testutil.AssertNoError(t, err, fmt.Sprintf("Error when saving recordpoint data"))
112+
113+
// read the savepoint
114+
blockNum, err = histMgr.GetBlockNumFromSavepoint()
115+
testutil.AssertNoError(t, err, fmt.Sprintf("Error when saving recordpoint data"))
116+
testutil.AssertEquals(t, blockNo, blockNum)
117+
}
118+
}
119+
86120
//History Database commit and read is being tested with kv_ledger_test.go.
87121
//This test will push some of the testing down into history itself
88122
func TestHistoryDatabaseCommit(t *testing.T) {

0 commit comments

Comments
 (0)