33import com .google .common .annotations .VisibleForTesting ;
44import com .google .common .collect .Lists ;
55import com .google .common .collect .Maps ;
6+ import com .google .common .primitives .Bytes ;
7+ import com .google .common .primitives .Ints ;
68import com .google .common .primitives .Longs ;
79import java .io .File ;
810import java .io .FileNotFoundException ;
@@ -153,10 +155,10 @@ public void generateSnapshot(String sourceDir, String snapshotDir) {
153155 long start = System .currentTimeMillis ();
154156 snapshotDir = Paths .get (snapshotDir , SNAPSHOT_DIR_NAME ).toString ();
155157 try {
156- mergeCheckpoint (sourceDir );
157158 hasEnoughBlock (sourceDir );
158159 List <String > snapshotDbs = getSnapshotDbs (sourceDir );
159160 split (sourceDir , snapshotDir , snapshotDbs );
161+ mergeCheckpoint2Snapshot (sourceDir , snapshotDir );
160162 // write genesisBlock , latest recent blocks and trans
161163 fillSnapshotBlockAndTransDb (sourceDir , snapshotDir );
162164 // save min block to info
@@ -190,9 +192,9 @@ public void generateHistory(String sourceDir, String historyDir) {
190192 throw new IllegalStateException (
191193 String .format ("Unavailable sourceDir: %s is not fullNode data." , sourceDir ));
192194 }
193- mergeCheckpoint (sourceDir );
194195 hasEnoughBlock (sourceDir );
195196 split (sourceDir , historyDir , archiveDbs );
197+ mergeCheckpoint2History (sourceDir , historyDir );
196198 // save max block to info
197199 generateInfoProperties (Paths .get (historyDir , INFO_FILE_NAME ).toString (),
198200 getLatestBlockHeaderNum (sourceDir ));
@@ -261,6 +263,15 @@ private List<String> getSnapshotDbs(String sourceDir) {
261263 return snapshotDbs ;
262264 }
263265
266+ private void mergeCheckpoint2Snapshot (String sourceDir , String historyDir ) {
267+ List <String > snapshotDbs = getSnapshotDbs (sourceDir );
268+ mergeCheckpoint (sourceDir , historyDir , snapshotDbs );
269+ }
270+
271+ private void mergeCheckpoint2History (String sourceDir , String destDir ) {
272+ mergeCheckpoint (sourceDir , destDir , archiveDbs );
273+ }
274+
264275 private void split (String sourceDir , String destDir , List <String > dbs ) throws IOException {
265276 logger .info ("Begin to split the dbs." );
266277 spec .commandLine ().getOut ().println ("Begin to split the dbs." );
@@ -278,7 +289,7 @@ private void split(String sourceDir, String destDir, List<String> dbs) throws IO
278289 FileUtils .copyDatabases (Paths .get (sourceDir ), Paths .get (destDir ), dbs );
279290 }
280291
281- private void mergeCheckpoint (String sourceDir ) {
292+ private void mergeCheckpoint (String sourceDir , String destDir , List < String > destDbs ) {
282293 logger .info ("Begin to merge checkpoint to dataset." );
283294 spec .commandLine ().getOut ().println ("Begin to merge checkpoint to dataset." );
284295 try {
@@ -287,18 +298,18 @@ private void mergeCheckpoint(String sourceDir) {
287298 for (String cp : cpList ) {
288299 DBInterface checkpointDb = DbTool .getDB (
289300 sourceDir + "/" + DBUtils .CHECKPOINT_DB_V2 , cp );
290- recover (checkpointDb , sourceDir );
301+ recover (checkpointDb , destDir , destDbs );
291302 }
292303 } else if (Paths .get (sourceDir , CHECKPOINT_DB ).toFile ().exists ()) {
293304 DBInterface tmpDb = DbTool .getDB (sourceDir , CHECKPOINT_DB );
294- recover (tmpDb , sourceDir );
305+ recover (tmpDb , destDir , destDbs );
295306 }
296307 } catch (IOException | RocksDBException e ) {
297308 throw new RuntimeException (e );
298309 }
299310 }
300311
301- private void recover (DBInterface db , String destDir )
312+ private void recover (DBInterface db , String destDir , List < String > destDbs )
302313 throws IOException , RocksDBException {
303314 try (DBIterator iterator = db .iterator ()) {
304315 for (iterator .seekToFirst (); iterator .hasNext (); iterator .next ()) {
@@ -312,15 +323,17 @@ private void recover(DBInterface db, String destDir)
312323 byte [] realKey = Arrays .copyOfRange (key , dbName .getBytes ().length + 4 , key .length );
313324 byte [] realValue =
314325 value .length == 1 ? null : Arrays .copyOfRange (value , 1 , value .length );
315- DBInterface destDb = DbTool .getDB (destDir , dbName );
316- if (realValue != null ) {
317- destDb .put (realKey , realValue );
318- } else {
319- byte op = value [0 ];
320- if (DBUtils .Operator .DELETE .getValue () == op ) {
321- destDb .delete (realKey );
326+ if (destDbs != null && destDbs .contains (dbName )) {
327+ DBInterface destDb = DbTool .getDB (destDir , dbName );
328+ if (realValue != null ) {
329+ destDb .put (realKey , realValue );
322330 } else {
323- destDb .put (realKey , new byte [0 ]);
331+ byte op = value [0 ];
332+ if (DBUtils .Operator .DELETE .getValue () == op ) {
333+ destDb .delete (realKey );
334+ } else {
335+ destDb .put (realKey , new byte [0 ]);
336+ }
324337 }
325338 }
326339 }
@@ -340,14 +353,30 @@ private void generateInfoProperties(String propertyfile, long num)
340353 }
341354
342355 private long getLatestBlockHeaderNum (String databaseDir ) throws IOException , RocksDBException {
356+ // query latest_block_header_number from checkpoint first
343357 final String latestBlockHeaderNumber = "latest_block_header_number" ;
358+ DBInterface checkpointDb = getCheckpointDb (databaseDir );
359+ Long blockNumber = getLatestBlockHeaderNumFromCP (checkpointDb ,
360+ latestBlockHeaderNumber .getBytes ());
361+ if (blockNumber != null ) {
362+ return blockNumber ;
363+ }
364+ // query from propertiesDb if checkpoint not contains latest_block_header_number
344365 DBInterface propertiesDb = DbTool .getDB (databaseDir , PROPERTIES_DB_NAME );
345366 return Optional .ofNullable (propertiesDb .get (ByteArray .fromString (latestBlockHeaderNumber )))
346367 .map (ByteArray ::toLong )
347368 .orElseThrow (
348369 () -> new IllegalArgumentException ("not found latest block header number" ));
349370 }
350371
372+ private Long getLatestBlockHeaderNumFromCP (DBInterface db , byte [] key ) {
373+ byte [] value = db .get (Bytes .concat (simpleEncode (PROPERTIES_DB_NAME ), key ));
374+ if (value != null && value .length > 1 ) {
375+ return ByteArray .toLong (Arrays .copyOfRange (value , 1 , value .length ));
376+ }
377+ return null ;
378+ }
379+
351380 /**
352381 * recent blocks, trans and genesis block.
353382 */
@@ -414,6 +443,15 @@ private byte[] getGenesisBlockHash(String parentDir) throws IOException, RocksDB
414443 return result ;
415444 }
416445
446+ private static byte [] simpleEncode (String s ) {
447+ byte [] bytes = s .getBytes ();
448+ byte [] length = Ints .toByteArray (bytes .length );
449+ byte [] r = new byte [4 + bytes .length ];
450+ System .arraycopy (length , 0 , r , 0 , 4 );
451+ System .arraycopy (bytes , 0 , r , 4 , bytes .length );
452+ return r ;
453+ }
454+
417455 private BlockNumInfo checkAndGetBlockNumInfo (String historyDir , String liteDir )
418456 throws IOException , RocksDBException {
419457 logger .info ("Check the compatibility of this history." );
@@ -485,6 +523,7 @@ private void trimExtraHistory(String liteDir, BlockNumInfo blockNumInfo)
485523 DBInterface transDb = DbTool .getDB (liteDir , TRANS_DB_NAME );
486524 DBInterface tranRetDb = DbTool .getDB (liteDir , TRANSACTION_RET_DB_NAME );
487525
526+
488527 ProgressBar .wrap (LongStream .rangeClosed (start , end )
489528 .boxed ()
490529 .sorted ((a , b ) -> Long .compare (b , a )), "trimHistory" ).forEach (n -> {
@@ -519,6 +558,7 @@ private void mergeBak2Database(String liteDir, BlockNumInfo blockNumInfo) throws
519558 return ;
520559 }
521560
561+
522562 Path bakDir = Paths .get (liteDir , BACKUP_DIR_PREFIX + START_TIME );
523563 logger .info ("Begin to merge {} to database, start {} end {}." , bakDir , start , end );
524564 spec .commandLine ().getOut ()
@@ -545,7 +585,17 @@ private void mergeBak2Database(String liteDir, BlockNumInfo blockNumInfo) throws
545585
546586 private byte [] getDataFromSourceDB (String sourceDir , String dbName , byte [] key )
547587 throws IOException , RocksDBException {
548- byte [] value = DbTool .getDB (sourceDir , dbName ).get (key );
588+ DBInterface sourceDb = DbTool .getDB (sourceDir , dbName );
589+ DBInterface checkpointDb = getCheckpointDb (sourceDir );
590+ // get data from tmp first.
591+ byte [] valueFromTmp = checkpointDb .get (Bytes .concat (simpleEncode (dbName ), key ));
592+ byte [] value ;
593+ if (isEmptyBytes (valueFromTmp )) {
594+ value = sourceDb .get (key );
595+ } else {
596+ value = valueFromTmp .length == 1
597+ ? null : Arrays .copyOfRange (valueFromTmp , 1 , valueFromTmp .length );
598+ }
549599 if (isEmptyBytes (value )) {
550600 throw new RuntimeException (String .format ("data not found in store, dbName: %s, key: %s" ,
551601 dbName , Arrays .toString (key )));
@@ -614,6 +664,19 @@ private long getSecondBlock(String databaseDir) throws RocksDBException, IOExcep
614664 return num ;
615665 }
616666
667+ private DBInterface getCheckpointDb (String sourceDir ) throws IOException , RocksDBException {
668+ List <String > cpList = getCheckpointV2List (sourceDir );
669+ DBInterface checkpointDb ;
670+ if (cpList .size () > 0 ) {
671+ String latestCp = cpList .get (cpList .size () - 1 );
672+ checkpointDb = DbTool .getDB (
673+ sourceDir + "/" + DBUtils .CHECKPOINT_DB_V2 , latestCp );
674+ } else {
675+ checkpointDb = DbTool .getDB (sourceDir , CHECKPOINT_DB );
676+ }
677+ return checkpointDb ;
678+ }
679+
617680 @ VisibleForTesting
618681 public static void setRecentBlks (long recentBlks ) {
619682 RECENT_BLKS = recentBlks ;
0 commit comments