From 4a119df1b2b346c7c07b571f03d8d255edbda41d Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 25 Nov 2025 22:32:18 +0000 Subject: [PATCH 1/5] Devirtualize filter{store | manager} --- .../Filters/FilterManagerTests.cs | 14 ++-- .../Filters/FilterStoreTests.cs | 1 + .../Nethermind.Facade/BlockchainBridge.cs | 4 +- .../Filters/FilterManager.cs | 6 +- .../Nethermind.Facade/Filters/FilterStore.cs | 36 ++++++++- .../Filters/IFilterManager.cs | 15 ---- .../Nethermind.Facade/Filters/IFilterStore.cs | 32 -------- .../Filters/NullFilterManager.cs | 42 ---------- .../Filters/NullFilterStore.cs | 76 ------------------- .../Nethermind.Init/Modules/RpcModules.cs | 4 +- .../Steps/RegisterRpcModules.cs | 2 +- .../Modules/SubscribeModuleTests.cs | 2 +- .../Modules/Subscribe/LogsSubscription.cs | 4 +- .../SubscriptionFactoryExtensions.cs | 10 +-- 14 files changed, 56 insertions(+), 192 deletions(-) delete mode 100644 src/Nethermind/Nethermind.Facade/Filters/IFilterManager.cs delete mode 100644 src/Nethermind/Nethermind.Facade/Filters/IFilterStore.cs delete mode 100644 src/Nethermind/Nethermind.Facade/Filters/NullFilterManager.cs delete mode 100644 src/Nethermind/Nethermind.Facade/Filters/NullFilterStore.cs diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs index 36194e57e04d..1937c15345d6 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using FluentAssertions; using Nethermind.Blockchain.Filters; using Nethermind.Blockchain.Test.Builders; @@ -11,6 +12,7 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Test.Builders; +using Nethermind.Core.Timers; using Nethermind.Facade.Filters; using Nethermind.Logging; using Nethermind.TxPool; @@ -21,7 +23,7 @@ namespace Nethermind.Blockchain.Test.Filters; public class FilterManagerTests { - private IFilterStore _filterStore = null!; + private FilterStore _filterStore = null!; private IBranchProcessor _branchProcessor = null!; private IMainProcessingContext _mainProcessingContext = null!; private ITxPool _txPool = null!; @@ -34,7 +36,7 @@ public class FilterManagerTests public void Setup() { _currentFilterId = 0; - _filterStore = Substitute.For(); + _filterStore = new FilterStore(new TimerFactory(), 20, 10); _branchProcessor = Substitute.For(); _mainProcessingContext = Substitute.For(); _mainProcessingContext.BranchProcessor.Returns(_branchProcessor); @@ -49,11 +51,11 @@ public void TearDown() } [Test, MaxTime(Timeout.MaxTestTime)] - public void removing_filter_removes_data() + public async Task removing_filter_removes_data() { LogsShouldNotBeEmpty(static _ => { }, static _ => { }); _filterManager.GetLogs(0).Should().NotBeEmpty(); - _filterStore.FilterRemoved += Raise.EventWith(new FilterEventArgs(0)); + await Task.Delay(60); _filterManager.GetLogs(0).Should().BeEmpty(); } @@ -324,8 +326,8 @@ private void Assert(IEnumerable> filterBuilders, BlockFilter blockFilter = new(_currentFilterId++); filters.Add(blockFilter); - _filterStore.GetFilters().Returns(filters.OfType().ToArray()); - _filterStore.GetFilters().Returns(filters.OfType().ToArray()); + _filterStore.SaveFilters(filters.OfType()); + _filterStore.SaveFilters(filters.OfType()); _filterManager = new FilterManager(_filterStore, _mainProcessingContext, _txPool, _logManager); _branchProcessor.BlockProcessed += Raise.EventWith(_branchProcessor, new BlockProcessedEventArgs(block, [])); diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterStoreTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterStoreTests.cs index f7fdea314f18..d6706b6a5e13 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterStoreTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterStoreTests.cs @@ -166,6 +166,7 @@ public async Task CleanUps_filters() Assert.That(() => store.FilterExists(1), Is.False.After(30, 5), "filter 1 doesn't exist"); Assert.That(() => store.FilterExists(2), Is.False.After(30, 5), "filter 2 doesn't exist"); Assert.That(() => store.FilterExists(3), Is.False.After(30, 5), "filter 3 doesn't exist"); + store.RefreshFilter(0); Assert.That(() => removedFilterIds, Is.EquivalentTo([1, 2, 3]).After(30, 5)); } } diff --git a/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs b/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs index bd319ebb8686..430ab4cb28b5 100644 --- a/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs +++ b/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs @@ -44,8 +44,8 @@ public class BlockchainBridge( IStateReader stateReader, ITxPool txPool, IReceiptFinder receiptStorage, - IFilterStore filterStore, - IFilterManager filterManager, + FilterStore filterStore, + FilterManager filterManager, IEthereumEcdsa ecdsa, ITimestamper timestamper, ILogFinder logFinder, diff --git a/src/Nethermind/Nethermind.Facade/Filters/FilterManager.cs b/src/Nethermind/Nethermind.Facade/Filters/FilterManager.cs index 4cdf04664915..179c8648e199 100644 --- a/src/Nethermind/Nethermind.Facade/Filters/FilterManager.cs +++ b/src/Nethermind/Nethermind.Facade/Filters/FilterManager.cs @@ -15,7 +15,7 @@ namespace Nethermind.Blockchain.Filters { - public class FilterManager : IFilterManager + public sealed class FilterManager { private readonly ConcurrentDictionary> _logs = new(); @@ -27,12 +27,12 @@ public class FilterManager : IFilterManager new(); private Hash256? _lastBlockHash; - private readonly IFilterStore _filterStore; + private readonly FilterStore _filterStore; private readonly ILogger _logger; private long _logIndex; public FilterManager( - IFilterStore filterStore, + FilterStore filterStore, IMainProcessingContext mainProcessingContext, ITxPool txPool, ILogManager logManager) diff --git a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs index 808f05d887df..5d4face9406f 100644 --- a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs +++ b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs @@ -17,7 +17,7 @@ namespace Nethermind.Blockchain.Filters { - public class FilterStore : IFilterStore + public sealed class FilterStore { private readonly TimeSpan _timeout; private int _currentFilterId = -1; @@ -84,6 +84,13 @@ private void CleanupStaleFilters() } public IEnumerable GetFilters() where T : FilterBase + { + if (_filters.IsEmpty) return Array.Empty(); + + return GetFiltersEnumerate(); + } + + private IEnumerable GetFiltersEnumerate() where T : FilterBase { // Reuse the enumerator var enumerator = Interlocked.Exchange(ref _enumerator, null) ?? _filters.GetEnumerator(); @@ -131,7 +138,7 @@ public void RemoveFilter(int filterId) public void SaveFilter(FilterBase filter) { - if (_filters.ContainsKey(filter.Id)) + if (!_filters.TryAdd(filter.Id, filter)) { throw new InvalidOperationException($"Filter with ID {filter.Id} already exists"); } @@ -140,8 +147,31 @@ public void SaveFilter(FilterBase filter) { _currentFilterId = Math.Max(filter.Id, _currentFilterId); } + } + + public void SaveFilters(IEnumerable filters) + { + int currentFilterId = Volatile.Read(ref _currentFilterId); + + try + { + foreach (FilterBase filter in filters) + { + if (!_filters.TryAdd(filter.Id, filter)) + { + throw new InvalidOperationException($"Filter with ID {filter.Id} already exists"); + } - _filters[filter.Id] = filter; + currentFilterId = Math.Max(filter.Id, currentFilterId); + } + } + finally + { + lock (_locker) + { + _currentFilterId = Math.Max(currentFilterId, _currentFilterId); + } + } } private int GetFilterId(bool generateId) diff --git a/src/Nethermind/Nethermind.Facade/Filters/IFilterManager.cs b/src/Nethermind/Nethermind.Facade/Filters/IFilterManager.cs deleted file mode 100644 index 27a07e029b89..000000000000 --- a/src/Nethermind/Nethermind.Facade/Filters/IFilterManager.cs +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Core.Crypto; -using Nethermind.Facade.Filters; - -namespace Nethermind.Blockchain.Filters -{ - public interface IFilterManager - { - FilterLog[] PollLogs(int filterId); - Hash256[] PollBlockHashes(int filterId); - Hash256[] PollPendingTransactionHashes(int filterId); - } -} diff --git a/src/Nethermind/Nethermind.Facade/Filters/IFilterStore.cs b/src/Nethermind/Nethermind.Facade/Filters/IFilterStore.cs deleted file mode 100644 index 58e978f67d96..000000000000 --- a/src/Nethermind/Nethermind.Facade/Filters/IFilterStore.cs +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using Nethermind.Blockchain.Find; - -namespace Nethermind.Blockchain.Filters -{ - public interface IFilterStore : IDisposable - { - bool FilterExists(int filterId); - IEnumerable GetFilters() where T : FilterBase; - T? GetFilter(int filterId) where T : FilterBase; - BlockFilter CreateBlockFilter(bool setId = true); - PendingTransactionFilter CreatePendingTransactionFilter(bool setId = true); - - LogFilter CreateLogFilter( - BlockParameter fromBlock, - BlockParameter toBlock, - object? address = null, - IEnumerable? topics = null, - bool setId = true); - - void SaveFilter(FilterBase filter); - void RemoveFilter(int filterId); - void RefreshFilter(int filterId); - FilterType GetFilterType(int filterId); - - event EventHandler FilterRemoved; - } -} diff --git a/src/Nethermind/Nethermind.Facade/Filters/NullFilterManager.cs b/src/Nethermind/Nethermind.Facade/Filters/NullFilterManager.cs deleted file mode 100644 index 6fc634af8d23..000000000000 --- a/src/Nethermind/Nethermind.Facade/Filters/NullFilterManager.cs +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Core.Crypto; -using Nethermind.Facade.Filters; - -namespace Nethermind.Blockchain.Filters -{ - public class NullFilterManager : IFilterManager - { - public static NullFilterManager Instance { get; } = new(); - - private NullFilterManager() - { - } - - public FilterLog[] GetLogs(int filterId) - { - return []; - } - - public FilterLog[] PollLogs(int filterId) - { - return []; - } - - public Hash256[] GetBlocksHashes(int filterId) - { - return []; - } - - public Hash256[] PollBlockHashes(int filterId) - { - return []; - } - - public Hash256[] PollPendingTransactionHashes(int filterId) - { - return []; - } - } -} diff --git a/src/Nethermind/Nethermind.Facade/Filters/NullFilterStore.cs b/src/Nethermind/Nethermind.Facade/Filters/NullFilterStore.cs deleted file mode 100644 index a034ba44364e..000000000000 --- a/src/Nethermind/Nethermind.Facade/Filters/NullFilterStore.cs +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using Nethermind.Blockchain.Find; - -namespace Nethermind.Blockchain.Filters -{ - public class NullFilterStore : IFilterStore - { - private NullFilterStore() - { - } - - public static NullFilterStore Instance { get; } = new(); - - public bool FilterExists(int filterId) - { - return false; - } - - public IEnumerable GetFilters() where T : FilterBase - { - return Array.Empty(); - } - - public BlockFilter CreateBlockFilter(bool setId = true) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public PendingTransactionFilter CreatePendingTransactionFilter(bool setId = true) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public LogFilter CreateLogFilter(BlockParameter fromBlock, BlockParameter toBlock, object? address = null, IEnumerable? topics = null, bool setId = true) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public void SaveFilter(FilterBase filter) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public void RemoveFilter(int filterId) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public void RefreshFilter(int filterId) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter refreshing"); - } - - public FilterType GetFilterType(int filterId) - { - throw new InvalidOperationException($"{nameof(NullFilterStore)} does not support filter creation"); - } - - public T? GetFilter(int filterId) where T : FilterBase - { - return null; - } - - public event EventHandler FilterRemoved - { - add { } - remove { } - } - - public void Dispose() { } - } -} diff --git a/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs b/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs index 420c1fa26b01..10cced1f01b5 100644 --- a/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs +++ b/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs @@ -81,8 +81,8 @@ protected override void Load(ContainerBuilder builder) .AddSingleton() .AddScoped((ctx) => ctx.Resolve().CreateBlockchainBridge()) .AddSingleton() - .AddSingleton((timerFactory, rpcConfig) => new FilterStore(timerFactory, rpcConfig.FiltersTimeout)) - .AddSingleton() + .AddSingleton((timerFactory, rpcConfig) => new FilterStore(timerFactory, rpcConfig.FiltersTimeout)) + .AddSingleton() .AddSingleton() // Proof diff --git a/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs b/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs index ed451c7272a9..bb808aad08dd 100644 --- a/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs +++ b/src/Nethermind/Nethermind.Init/Steps/RegisterRpcModules.cs @@ -30,7 +30,7 @@ public class RegisterRpcModules( IBlockTree blockTree, ISpecProvider specProvider, IReceiptMonitor receiptMonitor, - IFilterStore filterStore, + FilterStore filterStore, ITxPool txPool, IEthSyncingInfo ethSyncingInfo, IPeerPool peerPool, diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs index 64c5f12b8676..edf0f8bbfa5c 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/SubscribeModuleTests.cs @@ -52,7 +52,7 @@ public class SubscribeModuleTests private IBlockTree _blockTree = null!; private ITxPool _txPool = null!; private IReceiptStorage _receiptStorage = null!; - private IFilterStore _filterStore = null!; + private FilterStore _filterStore = null!; private ISubscriptionManager _subscriptionManager = null!; private IJsonRpcDuplexClient _jsonRpcDuplexClient = null!; private IJsonSerializer _jsonSerializer = null!; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/LogsSubscription.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/LogsSubscription.cs index 5afc5f206636..4ea94824146e 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/LogsSubscription.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/LogsSubscription.cs @@ -24,7 +24,7 @@ public class LogsSubscription : Subscription public LogsSubscription( IJsonRpcDuplexClient jsonRpcDuplexClient, IReceiptMonitor receiptCanonicalityMonitor, - IFilterStore? store, + FilterStore? store, IBlockTree? blockTree, ILogManager? logManager, Filter? filter = null) @@ -33,7 +33,7 @@ public LogsSubscription( _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); _receiptCanonicalityMonitor = receiptCanonicalityMonitor ?? throw new ArgumentNullException(nameof(receiptCanonicalityMonitor)); _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); - IFilterStore filterStore = store ?? throw new ArgumentNullException(nameof(store)); + FilterStore filterStore = store ?? throw new ArgumentNullException(nameof(store)); if (filter is not null) { diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactoryExtensions.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactoryExtensions.cs index 3b4fcc3d4df9..bc5ee9b6d7cd 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactoryExtensions.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Subscribe/SubscriptionFactoryExtensions.cs @@ -2,10 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Nethermind.Blockchain; using Nethermind.Blockchain.Filters; using Nethermind.Core.Specs; @@ -37,7 +33,7 @@ ISpecProvider specProvider public static void RegisterLogsSubscription( this ISubscriptionFactory subscriptionFactory, IReceiptMonitor receiptMonitor, - IFilterStore? filterStore, + FilterStore? filterStore, IBlockTree? blockTree, ILogManager? logManager ) @@ -110,7 +106,7 @@ public static void RegisterStandardSubscriptions( ILogManager? logManager, ISpecProvider specProvider, IReceiptMonitor receiptMonitor, - IFilterStore? filterStore, + FilterStore? filterStore, ITxPool? txPool, IEthSyncingInfo ethSyncingInfo, IPeerPool? peerPool, @@ -131,7 +127,7 @@ public static void RegisterStandardEthSubscriptions( ILogManager? logManager, ISpecProvider specProvider, IReceiptMonitor receiptMonitor, - IFilterStore? filterStore, + FilterStore? filterStore, ITxPool? txPool, IEthSyncingInfo ethSyncingInfo ) From 5039d86f40dc897d75cf8348d7a2bf231ba920d3 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 25 Nov 2025 22:47:44 +0000 Subject: [PATCH 2/5] Feedback --- .../Nethermind.Facade/Filters/FilterStore.cs | 15 +++------------ .../Nethermind.Init/Modules/RpcModules.cs | 2 +- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs index 5d4face9406f..226b25bbbbab 100644 --- a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs +++ b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs @@ -17,7 +17,7 @@ namespace Nethermind.Blockchain.Filters { - public sealed class FilterStore + public sealed class FilterStore : IDisposable { private readonly TimeSpan _timeout; private int _currentFilterId = -1; @@ -151,9 +151,7 @@ public void SaveFilter(FilterBase filter) public void SaveFilters(IEnumerable filters) { - int currentFilterId = Volatile.Read(ref _currentFilterId); - - try + lock (_locker) { foreach (FilterBase filter in filters) { @@ -162,14 +160,7 @@ public void SaveFilters(IEnumerable filters) throw new InvalidOperationException($"Filter with ID {filter.Id} already exists"); } - currentFilterId = Math.Max(filter.Id, currentFilterId); - } - } - finally - { - lock (_locker) - { - _currentFilterId = Math.Max(currentFilterId, _currentFilterId); + _currentFilterId = Math.Max(filter.Id, _currentFilterId); } } } diff --git a/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs b/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs index 10cced1f01b5..8f0ee78e45ff 100644 --- a/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs +++ b/src/Nethermind/Nethermind.Init/Modules/RpcModules.cs @@ -82,7 +82,7 @@ protected override void Load(ContainerBuilder builder) .AddScoped((ctx) => ctx.Resolve().CreateBlockchainBridge()) .AddSingleton() .AddSingleton((timerFactory, rpcConfig) => new FilterStore(timerFactory, rpcConfig.FiltersTimeout)) - .AddSingleton() + .AddSingleton() .AddSingleton() // Proof From 077813a40a4ec9947187872cde56614632ca71bd Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 25 Nov 2025 23:18:03 +0000 Subject: [PATCH 3/5] Lets not get fancy --- .../Nethermind.Facade/Filters/FilterStore.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs index 226b25bbbbab..bacd9a4503e3 100644 --- a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs +++ b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs @@ -85,6 +85,8 @@ private void CleanupStaleFilters() public IEnumerable GetFilters() where T : FilterBase { + // Return Array.Empty() to avoid allocating enumerator + // and which has a fast-path for foreach if (_filters.IsEmpty) return Array.Empty(); return GetFiltersEnumerate(); @@ -149,19 +151,12 @@ public void SaveFilter(FilterBase filter) } } + // Used by tests public void SaveFilters(IEnumerable filters) { - lock (_locker) + foreach (FilterBase filter in filters) { - foreach (FilterBase filter in filters) - { - if (!_filters.TryAdd(filter.Id, filter)) - { - throw new InvalidOperationException($"Filter with ID {filter.Id} already exists"); - } - - _currentFilterId = Math.Max(filter.Id, _currentFilterId); - } + SaveFilter(filter); } } From 0d30be60a1f5779b79e00c834d8083d63cf400d8 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Tue, 25 Nov 2025 23:20:23 +0000 Subject: [PATCH 4/5] More specific --- src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs index bacd9a4503e3..0d7ddfa4ce99 100644 --- a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs +++ b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs @@ -86,7 +86,8 @@ private void CleanupStaleFilters() public IEnumerable GetFilters() where T : FilterBase { // Return Array.Empty() to avoid allocating enumerator - // and which has a fast-path for foreach + // and which has a non-allocating fast-path for + // foreach via IEnumerable if (_filters.IsEmpty) return Array.Empty(); return GetFiltersEnumerate(); From edbf28958e5c76138026251b1a943884ce35f963 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 27 Nov 2025 10:02:38 +0000 Subject: [PATCH 5/5] Feedback --- .../Filters/FilterManagerTests.cs | 12 ++++++++++++ .../Nethermind.Facade/Filters/FilterStore.cs | 9 --------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs index 1937c15345d6..0b863d6112bc 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Filters/FilterManagerTests.cs @@ -369,3 +369,15 @@ private static TxReceipt BuildReceipt(Action builder) return builderInstance.TestObject; } } + +file static class FilterExtensions +{ + public static void SaveFilters(this FilterStore store, IEnumerable filters) + where T : FilterBase + { + foreach (T filter in filters) + { + store.SaveFilter(filter); + } + } +} diff --git a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs index 0d7ddfa4ce99..f7b192cb42ce 100644 --- a/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs +++ b/src/Nethermind/Nethermind.Facade/Filters/FilterStore.cs @@ -152,15 +152,6 @@ public void SaveFilter(FilterBase filter) } } - // Used by tests - public void SaveFilters(IEnumerable filters) - { - foreach (FilterBase filter in filters) - { - SaveFilter(filter); - } - } - private int GetFilterId(bool generateId) { if (generateId)