Skip to content

Commit

Permalink
Merge branch 'perf/improved-inmemory-pruning' into perf/somepath-hashdb
Browse files Browse the repository at this point in the history
  • Loading branch information
asdacap committed Feb 20, 2024
2 parents a5dd5a0 + 37cf01b commit 0bfd625
Show file tree
Hide file tree
Showing 24 changed files with 453 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public bool ShouldPersist(long blockNumber)
return inPruning;
}

public bool IsFullPruning => _inPruning != 0;

/// <inheritdoc/>
public void Dispose()
{
Expand Down
3 changes: 3 additions & 0 deletions src/Nethermind/Nethermind.Db/IPruningConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,7 @@ public interface IPruningConfig : IConfig

[ConfigItem(Description = "Whether to enables available disk space check.", DefaultValue = "true")]
bool AvailableSpaceCheckEnabled { get; set; }

[ConfigItem(Description = "[TECHNICAL] Number of past persisted keys to keep track off for possible pruning.", DefaultValue = "1000000")]
int TrackedPastKeyCount { get; set; }
}
5 changes: 5 additions & 0 deletions src/Nethermind/Nethermind.Db/MemDb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ public virtual void Set(ReadOnlySpan<byte> key, byte[]? value, WriteFlags flags
}

WritesCount++;
if (value == null)
{
_db.TryRemove(key, out _);
return;
}
_db[key] = value;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Nethermind/Nethermind.Db/PruningConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ public bool Enabled
public int FullPruningMinimumDelayHours { get; set; } = 240;
public FullPruningCompletionBehavior FullPruningCompletionBehavior { get; set; } = FullPruningCompletionBehavior.None;
public bool AvailableSpaceCheckEnabled { get; set; } = true;
public int TrackedPastKeyCount { get; set; } = 1000000;
}
}
3 changes: 2 additions & 1 deletion src/Nethermind/Nethermind.Init/InitializeStateDb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ public Task Execute(CancellationToken cancellationToken)
persistenceStrategy = persistenceStrategy.Or(triggerPersistenceStrategy);
}

pruningStrategy = Prune.WhenCacheReaches(pruningConfig.CacheMb.MB()); // TODO: memory hint should define this
pruningStrategy = Prune.WhenCacheReaches(pruningConfig.CacheMb.MB()) // TODO: memory hint should define this
.TrackingPastKeys(pruningConfig.TrackedPastKeyCount);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ namespace Nethermind.Trie.Test.Pruning
public class TestPruningStrategy : IPruningStrategy
{
private readonly bool _pruningEnabled;
public TestPruningStrategy(bool pruningEnabled, bool shouldPrune = false)
private readonly int _trackedPastKeyCount;

public TestPruningStrategy(bool pruningEnabled, bool shouldPrune = false, int trackedPastKeyCount = 0)
{
_pruningEnabled = pruningEnabled;
ShouldPruneEnabled = shouldPrune;
_trackedPastKeyCount = trackedPastKeyCount;
}

public bool PruningEnabled => _pruningEnabled;
Expand All @@ -27,5 +30,7 @@ public bool ShouldPrune(in long currentMemory)

return false;
}

public int TrackedPastKeyCount => _trackedPastKeyCount;
}
}
104 changes: 102 additions & 2 deletions src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Nethermind.State;
using Nethermind.State.Witnesses;
using Nethermind.Trie.Pruning;
using NSubstitute;
using NUnit.Framework;

namespace Nethermind.Trie.Test.Pruning
Expand All @@ -35,12 +36,22 @@ public TreeStoreTests(INodeStorage.KeyScheme scheme)
_scheme = scheme;
}

public TrieStore CreateTrieStore(IPruningStrategy? pruningStrategy = null, IKeyValueStoreWithBatching? kvStore = null, IPersistenceStrategy? persistenceStrategy = null)
private TrieStore CreateTrieStore(
IPruningStrategy? pruningStrategy = null,
IKeyValueStoreWithBatching? kvStore = null,
IPersistenceStrategy? persistenceStrategy = null,
long? reorgDepthOverride = null
)
{
pruningStrategy ??= No.Pruning;
kvStore ??= new TestMemDb();
persistenceStrategy ??= No.Persistence;
return new(new NodeStorage(kvStore, _scheme, requirePath: _scheme == INodeStorage.KeyScheme.HalfPath), pruningStrategy, persistenceStrategy, _logManager);
return new(
new NodeStorage(kvStore, _scheme, requirePath: _scheme == INodeStorage.KeyScheme.HalfPath),
pruningStrategy,
persistenceStrategy,
_logManager,
reorgDepthOverride: reorgDepthOverride);
}

[SetUp]
Expand Down Expand Up @@ -811,5 +822,94 @@ public void After_commit_should_have_has_root()
stateTree.Commit(0);
trieStore.HasRoot(stateTree.RootHash).Should().BeTrue();
}

[Test]
public async Task Will_RemovePastKeys_OnSnapshot()
{
MemDb memDb = new();

using TrieStore fullTrieStore = CreateTrieStore(
kvStore: memDb,
pruningStrategy: new TestPruningStrategy(true, true, 100000),
persistenceStrategy: No.Persistence,
reorgDepthOverride: 2);

IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null);

for (int i = 0; i < 64; i++)
{
TrieNode node = new(NodeType.Leaf, TestItem.Keccaks[i], new byte[2]);
trieStore.CommitNode(i, new NodeCommitInfo(node, TreePath.Empty));
trieStore.FinishBlockCommit(TrieType.State, i, node);

// Pruning is done in background
await Task.Delay(TimeSpan.FromMilliseconds(10));
}

if (_scheme == INodeStorage.KeyScheme.Hash)
{
memDb.Count.Should().NotBe(1);
}
else
{
memDb.Count.Should().Be(1);
}
}

[Test]
public async Task Will_Not_RemovePastKeys_OnSnapshot_DuringFullPruning()
{
MemDb memDb = new();

IPersistenceStrategy isPruningPersistenceStrategy = Substitute.For<IPersistenceStrategy>();
isPruningPersistenceStrategy.IsFullPruning.Returns(true);

using TrieStore fullTrieStore = CreateTrieStore(
kvStore: memDb,
pruningStrategy: new TestPruningStrategy(true, true, 100000),
persistenceStrategy: isPruningPersistenceStrategy,
reorgDepthOverride: 2);

IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null);

for (int i = 0; i < 64; i++)
{
TrieNode node = new(NodeType.Leaf, TestItem.Keccaks[i], new byte[2]);
trieStore.CommitNode(i, new NodeCommitInfo(node, TreePath.Empty));
trieStore.FinishBlockCommit(TrieType.State, i, node);

// Pruning is done in background
await Task.Delay(TimeSpan.FromMilliseconds(10));
}

memDb.Count.Should().Be(61);
}

[Test]
public async Task Will_NotRemove_ReCommittedNode()
{
MemDb memDb = new();

using TrieStore fullTrieStore = CreateTrieStore(
kvStore: memDb,
pruningStrategy: new TestPruningStrategy(true, true, 100000),
persistenceStrategy: No.Persistence,
reorgDepthOverride: 2);

IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null);

for (int i = 0; i < 64; i++)
{
TrieNode node = new(NodeType.Leaf, TestItem.Keccaks[i % 4], new byte[2]);
trieStore.CommitNode(i, new NodeCommitInfo(node, TreePath.Empty));
node = trieStore.FindCachedOrUnknown(TreePath.Empty, node.Keccak);
trieStore.FinishBlockCommit(TrieType.State, i, node);

// Pruning is done in background
await Task.Delay(TimeSpan.FromMilliseconds(10));
}

memDb.Count.Should().Be(4);
}
}
}
31 changes: 30 additions & 1 deletion src/Nethermind/Nethermind.Trie.Test/PruningScenariosTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public static PruningContext InMemory
public static PruningContext InMemoryAlwaysPrune
{
[DebuggerStepThrough]
get => new(new TestPruningStrategy(true, true), No.Persistence);
get => new(new TestPruningStrategy(true, true, 1000000), No.Persistence);
}

public static PruningContext SetupWithPersistenceEveryEightBlocks
Expand Down Expand Up @@ -746,5 +746,34 @@ public void StateRoot_reset_at_lower_level_and_accessed_at_just_the_right_time()
.VerifyAccountBalance(3, 101);

}

[Test]
public void When_Reorg_OldValueIsNotRemoved()
{
Reorganization.MaxDepth = 2;

PruningContext.InMemoryAlwaysPrune
.SetAccountBalance(1, 100)
.SetAccountBalance(2, 100)
.Commit()

.SetAccountBalance(3, 100)
.SetAccountBalance(4, 100)
.Commit()

.SaveBranchingPoint("revert_main")

.SetAccountBalance(4, 200)
.Commit()

.RestoreBranchingPoint("revert_main")

.Commit()
.Commit()
.Commit()
.Commit()

.VerifyAccountBalance(4, 100);
}
}
}
32 changes: 32 additions & 0 deletions src/Nethermind/Nethermind.Trie.Test/TinyTreePathTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using FluentAssertions;
using Nethermind.Core.Crypto;
using NUnit.Framework;

namespace Nethermind.Trie.Test;

public class TinyTreePathTests
{
[Test]
public void Should_ConvertFromAndToTreePath()
{
TreePath path = new TreePath(new ValueHash256("0123456789abcd00000000000000000000000000000000000000000000000000"), 14);

TinyTreePath tinyPath = new TinyTreePath(path);

tinyPath.ToTreePath().Should().Be(path);
}

[Test]
public void When_PathIsTooLong_Should_Throw()
{
TreePath path = new TreePath(new ValueHash256("0123456789000000000000000000000000000000000000000000000000000000"), 15);

Action act = () => new TinyTreePath(path);
act.Should().Throw<InvalidOperationException>();
}
}

1 change: 1 addition & 0 deletions src/Nethermind/Nethermind.Trie/INodeStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@ public enum KeyScheme
public interface WriteBatch : IDisposable
{
void Set(Hash256? address, in TreePath path, in ValueHash256 currentNodeKeccak, byte[] toArray, WriteFlags writeFlags);
void Remove(Hash256? address, in TreePath path, in ValueHash256 currentNodeKeccak);
}
}
6 changes: 6 additions & 0 deletions src/Nethermind/Nethermind.Trie/NodeStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,5 +220,11 @@ public void Set(Hash256? address, in TreePath path, in ValueHash256 keccak, byte

_writeBatch.Set(_nodeStorage.GetExpectedPath(stackalloc byte[StoragePathLength], address, path, keccak), toArray, writeFlags);
}

public void Remove(Hash256? address, in TreePath path, in ValueHash256 keccak)
{
// Only delete half path key. DO NOT delete hash based key.
_writeBatch.Remove(GetHalfPathNodeStoragePathSpan(stackalloc byte[StoragePathLength], address, path, keccak));
}
}
}
2 changes: 2 additions & 0 deletions src/Nethermind/Nethermind.Trie/Pruning/Archive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ public bool ShouldPersist(long blockNumber)
{
return true;
}

public bool IsFullPruning => false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ public IPersistenceStrategy AddStrategy(IPersistenceStrategy strategy)
}

public bool ShouldPersist(long blockNumber) => _strategies.Any(strategy => strategy.ShouldPersist(blockNumber));
public bool IsFullPruning => _strategies.Any(strategy => strategy.IsFullPruning);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ namespace Nethermind.Trie.Pruning
public interface IPersistenceStrategy
{
bool ShouldPersist(long blockNumber);
bool IsFullPruning { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public interface IPruningStrategy
{
bool PruningEnabled { get; }
bool ShouldPrune(in long currentMemory);
int TrackedPastKeyCount { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ public bool ShouldPersist(long blockNumber)
{
return blockNumber % _snapshotInterval == 0;
}

public bool IsFullPruning => false;
}
}
2 changes: 2 additions & 0 deletions src/Nethermind/Nethermind.Trie/Pruning/MemoryLimit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@ public bool ShouldPrune(in long currentMemory)
{
return PruningEnabled && currentMemory >= _memoryLimit;
}

public int TrackedPastKeyCount => 0;
}
}
2 changes: 2 additions & 0 deletions src/Nethermind/Nethermind.Trie/Pruning/NoPersistence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ public bool ShouldPersist(long blockNumber)
{
return false;
}

public bool IsFullPruning => false;
}
}
2 changes: 2 additions & 0 deletions src/Nethermind/Nethermind.Trie/Pruning/NoPruning.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ public bool ShouldPrune(in long currentMemory)
{
return false;
}

public int TrackedPastKeyCount => 0;
}
}
5 changes: 5 additions & 0 deletions src/Nethermind/Nethermind.Trie/Pruning/Prune.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,10 @@ public static class Prune
{
public static IPruningStrategy WhenCacheReaches(long sizeInBytes)
=> new MemoryLimit(sizeInBytes);

public static IPruningStrategy TrackingPastKeys(this IPruningStrategy baseStrategy, int trackedPastKeyCount)
=> trackedPastKeyCount <= 0
? baseStrategy
: new TrackedPastKeyCountStrategy(baseStrategy, trackedPastKeyCount);
}
}
Loading

0 comments on commit 0bfd625

Please sign in to comment.