@@ -18,6 +18,8 @@ package history
18
18
19
19
import (
20
20
"bytes"
21
+ "encoding/json"
22
+ "fmt"
21
23
"strconv"
22
24
23
25
"github.com/hyperledger/fabric/core/ledger"
@@ -28,6 +30,15 @@ import (
28
30
logging "github.com/op/go-logging"
29
31
)
30
32
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
+
31
42
var logger = logging .MustGetLogger ("history" )
32
43
33
44
var compositeKeySep = []byte {0x00 }
@@ -125,10 +136,55 @@ func (histmgr *CouchDBHistMgr) Commit(block *common.Block) error {
125
136
if rev != "" {
126
137
logger .Debugf ("===HISTORYDB=== Saved document revision number: %s\n " , rev )
127
138
}
128
-
129
139
}
130
140
}
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
131
176
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
132
188
}
133
189
return nil
134
190
}
@@ -149,6 +205,27 @@ func (histmgr *CouchDBHistMgr) getTransactionsForNsKey(namespace string, key str
149
205
return newHistScanner (compositeStartKey , * queryResult ), nil
150
206
}
151
207
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
+
152
229
func constructCompositeKey (ns string , key string , blocknum uint64 , trannum uint64 ) string {
153
230
//History Key is: "namespace key blocknum trannum"", with namespace being the chaincode id
154
231
0 commit comments