@@ -52,47 +52,109 @@ type blockfileMgr struct {
52
52
bcInfo atomic.Value
53
53
}
54
54
55
+ /*
56
+ Creates a new manager that will manage the files used for block persistence.
57
+ This manager manages the file system FS including
58
+ -- the directory where the files are stored
59
+ -- the individual files where the blocks are stored
60
+ -- the checkpoint which tracks the latest file being persisted to
61
+ -- the index which tracks what block and transaction is in what file
62
+ When a new blockfile manager is started (i.e. only on start-up), it checks
63
+ if this start-up is the first time the system is coming up or is this a restart
64
+ of the system.
65
+
66
+ The blockfile manager stores blocks of data into a file system. That file
67
+ storage is done by creating sequentially numbered files of a configured size
68
+ i.e blockfile_000000, blockfile_000001, etc..
69
+
70
+ Each transcation in a block is stored with information about the number of
71
+ bytes in that transaction
72
+ Adding txLoc [fileSuffixNum=0, offset=3, bytesLength=104] for tx [1:0] to index
73
+ Adding txLoc [fileSuffixNum=0, offset=107, bytesLength=104] for tx [1:1] to index
74
+ Each block is stored with the total encoded length of that block as well as the
75
+ tx location offsets.
76
+
77
+ Remember that these steps are only done once at start-up of the system.
78
+ At start up a new manager:
79
+ *) Checks if the directory for storing files exists, if not creates the dir
80
+ *) Checks if the key value database exists, if not creates one
81
+ (will create a db dir)
82
+ *) Determines the checkpoint information (cpinfo) used for storage
83
+ -- Loads from db if exist, if not instantiate a new cpinfo
84
+ -- If cpinfo was loaded from db, compares to FS
85
+ -- If cpinfo and file system are not in sync, syncs cpInfo from FS
86
+ *) Starts a new file writer
87
+ -- truncates file per cpinfo to remove any excess past last block
88
+ *) Determines the index information used to find tx and blocks in
89
+ the file blkstorage
90
+ -- Instantiates a new blockIdxInfo
91
+ -- Loads the index from the db if exists
92
+ -- syncIndex comparing the last block indexed to what is in the FS
93
+ -- If index and file system are not in sync, syncs index from the FS
94
+ *) Updates blockchain info used by the APIs
95
+ */
55
96
func newBlockfileMgr (conf * Conf , indexConfig * blkstorage.IndexConfig ) * blockfileMgr {
97
+ //Determine the root directory for the blockfile storage, if it does not exist create it
56
98
rootDir := conf .blockfilesDir
57
99
_ , err := util .CreateDirIfMissing (rootDir )
58
100
if err != nil {
59
101
panic (fmt .Sprintf ("Error: %s" , err ))
60
102
}
103
+ //Determine the kev value db instance, if it does not exist, create the directory and instantiate the database.
61
104
db := initDB (conf )
105
+ // Instantiate the manager, i.e. blockFileMgr structure
62
106
mgr := & blockfileMgr {rootDir : rootDir , conf : conf , db : db }
107
+
108
+ // cp = checkpointInfo, retrieve from the database the file suffix or number of where blocks were stored.
109
+ // It also retrieves the current size of that file and the last block number that was written to that file.
110
+ // At init checkpointInfo:latestFileChunkSuffixNum=[0], latestFileChunksize=[0], lastBlockNumber=[0]
63
111
cpInfo , err := mgr .loadCurrentInfo ()
64
112
if err != nil {
65
113
panic (fmt .Sprintf ("Could not get block file info for current block file from db: %s" , err ))
66
114
}
67
- if cpInfo == nil {
115
+ if cpInfo == nil { //if no cpInfo stored in db initiate to zero
68
116
cpInfo = & checkpointInfo {latestFileChunkSuffixNum : 0 , latestFileChunksize : 0 }
69
117
err = mgr .saveCurrentInfo (cpInfo , true )
70
118
if err != nil {
71
119
panic (fmt .Sprintf ("Could not save next block file info to db: %s" , err ))
72
120
}
73
121
}
122
+ //Verify that the checkpoint stored in db is accurate with what is actually stored in block file system
123
+ // If not the same, sync the cpInfo and the file system
74
124
syncCPInfoFromFS (conf , cpInfo )
125
+ //Open a writer to the file identified by the number and truncate it to only contain the latest block
126
+ // that was completely saved (file system, index, cpinfo, etc)
75
127
currentFileWriter , err := newBlockfileWriter (deriveBlockfilePath (rootDir , cpInfo .latestFileChunkSuffixNum ))
76
128
if err != nil {
77
129
panic (fmt .Sprintf ("Could not open writer to current file: %s" , err ))
78
130
}
131
+ //Truncate the file to remove excess past last block
79
132
err = currentFileWriter .truncateFile (cpInfo .latestFileChunksize )
80
133
if err != nil {
81
134
panic (fmt .Sprintf ("Could not truncate current file to known size in db: %s" , err ))
82
135
}
83
136
137
+ // Create a new KeyValue store database handler for the blocks index in the keyvalue database
84
138
mgr .index = newBlockIndex (indexConfig , db )
139
+
140
+ // Update the manager with the checkpoint info and the file writer
85
141
mgr .cpInfo = cpInfo
86
142
mgr .currentFileWriter = currentFileWriter
143
+ // Create a checkpoint condition (event) variable, for the goroutine waiting for
144
+ // or announcing the occurrence of an event.
87
145
mgr .cpInfoCond = sync .NewCond (& sync.Mutex {})
146
+
147
+ // Verify that the index stored in db is accurate with what is actually stored in block file system
148
+ // If not the same, sync the index and the file system
88
149
mgr .syncIndex ()
89
150
90
- // init BlockchainInfo
151
+ // init BlockchainInfo for external API's
91
152
bcInfo := & pb.BlockchainInfo {
92
153
Height : 0 ,
93
154
CurrentBlockHash : nil ,
94
155
PreviousBlockHash : nil }
95
156
157
+ //If start up is a restart of an existing storage, update BlockchainInfo for external API's
96
158
if cpInfo .lastBlockNumber > 0 {
97
159
lastBlock , err := mgr .retrieveSerBlockByNumber (cpInfo .lastBlockNumber )
98
160
if err != nil {
@@ -109,6 +171,7 @@ func newBlockfileMgr(conf *Conf, indexConfig *blkstorage.IndexConfig) *blockfile
109
171
PreviousBlockHash : previousBlockHash }
110
172
}
111
173
mgr .bcInfo .Store (bcInfo )
174
+ //return the new manager (blockfileMgr)
112
175
return mgr
113
176
}
114
177
@@ -119,24 +182,34 @@ func initDB(conf *Conf) *db.DB {
119
182
return dbInst
120
183
}
121
184
185
+ //cp = checkpointInfo, from the database gets the file suffix and the size of
186
+ // the file of where the last block was written. Also retrieves contains the
187
+ // last block number that was written. At init
188
+ //checkpointInfo:latestFileChunkSuffixNum=[0], latestFileChunksize=[0], lastBlockNumber=[0]
122
189
func syncCPInfoFromFS (conf * Conf , cpInfo * checkpointInfo ) {
123
190
logger .Debugf ("Starting checkpoint=%s" , cpInfo )
191
+ //Checks if the file suffix of where the last block was written exists
124
192
rootDir := conf .blockfilesDir
125
193
filePath := deriveBlockfilePath (rootDir , cpInfo .latestFileChunkSuffixNum )
126
194
exists , size , err := util .FileExists (filePath )
127
195
if err != nil {
128
196
panic (fmt .Sprintf ("Error in checking whether file [%s] exists: %s" , filePath , err ))
129
197
}
130
198
logger .Debugf ("status of file [%s]: exists=[%t], size=[%d]" , filePath , exists , size )
199
+ //Test is !exists because when file number is first used the file does not exist yet
200
+ //checks that the file exists and that the size of the file is what is stored in cpinfo
201
+ //status of file [/tmp/tests/ledger/blkstorage/fsblkstorage/blocks/blockfile_000000]: exists=[false], size=[0]
131
202
if ! exists || int (size ) == cpInfo .latestFileChunksize {
132
203
// check point info is in sync with the file on disk
133
204
return
134
205
}
206
+ //Scan the file system to verify that the checkpoint info stored in db is correct
135
207
endOffsetLastBlock , numBlocks , err := scanForLastCompleteBlock (
136
208
rootDir , cpInfo .latestFileChunkSuffixNum , int64 (cpInfo .latestFileChunksize ))
137
209
if err != nil {
138
210
panic (fmt .Sprintf ("Could not open current file for detecting last block in the file: %s" , err ))
139
211
}
212
+ //Updates the checkpoint info for the actual last block number stored and it's end location
140
213
cpInfo .lastBlockNumber += uint64 (numBlocks )
141
214
cpInfo .latestFileChunksize = int (endOffsetLastBlock )
142
215
logger .Debugf ("Checkpoint after updates by scanning the last file segment:%s" , cpInfo )
@@ -183,6 +256,7 @@ func (mgr *blockfileMgr) addBlock(block *pb.Block2) error {
183
256
}
184
257
blockBytes := serBlock .GetBytes ()
185
258
blockHash := serBlock .ComputeHash ()
259
+ //Get the location / offset where each transaction starts in the block and where the block ends
186
260
txOffsets , err := serBlock .GetTxOffsets ()
187
261
currentOffset := mgr .cpInfo .latestFileChunksize
188
262
if err != nil {
@@ -192,12 +266,16 @@ func (mgr *blockfileMgr) addBlock(block *pb.Block2) error {
192
266
blockBytesEncodedLen := proto .EncodeVarint (uint64 (blockBytesLen ))
193
267
totalBytesToAppend := blockBytesLen + len (blockBytesEncodedLen )
194
268
269
+ //Determine if we need to start a new file since the size of this block
270
+ //exceeds the amount of space left in the current file
195
271
if currentOffset + totalBytesToAppend > mgr .conf .maxBlockfileSize {
196
272
mgr .moveToNextFile ()
197
273
currentOffset = 0
198
274
}
275
+ //append blockBytesEncodedLen to the file
199
276
err = mgr .currentFileWriter .append (blockBytesEncodedLen , false )
200
277
if err == nil {
278
+ //append the actual block bytes to the file
201
279
err = mgr .currentFileWriter .append (blockBytes , true )
202
280
}
203
281
if err != nil {
@@ -208,11 +286,13 @@ func (mgr *blockfileMgr) addBlock(block *pb.Block2) error {
208
286
return fmt .Errorf ("Error while appending block to file: %s" , err )
209
287
}
210
288
289
+ //Update the checkpoint info with the results of adding the new block
211
290
currentCPInfo := mgr .cpInfo
212
291
newCPInfo := & checkpointInfo {
213
292
latestFileChunkSuffixNum : currentCPInfo .latestFileChunkSuffixNum ,
214
293
latestFileChunksize : currentCPInfo .latestFileChunksize + totalBytesToAppend ,
215
294
lastBlockNumber : currentCPInfo .lastBlockNumber + 1 }
295
+ //save the checkpoint information in the database
216
296
if err = mgr .saveCurrentInfo (newCPInfo , false ); err != nil {
217
297
truncateErr := mgr .currentFileWriter .truncateFile (currentCPInfo .latestFileChunksize )
218
298
if truncateErr != nil {
@@ -221,16 +301,19 @@ func (mgr *blockfileMgr) addBlock(block *pb.Block2) error {
221
301
return fmt .Errorf ("Error while saving current file info to db: %s" , err )
222
302
}
223
303
304
+ //Index block file location pointer updated with file suffex and offset for the new block
224
305
blockFLP := & fileLocPointer {fileSuffixNum : newCPInfo .latestFileChunkSuffixNum }
225
306
blockFLP .offset = currentOffset
226
307
// shift the txoffset because we prepend length of bytes before block bytes
227
308
for i := 0 ; i < len (txOffsets ); i ++ {
228
309
txOffsets [i ] += len (blockBytesEncodedLen )
229
310
}
311
+ //save the index in the database
230
312
mgr .index .indexBlock (& blockIdxInfo {
231
313
blockNum : newCPInfo .lastBlockNumber , blockHash : blockHash ,
232
314
flp : blockFLP , txOffsets : txOffsets })
233
315
316
+ //update the checkpoint info (for storage) and the blockchain info (for APIs) in the manager
234
317
mgr .updateCheckpoint (newCPInfo )
235
318
mgr .updateBlockchainInfo (blockHash , block )
236
319
return nil
@@ -239,13 +322,17 @@ func (mgr *blockfileMgr) addBlock(block *pb.Block2) error {
239
322
func (mgr * blockfileMgr ) syncIndex () error {
240
323
var lastBlockIndexed uint64
241
324
var err error
325
+ //from the database, get the last block that was indexed
242
326
if lastBlockIndexed , err = mgr .index .getLastBlockIndexed (); err != nil {
243
327
return err
244
328
}
329
+ //initialize index to file number:zero, offset:zero and block:1
245
330
startFileNum := 0
246
331
startOffset := 0
247
332
blockNum := uint64 (1 )
333
+ //get the last file that blocks were added to using the checkpoint info
248
334
endFileNum := mgr .cpInfo .latestFileChunkSuffixNum
335
+ //if the index stored in the db has value, update the index information with those values
249
336
if lastBlockIndexed != 0 {
250
337
var flp * fileLocPointer
251
338
if flp , err = mgr .index .getBlockLocByBlockNum (lastBlockIndexed ); err != nil {
@@ -256,13 +343,16 @@ func (mgr *blockfileMgr) syncIndex() error {
256
343
blockNum = lastBlockIndexed
257
344
}
258
345
346
+ //open a blockstream to the file location that was stored in the index
259
347
var stream * blockStream
260
348
if stream , err = newBlockStream (mgr .rootDir , startFileNum , int64 (startOffset ), endFileNum ); err != nil {
261
349
return err
262
350
}
263
351
var blockBytes []byte
264
352
var blockPlacementInfo * blockPlacementInfo
265
353
354
+ //Should be at the last block, but go ahead and loop looking for next blockBytes
355
+ //If there is another block, add it to the index
266
356
for {
267
357
if blockBytes , blockPlacementInfo , err = stream .nextBlockBytesAndPlacementInfo (); err != nil {
268
358
return err
@@ -278,6 +368,7 @@ func (mgr *blockfileMgr) syncIndex() error {
278
368
for i := 0 ; i < len (txOffsets ); i ++ {
279
369
txOffsets [i ] += int (blockPlacementInfo .blockBytesOffset )
280
370
}
371
+ //Update the blockIndexInfo with what was actually stored in file system
281
372
blockIdxInfo := & blockIdxInfo {}
282
373
blockIdxInfo .blockHash = serBlock2 .ComputeHash ()
283
374
blockIdxInfo .blockNum = blockNum
@@ -414,6 +505,7 @@ func (mgr *blockfileMgr) fetchRawBytes(lp *fileLocPointer) ([]byte, error) {
414
505
return b , nil
415
506
}
416
507
508
+ //Get the current checkpoint information that is stored in the database
417
509
func (mgr * blockfileMgr ) loadCurrentInfo () (* checkpointInfo , error ) {
418
510
var b []byte
419
511
var err error
@@ -442,6 +534,7 @@ func (mgr *blockfileMgr) saveCurrentInfo(i *checkpointInfo, sync bool) error {
442
534
// scanForLastCompleteBlock scan a given block file and detects the last offset in the file
443
535
// after which there may lie a block partially written (towards the end of the file in a crash scenario).
444
536
func scanForLastCompleteBlock (rootDir string , fileNum int , startingOffset int64 ) (int64 , int , error ) {
537
+ //scan the passed file number suffix starting from the passed offset to find the last completed block
445
538
numBlocks := 0
446
539
blockStream , errOpen := newBlockfileStream (rootDir , fileNum , startingOffset )
447
540
if errOpen != nil {
0 commit comments