Skip to content

Commit 8fe01ad

Browse files
committed
feat(db/rocksdb): improve resource management with try-with-resources
1 parent 48cad61 commit 8fe01ad

File tree

18 files changed

+148
-62
lines changed

18 files changed

+148
-62
lines changed

actuator/src/main/java/org/tron/core/vm/repository/WriteOptionsWrapper.java

Lines changed: 0 additions & 24 deletions
This file was deleted.

chainbase/src/main/java/org/tron/common/storage/WriteOptionsWrapper.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.tron.common.storage;
22

3-
public class WriteOptionsWrapper {
3+
import java.io.Closeable;
4+
5+
public class WriteOptionsWrapper implements Closeable {
46

57
public org.rocksdb.WriteOptions rocks = null;
68
public org.iq80.leveldb.WriteOptions level = null;
@@ -9,6 +11,23 @@ private WriteOptionsWrapper() {
911

1012
}
1113

14+
/**
15+
* Returns an WriteOptionsWrapper.
16+
*
17+
* <p><b>CRITICAL:</b> The returned WriteOptionsWrapper holds native resources
18+
* and <b>MUST</b> be closed
19+
* after use to prevent memory leaks. It is strongly recommended to use a try-with-resources
20+
* statement.
21+
*
22+
* <p>Example of correct usage:
23+
* <pre>{@code
24+
* try ( WriteOptionsWrapper readOptions = WriteOptionsWrapper.getInstance()) {
25+
* // do something
26+
* }
27+
* }</pre>
28+
*
29+
* @return a new WriteOptionsWrapper that must be closed.
30+
*/
1231
public static WriteOptionsWrapper getInstance() {
1332
WriteOptionsWrapper wrapper = new WriteOptionsWrapper();
1433
wrapper.level = new org.iq80.leveldb.WriteOptions();
@@ -23,4 +42,12 @@ public WriteOptionsWrapper sync(boolean bool) {
2342
this.rocks.setSync(bool);
2443
return this;
2544
}
45+
46+
@Override
47+
public void close() {
48+
if (rocks != null) {
49+
rocks.close();
50+
}
51+
// leveldb WriteOptions has no close method, and does not need to be closed
52+
}
2653
}

chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,9 @@ public void updateByBatch(Map<byte[], byte[]> rows, WriteOptionsWrapper optionsW
308308

309309
@Override
310310
public void updateByBatch(Map<byte[], byte[]> rows) {
311-
this.updateByBatch(rows, new WriteOptions());
311+
try (WriteOptions writeOptions = new WriteOptions()) {
312+
this.updateByBatch(rows, writeOptions);
313+
}
312314
}
313315

314316
private void updateByBatch(Map<byte[], byte[]> rows, WriteOptions options) {
@@ -419,6 +421,22 @@ public void backup(String dir) throws RocksDBException {
419421
}
420422
}
421423

424+
/**
425+
* Returns an iterator over the database.
426+
*
427+
* <p><b>CRITICAL:</b> The returned iterator holds native resources and <b>MUST</b> be closed
428+
* after use to prevent memory leaks. It is strongly recommended to use a try-with-resources
429+
* statement.
430+
*
431+
* <p>Example of correct usage:
432+
* <pre>{@code
433+
* try (RocksIterator iterator = getRocksIterator()) {
434+
* // do something
435+
* }
436+
* }</pre>
437+
*
438+
* @return a new database iterator that must be closed.
439+
*/
422440
private RocksIterator getRocksIterator() {
423441
try (ReadOptions readOptions = new ReadOptions().setFillCache(false)) {
424442
throwIfNotAlive();

chainbase/src/main/java/org/tron/core/db/TronDatabase.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public abstract class TronDatabase<T> implements ITronChainBase<T> {
2727
protected DbSourceInter<byte[]> dbSource;
2828
@Getter
2929
private String dbName;
30-
private WriteOptionsWrapper writeOptions = WriteOptionsWrapper.getInstance()
30+
private final WriteOptionsWrapper writeOptions = WriteOptionsWrapper.getInstance()
3131
.sync(CommonParameter.getInstance().getStorage().isDbSync());
3232

3333
@Autowired
@@ -77,6 +77,7 @@ public void reset() {
7777
public void close() {
7878
logger.info("******** Begin to close {}. ********", getName());
7979
try {
80+
writeOptions.close();
8081
dbSource.closeDB();
8182
} catch (Exception e) {
8283
logger.warn("Failed to close {}.", getName(), e);

chainbase/src/main/java/org/tron/core/db2/common/LevelDB.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class LevelDB implements DB<byte[], byte[]>, Flusher {
1313

1414
@Getter
1515
private LevelDbDataSourceImpl db;
16-
private WriteOptionsWrapper writeOptions = WriteOptionsWrapper.getInstance()
16+
private final WriteOptionsWrapper writeOptions = WriteOptionsWrapper.getInstance()
1717
.sync(CommonParameter.getInstance().getStorage().isDbSync());
1818

1919
public LevelDB(LevelDbDataSourceImpl db) {
@@ -65,6 +65,7 @@ public void flush(Map<WrappedByteArray, WrappedByteArray> batch) {
6565

6666
@Override
6767
public void close() {
68+
this.writeOptions.close();
6869
db.closeDB();
6970
}
7071

chainbase/src/main/java/org/tron/core/db2/common/RocksDB.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class RocksDB implements DB<byte[], byte[]>, Flusher {
1414
@Getter
1515
private RocksDbDataSourceImpl db;
1616

17-
private WriteOptionsWrapper optionsWrapper = WriteOptionsWrapper.getInstance()
17+
private final WriteOptionsWrapper writeOptions = WriteOptionsWrapper.getInstance()
1818
.sync(CommonParameter.getInstance().getStorage().isDbSync());
1919

2020
public RocksDB(RocksDbDataSourceImpl db) {
@@ -61,11 +61,12 @@ public void flush(Map<WrappedByteArray, WrappedByteArray> batch) {
6161
Map<byte[], byte[]> rows = batch.entrySet().stream()
6262
.map(e -> Maps.immutableEntry(e.getKey().getBytes(), e.getValue().getBytes()))
6363
.collect(HashMap::new, (m, k) -> m.put(k.getKey(), k.getValue()), HashMap::putAll);
64-
db.updateByBatch(rows, optionsWrapper);
64+
db.updateByBatch(rows, writeOptions);
6565
}
6666

6767
@Override
6868
public void close() {
69+
writeOptions.close();
6970
db.closeDB();
7071
}
7172

chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.tron.common.error.TronDBException;
3030
import org.tron.common.es.ExecutorServiceManager;
3131
import org.tron.common.parameter.CommonParameter;
32-
import org.tron.common.storage.WriteOptionsWrapper;
3332
import org.tron.common.utils.FileUtil;
3433
import org.tron.common.utils.StorageUtils;
3534
import org.tron.core.db.RevokingDatabase;
@@ -357,7 +356,6 @@ public void flush() {
357356

358357
public void createCheckpoint() {
359358
TronDatabase<byte[]> checkPointStore = null;
360-
boolean syncFlag;
361359
try {
362360
Map<WrappedByteArray, WrappedByteArray> batch = new HashMap<>();
363361
for (Chainbase db : dbs) {
@@ -389,16 +387,13 @@ public void createCheckpoint() {
389387
if (isV2Open()) {
390388
String dbName = String.valueOf(System.currentTimeMillis());
391389
checkPointStore = getCheckpointDB(dbName);
392-
syncFlag = CommonParameter.getInstance().getStorage().isCheckpointSync();
393390
} else {
394391
checkPointStore = checkTmpStore;
395-
syncFlag = CommonParameter.getInstance().getStorage().isDbSync();
396392
}
397393

398-
checkPointStore.getDbSource().updateByBatch(batch.entrySet().stream()
394+
checkPointStore.updateByBatch(batch.entrySet().stream()
399395
.map(e -> Maps.immutableEntry(e.getKey().getBytes(), e.getValue().getBytes()))
400-
.collect(HashMap::new, (m, k) -> m.put(k.getKey(), k.getValue()), HashMap::putAll),
401-
WriteOptionsWrapper.getInstance().sync(syncFlag));
396+
.collect(HashMap::new, (m, k) -> m.put(k.getKey(), k.getValue()), HashMap::putAll));
402397

403398
} catch (Exception e) {
404399
throw new TronDBException(e);

chainbase/src/main/java/org/tron/core/store/CheckPointV2Store.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
package org.tron.core.store;
22

33
import com.google.protobuf.InvalidProtocolBufferException;
4+
import java.util.Map;
5+
import java.util.Spliterator;
6+
import java.util.function.Consumer;
47
import lombok.extern.slf4j.Slf4j;
58
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.tron.common.parameter.CommonParameter;
10+
import org.tron.common.storage.WriteOptionsWrapper;
611
import org.tron.core.db.TronDatabase;
712
import org.tron.core.exception.BadItemException;
813
import org.tron.core.exception.ItemNotFoundException;
914

10-
import java.util.Spliterator;
11-
import java.util.function.Consumer;
12-
1315
@Slf4j(topic = "DB")
1416
public class CheckPointV2Store extends TronDatabase<byte[]> {
1517

18+
private final WriteOptionsWrapper writeOptions = WriteOptionsWrapper.getInstance()
19+
.sync(CommonParameter.getInstance().getStorage().isCheckpointSync());
20+
1621
@Autowired
1722
public CheckPointV2Store(String dbPath) {
1823
super(dbPath);
@@ -52,13 +57,19 @@ public Spliterator spliterator() {
5257
protected void init() {
5358
}
5459

60+
@Override
61+
public void updateByBatch(Map<byte[], byte[]> rows) {
62+
this.dbSource.updateByBatch(rows, writeOptions);
63+
}
64+
5565
/**
5666
* close the database.
5767
*/
5868
@Override
5969
public void close() {
6070
logger.debug("******** Begin to close {}. ********", getName());
6171
try {
72+
writeOptions.close();
6273
dbSource.closeDB();
6374
} catch (Exception e) {
6475
logger.warn("Failed to close {}.", getName(), e);

common/src/main/java/org/tron/common/setting/RocksDbSettings.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,21 @@ public static LRUCache getCache() {
163163
return cache;
164164
}
165165

166+
/**
167+
* Creates a new RocksDB Options.
168+
*
169+
* <p><b>CRITICAL:</b> Must be closed after use to prevent native memory leaks.
170+
* Use try-with-resources.
171+
*
172+
* <pre>{@code
173+
* try (Options options = getOptionsByDbName(dbName)) {
174+
* // do something
175+
* }
176+
* }</pre>
177+
*
178+
* @param dbName db name
179+
* @return a new Options instance that must be closed
180+
*/
166181
public static Options getOptionsByDbName(String dbName) {
167182
RocksDbSettings settings = getSettings();
168183

framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ public void testupdateByBatchInner() {
172172
rows.clear();
173173
rows.put(key1.getBytes(), null);
174174
rows.put(key2.getBytes(), null);
175-
dataSource.updateByBatch(rows, WriteOptionsWrapper.getInstance());
175+
try (WriteOptionsWrapper options = WriteOptionsWrapper.getInstance()) {
176+
dataSource.updateByBatch(rows, options);
177+
}
176178
assertEquals(0, dataSource.allKeys().size());
177179

178180
rows.clear();

0 commit comments

Comments
 (0)