Skip to content
This repository was archived by the owner on Jul 1, 2025. It is now read-only.

Commit 43308a6

Browse files
ahamlatgaryschulte
authored andcommitted
Revise the approach for setting level_compaction_dynamic_level_bytes (#8037)
* Create a RocksDB opener that display a warning if it takes too much time to open the database * Change the strategy levelCompactionDynamicLevelBytes is set Signed-off-by: Ameziane H. <[email protected]>
1 parent 08686ad commit 43308a6

File tree

5 files changed

+212
-8
lines changed

5 files changed

+212
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
### Bug fixes
2525
- Fix serialization of state overrides when `movePrecompileToAddress` is present [#8204](https://github.com/hyperledger/besu/pull/8024)
26+
- Revise the approach for setting level_compaction_dynamic_level_bytes RocksDB configuration option [#8037](https://github.com/hyperledger/besu/pull/8037)
2627

2728
## 24.12.2 Hotfix
2829

plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/OptimisticRocksDBColumnarKeyValueStorage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public OptimisticRocksDBColumnarKeyValueStorage(
5757
try {
5858

5959
db =
60-
OptimisticTransactionDB.open(
60+
RocksDBOpener.openOptimisticTransactionDBWithWarning(
6161
options, configuration.getDatabaseDir().toString(), columnDescriptors, columnHandles);
6262
initMetrics();
6363
initColumnHandles();

plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/RocksDBColumnarKeyValueStorage.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@
5151
import org.rocksdb.ColumnFamilyHandle;
5252
import org.rocksdb.ColumnFamilyOptions;
5353
import org.rocksdb.CompressionType;
54+
import org.rocksdb.ConfigOptions;
5455
import org.rocksdb.DBOptions;
5556
import org.rocksdb.Env;
5657
import org.rocksdb.LRUCache;
5758
import org.rocksdb.Options;
59+
import org.rocksdb.OptionsUtil;
5860
import org.rocksdb.ReadOptions;
5961
import org.rocksdb.RocksDB;
6062
import org.rocksdb.RocksDBException;
@@ -76,9 +78,6 @@ public abstract class RocksDBColumnarKeyValueStorage implements SegmentedKeyValu
7678
/** RocksDb blockcache size when using the high spec option */
7779
protected static final long ROCKSDB_BLOCKCACHE_SIZE_HIGH_SPEC = 1_073_741_824L;
7880

79-
/** RocksDb memtable size when using the high spec option */
80-
protected static final long ROCKSDB_MEMTABLE_SIZE_HIGH_SPEC = 536_870_912L;
81-
8281
/** Max total size of all WAL file, after which a flush is triggered */
8382
protected static final long WAL_MAX_TOTAL_SIZE = 1_073_741_824L;
8483

@@ -186,15 +185,47 @@ public RocksDBColumnarKeyValueStorage(
186185
*/
187186
private ColumnFamilyDescriptor createColumnDescriptor(
188187
final SegmentIdentifier segment, final RocksDBConfiguration configuration) {
189-
188+
boolean dynamicLevelBytes = true;
189+
try {
190+
ConfigOptions configOptions = new ConfigOptions();
191+
DBOptions dbOptions = new DBOptions();
192+
List<ColumnFamilyDescriptor> cfDescriptors = new ArrayList<>();
193+
194+
String latestOptionsFileName =
195+
OptionsUtil.getLatestOptionsFileName(
196+
configuration.getDatabaseDir().toString(), Env.getDefault());
197+
LOG.trace("Latest OPTIONS file detected: " + latestOptionsFileName);
198+
199+
String optionsFilePath =
200+
configuration.getDatabaseDir().toString() + "/" + latestOptionsFileName;
201+
OptionsUtil.loadOptionsFromFile(configOptions, optionsFilePath, dbOptions, cfDescriptors);
202+
203+
LOG.trace("RocksDB options loaded successfully from: " + optionsFilePath);
204+
205+
if (!cfDescriptors.isEmpty()) {
206+
Optional<ColumnFamilyOptions> matchedCfOptions = Optional.empty();
207+
for (ColumnFamilyDescriptor descriptor : cfDescriptors) {
208+
if (Arrays.equals(descriptor.getName(), segment.getId())) {
209+
matchedCfOptions = Optional.of(descriptor.getOptions());
210+
break;
211+
}
212+
}
213+
if (matchedCfOptions.isPresent()) {
214+
dynamicLevelBytes = matchedCfOptions.get().levelCompactionDynamicLevelBytes();
215+
LOG.trace("dynamicLevelBytes is set to an existing value : " + dynamicLevelBytes);
216+
}
217+
}
218+
} catch (RocksDBException ex) {
219+
// Options file is not found in the database
220+
}
190221
BlockBasedTableConfig basedTableConfig = createBlockBasedTableConfig(segment, configuration);
191222

192223
final var options =
193224
new ColumnFamilyOptions()
194225
.setTtl(0)
195226
.setCompressionType(CompressionType.LZ4_COMPRESSION)
196-
.setTableFormatConfig(basedTableConfig);
197-
227+
.setTableFormatConfig(basedTableConfig)
228+
.setLevelCompactionDynamicLevelBytes(dynamicLevelBytes);
198229
if (segment.containsStaticData()) {
199230
options
200231
.setEnableBlobFiles(true)
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* Copyright contributors to Besu.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*
13+
* SPDX-License-Identifier: Apache-2.0
14+
*/
15+
package org.hyperledger.besu.plugin.services.storage.rocksdb.segmented;
16+
17+
import java.util.List;
18+
import java.util.concurrent.Executors;
19+
import java.util.concurrent.ScheduledExecutorService;
20+
import java.util.concurrent.TimeUnit;
21+
import java.util.concurrent.atomic.AtomicBoolean;
22+
23+
import org.rocksdb.ColumnFamilyDescriptor;
24+
import org.rocksdb.ColumnFamilyHandle;
25+
import org.rocksdb.DBOptions;
26+
import org.rocksdb.OptimisticTransactionDB;
27+
import org.rocksdb.RocksDBException;
28+
import org.rocksdb.TransactionDB;
29+
import org.rocksdb.TransactionDBOptions;
30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
32+
33+
/**
34+
* Utility class for opening RocksDB instances with a warning mechanism.
35+
*
36+
* <p>This class provides methods to open RocksDB databases ({@link OptimisticTransactionDB} and
37+
* {@link TransactionDB}) while monitoring the operation's duration. If the database takes longer
38+
* than a predefined delay (default: 60 seconds) to open, a warning message is logged. This warning
39+
* helps identify potential delays caused by factors such as RocksDB compaction.
40+
*/
41+
public class RocksDBOpener {
42+
43+
/**
44+
* Default delay (in seconds) after which a warning is logged if the database opening operation
45+
* has not completed.
46+
*/
47+
public static final int DEFAULT_DELAY = 60;
48+
49+
/**
50+
* Warning message logged when the database opening operation takes longer than the predefined
51+
* delay.
52+
*/
53+
public static final String WARN_MESSAGE =
54+
"Opening RocksDB database is taking longer than 60 seconds... "
55+
+ "This may be due to prolonged RocksDB compaction. Please wait until the end of the compaction. "
56+
+ "No action is needed from the user.";
57+
58+
private static final Logger LOG = LoggerFactory.getLogger(RocksDBOpener.class);
59+
60+
/**
61+
* Default constructor.
62+
*
63+
* <p>This is a utility class and is not meant to be instantiated directly.
64+
*/
65+
private RocksDBOpener() {
66+
// Default constructor for RocksDBOpener
67+
}
68+
69+
/**
70+
* Opens an {@link OptimisticTransactionDB} instance with a warning mechanism.
71+
*
72+
* <p>If the database opening operation takes longer than {@link #DEFAULT_DELAY} seconds, a
73+
* warning message is logged indicating a possible delay caused by compaction.
74+
*
75+
* @param options the {@link DBOptions} instance used to configure the database.
76+
* @param dbPath the file path to the RocksDB database directory.
77+
* @param columnDescriptors a list of {@link ColumnFamilyDescriptor} objects for the column
78+
* families to open.
79+
* @param columnHandles a list of {@link ColumnFamilyHandle} objects to be populated with the
80+
* opened column families.
81+
* @return an instance of {@link OptimisticTransactionDB}.
82+
* @throws RocksDBException if the database cannot be opened.
83+
*/
84+
public static OptimisticTransactionDB openOptimisticTransactionDBWithWarning(
85+
final DBOptions options,
86+
final String dbPath,
87+
final List<ColumnFamilyDescriptor> columnDescriptors,
88+
final List<ColumnFamilyHandle> columnHandles)
89+
throws RocksDBException {
90+
return openDBWithWarning(
91+
() -> OptimisticTransactionDB.open(options, dbPath, columnDescriptors, columnHandles));
92+
}
93+
94+
/**
95+
* Opens a {@link TransactionDB} instance with a warning mechanism.
96+
*
97+
* <p>If the database opening operation takes longer than {@link #DEFAULT_DELAY} seconds, a
98+
* warning message is logged indicating a possible delay caused by compaction.
99+
*
100+
* @param options the {@link DBOptions} instance used to configure the database.
101+
* @param transactionDBOptions the {@link TransactionDBOptions} for transaction-specific
102+
* configuration.
103+
* @param dbPath the file path to the RocksDB database directory.
104+
* @param columnDescriptors a list of {@link ColumnFamilyDescriptor} objects for the column
105+
* families to open.
106+
* @param columnHandles a list of {@link ColumnFamilyHandle} objects to be populated with the
107+
* opened column families.
108+
* @return an instance of {@link TransactionDB}.
109+
* @throws RocksDBException if the database cannot be opened.
110+
*/
111+
public static TransactionDB openTransactionDBWithWarning(
112+
final DBOptions options,
113+
final TransactionDBOptions transactionDBOptions,
114+
final String dbPath,
115+
final List<ColumnFamilyDescriptor> columnDescriptors,
116+
final List<ColumnFamilyHandle> columnHandles)
117+
throws RocksDBException {
118+
return openDBWithWarning(
119+
() ->
120+
TransactionDB.open(
121+
options, transactionDBOptions, dbPath, columnDescriptors, columnHandles));
122+
}
123+
124+
/**
125+
* A generic method to open a RocksDB database with a warning mechanism.
126+
*
127+
* <p>If the operation takes longer than {@link #DEFAULT_DELAY} seconds, a warning message is
128+
* logged.
129+
*
130+
* @param dbOperation a lambda or method reference representing the database opening logic.
131+
* @param <T> the type of the database being opened (e.g., {@link OptimisticTransactionDB} or
132+
* {@link TransactionDB}).
133+
* @return an instance of the database being opened.
134+
* @throws RocksDBException if the database cannot be opened.
135+
*/
136+
private static <T> T openDBWithWarning(final DBOperation<T> dbOperation) throws RocksDBException {
137+
AtomicBoolean operationCompleted = new AtomicBoolean(false);
138+
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
139+
scheduler.schedule(
140+
() -> {
141+
if (!operationCompleted.get()) {
142+
LOG.warn(WARN_MESSAGE);
143+
}
144+
},
145+
DEFAULT_DELAY,
146+
TimeUnit.SECONDS);
147+
148+
try {
149+
T db = dbOperation.open();
150+
operationCompleted.set(true);
151+
return db;
152+
} finally {
153+
scheduler.shutdown();
154+
}
155+
}
156+
157+
/**
158+
* Functional interface representing a database opening operation.
159+
*
160+
* @param <T> the type of the database being opened.
161+
*/
162+
@FunctionalInterface
163+
private interface DBOperation<T> {
164+
/**
165+
* Opens the database.
166+
*
167+
* @return the opened database instance.
168+
* @throws RocksDBException if an error occurs while opening the database.
169+
*/
170+
T open() throws RocksDBException;
171+
}
172+
}

plugins/rocksdb/src/main/java/org/hyperledger/besu/plugin/services/storage/rocksdb/segmented/TransactionDBRocksDBColumnarKeyValueStorage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public TransactionDBRocksDBColumnarKeyValueStorage(
5656
try {
5757

5858
db =
59-
TransactionDB.open(
59+
RocksDBOpener.openTransactionDBWithWarning(
6060
options,
6161
txOptions,
6262
configuration.getDatabaseDir().toString(),

0 commit comments

Comments
 (0)