Skip to content

Commit 626e31e

Browse files
authored
Merge pull request #4607 from halibobo1205/feature/liteTool_opt
feat(liteFullNodeTool): optimize liteFullNodeTool
2 parents a0874d4 + 046eab5 commit 626e31e

2 files changed

Lines changed: 165 additions & 168 deletions

File tree

framework/src/main/java/org/tron/tool/litefullnode/LiteFullNodeTool.java

Lines changed: 106 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.beust.jcommander.Parameter;
55
import com.beust.jcommander.ParameterException;
66
import com.beust.jcommander.internal.Lists;
7+
import com.google.common.annotations.VisibleForTesting;
78
import com.google.common.base.Strings;
89
import com.google.common.collect.Maps;
910
import com.google.common.primitives.Bytes;
@@ -16,9 +17,9 @@
1617
import java.nio.file.Paths;
1718
import java.util.Arrays;
1819
import java.util.List;
20+
import java.util.NoSuchElementException;
1921
import java.util.Objects;
2022
import java.util.Optional;
21-
import java.util.stream.LongStream;
2223
import lombok.extern.slf4j.Slf4j;
2324
import org.apache.commons.lang3.StringUtils;
2425
import org.rocksdb.RocksDBException;
@@ -47,50 +48,23 @@ public class LiteFullNodeTool {
4748
private static final String INFO_FILE_NAME = "info.properties";
4849
private static final String BACKUP_DIR_PREFIX = ".bak_";
4950
private static final String CHECKPOINT_DB = "tmp";
50-
private static final long VM_NEED_RECENT_BLKS = 256;
51+
private static long RECENT_BLKS = 65536;
5152

5253
private static final String BLOCK_DB_NAME = "block";
5354
private static final String BLOCK_INDEX_DB_NAME = "block-index";
54-
private static final String TRANS_CACHE_DB_NAME = "trans-cache";
55+
private static final String TRANS_DB_NAME = "trans";
5556
private static final String COMMON_DB_NAME = "common";
57+
private static final String TRANSACTION_RET_DB_NAME = "transactionRetStore";
58+
private static final String TRANSACTION_HISTORY_DB_NAME = "transactionHistoryStore";
5659

5760
private static final String DIR_FORMAT_STRING = "%s%s%s";
5861

5962
private static List<String> archiveDbs = Arrays.asList(
6063
BLOCK_DB_NAME,
6164
BLOCK_INDEX_DB_NAME,
62-
"trans",
63-
"transactionRetStore",
64-
"transactionHistoryStore");
65-
private static List<String> minimumDbsForLiteNode = Arrays.asList(
66-
"DelegatedResource",
67-
"DelegatedResourceAccountIndex",
68-
"IncrementalMerkleTree",
69-
"account",
70-
"account-index",
71-
"accountTrie",
72-
"accountid-index",
73-
"asset-issue",
74-
"asset-issue-v2",
75-
//"block_KDB",
76-
"code",
77-
//"common",
78-
"contract",
79-
"delegation",
80-
"exchange",
81-
"exchange-v2",
82-
//"nullifier",
83-
"properties",
84-
"proposal",
85-
"recent-block",
86-
//"recent-transaction",
87-
"storage-row",
88-
//TRANS_CACHE_DB_NAME,
89-
//"tree-block-index",
90-
"votes",
91-
"witness",
92-
"witness_schedule"
93-
);
65+
TRANS_DB_NAME,
66+
TRANSACTION_RET_DB_NAME,
67+
TRANSACTION_HISTORY_DB_NAME);
9468

9569
/**
9670
* Create the snapshot dataset.
@@ -104,13 +78,12 @@ public void generateSnapshot(String sourceDir, String snapshotDir) {
10478
long start = System.currentTimeMillis();
10579
snapshotDir = Paths.get(snapshotDir, SNAPSHOT_DIR_NAME).toString();
10680
try {
81+
hasEnoughBlock(sourceDir);
10782
List<String> snapshotDbs = getSnapshotDbs(sourceDir);
10883
split(sourceDir, snapshotDir, snapshotDbs);
10984
mergeCheckpoint2Snapshot(sourceDir, snapshotDir);
110-
// write genesisBlock and latestBlock
111-
fillSnapshotBlockDb(sourceDir, snapshotDir);
112-
// create tran-cache if not exist, for compatible
113-
checkTranCacheStore(sourceDir, snapshotDir);
85+
// write genesisBlock , latest recent blocks and trans
86+
fillSnapshotBlockAndTransDb(sourceDir, snapshotDir);
11487
generateInfoProperties(Paths.get(snapshotDir, INFO_FILE_NAME).toString(), sourceDir);
11588
} catch (IOException | RocksDBException e) {
11689
logger.error("create snapshot failed, " + e.getMessage());
@@ -132,6 +105,11 @@ public void generateHistory(String sourceDir, String historyDir) {
132105
long start = System.currentTimeMillis();
133106
historyDir = Paths.get(historyDir, HISTORY_DIR_NAME).toString();
134107
try {
108+
if (isLite(sourceDir)) {
109+
throw new IllegalStateException(
110+
String.format("Unavailable sourceDir: %s is not fullNode data.", sourceDir));
111+
}
112+
hasEnoughBlock(sourceDir);
135113
split(sourceDir, historyDir, archiveDbs);
136114
mergeCheckpoint2History(sourceDir, historyDir);
137115
generateInfoProperties(Paths.get(historyDir, INFO_FILE_NAME).toString(), sourceDir);
@@ -155,6 +133,12 @@ public void completeHistoryData(String historyDir, String databaseDir) {
155133
long start = System.currentTimeMillis();
156134
BlockNumInfo blockNumInfo = null;
157135
try {
136+
// check historyDir is from lite data
137+
if (isLite(historyDir)) {
138+
throw new IllegalStateException(
139+
String.format("Unavailable history: %s is not generated by fullNode data.",
140+
historyDir));
141+
}
158142
// 1. check block number and genesis block are compatible,
159143
// and return the block numbers of snapshot and history
160144
blockNumInfo = checkAndGetBlockNumInfo(historyDir, databaseDir);
@@ -183,11 +167,6 @@ private List<String> getSnapshotDbs(String sourceDir) {
183167
.filter(File::isDirectory)
184168
.filter(dir -> !archiveDbs.contains(dir.getName()))
185169
.forEach(dir -> snapshotDbs.add(dir.getName()));
186-
for (String dir : minimumDbsForLiteNode) {
187-
if (!snapshotDbs.contains(dir)) {
188-
throw new RuntimeException("databaseDir does not contain all the necessary databases");
189-
}
190-
}
191170
return snapshotDbs;
192171
}
193172

@@ -270,88 +249,50 @@ private long getLatestBlockHeaderNum(String databaseDir) throws IOException, Roc
270249
}
271250

272251
/**
273-
* Syncing block from peer that needs latest block and genesis block,
274-
* also VM need recent blocks.
252+
* recent blocks, trans and genesis block.
275253
*/
276-
private void fillSnapshotBlockDb(String sourceDir, String snapshotDir)
254+
private void fillSnapshotBlockAndTransDb(String sourceDir, String snapshotDir)
277255
throws IOException, RocksDBException {
278-
logger.info("-- begin to fill latest block and genesis block to snapshot");
256+
logger.info("-- begin to fill {} block , genesis block and trans to snapshot", RECENT_BLKS);
279257
DBInterface sourceBlockIndexDb = DbTool.getDB(sourceDir, BLOCK_INDEX_DB_NAME);
280258
DBInterface sourceBlockDb = DbTool.getDB(sourceDir, BLOCK_DB_NAME);
281259
DBInterface destBlockDb = DbTool.getDB(snapshotDir, BLOCK_DB_NAME);
282260
DBInterface destBlockIndexDb = DbTool.getDB(snapshotDir, BLOCK_INDEX_DB_NAME);
261+
DBInterface destTransDb = DbTool.getDB(snapshotDir, TRANS_DB_NAME);
283262
// put genesis block and block-index into snapshot
284263
long genesisBlockNum = 0L;
285264
byte[] genesisBlockID = sourceBlockIndexDb.get(ByteArray.fromLong(genesisBlockNum));
286265
destBlockIndexDb.put(ByteArray.fromLong(genesisBlockNum), genesisBlockID);
287266
destBlockDb.put(genesisBlockID, sourceBlockDb.get(genesisBlockID));
288267

289268
long latestBlockNum = getLatestBlockHeaderNum(sourceDir);
290-
long startIndex = latestBlockNum > VM_NEED_RECENT_BLKS
291-
? latestBlockNum - VM_NEED_RECENT_BLKS : 0;
292-
// put the recent blocks in snapshot, VM needs recent 256 blocks.
293-
LongStream.rangeClosed(startIndex, latestBlockNum).forEach(
294-
blockNum -> {
295-
byte[] blockId = null;
296-
byte[] block = null;
297-
try {
298-
blockId = getDataFromSourceDB(sourceDir, BLOCK_INDEX_DB_NAME,
299-
Longs.toByteArray(blockNum));
300-
block = getDataFromSourceDB(sourceDir, BLOCK_DB_NAME, blockId);
301-
} catch (IOException | RocksDBException e) {
302-
throw new RuntimeException(e.getMessage());
303-
}
304-
// put recent blocks index into snapshot
305-
destBlockIndexDb.put(ByteArray.fromLong(blockNum), blockId);
306-
// put latest blocks into snapshot
307-
destBlockDb.put(blockId, block);
308-
});
269+
long startIndex = latestBlockNum - RECENT_BLKS + 1;
270+
// put the recent blocks and trans in snapshot
271+
for (long blockNum = startIndex; blockNum <= latestBlockNum; blockNum++) {
272+
try {
273+
byte[] blockId = getDataFromSourceDB(sourceDir, BLOCK_INDEX_DB_NAME,
274+
Longs.toByteArray(blockNum));
275+
byte[] block = getDataFromSourceDB(sourceDir, BLOCK_DB_NAME, blockId);
276+
// put block
277+
destBlockDb.put(blockId, block);
278+
// put block index
279+
destBlockIndexDb.put(ByteArray.fromLong(blockNum), blockId);
280+
// put trans
281+
long finalBlockNum = blockNum;
282+
new BlockCapsule(block).getTransactions().stream().map(
283+
tc -> tc.getTransactionId().getBytes())
284+
.map(bytes -> Maps.immutableEntry(bytes, Longs.toByteArray(finalBlockNum)))
285+
.forEach(e -> destTransDb.put(e.getKey(), e.getValue()));
286+
} catch (IOException | RocksDBException | BadItemException e) {
287+
throw new RuntimeException(e.getMessage());
288+
}
289+
}
309290

310291
DBInterface destCommonDb = DbTool.getDB(snapshotDir, COMMON_DB_NAME);
311292
destCommonDb.put(DB_KEY_NODE_TYPE, ByteArray.fromInt(Constant.NODE_TYPE_LIGHT_NODE));
312293
destCommonDb.put(DB_KEY_LOWEST_BLOCK_NUM, ByteArray.fromLong(startIndex));
313294
}
314295

315-
private void checkTranCacheStore(String sourceDir, String snapshotDir)
316-
throws IOException, RocksDBException {
317-
logger.info("-- create trans-cache db if not exists.");
318-
if (FileUtil.isExists(String.format(DIR_FORMAT_STRING, snapshotDir,
319-
File.separator, TRANS_CACHE_DB_NAME))) {
320-
return;
321-
}
322-
// fullnode is old version, create trans-cache database
323-
DBInterface recentBlockDb = DbTool.getDB(snapshotDir, "recent-block");
324-
DBInterface transCacheDb = DbTool.getDB(snapshotDir, TRANS_CACHE_DB_NAME);
325-
long headNum = getLatestBlockHeaderNum(sourceDir);
326-
long recentBlockCount = recentBlockDb.size();
327-
328-
LongStream.rangeClosed(headNum - recentBlockCount + 1, headNum).forEach(
329-
blockNum -> {
330-
byte[] blockId = null;
331-
byte[] block = null;
332-
try {
333-
blockId = getDataFromSourceDB(sourceDir, BLOCK_INDEX_DB_NAME,
334-
Longs.toByteArray(blockNum));
335-
block = getDataFromSourceDB(sourceDir, BLOCK_DB_NAME, blockId);
336-
} catch (IOException | RocksDBException e) {
337-
throw new RuntimeException(e.getMessage());
338-
}
339-
BlockCapsule blockCapsule = null;
340-
try {
341-
blockCapsule = new BlockCapsule(block);
342-
} catch (BadItemException e) {
343-
throw new RuntimeException("construct block failed, num: " + blockNum);
344-
}
345-
if (blockCapsule.getTransactions().isEmpty()) {
346-
return;
347-
}
348-
blockCapsule.getTransactions().stream()
349-
.map(tc -> tc.getTransactionId().getBytes())
350-
.map(bytes -> Maps.immutableEntry(bytes, Longs.toByteArray(blockNum)))
351-
.forEach(e -> transCacheDb.put(e.getKey(), e.getValue()));
352-
});
353-
}
354-
355296
private byte[] getGenesisBlockHash(String parentDir) throws IOException, RocksDBException {
356297
long genesisBlockNum = 0L;
357298
DBInterface blockIndexDb = DbTool.getDB(parentDir, BLOCK_INDEX_DB_NAME);
@@ -424,8 +365,8 @@ private void trimHistory(String databaseDir, BlockNumInfo blockNumInfo)
424365
logger.info("-- begin to trim the history data.");
425366
DBInterface blockIndexDb = DbTool.getDB(databaseDir, BLOCK_INDEX_DB_NAME);
426367
DBInterface blockDb = DbTool.getDB(databaseDir, BLOCK_DB_NAME);
427-
DBInterface transDb = DbTool.getDB(databaseDir, "trans");
428-
DBInterface tranRetDb = DbTool.getDB(databaseDir, "transactionRetStore");
368+
DBInterface transDb = DbTool.getDB(databaseDir, TRANS_DB_NAME);
369+
DBInterface tranRetDb = DbTool.getDB(databaseDir, TRANSACTION_RET_DB_NAME);
429370
for (long n = blockNumInfo.getHistoryBlkNum(); n > blockNumInfo.getSnapshotBlkNum(); n--) {
430371
byte[] blockIdHash = blockIndexDb.get(ByteArray.fromLong(n));
431372
BlockCapsule block = new BlockCapsule(blockDb.get(blockIdHash));
@@ -460,12 +401,15 @@ private void mergeBak2Database(String databaseDir) throws IOException, RocksDBEx
460401
private byte[] getDataFromSourceDB(String sourceDir, String dbName, byte[] key)
461402
throws IOException, RocksDBException {
462403
DBInterface sourceDb = DbTool.getDB(sourceDir, dbName);
463-
DBInterface checkpointDb = DbTool.getDB(sourceDir, "tmp");
464-
byte[] value = sourceDb.get(key);
465-
if (isEmptyBytes(value)) {
466-
byte[] valueFromTmp = checkpointDb.get(Bytes.concat(simpleEncode(dbName), key));
404+
DBInterface checkpointDb = DbTool.getDB(sourceDir, CHECKPOINT_DB);
405+
// get data from tmp first.
406+
byte[] valueFromTmp = checkpointDb.get(Bytes.concat(simpleEncode(dbName), key));
407+
byte[] value;
408+
if (isEmptyBytes(valueFromTmp)) {
409+
value = sourceDb.get(key);
410+
} else {
467411
value = valueFromTmp.length == 1
468-
? null : Arrays.copyOfRange(valueFromTmp, 1, valueFromTmp.length);
412+
? null : Arrays.copyOfRange(valueFromTmp, 1, valueFromTmp.length);
469413
}
470414
if (isEmptyBytes(value)) {
471415
throw new RuntimeException(String.format("data not found in store, dbName: %s, key: %s",
@@ -489,8 +433,7 @@ private static boolean isEmptyBytes(byte[] b) {
489433
private void deleteSnapshotFlag(String databaseDir) throws IOException, RocksDBException {
490434
logger.info("-- delete the info file.");
491435
Files.delete(Paths.get(databaseDir, INFO_FILE_NAME));
492-
DBInterface destBlockIndexDb = DbTool.getDB(databaseDir, BLOCK_INDEX_DB_NAME);
493-
if (destBlockIndexDb.get(ByteArray.fromLong(1)) != null) {
436+
if (!isLite(databaseDir)) {
494437
DBInterface destCommonDb = DbTool.getDB(databaseDir, COMMON_DB_NAME);
495438
destCommonDb.delete(DB_KEY_NODE_TYPE);
496439
destCommonDb.delete(DB_KEY_LOWEST_BLOCK_NUM);
@@ -500,6 +443,53 @@ private void deleteSnapshotFlag(String databaseDir) throws IOException, RocksDBE
500443

501444
}
502445

446+
private void hasEnoughBlock(String sourceDir) throws RocksDBException, IOException {
447+
// check latest
448+
long latest = getLatestBlockHeaderNum(sourceDir);
449+
// check first, not 0;
450+
long first = 0;
451+
DBInterface sourceBlockIndexDb = DbTool.getDB(sourceDir, BLOCK_INDEX_DB_NAME);
452+
DBIterator iterator = sourceBlockIndexDb.iterator();
453+
iterator.seekToFirst();
454+
if (iterator.hasNext()) {
455+
iterator.next();
456+
if (iterator.hasNext()) {
457+
first = Longs.fromByteArray(iterator.getKey());
458+
}
459+
}
460+
461+
if (latest - first + 1 < RECENT_BLKS) {
462+
throw new NoSuchElementException(
463+
String.format("At least %d blocks in block store, actual latestBlock:%d, firstBlock:%d.",
464+
RECENT_BLKS, latest, first));
465+
}
466+
}
467+
468+
private boolean isLite(String databaseDir) throws RocksDBException, IOException {
469+
DBInterface sourceDb = DbTool.getDB(databaseDir, BLOCK_INDEX_DB_NAME);
470+
DBInterface checkpointDb = DbTool.getDB(databaseDir, CHECKPOINT_DB);
471+
byte[] key = ByteArray.fromLong(1);
472+
byte[] valueFromTmp = checkpointDb.get(Bytes.concat(simpleEncode(BLOCK_INDEX_DB_NAME), key));
473+
byte[] value;
474+
if (isEmptyBytes(valueFromTmp)) {
475+
value = sourceDb.get(key);
476+
} else {
477+
value = valueFromTmp.length == 1
478+
? null : Arrays.copyOfRange(valueFromTmp, 1, valueFromTmp.length);
479+
}
480+
return isEmptyBytes(value);
481+
}
482+
483+
@VisibleForTesting
484+
public static void setRecentBlks(long recentBlks) {
485+
RECENT_BLKS = recentBlks;
486+
}
487+
488+
@VisibleForTesting
489+
public static void reSetRecentBlks() {
490+
RECENT_BLKS = 65536;
491+
}
492+
503493
private void run(Args argv) {
504494
if (StringUtils.isBlank(argv.fnDataPath) || StringUtils.isBlank(argv.datasetPath)) {
505495
throw new ParameterException("fnDataPath or datasetPath can't be null");

0 commit comments

Comments
 (0)