Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions besu/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ dependencies {
implementation 'org.springframework.security:spring-security-crypto'
implementation 'org.xerial.snappy:snappy-java'
implementation 'tech.pegasys:jc-kzg-4844'
implementation 'org.rocksdb:rocksdbjni'

runtimeOnly 'org.apache.logging.log4j:log4j-jul'
runtimeOnly 'com.splunk.logging:splunk-library-javalogging'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.cli.subcommands.storage;

import static org.hyperledger.besu.controller.BesuController.DATABASE_PATH;

import org.hyperledger.besu.cli.util.VersionProvider;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import org.rocksdb.ColumnFamilyDescriptor;
import org.rocksdb.ColumnFamilyHandle;
import org.rocksdb.Options;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.ParentCommand;

/** The RocksDB subcommand. */
@Command(
name = "rocksdb",
description = "Print RocksDB information",
mixinStandardHelpOptions = true,
versionProvider = VersionProvider.class,
subcommands = {RocksDbSubCommand.RocksDbUsage.class})
public class RocksDbSubCommand implements Runnable {

@SuppressWarnings("unused")
@ParentCommand
private StorageSubCommand parentCommand;

@SuppressWarnings("unused")
@CommandLine.Spec
private CommandLine.Model.CommandSpec spec;

@Override
public void run() {
spec.commandLine().usage(System.out);
}

@Command(
name = "usage",
description = "Print disk usage",
mixinStandardHelpOptions = true,
versionProvider = VersionProvider.class)
static class RocksDbUsage implements Runnable {

@SuppressWarnings("unused")
@CommandLine.Spec
private CommandLine.Model.CommandSpec spec;

@SuppressWarnings("unused")
@ParentCommand
private RocksDbSubCommand parentCommand;

@Override
public void run() {

final PrintWriter out = spec.commandLine().getOut();

final String dbPath =
parentCommand
.parentCommand
.parentCommand
.dataDir()
.toString()
.concat("/")
.concat(DATABASE_PATH);

RocksDB.loadLibrary();
Options options = new Options();
options.setCreateIfMissing(true);

// Open the RocksDB database with multiple column families
List<byte[]> cfNames;
try {
cfNames = RocksDB.listColumnFamilies(options, dbPath);
} catch (RocksDBException e) {
throw new RuntimeException(e);
}
final List<ColumnFamilyHandle> cfHandles = new ArrayList<>();
final List<ColumnFamilyDescriptor> cfDescriptors = new ArrayList<>();
for (byte[] cfName : cfNames) {
cfDescriptors.add(new ColumnFamilyDescriptor(cfName));
}
RocksDbUsageHelper.printTableHeader(out);
try (final RocksDB rocksdb = RocksDB.openReadOnly(dbPath, cfDescriptors, cfHandles)) {
for (ColumnFamilyHandle cfHandle : cfHandles) {
RocksDbUsageHelper.printUsageForColumnFamily(rocksdb, cfHandle, out);
}
} catch (RocksDBException e) {
throw new RuntimeException(e);
} finally {
for (ColumnFamilyHandle cfHandle : cfHandles) {
cfHandle.close();
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright contributors to Hyperledger 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.cli.subcommands.storage;

import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;

import java.io.PrintWriter;

import org.bouncycastle.util.Arrays;
import org.rocksdb.ColumnFamilyHandle;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;

/** RocksDB Usage subcommand helper methods for formatting and printing. */
public class RocksDbUsageHelper {

static void printUsageForColumnFamily(
final RocksDB rocksdb, final ColumnFamilyHandle cfHandle, final PrintWriter out)
throws RocksDBException {
final String size = rocksdb.getProperty(cfHandle, "rocksdb.estimate-live-data-size");
boolean emptyColumnFamily = false;
if (!size.isEmpty() && !size.isBlank()) {
final long sizeLong = Long.parseLong(size);
if (sizeLong == 0) {
emptyColumnFamily = true;
}
String totolSstFilesSize = rocksdb.getProperty(cfHandle, "rocksdb.total-sst-files-size");
if (!emptyColumnFamily) {
printLine(
out,
getNameById(cfHandle.getName()),
rocksdb.getProperty(cfHandle, "rocksdb.estimate-num-keys"),
formatOutputSize(sizeLong),
formatOutputSize(
!totolSstFilesSize.isEmpty() && !totolSstFilesSize.isBlank()
? Long.parseLong(rocksdb.getProperty(cfHandle, "rocksdb.total-sst-files-size"))
: 0));
}
}
}

private static String formatOutputSize(final long size) {
if (size > (1024 * 1024 * 1024)) {
long sizeInGiB = size / (1024 * 1024 * 1024);
return sizeInGiB + " GiB";
} else if (size > (1024 * 1024)) {
long sizeInMiB = size / (1024 * 1024);
return sizeInMiB + " MiB";
} else if (size > 1024) {
long sizeInKiB = size / 1024;
return sizeInKiB + " KiB";
} else {
return size + " B";
}
}

private static String getNameById(final byte[] id) {
for (KeyValueSegmentIdentifier segment : KeyValueSegmentIdentifier.values()) {
if (Arrays.areEqual(segment.getId(), id)) {
return segment.getName();
}
}
return null; // id not found
}

static void printTableHeader(final PrintWriter out) {
out.format(
"| Column Family | Keys | Column Size | SST Files Size |\n");
out.format(
"|--------------------------------|-----------------|--------------|-----------------|\n");
}

static void printLine(
final PrintWriter out,
final String cfName,
final String keys,
final String columnSize,
final String sstFilesSize) {
final String format = "| %-30s | %-15s | %-12s | %-15s |\n";
out.format(format, cfName, keys, columnSize, sstFilesSize);
}
}
Comment on lines +89 to +105
Copy link
Copy Markdown
Contributor

@garyschulte garyschulte Dec 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using https://github.com/vdmeer/asciitable this could be replaced by:

AsciiTable at = new AsciiTable();
at.addRow("Column Family", "Keys", "Column Size", "SST FIles Size")
at.addRule()

per cf: at.addRow(cfName, keys, columnSize, sstFilesSize);

and finally out.print(at.render());

non-blocking feedback. Using this lib might make the output a bit more flexible for different sized terminals and such

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spiked on this and didn't like the result tbh. It is sort of terminal-friendly but it did break the first column in my embedded terminal on IntelliJ. There's some customization we could use like defining the width of columns etc, but I think this might be an overkill. I'd rather have this MD formatted at the cost of having 3 output.format().

Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@
description = "This command provides storage related actions.",
mixinStandardHelpOptions = true,
versionProvider = VersionProvider.class,
subcommands = {StorageSubCommand.RevertVariablesStorage.class})
subcommands = {StorageSubCommand.RevertVariablesStorage.class, RocksDbSubCommand.class})
public class StorageSubCommand implements Runnable {

/** The constant COMMAND_NAME. */
public static final String COMMAND_NAME = "storage";

@SuppressWarnings("unused")
@ParentCommand
private BesuCommand parentCommand;
BesuCommand parentCommand;

@SuppressWarnings("unused")
@Spec
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public static KeyPair loadKeyPair(final File keyFile) {
final KeyPair key;
if (keyFile.exists()) {

LOG.info("Attempting to load public key from {}", keyFile.getAbsolutePath());
key = load(keyFile);
LOG.info(
"Loaded public key {} from {}", key.getPublicKey().toString(), keyFile.getAbsolutePath());
Expand Down