Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@
"preimage",
"preimages",
"preinstallation",
"prepopulate",
"prestate",
"prevop",
"prevrandao",
Expand Down
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public void Compact()
public void Clear() { throw new NotSupportedException(); }

// Maybe it should be column specific metric?
public IDbMeta.DbMetric GatherMetric(bool includeSharedCache = false) => _mainDb.GatherMetric(includeSharedCache);
public IDbMeta.DbMetric GatherMetric() => _mainDb.GatherMetric();

public byte[]? FirstKey
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;

namespace Nethermind.Db.Rocks.Config;

public class AdjustedRocksdbConfig(
IRocksDbConfig baseConfig,
string additionalRocksDbOptions,
ulong writeBufferSize
ulong writeBufferSize,
IntPtr? blockCache = null
) : IRocksDbConfig
{
public ulong? WriteBufferSize => writeBufferSize;
Expand Down Expand Up @@ -37,4 +40,5 @@ ulong writeBufferSize
public double CompressibilityHint => baseConfig.CompressibilityHint;

public bool FlushOnExit => baseConfig.FlushOnExit;
public IntPtr? BlockCache => blockCache ?? baseConfig.BlockCache;
}
3 changes: 3 additions & 0 deletions src/Nethermind/Nethermind.Db.Rocks/Config/IRocksDbConfig.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;

namespace Nethermind.Db.Rocks.Config;

public interface IRocksDbConfig
Expand All @@ -19,4 +21,5 @@ public interface IRocksDbConfig
bool EnableFileWarmer { get; }
double CompressibilityHint { get; }
bool FlushOnExit { get; }
IntPtr? BlockCache { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ private void EnsureConfigIsAvailable(string propertyName)
public bool EnableFileWarmer => ReadConfig<bool>(nameof(EnableFileWarmer));
public double CompressibilityHint => ReadConfig<double>(nameof(CompressibilityHint));
public bool FlushOnExit => ReadConfig<bool?>(nameof(FlushOnExit)) ?? true;
public IntPtr? BlockCache => null;

private T? ReadConfig<T>(string propertyName)
{
Expand Down
77 changes: 59 additions & 18 deletions src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public partial class DbOnTheRocks : IDb, ITunableDb, IReadOnlyNativeKeyValueStor
private ulong _writeBufferSize;
private int _maxWriteBufferNumber;
private readonly RocksDbReader _reader;
private bool _isUsingSharedBlockCache;

public DbOnTheRocks(
string basePath,
Expand Down Expand Up @@ -352,7 +353,7 @@ protected virtual long FetchTotalPropertyValue(string propertyName)
return value;
}

public IDbMeta.DbMetric GatherMetric(bool isUsingSharedCache = false)
public IDbMeta.DbMetric GatherMetric()
{
if (_isDisposed)
{
Expand All @@ -369,7 +370,7 @@ public IDbMeta.DbMetric GatherMetric(bool isUsingSharedCache = false)
return new IDbMeta.DbMetric()
{
Size = GetSize(),
CacheSize = GetCacheSize(isUsingSharedCache),
CacheSize = GetCacheSize(),
IndexSize = GetIndexSize(),
MemtableSize = GetMemtableSize(),
TotalReads = _totalReads,
Expand All @@ -394,11 +395,11 @@ private long GetSize()
return 0;
}

private long GetCacheSize(bool isUsingSharedCache = false)
private long GetCacheSize()
{
try
{
if (isUsingSharedCache)
if (_isUsingSharedBlockCache)
{
// returning 0 as we are using shared cache.
return 0;
Expand Down Expand Up @@ -460,6 +461,38 @@ public static IDictionary<string, string> ExtractOptions(string dbOptions)
return asDict;
}

private const string OptimizeFiltersForHitsOption = "optimize_filters_for_hits=";

/// <summary>
/// Normalizes a RocksDB options string by removing earlier occurrences of optimize_filters_for_hits,
/// keeping only the last one. This is needed because RocksDB does not allow overriding this option
/// when specified multiple times in the same options string.
/// </summary>
public static string NormalizeRocksDbOptions(string dbOptions)
{
if (string.IsNullOrEmpty(dbOptions)) return dbOptions ?? string.Empty;

int lastIndex = dbOptions.LastIndexOf(OptimizeFiltersForHitsOption, StringComparison.Ordinal);
if (lastIndex == -1) return dbOptions;

// Remove all earlier occurrences, keep only the last one
int searchStart = 0;
while (true)
{
int index = dbOptions.IndexOf(OptimizeFiltersForHitsOption, searchStart, StringComparison.Ordinal);
if (index == -1 || index == lastIndex) break;

// Find the end of this option (next semicolon)
int endIndex = dbOptions.IndexOf(';', index);
if (endIndex == -1) break;

dbOptions = dbOptions.Remove(index, endIndex - index + 1);
lastIndex = dbOptions.LastIndexOf(OptimizeFiltersForHitsOption, StringComparison.Ordinal);
}

return dbOptions;
}

protected virtual void BuildOptions<T>(IRocksDbConfig dbConfig, Options<T> options, IntPtr? sharedCache, IMergeOperator? mergeOperator) where T : Options<T>
{
// This section is about the table factory.. and block cache apparently.
Expand All @@ -475,27 +508,35 @@ protected virtual void BuildOptions<T>(IRocksDbConfig dbConfig, Options<T> optio
_writeBufferSize = ulong.Parse(optionsAsDict["write_buffer_size"]);
_maxWriteBufferNumber = int.Parse(optionsAsDict["max_write_buffer_number"]);

BlockBasedTableOptions tableOptions = new();
options.SetBlockBasedTableFactory(tableOptions);
IntPtr optsPtr = Marshal.StringToHGlobalAnsi(dbConfig.RocksDbOptions);
try
{
_rocksDbNative.rocksdb_get_options_from_string(options.Handle, optsPtr, options.Handle);
}
finally
{
Marshal.FreeHGlobal(optsPtr);
}

ulong blockCacheSize = 0;
if (optionsAsDict.TryGetValue("block_based_table_factory.block_cache", out string? blockCacheSizeStr))
{
blockCacheSize = ulong.Parse(blockCacheSizeStr);
}

if (sharedCache is not null && blockCacheSize == 0)
BlockBasedTableOptions? tableOptions = new();
if (dbConfig.BlockCache is not null)
{
tableOptions.SetBlockCache(dbConfig.BlockCache.Value);
}
else if (sharedCache is not null && blockCacheSize == 0)
{
tableOptions.SetBlockCache(sharedCache.Value);
_isUsingSharedBlockCache = true;
}

// Note: the ordering is important.
// changes to the table options must be applied before setting to set.
options.SetBlockBasedTableFactory(tableOptions);

IntPtr optsPtr = Marshal.StringToHGlobalAnsi(NormalizeRocksDbOptions(dbConfig.RocksDbOptions));
try
{
_rocksDbNative.rocksdb_get_options_from_string(options.Handle, optsPtr, options.Handle);
}
finally
{
Marshal.FreeHGlobal(optsPtr);
}

if (dbConfig.WriteBufferSize is not null)
Expand Down Expand Up @@ -579,7 +620,7 @@ protected virtual void BuildOptions<T>(IRocksDbConfig dbConfig, Options<T> optio

if (dbConfig.AdditionalRocksDbOptions is not null)
{
optsPtr = Marshal.StringToHGlobalAnsi(dbConfig.AdditionalRocksDbOptions);
optsPtr = Marshal.StringToHGlobalAnsi(NormalizeRocksDbOptions(dbConfig.AdditionalRocksDbOptions));
try
{
_rocksDbNative.rocksdb_get_options_from_string(options.Handle, optsPtr, options.Handle);
Expand Down
30 changes: 30 additions & 0 deletions src/Nethermind/Nethermind.Db.Rocks/HyperClockCacheWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2026 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using Microsoft.Win32.SafeHandles;
using RocksDbSharp;

namespace Nethermind.Db.Rocks;

public class HyperClockCacheWrapper : SafeHandleZeroOrMinusOneIsInvalid
{
public HyperClockCacheWrapper(ulong capacity = 32_000_000) : base(ownsHandle: true)
{
SetHandle(RocksDbSharp.Native.Instance.rocksdb_cache_create_hyper_clock(new UIntPtr(capacity), 0));
}

public IntPtr Handle => DangerousGetHandle();

protected override bool ReleaseHandle()
{
RocksDbSharp.Native.Instance.rocksdb_cache_destroy(handle);
return true;
}

public long GetUsage()
{
ObjectDisposedException.ThrowIf(IsClosed, this);
return (long)Native.Instance.rocksdb_cache_get_usage(DangerousGetHandle());
}
}
20 changes: 8 additions & 12 deletions src/Nethermind/Nethermind.Db.Rocks/RocksDbFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,33 @@ public class RocksDbFactory : IDbFactory

private readonly string _basePath;

private readonly IntPtr _sharedCache;
private readonly HyperClockCacheWrapper _sharedCache;
private readonly IRocksDbConfigFactory _rocksDbConfigFactory;

public RocksDbFactory(IRocksDbConfigFactory rocksDbConfigFactory, IDbConfig dbConfig, IInitConfig initConfig, ILogManager logManager)
: this(rocksDbConfigFactory, dbConfig, logManager, initConfig.BaseDbPath)
public RocksDbFactory(IRocksDbConfigFactory rocksDbConfigFactory, IDbConfig dbConfig, IInitConfig initConfig, HyperClockCacheWrapper sharedCache, ILogManager logManager)
: this(rocksDbConfigFactory, dbConfig, sharedCache, logManager, initConfig.BaseDbPath)
{

}

public RocksDbFactory(IRocksDbConfigFactory rocksDbConfigFactory, IDbConfig dbConfig, ILogManager logManager, string basePath)
public RocksDbFactory(IRocksDbConfigFactory rocksDbConfigFactory, IDbConfig dbConfig, HyperClockCacheWrapper sharedCache, ILogManager logManager, string basePath)
{
_rocksDbConfigFactory = rocksDbConfigFactory;
_dbConfig = dbConfig;
_logManager = logManager;
_basePath = basePath;

ILogger logger = _logManager.GetClassLogger<RocksDbFactory>();
if (logger.IsDebug) logger.Debug($"Shared memory size is {dbConfig.SharedBlockCacheSize}");

if (logger.IsDebug)
{
logger.Debug($"Shared memory size is {dbConfig.SharedBlockCacheSize}");
}

_sharedCache = RocksDbSharp.Native.Instance.rocksdb_cache_create_lru(new UIntPtr(dbConfig.SharedBlockCacheSize));
_sharedCache = sharedCache;
}

public IDb CreateDb(DbSettings dbSettings) =>
new DbOnTheRocks(_basePath, dbSettings, _dbConfig, _rocksDbConfigFactory, _logManager, sharedCache: _sharedCache);
new DbOnTheRocks(_basePath, dbSettings, _dbConfig, _rocksDbConfigFactory, _logManager, sharedCache: _sharedCache.Handle);

public IColumnsDb<T> CreateColumnsDb<T>(DbSettings dbSettings) where T : struct, Enum =>
new ColumnsDb<T>(_basePath, dbSettings, _dbConfig, _rocksDbConfigFactory, _logManager, Array.Empty<T>(), sharedCache: _sharedCache);
new ColumnsDb<T>(_basePath, dbSettings, _dbConfig, _rocksDbConfigFactory, _logManager, Array.Empty<T>(), sharedCache: _sharedCache.Handle);

public string GetFullDbPath(DbSettings dbSettings) => DbOnTheRocks.GetFullDbPath(dbSettings.DbPath, _basePath);
}
Loading