Skip to content

Commit

Permalink
Revise the approach for setting level_compaction_dynamic_level_bytes (h…
Browse files Browse the repository at this point in the history
…yperledger#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]>
Signed-off-by: Daniel Lehrner <[email protected]>
  • Loading branch information
ahamlat authored and daniellehrner committed Dec 18, 2024
1 parent 384e293 commit 1aa0f6d
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

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

## 24.12.2 Hotfix

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public OptimisticRocksDBColumnarKeyValueStorage(
try {

db =
OptimisticTransactionDB.open(
RocksDBOpener.openOptimisticTransactionDBWithWarning(
options, configuration.getDatabaseDir().toString(), columnDescriptors, columnHandles);
initMetrics();
initColumnHandles();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@
import org.rocksdb.ColumnFamilyHandle;
import org.rocksdb.ColumnFamilyOptions;
import org.rocksdb.CompressionType;
import org.rocksdb.ConfigOptions;
import org.rocksdb.DBOptions;
import org.rocksdb.Env;
import org.rocksdb.LRUCache;
import org.rocksdb.Options;
import org.rocksdb.OptionsUtil;
import org.rocksdb.ReadOptions;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;
Expand All @@ -76,9 +78,6 @@ public abstract class RocksDBColumnarKeyValueStorage implements SegmentedKeyValu
/** RocksDb blockcache size when using the high spec option */
protected static final long ROCKSDB_BLOCKCACHE_SIZE_HIGH_SPEC = 1_073_741_824L;

/** RocksDb memtable size when using the high spec option */
protected static final long ROCKSDB_MEMTABLE_SIZE_HIGH_SPEC = 536_870_912L;

/** Max total size of all WAL file, after which a flush is triggered */
protected static final long WAL_MAX_TOTAL_SIZE = 1_073_741_824L;

Expand Down Expand Up @@ -186,15 +185,47 @@ public RocksDBColumnarKeyValueStorage(
*/
private ColumnFamilyDescriptor createColumnDescriptor(
final SegmentIdentifier segment, final RocksDBConfiguration configuration) {

boolean dynamicLevelBytes = true;
try {
ConfigOptions configOptions = new ConfigOptions();
DBOptions dbOptions = new DBOptions();
List<ColumnFamilyDescriptor> cfDescriptors = new ArrayList<>();

String latestOptionsFileName =
OptionsUtil.getLatestOptionsFileName(
configuration.getDatabaseDir().toString(), Env.getDefault());
LOG.trace("Latest OPTIONS file detected: " + latestOptionsFileName);

String optionsFilePath =
configuration.getDatabaseDir().toString() + "/" + latestOptionsFileName;
OptionsUtil.loadOptionsFromFile(configOptions, optionsFilePath, dbOptions, cfDescriptors);

LOG.trace("RocksDB options loaded successfully from: " + optionsFilePath);

if (!cfDescriptors.isEmpty()) {
Optional<ColumnFamilyOptions> matchedCfOptions = Optional.empty();
for (ColumnFamilyDescriptor descriptor : cfDescriptors) {
if (Arrays.equals(descriptor.getName(), segment.getId())) {
matchedCfOptions = Optional.of(descriptor.getOptions());
break;
}
}
if (matchedCfOptions.isPresent()) {
dynamicLevelBytes = matchedCfOptions.get().levelCompactionDynamicLevelBytes();
LOG.trace("dynamicLevelBytes is set to an existing value : " + dynamicLevelBytes);
}
}
} catch (RocksDBException ex) {
// Options file is not found in the database
}
BlockBasedTableConfig basedTableConfig = createBlockBasedTableConfig(segment, configuration);

final var options =
new ColumnFamilyOptions()
.setTtl(0)
.setCompressionType(CompressionType.LZ4_COMPRESSION)
.setTableFormatConfig(basedTableConfig);

.setTableFormatConfig(basedTableConfig)
.setLevelCompactionDynamicLevelBytes(dynamicLevelBytes);
if (segment.containsStaticData()) {
options
.setEnableBlobFiles(true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright contributors to Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.plugin.services.storage.rocksdb.segmented;

import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.rocksdb.ColumnFamilyDescriptor;
import org.rocksdb.ColumnFamilyHandle;
import org.rocksdb.DBOptions;
import org.rocksdb.OptimisticTransactionDB;
import org.rocksdb.RocksDBException;
import org.rocksdb.TransactionDB;
import org.rocksdb.TransactionDBOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Utility class for opening RocksDB instances with a warning mechanism.
*
* <p>This class provides methods to open RocksDB databases ({@link OptimisticTransactionDB} and
* {@link TransactionDB}) while monitoring the operation's duration. If the database takes longer
* than a predefined delay (default: 60 seconds) to open, a warning message is logged. This warning
* helps identify potential delays caused by factors such as RocksDB compaction.
*/
public class RocksDBOpener {

/**
* Default delay (in seconds) after which a warning is logged if the database opening operation
* has not completed.
*/
public static final int DEFAULT_DELAY = 60;

/**
* Warning message logged when the database opening operation takes longer than the predefined
* delay.
*/
public static final String WARN_MESSAGE =
"Opening RocksDB database is taking longer than 60 seconds... "
+ "This may be due to prolonged RocksDB compaction. Please wait until the end of the compaction. "
+ "No action is needed from the user.";

private static final Logger LOG = LoggerFactory.getLogger(RocksDBOpener.class);

/**
* Default constructor.
*
* <p>This is a utility class and is not meant to be instantiated directly.
*/
private RocksDBOpener() {
// Default constructor for RocksDBOpener
}

/**
* Opens an {@link OptimisticTransactionDB} instance with a warning mechanism.
*
* <p>If the database opening operation takes longer than {@link #DEFAULT_DELAY} seconds, a
* warning message is logged indicating a possible delay caused by compaction.
*
* @param options the {@link DBOptions} instance used to configure the database.
* @param dbPath the file path to the RocksDB database directory.
* @param columnDescriptors a list of {@link ColumnFamilyDescriptor} objects for the column
* families to open.
* @param columnHandles a list of {@link ColumnFamilyHandle} objects to be populated with the
* opened column families.
* @return an instance of {@link OptimisticTransactionDB}.
* @throws RocksDBException if the database cannot be opened.
*/
public static OptimisticTransactionDB openOptimisticTransactionDBWithWarning(
final DBOptions options,
final String dbPath,
final List<ColumnFamilyDescriptor> columnDescriptors,
final List<ColumnFamilyHandle> columnHandles)
throws RocksDBException {
return openDBWithWarning(
() -> OptimisticTransactionDB.open(options, dbPath, columnDescriptors, columnHandles));
}

/**
* Opens a {@link TransactionDB} instance with a warning mechanism.
*
* <p>If the database opening operation takes longer than {@link #DEFAULT_DELAY} seconds, a
* warning message is logged indicating a possible delay caused by compaction.
*
* @param options the {@link DBOptions} instance used to configure the database.
* @param transactionDBOptions the {@link TransactionDBOptions} for transaction-specific
* configuration.
* @param dbPath the file path to the RocksDB database directory.
* @param columnDescriptors a list of {@link ColumnFamilyDescriptor} objects for the column
* families to open.
* @param columnHandles a list of {@link ColumnFamilyHandle} objects to be populated with the
* opened column families.
* @return an instance of {@link TransactionDB}.
* @throws RocksDBException if the database cannot be opened.
*/
public static TransactionDB openTransactionDBWithWarning(
final DBOptions options,
final TransactionDBOptions transactionDBOptions,
final String dbPath,
final List<ColumnFamilyDescriptor> columnDescriptors,
final List<ColumnFamilyHandle> columnHandles)
throws RocksDBException {
return openDBWithWarning(
() ->
TransactionDB.open(
options, transactionDBOptions, dbPath, columnDescriptors, columnHandles));
}

/**
* A generic method to open a RocksDB database with a warning mechanism.
*
* <p>If the operation takes longer than {@link #DEFAULT_DELAY} seconds, a warning message is
* logged.
*
* @param dbOperation a lambda or method reference representing the database opening logic.
* @param <T> the type of the database being opened (e.g., {@link OptimisticTransactionDB} or
* {@link TransactionDB}).
* @return an instance of the database being opened.
* @throws RocksDBException if the database cannot be opened.
*/
private static <T> T openDBWithWarning(final DBOperation<T> dbOperation) throws RocksDBException {
AtomicBoolean operationCompleted = new AtomicBoolean(false);
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.schedule(
() -> {
if (!operationCompleted.get()) {
LOG.warn(WARN_MESSAGE);
}
},
DEFAULT_DELAY,
TimeUnit.SECONDS);

try {
T db = dbOperation.open();
operationCompleted.set(true);
return db;
} finally {
scheduler.shutdown();
}
}

/**
* Functional interface representing a database opening operation.
*
* @param <T> the type of the database being opened.
*/
@FunctionalInterface
private interface DBOperation<T> {
/**
* Opens the database.
*
* @return the opened database instance.
* @throws RocksDBException if an error occurs while opening the database.
*/
T open() throws RocksDBException;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public TransactionDBRocksDBColumnarKeyValueStorage(
try {

db =
TransactionDB.open(
RocksDBOpener.openTransactionDBWithWarning(
options,
txOptions,
configuration.getDatabaseDir().toString(),
Expand Down

0 comments on commit 1aa0f6d

Please sign in to comment.