diff --git a/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/CopyTreeVisitorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/CopyTreeVisitorTests.cs index 5c7242e0032..5acae528fe8 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/CopyTreeVisitorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/CopyTreeVisitorTests.cs @@ -1,10 +1,14 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using FluentAssertions; using Nethermind.Blockchain.FullPruning; +using Nethermind.Core; using Nethermind.Core.Extensions; +using Nethermind.Core.Test; using Nethermind.Core.Test.Builders; using Nethermind.Db; using Nethermind.Db.FullPruning; @@ -27,8 +31,8 @@ public class CopyTreeVisitorTests [Timeout(Timeout.MaxTestTime)] public void copies_state_between_dbs(int fullPruningMemoryBudgetMb, int maxDegreeOfParallelism) { - MemDb trieDb = new(); - MemDb clonedDb = new(); + TestMemDb trieDb = new(); + TestMemDb clonedDb = new(); VisitingOptions visitingOptions = new() { @@ -36,11 +40,19 @@ public void copies_state_between_dbs(int fullPruningMemoryBudgetMb, int maxDegre FullScanMemoryBudget = fullPruningMemoryBudgetMb.MiB(), }; - CopyDb(StartPruning(trieDb, clonedDb), trieDb, clonedDb, visitingOptions); + IPruningContext ctx = StartPruning(trieDb, clonedDb); + CopyDb(ctx, trieDb, clonedDb, visitingOptions); + + List keys = trieDb.Keys.ToList(); + List values = trieDb.Values.ToList(); + + ctx.Commit(); clonedDb.Count.Should().Be(132); - clonedDb.Keys.Should().BeEquivalentTo(trieDb.Keys); - clonedDb.Values.Should().BeEquivalentTo(trieDb.Values); + clonedDb.Keys.Should().BeEquivalentTo(keys); + clonedDb.Values.Should().BeEquivalentTo(values); + + clonedDb.KeyWasWrittenWithFlags(keys[0], WriteFlags.LowPriority); } [Test, Timeout(Timeout.MaxTestTime)] diff --git a/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/FullPrunerTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/FullPrunerTests.cs index 19efcc75f2f..16617612dd6 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/FullPrunerTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/FullPrunerTests.cs @@ -285,6 +285,16 @@ public byte[]? this[ReadOnlySpan key] set => _context[key] = value; } + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + _context.Set(key, value, flags); + } + + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + return _context.Get(key, flags); + } + public void Commit() { WaitForFinish.Set(); diff --git a/src/Nethermind/Nethermind.Blockchain/FullPruning/CopyTreeVisitor.cs b/src/Nethermind/Nethermind.Blockchain/FullPruning/CopyTreeVisitor.cs index f1402626770..1c0207493bb 100644 --- a/src/Nethermind/Nethermind.Blockchain/FullPruning/CopyTreeVisitor.cs +++ b/src/Nethermind/Nethermind.Blockchain/FullPruning/CopyTreeVisitor.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.Threading; +using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Db.FullPruning; using Nethermind.Logging; @@ -70,7 +71,7 @@ private void PersistNode(TrieNode node) if (node.Keccak is not null) { // simple copy of nodes RLP - _pruningContext[node.Keccak.Bytes] = node.FullRlp; + _pruningContext.Set(node.Keccak.Bytes, node.FullRlp, WriteFlags.LowPriority); Interlocked.Increment(ref _persistedNodes); // log message every 1 mln nodes diff --git a/src/Nethermind/Nethermind.Core.Test/InMemoryBatch.cs b/src/Nethermind/Nethermind.Core.Test/InMemoryBatch.cs index acfc1bb28c2..e23299ad3e0 100644 --- a/src/Nethermind/Nethermind.Core.Test/InMemoryBatch.cs +++ b/src/Nethermind/Nethermind.Core.Test/InMemoryBatch.cs @@ -11,6 +11,7 @@ public class InMemoryBatch : IBatch { private readonly IKeyValueStore _store; private readonly ConcurrentDictionary _currentItems = new(); + private WriteFlags _writeFlags = WriteFlags.None; public InMemoryBatch(IKeyValueStore storeWithNoBatchSupport) { @@ -21,19 +22,21 @@ public void Dispose() { foreach (KeyValuePair keyValuePair in _currentItems) { - _store[keyValuePair.Key] = keyValuePair.Value; + _store.Set(keyValuePair.Key, keyValuePair.Value, _writeFlags); } GC.SuppressFinalize(this); } - public byte[]? this[ReadOnlySpan key] + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) { - get => _store[key]; - set - { - _currentItems[key.ToArray()] = value; - } + _currentItems[key.ToArray()] = value; + _writeFlags = flags; + } + + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + return _store.Get(key, flags); } } } diff --git a/src/Nethermind/Nethermind.Core.Test/TestMemDb.cs b/src/Nethermind/Nethermind.Core.Test/TestMemDb.cs index e147e01094c..91b62a80c2b 100644 --- a/src/Nethermind/Nethermind.Core.Test/TestMemDb.cs +++ b/src/Nethermind/Nethermind.Core.Test/TestMemDb.cs @@ -16,25 +16,12 @@ namespace Nethermind.Core.Test; public class TestMemDb : MemDb { private List<(byte[], ReadFlags)> _readKeys = new(); - private List _writeKeys = new(); + private List<(byte[], WriteFlags)> _writeKeys = new(); private List _removedKeys = new(); public Func? ReadFunc { get; set; } public Action? RemoveFunc { get; set; } - public override byte[]? this[ReadOnlySpan key] - { - get - { - return Get(key); - } - set - { - _writeKeys.Add(key.ToArray()); - base[key] = value; - } - } - public override byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) { _readKeys.Add((key.ToArray(), flags)); @@ -43,9 +30,15 @@ public override byte[]? this[ReadOnlySpan key] return base.Get(key, flags); } + public override void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + _writeKeys.Add((key.ToArray(), flags)); + base.Set(key, value, flags); + } + public override Span GetSpan(ReadOnlySpan key) { - return this[key]; + return Get(key); } public override void Remove(ReadOnlySpan key) @@ -72,7 +65,12 @@ public void KeyWasReadWithFlags(byte[] key, ReadFlags flags, int times = 1) public void KeyWasWritten(byte[] key, int times = 1) { - _writeKeys.Count(it => Bytes.AreEqual(it, key)).Should().Be(times); + _writeKeys.Count(it => Bytes.AreEqual(it.Item1, key)).Should().Be(times); + } + + public void KeyWasWrittenWithFlags(byte[] key, WriteFlags flags, int times = 1) + { + _writeKeys.Count(it => Bytes.AreEqual(it.Item1, key) && it.Item2 == flags).Should().Be(times); } public void KeyWasRemoved(Func cond, int times = 1) diff --git a/src/Nethermind/Nethermind.Core/FakeBatch.cs b/src/Nethermind/Nethermind.Core/FakeBatch.cs index b3d80a1c73c..5c8a7024c4d 100644 --- a/src/Nethermind/Nethermind.Core/FakeBatch.cs +++ b/src/Nethermind/Nethermind.Core/FakeBatch.cs @@ -27,10 +27,14 @@ public void Dispose() _onDispose?.Invoke(); } - public byte[]? this[ReadOnlySpan key] + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) { - get => _storePretendingToSupportBatches[key]; - set => _storePretendingToSupportBatches[key] = value; + return _storePretendingToSupportBatches.Get(key, flags); + } + + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + _storePretendingToSupportBatches.Set(key, value, flags); } } } diff --git a/src/Nethermind/Nethermind.Core/IKeyValueStore.cs b/src/Nethermind/Nethermind.Core/IKeyValueStore.cs index 30477652177..41e16f0b22a 100644 --- a/src/Nethermind/Nethermind.Core/IKeyValueStore.cs +++ b/src/Nethermind/Nethermind.Core/IKeyValueStore.cs @@ -7,17 +7,20 @@ namespace Nethermind.Core { public interface IKeyValueStore : IReadOnlyKeyValueStore { - new byte[]? this[ReadOnlySpan key] { get; set; } + new byte[]? this[ReadOnlySpan key] + { + get => Get(key, ReadFlags.None); + set => Set(key, value, WriteFlags.None); + } + + void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None); } public interface IReadOnlyKeyValueStore { - byte[]? this[ReadOnlySpan key] { get; } + byte[]? this[ReadOnlySpan key] => Get(key, ReadFlags.None); - byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) - { - return this[key]; - } + byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None); } public enum ReadFlags @@ -28,4 +31,12 @@ public enum ReadFlags // to reduce CPU usage HintCacheMiss, } + + public enum WriteFlags + { + None, + + // Hint that this is a low priority write + LowPriority, + } } diff --git a/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs b/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs index 9f430a08745..6b3c2040bc0 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/ColumnDb.cs @@ -27,24 +27,22 @@ public void Dispose() { } public string Name { get; } - public byte[]? this[ReadOnlySpan key] + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) { - get + UpdateReadMetrics(); + return _rocksDb.Get(key, _columnFamily); + } + + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + UpdateWriteMetrics(); + if (value is null) { - UpdateReadMetrics(); - return _rocksDb.Get(key, _columnFamily); + _rocksDb.Remove(key, _columnFamily, _mainDb.WriteFlagsToWriteOptions(flags)); } - set + else { - UpdateWriteMetrics(); - if (value is null) - { - _rocksDb.Remove(key, _columnFamily, _mainDb.WriteOptions); - } - else - { - _rocksDb.Put(key, value, _columnFamily, _mainDb.WriteOptions); - } + _rocksDb.Put(key, value, _columnFamily, _mainDb.WriteFlagsToWriteOptions(flags)); } } @@ -84,19 +82,20 @@ public void Dispose() _underlyingBatch.Dispose(); } - public byte[]? this[ReadOnlySpan key] + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + return _underlyingBatch.Get(key, flags); + } + + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) { - get => _underlyingBatch[key]; - set + if (value is null) + { + _underlyingBatch._rocksBatch.Delete(key, _columnDb._columnFamily); + } + else { - if (value is null) - { - _underlyingBatch._rocksBatch.Delete(key, _columnDb._columnFamily); - } - else - { - _underlyingBatch._rocksBatch.Put(key, value, _columnDb._columnFamily); - } + _underlyingBatch._rocksBatch.Put(key, value, _columnDb._columnFamily); } } } diff --git a/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs b/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs index bf111842f57..a20dfc054c5 100644 --- a/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs +++ b/src/Nethermind/Nethermind.Db.Rocks/DbOnTheRocks.cs @@ -36,6 +36,7 @@ public class DbOnTheRocks : IDbWithSpan, ITunableDb private IntPtr? _rateLimiter; internal WriteOptions? WriteOptions { get; private set; } + internal WriteOptions? LowPriorityWriteOptions { get; private set; } internal DbOptions? DbOptions { get; private set; } @@ -306,6 +307,10 @@ protected virtual void BuildOptions(PerTableDbConfig dbConfig, Options opt WriteOptions.SetSync(dbConfig .WriteAheadLogSync); // potential fix for corruption on hard process termination, may cause performance degradation + LowPriorityWriteOptions = new WriteOptions(); + LowPriorityWriteOptions.SetSync(dbConfig.WriteAheadLogSync); + RocksDbSharp.Native.Instance.rocksdb_writeoptions_set_low_pri(LowPriorityWriteOptions.Handle, true); + if (dbConfig.EnableDbStatistics) { options.EnableStatistics(); @@ -315,53 +320,68 @@ protected virtual void BuildOptions(PerTableDbConfig dbConfig, Options opt public byte[]? this[ReadOnlySpan key] { - get + get => Get(key, ReadFlags.None); + set => Set(key, value, WriteFlags.None); + } + + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + if (_isDisposing) { - if (_isDisposing) - { - throw new ObjectDisposedException($"Attempted to read form a disposed database {Name}"); - } + throw new ObjectDisposedException($"Attempted to read form a disposed database {Name}"); + } - UpdateReadMetrics(); + UpdateReadMetrics(); - try - { - return _db.Get(key); - } - catch (RocksDbSharpException e) - { - CreateMarkerIfCorrupt(e); - throw; - } + try + { + return _db.Get(key); } - set + catch (RocksDbSharpException e) { - if (_isDisposing) - { - throw new ObjectDisposedException($"Attempted to write to a disposed database {Name}"); - } + CreateMarkerIfCorrupt(e); + throw; + } + } - UpdateWriteMetrics(); + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + if (_isDisposing) + { + throw new ObjectDisposedException($"Attempted to write to a disposed database {Name}"); + } - try + UpdateWriteMetrics(); + + try + { + if (value is null) { - if (value is null) - { - _db.Remove(key, null, WriteOptions); - } - else - { - _db.Put(key, value, null, WriteOptions); - } + _db.Remove(key, null, WriteFlagsToWriteOptions(flags)); } - catch (RocksDbSharpException e) + else { - CreateMarkerIfCorrupt(e); - throw; + _db.Put(key, value, null, WriteFlagsToWriteOptions(flags)); } } + catch (RocksDbSharpException e) + { + CreateMarkerIfCorrupt(e); + throw; + } + } + + public WriteOptions? WriteFlagsToWriteOptions(WriteFlags flags) + { + if (flags == WriteFlags.LowPriority) + { + return LowPriorityWriteOptions; + } + + return WriteOptions; } + public KeyValuePair[] this[byte[][] keys] { get @@ -594,6 +614,7 @@ public IBatch StartBatch() internal class RocksDbBatch : IBatch { private readonly DbOnTheRocks _dbOnTheRocks; + private WriteFlags _writeFlags = WriteFlags.None; private bool _isDisposed; internal readonly WriteBatch _rocksBatch; @@ -625,7 +646,7 @@ public void Dispose() try { - _dbOnTheRocks._db.Write(_rocksBatch, _dbOnTheRocks.WriteOptions); + _dbOnTheRocks._db.Write(_rocksBatch, _dbOnTheRocks.WriteFlagsToWriteOptions(_writeFlags)); _dbOnTheRocks._currentBatches.TryRemove(this); _rocksBatch.Dispose(); } @@ -636,29 +657,29 @@ public void Dispose() } } - public byte[]? this[ReadOnlySpan key] + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + // Not checking _isDisposing here as for some reason, sometimes is is read after dispose + return _dbOnTheRocks.Get(key, flags); + } + + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) { - get + if (_isDisposed) { - // Not checking _isDisposing here as for some reason, sometimes is is read after dispose - return _dbOnTheRocks[key]; + throw new ObjectDisposedException($"Attempted to write a disposed batch {_dbOnTheRocks.Name}"); } - set - { - if (_isDisposed) - { - throw new ObjectDisposedException($"Attempted to write a disposed batch {_dbOnTheRocks.Name}"); - } - if (value is null) - { - _rocksBatch.Delete(key); - } - else - { - _rocksBatch.Put(key, value); - } + if (value is null) + { + _rocksBatch.Delete(key); + } + else + { + _rocksBatch.Put(key, value); } + + _writeFlags = flags; } } diff --git a/src/Nethermind/Nethermind.Db.Rpc/RpcDb.cs b/src/Nethermind/Nethermind.Db.Rpc/RpcDb.cs index ee0eb051c5a..d8dd6d38cea 100644 --- a/src/Nethermind/Nethermind.Db.Rpc/RpcDb.cs +++ b/src/Nethermind/Nethermind.Db.Rpc/RpcDb.cs @@ -40,8 +40,18 @@ public void Dispose() public byte[] this[ReadOnlySpan key] { - get => GetThroughRpc(key); - set => throw new InvalidOperationException("RPC DB does not support writes"); + get => Get(key); + set => Set(key, value); + } + + public void Set(ReadOnlySpan key, byte[] value, WriteFlags flags = WriteFlags.None) + { + throw new InvalidOperationException("RPC DB does not support writes"); + } + + public byte[] Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + return GetThroughRpc(key); } public KeyValuePair[] this[byte[][] keys] => keys.Select(k => new KeyValuePair(k, GetThroughRpc(k))).ToArray(); diff --git a/src/Nethermind/Nethermind.Db.Test/DbOnTheRocksTests.cs b/src/Nethermind/Nethermind.Db.Test/DbOnTheRocksTests.cs index a3d8159c606..178ae4b8c72 100644 --- a/src/Nethermind/Nethermind.Db.Test/DbOnTheRocksTests.cs +++ b/src/Nethermind/Nethermind.Db.Test/DbOnTheRocksTests.cs @@ -32,6 +32,12 @@ public void Smoke_test() DbOnTheRocks db = new("blocks", GetRocksDbSettings("blocks", "Blocks"), config, LimboLogs.Instance); db[new byte[] { 1, 2, 3 }] = new byte[] { 4, 5, 6 }; Assert.AreEqual(new byte[] { 4, 5, 6 }, db[new byte[] { 1, 2, 3 }]); + + WriteOptions? options = db.WriteFlagsToWriteOptions(WriteFlags.LowPriority); + RocksDbSharp.Native.Instance.rocksdb_writeoptions_get_low_pri(options.Handle).Should().BeTrue(); + + db.Set(new byte[] { 2, 3, 4 }, new byte[] { 5, 6, 7 }, WriteFlags.LowPriority); + Assert.AreEqual(new byte[] { 5, 6, 7 }, db[new byte[] { 2, 3, 4 }]); } [Test] diff --git a/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs b/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs index 96b33d07162..eba0d5744f7 100755 --- a/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs +++ b/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs @@ -46,25 +46,8 @@ public FullPruningDb(RocksDbSettings settings, IRocksDbFactory dbFactory, Action public byte[]? this[ReadOnlySpan key] { - get - { - byte[]? value = _currentDb[key]; // we are reading from the main DB - if (_pruningContext?.DuplicateReads == true) - { - Duplicate(_pruningContext.CloningDb, key, value); - } - - return value; - } - set - { - _currentDb[key] = value; // we are writing to the main DB - IDb? cloningDb = _pruningContext?.CloningDb; - if (cloningDb is not null) // if pruning is in progress we are also writing to the secondary, copied DB - { - Duplicate(cloningDb, key, value); - } - } + get => Get(key, ReadFlags.None); + set => Set(key, value, WriteFlags.None); } public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) @@ -72,15 +55,25 @@ public byte[]? this[ReadOnlySpan key] byte[]? value = _currentDb.Get(key, flags); // we are reading from the main DB if (_pruningContext?.DuplicateReads == true) { - Duplicate(_pruningContext.CloningDb, key, value); + Duplicate(_pruningContext.CloningDb, key, value, WriteFlags.None); } return value; } - private void Duplicate(IKeyValueStore db, ReadOnlySpan key, byte[]? value) + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) { - db[key] = value; + _currentDb.Set(key, value, flags); // we are writing to the main DB + IDb? cloningDb = _pruningContext?.CloningDb; + if (cloningDb is not null) // if pruning is in progress we are also writing to the secondary, copied DB + { + Duplicate(cloningDb, key, value, flags); + } + } + + private void Duplicate(IKeyValueStore db, ReadOnlySpan key, byte[]? value, WriteFlags flags) + { + db.Set(key, value, flags); _updateDuplicateWriteMetrics?.Invoke(); } @@ -208,30 +201,30 @@ public PruningContext(FullPruningDb db, IDb cloningDb, bool duplicateReads) _db = db; } - /// - public byte[]? this[ReadOnlySpan key] + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) { - get => CloningDb[key]; - set + if (!_batches.TryDequeue(out IBatch currentBatch)) { - if (!_batches.TryDequeue(out IBatch currentBatch)) - { - currentBatch = CloningDb.StartBatch(); - } + currentBatch = CloningDb.StartBatch(); + } - _db.Duplicate(currentBatch, key, value); - long val = Interlocked.Increment(ref _counter); - if (val % 10000 == 0) - { - currentBatch.Dispose(); - } - else - { - _batches.Enqueue(currentBatch); - } + _db.Duplicate(currentBatch, key, value, flags); + long val = Interlocked.Increment(ref _counter); + if (val % 10000 == 0) + { + currentBatch.Dispose(); + } + else + { + _batches.Enqueue(currentBatch); } } + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + return CloningDb.Get(key, flags); + } + /// public void Commit() { @@ -296,14 +289,15 @@ public void Dispose() _clonedBatch.Dispose(); } - public byte[]? this[ReadOnlySpan key] + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) { - get => _batch[key]; - set - { - _batch[key] = value; - _db.Duplicate(_clonedBatch, key, value); - } + return _batch.Get(key, flags); + } + + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + _batch.Set(key, value, flags); + _db.Duplicate(_clonedBatch, key, value, flags); } } diff --git a/src/Nethermind/Nethermind.Db/MemDb.cs b/src/Nethermind/Nethermind.Db/MemDb.cs index c10f145b0b3..a7690ebe6a8 100644 --- a/src/Nethermind/Nethermind.Db/MemDb.cs +++ b/src/Nethermind/Nethermind.Db/MemDb.cs @@ -48,13 +48,7 @@ public virtual byte[]? this[ReadOnlySpan key] } set { - if (_writeDelay > 0) - { - Thread.Sleep(_writeDelay); - } - - WritesCount++; - _db[key] = value; + Set(key, value); } } @@ -113,12 +107,12 @@ public void Dispose() public virtual Span GetSpan(ReadOnlySpan key) { - return this[key].AsSpan(); + return Get(key).AsSpan(); } public void PutSpan(ReadOnlySpan key, ReadOnlySpan value) { - this[key] = value.ToArray(); + Set(key, value.ToArray()); } public void DangerousReleaseMemory(in Span span) @@ -135,5 +129,16 @@ public void DangerousReleaseMemory(in Span span) ReadsCount++; return _db.TryGetValue(key, out byte[] value) ? value : null; } + + public virtual void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + if (_writeDelay > 0) + { + Thread.Sleep(_writeDelay); + } + + WritesCount++; + _db[key] = value; + } } } diff --git a/src/Nethermind/Nethermind.Db/NullDb.cs b/src/Nethermind/Nethermind.Db/NullDb.cs index 9f3a96a0e63..09af0e916c7 100644 --- a/src/Nethermind/Nethermind.Db/NullDb.cs +++ b/src/Nethermind/Nethermind.Db/NullDb.cs @@ -21,10 +21,14 @@ private NullDb() public string Name { get; } = "NullDb"; - public byte[]? this[ReadOnlySpan key] + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) { - get => null; - set => throw new NotSupportedException(); + return null; + } + + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + throw new NotSupportedException(); } public KeyValuePair[] this[byte[][] keys] => keys.Select(k => new KeyValuePair(k, null)).ToArray(); diff --git a/src/Nethermind/Nethermind.Db/ReadOnlyDb.cs b/src/Nethermind/Nethermind.Db/ReadOnlyDb.cs index c3776e1f861..eeaf198bc5c 100644 --- a/src/Nethermind/Nethermind.Db/ReadOnlyDb.cs +++ b/src/Nethermind/Nethermind.Db/ReadOnlyDb.cs @@ -27,18 +27,19 @@ public void Dispose() public string Name { get; } = "ReadOnlyDb"; - public byte[]? this[ReadOnlySpan key] + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) { - get => _memDb[key] ?? _wrappedDb[key]; - set - { - if (!_createInMemWriteStore) - { - throw new InvalidOperationException($"This {nameof(ReadOnlyDb)} did not expect any writes."); - } + return _memDb.Get(key, flags) ?? _wrappedDb.Get(key, flags); + } - _memDb[key] = value; + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + if (!_createInMemWriteStore) + { + throw new InvalidOperationException($"This {nameof(ReadOnlyDb)} did not expect any writes."); } + + _memDb.Set(key, value, flags); } public KeyValuePair[] this[byte[][] keys] @@ -89,7 +90,7 @@ public virtual void ClearTempChanges() _memDb.Clear(); } - public Span GetSpan(ReadOnlySpan key) => this[key].AsSpan(); + public Span GetSpan(ReadOnlySpan key) => _memDb.Get(key).AsSpan(); public void PutSpan(ReadOnlySpan keyBytes, ReadOnlySpan value) { if (!_createInMemWriteStore) @@ -97,7 +98,7 @@ public void PutSpan(ReadOnlySpan keyBytes, ReadOnlySpan value) throw new InvalidOperationException($"This {nameof(ReadOnlyDb)} did not expect any writes."); } - _memDb[keyBytes] = value.ToArray(); + _memDb.Set(keyBytes, value.ToArray()); } public void DangerousReleaseMemory(in Span span) { } diff --git a/src/Nethermind/Nethermind.Db/SimpleFilePublicKeyDb.cs b/src/Nethermind/Nethermind.Db/SimpleFilePublicKeyDb.cs index 7cc1fc2e888..6cdac73d27c 100644 --- a/src/Nethermind/Nethermind.Db/SimpleFilePublicKeyDb.cs +++ b/src/Nethermind/Nethermind.Db/SimpleFilePublicKeyDb.cs @@ -50,19 +50,26 @@ public SimpleFilePublicKeyDb(string name, string dbDirectoryPath, ILogManager lo LoadData(); } - public byte[] this[ReadOnlySpan key] + public byte[]? this[ReadOnlySpan key] { - get => _cache[key]; - set + get => Get(key, ReadFlags.None); + set => Set(key, value, WriteFlags.None); + } + + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + return _cache[key]; + } + + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + if (value is null) { - if (value is null) - { - _cache.TryRemove(key, out _); - } - else - { - _cache.AddOrUpdate(key.ToArray(), newValue => Add(value), (x, oldValue) => Update(oldValue, value)); - } + _cache.TryRemove(key, out _); + } + else + { + _cache.AddOrUpdate(key.ToArray(), newValue => Add(value), (x, oldValue) => Update(oldValue, value)); } } diff --git a/src/Nethermind/Nethermind.State/Witnesses/WitnessingStore.cs b/src/Nethermind/Nethermind.State/Witnesses/WitnessingStore.cs index aae5555addb..1f1fe1f24c2 100644 --- a/src/Nethermind/Nethermind.State/Witnesses/WitnessingStore.cs +++ b/src/Nethermind/Nethermind.State/Witnesses/WitnessingStore.cs @@ -32,17 +32,24 @@ public WitnessingStore(IKeyValueStoreWithBatching? wrapped, IWitnessCollector? w public byte[]? this[ReadOnlySpan key] { - get - { - if (key.Length != 32) - { - throw new NotSupportedException($"{nameof(WitnessingStore)} requires 32 bytes long keys."); - } + get => Get(key); + set => Set(key, value); + } - Touch(key); - return _wrapped[key]; + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + if (key.Length != 32) + { + throw new NotSupportedException($"{nameof(WitnessingStore)} requires 32 bytes long keys."); } - set => _wrapped[key] = value; + + Touch(key); + return _wrapped.Get(key, flags); + } + + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + _wrapped.Set(key, value, flags); } public IBatch StartBatch() diff --git a/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs b/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs index aeb493c768d..44812b69a35 100644 --- a/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs +++ b/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs @@ -384,8 +384,18 @@ private class BadDb : IKeyValueStoreWithBatching public byte[]? this[ReadOnlySpan key] { - get => _db[key.ToArray()]; - set => _db[key.ToArray()] = value; + get => Get(key); + set => Set(key, value); + } + + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + _db[key.ToArray()] = value; + } + + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + return _db[key.ToArray()]; } public IBatch StartBatch() @@ -403,8 +413,18 @@ public void Dispose() public byte[]? this[ReadOnlySpan key] { - get => _inBatched[key.ToArray()]; - set => _inBatched[key.ToArray()] = value; + get => Get(key); + set => Set(key, value); + } + + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + _inBatched[key.ToArray()] = value; + } + + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + return _inBatched[key.ToArray()]; } } } diff --git a/src/Nethermind/Nethermind.Trie/CachingStore.cs b/src/Nethermind/Nethermind.Trie/CachingStore.cs index 41cdff90a39..f2c1822c6f6 100644 --- a/src/Nethermind/Nethermind.Trie/CachingStore.cs +++ b/src/Nethermind/Nethermind.Trie/CachingStore.cs @@ -40,8 +40,7 @@ public byte[]? this[ReadOnlySpan key] } set { - _cache.Set(key, value); - _wrappedStore[key] = value; + Set(key, value); } } @@ -66,6 +65,13 @@ public byte[]? this[ReadOnlySpan key] return value; } + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + _cache.Set(key, value); + _wrappedStore.Set(key, value, flags); + } + + public IBatch StartBatch() => _wrappedStore.StartBatch(); public void PersistCache(IKeyValueStore pruningContext) diff --git a/src/Nethermind/Nethermind.Trie/NullKeyValueStore.cs b/src/Nethermind/Nethermind.Trie/NullKeyValueStore.cs index d42dd376005..53eda887128 100644 --- a/src/Nethermind/Nethermind.Trie/NullKeyValueStore.cs +++ b/src/Nethermind/Nethermind.Trie/NullKeyValueStore.cs @@ -16,10 +16,14 @@ private NullKeyValueStore() private static NullKeyValueStore _instance; public static NullKeyValueStore Instance => LazyInitializer.EnsureInitialized(ref _instance, () => new NullKeyValueStore()); - public byte[] this[ReadOnlySpan key] + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) { - get => null; - set => throw new NotSupportedException(); + return null; + } + + public void Set(ReadOnlySpan key, byte[]? value, WriteFlags flags = WriteFlags.None) + { + throw new NotSupportedException(); } } } diff --git a/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs index 70219c5a6ae..44718227e05 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs @@ -44,6 +44,9 @@ public byte[] LoadRlp(Keccak hash, ReadFlags flags = ReadFlags.None) public void Dispose() { } - public byte[]? this[ReadOnlySpan key] => null; + public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) + { + return null; + } } }