From a1954fa75a91e19f2c98adda49b591b4082d1d65 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 10 Jul 2025 12:11:14 +0100 Subject: [PATCH 1/4] Don't block background tasks when syncing --- .../Scheduler/BackgroundTaskSchedulerTests.cs | 14 +++++++++----- .../Scheduler/BackgroundTaskScheduler.cs | 18 +++++++++++++----- .../Modules/PseudoNethermindModule.cs | 4 ++++ .../Steps/InitializeBlockchain.cs | 1 + 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus.Test/Scheduler/BackgroundTaskSchedulerTests.cs b/src/Nethermind/Nethermind.Consensus.Test/Scheduler/BackgroundTaskSchedulerTests.cs index 7f803c531b3..934446632d7 100644 --- a/src/Nethermind/Nethermind.Consensus.Test/Scheduler/BackgroundTaskSchedulerTests.cs +++ b/src/Nethermind/Nethermind.Consensus.Test/Scheduler/BackgroundTaskSchedulerTests.cs @@ -9,6 +9,7 @@ using Nethermind.Consensus.Scheduler; using Nethermind.Core.Extensions; using Nethermind.Logging; +using Nethermind.TxPool; using NSubstitute; using NUnit.Framework; using TaskCompletionSource = DotNetty.Common.Concurrency.TaskCompletionSource; @@ -18,18 +19,21 @@ namespace Nethermind.Consensus.Test.Scheduler; public class BackgroundTaskSchedulerTests { private IBlockProcessor _blockProcessor; + private IChainHeadInfoProvider _chainHeadInfo; [SetUp] public void Setup() { _blockProcessor = Substitute.For(); + _chainHeadInfo = Substitute.For(); + _chainHeadInfo.IsSyncing.Returns(false); } [Test] public async Task Test_task_will_execute() { TaskCompletionSource tcs = new TaskCompletionSource(); - await using BackgroundTaskScheduler scheduler = new BackgroundTaskScheduler(_blockProcessor, 1, 65536, LimboLogs.Instance); + await using BackgroundTaskScheduler scheduler = new BackgroundTaskScheduler(_blockProcessor, _chainHeadInfo, 1, 65536, LimboLogs.Instance); scheduler.ScheduleTask(1, (_, token) => { @@ -43,7 +47,7 @@ public async Task Test_task_will_execute() [Test] public async Task Test_task_will_execute_concurrently_when_configured_so() { - await using BackgroundTaskScheduler scheduler = new BackgroundTaskScheduler(_blockProcessor, 2, 65536, LimboLogs.Instance); + await using BackgroundTaskScheduler scheduler = new BackgroundTaskScheduler(_blockProcessor, _chainHeadInfo, 2, 65536, LimboLogs.Instance); int counter = 0; @@ -68,7 +72,7 @@ public async Task Test_task_will_execute_concurrently_when_configured_so() [Test] public async Task Test_task_will_cancel_on_block_processing() { - await using BackgroundTaskScheduler scheduler = new BackgroundTaskScheduler(_blockProcessor, 2, 65536, LimboLogs.Instance); + await using BackgroundTaskScheduler scheduler = new BackgroundTaskScheduler(_blockProcessor, _chainHeadInfo, 2, 65536, LimboLogs.Instance); bool wasCancelled = false; @@ -96,7 +100,7 @@ public async Task Test_task_will_cancel_on_block_processing() [Test] public async Task Test_task_that_is_scheduled_during_block_processing_will_continue_after() { - await using BackgroundTaskScheduler scheduler = new BackgroundTaskScheduler(_blockProcessor, 2, 65536, LimboLogs.Instance); + await using BackgroundTaskScheduler scheduler = new BackgroundTaskScheduler(_blockProcessor, _chainHeadInfo, 2, 65536, LimboLogs.Instance); _blockProcessor.BlocksProcessing += Raise.EventWith(new BlocksProcessingEventArgs(null)); int executionCount = 0; @@ -119,7 +123,7 @@ public async Task Test_task_that_is_scheduled_during_block_processing_will_conti [Test] public async Task Test_task_that_is_scheduled_during_block_processing_but_deadlined_will_get_called_and_cancelled() { - await using BackgroundTaskScheduler scheduler = new BackgroundTaskScheduler(_blockProcessor, 2, 65536, LimboLogs.Instance); + await using BackgroundTaskScheduler scheduler = new BackgroundTaskScheduler(_blockProcessor, _chainHeadInfo, 2, 65536, LimboLogs.Instance); _blockProcessor.BlocksProcessing += Raise.EventWith(new BlocksProcessingEventArgs(null)); bool wasCancelled = false; diff --git a/src/Nethermind/Nethermind.Consensus/Scheduler/BackgroundTaskScheduler.cs b/src/Nethermind/Nethermind.Consensus/Scheduler/BackgroundTaskScheduler.cs index aac8a86994d..99e855b006a 100644 --- a/src/Nethermind/Nethermind.Consensus/Scheduler/BackgroundTaskScheduler.cs +++ b/src/Nethermind/Nethermind.Consensus/Scheduler/BackgroundTaskScheduler.cs @@ -11,6 +11,7 @@ using Nethermind.Consensus.Processing; using Nethermind.Core.Extensions; using Nethermind.Logging; +using Nethermind.TxPool; namespace Nethermind.Consensus.Scheduler; @@ -38,11 +39,12 @@ public class BackgroundTaskScheduler : IBackgroundTaskScheduler, IAsyncDisposabl private readonly Task[] _tasksExecutors; private readonly ILogger _logger; private readonly IBlockProcessor _blockProcessor; + private readonly IChainHeadInfoProvider _headInfo; private CancellationTokenSource _blockProcessorCancellationTokenSource; private bool _disposed = false; - public BackgroundTaskScheduler(IBlockProcessor blockProcessor, int concurrency, int capacity, ILogManager logManager) + public BackgroundTaskScheduler(IBlockProcessor blockProcessor, IChainHeadInfoProvider headInfo, int concurrency, int capacity, ILogManager logManager) { ArgumentOutOfRangeException.ThrowIfLessThan(concurrency, 1); ArgumentOutOfRangeException.ThrowIfLessThan(capacity, 1); @@ -55,6 +57,7 @@ public BackgroundTaskScheduler(IBlockProcessor blockProcessor, int concurrency, _taskQueue = Channel.CreateUnboundedPrioritized(); _logger = logManager.GetClassLogger(); _blockProcessor = blockProcessor; + _headInfo = headInfo; _restartQueueSignal = new ManualResetEventSlim(initialState: true); // As channel is unbounded (to be prioritized) we gate capacity with a semaphore _capacity = new SemaphoreSlim(initialCount: capacity); @@ -75,10 +78,15 @@ public BackgroundTaskScheduler(IBlockProcessor blockProcessor, int concurrency, private void BlockProcessorOnBlocksProcessing(object? sender, BlocksProcessingEventArgs e) { - // Reset background queue processing signal, causing it to wait - _restartQueueSignal.Reset(); - // On block processing, we cancel the block process cts, causing current task to get cancelled. - _blockProcessorCancellationTokenSource.Cancel(); + // If we are syncing we don't block background task processing + // as there are potentially no gaps between blocks + if (!_headInfo.IsSyncing) + { + // Reset background queue processing signal, causing it to wait + _restartQueueSignal.Reset(); + // On block processing, we cancel the block process cts, causing current task to get cancelled. + _blockProcessorCancellationTokenSource.Cancel(); + } } private void BlockProcessorOnBlockProcessed(object? sender, BlockProcessedEventArgs e) diff --git a/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs b/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs index 6b8ce72bd32..0a1dc45a2d1 100644 --- a/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs +++ b/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs @@ -39,6 +39,9 @@ public class PseudoNethermindModule(ChainSpec spec, IConfigProvider configProvid { protected override void Load(ContainerBuilder builder) { + IChainHeadInfoProvider chainHeadInfo = Substitute.For(); + chainHeadInfo.IsSyncing.Returns(false); + IInitConfig initConfig = configProvider.GetConfig(); base.Load(builder); @@ -53,6 +56,7 @@ protected override void Load(ContainerBuilder builder) .AddSingleton() .AddSingleton((blockProcessingContext) => new BackgroundTaskScheduler( blockProcessingContext.BlockProcessor, + chainHeadInfo, initConfig.BackgroundTaskConcurrency, initConfig.BackgroundTaskMaxNumber, logManager)) diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs index 8069ee6438a..158a794471f 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs @@ -132,6 +132,7 @@ protected virtual Task InitBlockchain() BackgroundTaskScheduler backgroundTaskScheduler = new BackgroundTaskScheduler( mainBlockProcessor, + chainHeadInfoProvider, initConfig.BackgroundTaskConcurrency, initConfig.BackgroundTaskMaxNumber, _api.LogManager); From 7ca231f85b492578e5607650d58a877dabcc8099 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 10 Jul 2025 12:28:54 +0100 Subject: [PATCH 2/4] fix test --- .../Modules/PseudoNethermindModule.cs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs b/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs index 0a1dc45a2d1..c5c902092c0 100644 --- a/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs +++ b/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs @@ -1,11 +1,11 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.IO.Abstractions; using System.Reflection; using Autofac; using Nethermind.Api; -using Nethermind.Blockchain.Filters; using Nethermind.Config; using Nethermind.Consensus; using Nethermind.Consensus.Scheduler; @@ -13,7 +13,10 @@ using Nethermind.Core.Timers; using Nethermind.Crypto; using Nethermind.Db; +using Nethermind.Evm; +using Nethermind.Evm.State; using Nethermind.Init.Modules; +using Nethermind.Int256; using Nethermind.JsonRpc; using Nethermind.KeyStore; using Nethermind.Logging; @@ -39,9 +42,6 @@ public class PseudoNethermindModule(ChainSpec spec, IConfigProvider configProvid { protected override void Load(ContainerBuilder builder) { - IChainHeadInfoProvider chainHeadInfo = Substitute.For(); - chainHeadInfo.IsSyncing.Returns(false); - IInitConfig initConfig = configProvider.GetConfig(); base.Load(builder); @@ -56,7 +56,7 @@ protected override void Load(ContainerBuilder builder) .AddSingleton() .AddSingleton((blockProcessingContext) => new BackgroundTaskScheduler( blockProcessingContext.BlockProcessor, - chainHeadInfo, + new ChainHeadInfoMock(), initConfig.BackgroundTaskConcurrency, initConfig.BackgroundTaskMaxNumber, logManager)) @@ -89,4 +89,19 @@ protected override void Load(ContainerBuilder builder) } }); } + + private class ChainHeadInfoMock : IChainHeadInfoProvider + { + public IChainHeadSpecProvider SpecProvider { get; } = null!; + public IReadOnlyStateProvider ReadOnlyStateProvider { get; } = null!; + public ICodeInfoRepository CodeInfoRepository { get; } = null!; + public long HeadNumber { get; } + public long? BlockGasLimit { get; } + public UInt256 CurrentBaseFee { get; } + public UInt256 CurrentFeePerBlobGas { get; } + public ProofVersion CurrentProofVersion { get; } + public bool IsSyncing { get => false; } + public bool IsProcessingBlock { get; } + public event EventHandler? HeadChanged; + } } From e55efe29869b83146333dcb384c1269211e7666d Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 10 Jul 2025 12:42:10 +0100 Subject: [PATCH 3/4] fix compile warning --- .../Nethermind.Core.Test/Modules/PseudoNethermindModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs b/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs index c5c902092c0..c141f4418e9 100644 --- a/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs +++ b/src/Nethermind/Nethermind.Core.Test/Modules/PseudoNethermindModule.cs @@ -102,6 +102,6 @@ private class ChainHeadInfoMock : IChainHeadInfoProvider public ProofVersion CurrentProofVersion { get; } public bool IsSyncing { get => false; } public bool IsProcessingBlock { get; } - public event EventHandler? HeadChanged; + public event EventHandler HeadChanged { add { } remove { } } } } From 17c76cc695562d33c331e92b1906ccf10e9d594f Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 10 Jul 2025 14:04:40 +0100 Subject: [PATCH 4/4] Concurrency 2 --- src/Nethermind/Nethermind.Api/IInitConfig.cs | 2 +- src/Nethermind/Nethermind.Api/InitConfig.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Api/IInitConfig.cs b/src/Nethermind/Nethermind.Api/IInitConfig.cs index 8781c46716f..b22657683da 100644 --- a/src/Nethermind/Nethermind.Api/IInitConfig.cs +++ b/src/Nethermind/Nethermind.Api/IInitConfig.cs @@ -90,7 +90,7 @@ public interface IInitConfig : IConfig [ConfigItem(Description = "[TECHNICAL] Exit when block number is reached. Useful for scripting and testing.", DefaultValue = "null", HiddenFromDocs = true)] long? ExitOnBlockNumber { get; set; } - [ConfigItem(Description = "[TECHNICAL] Specify concurrency limit for background task.", DefaultValue = "1", HiddenFromDocs = true)] + [ConfigItem(Description = "[TECHNICAL] Specify concurrency limit for background task.", DefaultValue = "2", HiddenFromDocs = true)] int BackgroundTaskConcurrency { get; set; } [ConfigItem(Description = "[TECHNICAL] Specify max number of background task.", DefaultValue = "1024", HiddenFromDocs = true)] diff --git a/src/Nethermind/Nethermind.Api/InitConfig.cs b/src/Nethermind/Nethermind.Api/InitConfig.cs index 466d63e91b2..b1e2fff247b 100644 --- a/src/Nethermind/Nethermind.Api/InitConfig.cs +++ b/src/Nethermind/Nethermind.Api/InitConfig.cs @@ -36,7 +36,7 @@ public class InitConfig : IInitConfig public bool DisableMallocOpts { get; set; } = false; public INodeStorage.KeyScheme StateDbKeyScheme { get; set; } = INodeStorage.KeyScheme.Current; public long? ExitOnBlockNumber { get; set; } = null; - public int BackgroundTaskConcurrency { get; set; } = 1; + public int BackgroundTaskConcurrency { get; set; } = 2; public int BackgroundTaskMaxNumber { get; set; } = 1024; public bool InRunnerTest { get; set; } = false;