diff --git a/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/CopyTreeVisitorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/CopyTreeVisitorTests.cs index 7150f0fbabb..16488ae0393 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/CopyTreeVisitorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/FullPruning/CopyTreeVisitorTests.cs @@ -53,6 +53,7 @@ public void copies_state_between_dbs(int fullPruningMemoryBudgetMb, int maxDegre clonedDb.Values.Should().BeEquivalentTo(values); clonedDb.KeyWasWrittenWithFlags(keys[0], WriteFlags.LowPriority); + trieDb.KeyWasReadWithFlags(keys[0], ReadFlags.SkipDuplicateRead | ReadFlags.HintCacheMiss); } [Test, Timeout(Timeout.MaxTestTime)] diff --git a/src/Nethermind/Nethermind.Blockchain/FullPruning/CopyTreeVisitor.cs b/src/Nethermind/Nethermind.Blockchain/FullPruning/CopyTreeVisitor.cs index ee85beb5d79..cd411837ed0 100644 --- a/src/Nethermind/Nethermind.Blockchain/FullPruning/CopyTreeVisitor.cs +++ b/src/Nethermind/Nethermind.Blockchain/FullPruning/CopyTreeVisitor.cs @@ -43,6 +43,9 @@ public CopyTreeVisitor( } public bool IsFullDbScan => true; + + public ReadFlags ExtraReadFlag => ReadFlags.SkipDuplicateRead; + public bool ShouldVisit(Hash256 nextNode) => !_cancellationToken.IsCancellationRequested; public void VisitTree(Hash256 rootHash, TrieVisitContext trieVisitContext) diff --git a/src/Nethermind/Nethermind.Core/IKeyValueStore.cs b/src/Nethermind/Nethermind.Core/IKeyValueStore.cs index 0e2e34166e1..f205b7cffc1 100644 --- a/src/Nethermind/Nethermind.Core/IKeyValueStore.cs +++ b/src/Nethermind/Nethermind.Core/IKeyValueStore.cs @@ -70,6 +70,9 @@ public enum ReadFlags // Hint that the workload is likely to need the next value in the sequence and should prefetch it. HintReadAhead = 2, + + // Used for full pruning db to skip duplicate read + SkipDuplicateRead = 4, } [Flags] diff --git a/src/Nethermind/Nethermind.Db.Test/FullPruning/FullPruningDbTests.cs b/src/Nethermind/Nethermind.Db.Test/FullPruning/FullPruningDbTests.cs index 3146ea71d89..18da3dabb9b 100644 --- a/src/Nethermind/Nethermind.Db.Test/FullPruning/FullPruningDbTests.cs +++ b/src/Nethermind/Nethermind.Db.Test/FullPruning/FullPruningDbTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using FluentAssertions; +using Nethermind.Core; using Nethermind.Db.FullPruning; using NSubstitute; using NUnit.Framework; @@ -65,6 +66,34 @@ public void during_pruning_writes_to_both_dbs() test.CurrentMirrorDb[key].Should().BeEquivalentTo(value); } + [Test] + public void during_pruning_duplicate_on_read() + { + TestContext test = new(); + byte[] key = { 1, 2 }; + byte[] value = { 5, 6 }; + test.FullPruningDb[key] = value; + + test.FullPruningDb.TryStartPruning(out IPruningContext _); + + test.FullPruningDb.Get(key); + test.CurrentMirrorDb[key].Should().BeEquivalentTo(value); + } + + [Test] + public void during_pruning_dont_duplicate_read_with_skip_duplicate_read() + { + TestContext test = new(); + byte[] key = { 1, 2 }; + byte[] value = { 5, 6 }; + test.FullPruningDb[key] = value; + + test.FullPruningDb.TryStartPruning(out IPruningContext _); + + test.FullPruningDb.Get(key, ReadFlags.SkipDuplicateRead); + test.CurrentMirrorDb[key].Should().BeNull(); + } + [Test] public void increments_metrics_on_write_to_mirrored_db() { diff --git a/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs b/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs index e610a94e49f..a0c666bdcc5 100755 --- a/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs +++ b/src/Nethermind/Nethermind.Db/FullPruning/FullPruningDb.cs @@ -52,7 +52,7 @@ public byte[]? this[ReadOnlySpan key] public byte[]? Get(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) { byte[]? value = _currentDb.Get(key, flags); // we are reading from the main DB - if (_pruningContext?.DuplicateReads == true) + if (value != null && _pruningContext?.DuplicateReads == true && (flags & ReadFlags.SkipDuplicateRead) == 0) { Duplicate(_pruningContext.CloningDb, key, value, WriteFlags.None); } @@ -63,7 +63,7 @@ public byte[]? this[ReadOnlySpan key] public Span GetSpan(ReadOnlySpan key, ReadFlags flags = ReadFlags.None) { Span value = _currentDb.GetSpan(key, flags); // we are reading from the main DB - if (!value.IsNull() && _pruningContext?.DuplicateReads == true) + if (!value.IsNull() && _pruningContext?.DuplicateReads == true && (flags & ReadFlags.SkipDuplicateRead) == 0) { Duplicate(_pruningContext.CloningDb, key, value, WriteFlags.None); } diff --git a/src/Nethermind/Nethermind.Trie/ITreeVisitor.cs b/src/Nethermind/Nethermind.Trie/ITreeVisitor.cs index 6cdd3ecd553..48579ca1804 100644 --- a/src/Nethermind/Nethermind.Trie/ITreeVisitor.cs +++ b/src/Nethermind/Nethermind.Trie/ITreeVisitor.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Core; using Nethermind.Core.Crypto; namespace Nethermind.Trie @@ -12,6 +13,8 @@ public interface ITreeVisitor /// public bool IsFullDbScan { get; } + ReadFlags ExtraReadFlag => ReadFlags.None; + bool ShouldVisit(Hash256 nextNode); void VisitTree(Hash256 rootHash, TrieVisitContext trieVisitContext); diff --git a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs index cdcc6361753..7293c7c3918 100644 --- a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs +++ b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs @@ -1088,10 +1088,16 @@ public void Accept(ITreeVisitor visitor, Hash256 rootHash, VisitingOptions? visi } } - ITrieNodeResolver resolver = TrieStore; + ReadFlags flags = visitor.ExtraReadFlag; if (visitor.IsFullDbScan) { - resolver = new TrieNodeResolverWithReadFlags(TrieStore, ReadFlags.HintCacheMiss); + flags |= ReadFlags.HintCacheMiss; + } + + ITrieNodeResolver resolver = TrieStore; + if (flags != ReadFlags.None) + { + resolver = new TrieNodeResolverWithReadFlags(TrieStore, flags); } visitor.VisitTree(rootHash, trieVisitContext);