From b23de0ae7f1fe6b14f53ffd685d9215e4a2c2b8a Mon Sep 17 00:00:00 2001 From: Ahmad Bitar <33181301+smartprogrammer93@users.noreply.github.com> Date: Thu, 29 Sep 2022 17:16:54 +0300 Subject: [PATCH] Exit early on error (#4220) (#4658) * Exit early on error (#4220) Exit early on error in steps except for step with "mustInitialize" set to false. * Fix hanging on exit * Fix exitcode not forwarded * Rollback unintended commit * Trying to invoke build * Fix build Co-authored-by: Amirul Ashraf --- .../Nethermind.Api/Extensions/IPlugin.cs | 10 ++- src/Nethermind/Nethermind.Config/ExitCodes.cs | 3 + .../Exceptions/IExceptionWithExitCode.cs | 23 ++++++ .../InvalidConfigurationException.cs | 30 ++++++++ .../Steps/EthereumStepsManager.cs | 77 +++++++++++-------- .../Steps/InitializeNodeStats.cs | 8 +- .../Steps/InitializePlugins.cs | 9 ++- .../Nethermind.Init/Steps/StartLogProducer.cs | 8 +- .../Nethermind.Init/Steps/StartMonitoring.cs | 10 ++- .../MergePluginTests.cs | 25 +----- .../Nethermind.Merge.Plugin/MergePlugin.cs | 21 +++-- .../Steps/EthereumStepsManagerTests.cs | 23 +++--- src/Nethermind/Nethermind.Runner/Program.cs | 56 +++++++++++--- .../Nethermind.Runner/configs/mainnet.cfg | 2 +- 14 files changed, 199 insertions(+), 106 deletions(-) create mode 100644 src/Nethermind/Nethermind.Core/Exceptions/IExceptionWithExitCode.cs create mode 100644 src/Nethermind/Nethermind.Core/Exceptions/InvalidConfigurationException.cs diff --git a/src/Nethermind/Nethermind.Api/Extensions/IPlugin.cs b/src/Nethermind/Nethermind.Api/Extensions/IPlugin.cs index 09a872ad8af..387a483504d 100644 --- a/src/Nethermind/Nethermind.Api/Extensions/IPlugin.cs +++ b/src/Nethermind/Nethermind.Api/Extensions/IPlugin.cs @@ -1,19 +1,19 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . -// +// using System; using System.Threading.Tasks; @@ -33,5 +33,7 @@ public interface INethermindPlugin : IAsyncDisposable Task InitNetworkProtocol(); Task InitRpcModules(); + + bool MustInitialize { get => false; } } } diff --git a/src/Nethermind/Nethermind.Config/ExitCodes.cs b/src/Nethermind/Nethermind.Config/ExitCodes.cs index 1e90f871703..9d126ba1784 100644 --- a/src/Nethermind/Nethermind.Config/ExitCodes.cs +++ b/src/Nethermind/Nethermind.Config/ExitCodes.cs @@ -19,6 +19,9 @@ namespace Nethermind.Config; public static class ExitCodes { + public const int Ok = 0; + public const int GeneralError = 1; + // config errors 100...199 public const int NoEngineModule = 100; diff --git a/src/Nethermind/Nethermind.Core/Exceptions/IExceptionWithExitCode.cs b/src/Nethermind/Nethermind.Core/Exceptions/IExceptionWithExitCode.cs new file mode 100644 index 00000000000..550483fd37d --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Exceptions/IExceptionWithExitCode.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . +// + +namespace Nethermind.Core.Exceptions; + +public interface IExceptionWithExitCode +{ + int ExitCode { get; } +} diff --git a/src/Nethermind/Nethermind.Core/Exceptions/InvalidConfigurationException.cs b/src/Nethermind/Nethermind.Core/Exceptions/InvalidConfigurationException.cs new file mode 100644 index 00000000000..a83931fd3ea --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Exceptions/InvalidConfigurationException.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . +// + +using System; + +namespace Nethermind.Core.Exceptions; + +public class InvalidConfigurationException: Exception, IExceptionWithExitCode +{ + public InvalidConfigurationException(string message, int exitCode): base(message) + { + ExitCode = exitCode; + } + + public int ExitCode { get; } +} diff --git a/src/Nethermind/Nethermind.Init/Steps/EthereumStepsManager.cs b/src/Nethermind/Nethermind.Init/Steps/EthereumStepsManager.cs index 10b420cf776..e1f6e64f817 100644 --- a/src/Nethermind/Nethermind.Init/Steps/EthereumStepsManager.cs +++ b/src/Nethermind/Nethermind.Init/Steps/EthereumStepsManager.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -135,40 +135,13 @@ private void RunOneRoundOfInitialization(CancellationToken cancellationToken) if (_logger.IsDebug) _logger.Debug($"Executing step: {stepInfo}"); - Stopwatch stopwatch = Stopwatch.StartNew(); stepInfo.Stage = StepInitializationStage.Executing; - Task task = step.Execute(cancellationToken); startedThisRound++; - Task continuationTask = task.ContinueWith(t => - { - stopwatch.Stop(); - - if (t.IsFaulted && step.MustInitialize) - { - if (_logger.IsError) _logger.Error( - $"Step {step.GetType().Name.PadRight(24)} failed after {stopwatch.ElapsedMilliseconds}ms", - t.Exception); - } - else if (t.IsFaulted) - { - if (_logger.IsWarn) _logger.Warn( - $"Step {step.GetType().Name.PadRight(24)} failed after {stopwatch.ElapsedMilliseconds}ms"); - } - else - { - if (_logger.IsDebug) _logger.Debug( - $"Step {step.GetType().Name.PadRight(24)} executed in {stopwatch.ElapsedMilliseconds}ms"); - } - - stepInfo.Stage = StepInitializationStage.Complete; - _autoResetEvent.Set(); - - if (_logger.IsDebug) _logger.Debug($"{step.GetType().Name.PadRight(24)} complete"); - }); + Task task = ExecuteStep(step, stepInfo, cancellationToken); if (step.MustInitialize) { - _allPending.Enqueue(continuationTask); + _allPending.Enqueue(task); } else { @@ -186,6 +159,46 @@ private void RunOneRoundOfInitialization(CancellationToken cancellationToken) } } + private async Task ExecuteStep(IStep step, StepInfo stepInfo, CancellationToken cancellationToken) + { + Stopwatch stopwatch = Stopwatch.StartNew(); + try + { + await step.Execute(cancellationToken); + + if (_logger.IsDebug) + _logger.Debug( + $"Step {step.GetType().Name.PadRight(24)} executed in {stopwatch.ElapsedMilliseconds}ms"); + } + catch (Exception exception) + { + if (step.MustInitialize) + { + if (_logger.IsError) + _logger.Error( + $"Step {step.GetType().Name.PadRight(24)} failed after {stopwatch.ElapsedMilliseconds}ms", + exception); + + throw; + } + + if (_logger.IsWarn) + { + _logger.Warn( + $"Step {step.GetType().Name.PadRight(24)} failed after {stopwatch.ElapsedMilliseconds}ms {exception}"); + } + } + finally + { + stopwatch.Stop(); + + stepInfo.Stage = StepInitializationStage.Complete; + _autoResetEvent.Set(); + + if (_logger.IsDebug) _logger.Debug($"{step.GetType().Name.PadRight(24)} complete"); + } + } + private IStep? CreateStepInstance(StepInfo stepInfo) { IStep? step = null; diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeNodeStats.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeNodeStats.cs index 5e4f1be6fda..28a93406b73 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeNodeStats.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeNodeStats.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -43,5 +43,7 @@ public Task Execute(CancellationToken _) return Task.CompletedTask; } + + public bool MustInitialize => false; } } diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializePlugins.cs b/src/Nethermind/Nethermind.Init/Steps/InitializePlugins.cs index 82ac1ed05c1..10ae39152fe 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializePlugins.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializePlugins.cs @@ -1,19 +1,19 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . -// +// using System; using System.Diagnostics; @@ -53,6 +53,7 @@ public async Task Execute(CancellationToken cancellationToken) catch (Exception e) { if (logger.IsError) logger.Error($"Failed to initialize plugin {plugin.Name} by {plugin.Author}", e); + if (plugin.MustInitialize) throw; } } } diff --git a/src/Nethermind/Nethermind.Init/Steps/StartLogProducer.cs b/src/Nethermind/Nethermind.Init/Steps/StartLogProducer.cs index df90e010f82..870f7811cc0 100644 --- a/src/Nethermind/Nethermind.Init/Steps/StartLogProducer.cs +++ b/src/Nethermind/Nethermind.Init/Steps/StartLogProducer.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -38,5 +38,7 @@ public Task Execute(CancellationToken cancellationToken) _api.Publishers.Add(logPublisher); return Task.CompletedTask; } + + public bool MustInitialize => false; } } diff --git a/src/Nethermind/Nethermind.Init/Steps/StartMonitoring.cs b/src/Nethermind/Nethermind.Init/Steps/StartMonitoring.cs index 43274a55a7c..d92e65f8f0c 100644 --- a/src/Nethermind/Nethermind.Init/Steps/StartMonitoring.cs +++ b/src/Nethermind/Nethermind.Init/Steps/StartMonitoring.cs @@ -1,19 +1,19 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . -// +// using System; using System.Collections.Generic; @@ -74,4 +74,6 @@ await _api.MonitoringService.StartAsync().ContinueWith(x => logger.Info("Grafana / Prometheus metrics are disabled in configuration"); } } + + public bool MustInitialize => false; } diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs index a09a2a7b5df..c2df6bfa3e0 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/MergePluginTests.cs @@ -26,6 +26,7 @@ using Nethermind.Consensus.Clique; using Nethermind.Consensus.Producers; using Nethermind.Core; +using Nethermind.Core.Exceptions; using Nethermind.Core.Test.Builders; using Nethermind.Db; using Nethermind.JsonRpc; @@ -80,7 +81,7 @@ public void Setup() Epoch = CliqueConfig.Default.Epoch, Period = CliqueConfig.Default.BlockPeriod }; - _plugin = new MergePlugin(new EnvironmentExitMock()); + _plugin = new MergePlugin(); _consensusPlugin = new(); } @@ -140,7 +141,7 @@ public async Task InitThrowsWhenNoEngineApiUrlsConfigured(bool jsonRpcEnabled, b await _plugin.Invoking((plugin) => plugin.Init(_context)) .Should() - .ThrowAsync(); + .ThrowAsync(); } [Test] @@ -187,25 +188,7 @@ public async Task InitThrowExceptionIfBodiesAndReceiptIsDisabled(bool downloadBo } else { - await invocation.Should().ThrowAsync(); - } - } - - private class EnvironmentExitMock : IEnvironment - { - public string GetEnvironmentVariable(string variableName) - { - throw new NotImplementedException(); - } - - public IDictionary GetEnvironmentVariables() - { - throw new NotImplementedException(); - } - - public void Exit(int exitCode) - { - throw new InvalidOperationException($"Exit with exitCode: {exitCode}"); + await invocation.Should().ThrowAsync(); } } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs index 85d3476376b..73d1302101f 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs @@ -29,6 +29,7 @@ using Nethermind.Consensus.Validators; using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Core.Exceptions; using Nethermind.Db; using Nethermind.Facade.Proxy; using Nethermind.JsonRpc; @@ -67,16 +68,8 @@ public partial class MergePlugin : IConsensusWrapperPlugin, ISynchronizationPlug public virtual bool MergeEnabled => _mergeConfig.Enabled; - private readonly IEnvironment _environment = new EnvironmentWrapper(); - public MergePlugin() { } - public MergePlugin(IEnvironment? environment = null) - { - if (environment != null) - _environment = environment; - } - public virtual Task Init(INethermindApi nethermindApi) { _api = nethermindApi; @@ -167,8 +160,9 @@ private void EnsureReceiptAvailable() { if (!syncConfig.DownloadReceiptsInFastSync || !syncConfig.DownloadBodiesInFastSync) { - if (_logger.IsError) _logger.Error("Receipt and body must be available for merge to function. The following configs values should be set to true: Sync.DownloadReceiptsInFastSync, Sync.DownloadBodiesInFastSync"); - _environment.Exit(ExitCodes.NoDownloadOldReceiptsOrBlocks); + throw new InvalidConfigurationException( + "Receipt and body must be available for merge to function. The following configs values should be set to true: Sync.DownloadReceiptsInFastSync, Sync.DownloadBodiesInFastSync", + ExitCodes.NoDownloadOldReceiptsOrBlocks); } } } @@ -210,8 +204,9 @@ private void EnsureEngineModuleIsConfigured() if (!hasEngineApiConfigured) { - if (_logger.IsError) _logger.Error("Engine module wasn't configured on any port. Nethermind can't work without engine port configured. Verify your RPC configuration. You can find examples in our docs: https://docs.nethermind.io/nethermind/ethereum-client/engine-jsonrpc-configuration-examples"); - _environment.Exit(ExitCodes.NoEngineModule); + throw new InvalidConfigurationException( + "Engine module wasn't configured on any port. Nethermind can't work without engine port configured. Verify your RPC configuration. You can find examples in our docs: https://docs.nethermind.io/nethermind/ethereum-client/engine-jsonrpc-configuration-examples", + ExitCodes.NoEngineModule); } } @@ -453,5 +448,7 @@ public Task InitSynchronization() public ValueTask DisposeAsync() => ValueTask.CompletedTask; public string SealEngineType => "Eth2Merge"; + + public bool MustInitialize { get => true; } } } diff --git a/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/EthereumStepsManagerTests.cs b/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/EthereumStepsManagerTests.cs index 1136919e68f..afdb7128cce 100644 --- a/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/EthereumStepsManagerTests.cs +++ b/src/Nethermind/Nethermind.Runner.Test/Ethereum/Steps/EthereumStepsManagerTests.cs @@ -1,22 +1,23 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . using System; using System.Threading; using System.Threading.Tasks; +using FluentAssertions; using FluentAssertions.Execution; using Nethermind.Api; using Nethermind.Config; @@ -26,6 +27,7 @@ using Nethermind.Runner.Ethereum.Api; using Nethermind.Runner.Ethereum.Steps; using Nethermind.Serialization.Json; +using NSubstitute.ExceptionExtensions; using NUnit.Framework; namespace Nethermind.Runner.Test.Ethereum.Steps @@ -85,7 +87,7 @@ public async Task With_steps_from_here_AuRa() runnerContext, LimboLogs.Instance); - using CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromSeconds(1)); + using CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromSeconds(2)); try { @@ -93,10 +95,7 @@ public async Task With_steps_from_here_AuRa() } catch (Exception e) { - if (!(e is OperationCanceledException)) - { - throw new AssertionFailedException($"Exception should be {nameof(OperationCanceledException)}"); - } + e.Should().BeOfType(); } } @@ -111,7 +110,7 @@ public async Task With_failing_steps() runnerContext, LimboLogs.Instance); - using CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromSeconds(1)); + using CancellationTokenSource source = new CancellationTokenSource(TimeSpan.FromSeconds(2)); try { @@ -211,7 +210,7 @@ public StepCAuRa(AuRaNethermindApi runnerContext) public override async Task Execute(CancellationToken cancellationToken) { - await Task.Run(() => throw new Exception()); + await Task.Run(() => throw new TestException()); } } @@ -221,4 +220,8 @@ public StepCStandard(NethermindApi runnerContext) { } } + + class TestException : Exception + { + } } diff --git a/src/Nethermind/Nethermind.Runner/Program.cs b/src/Nethermind/Nethermind.Runner/Program.cs index 16c8bf7d82f..8f543ae8470 100644 --- a/src/Nethermind/Nethermind.Runner/Program.cs +++ b/src/Nethermind/Nethermind.Runner/Program.cs @@ -1,16 +1,16 @@ // Copyright (c) 2021 Demerzel Solutions Limited // This file is part of the Nethermind library. -// +// // The Nethermind library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // The Nethermind library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . @@ -34,6 +34,7 @@ using Nethermind.Consensus.Clique; using Nethermind.Consensus.Ethash; using Nethermind.Core; +using Nethermind.Core.Exceptions; using Nethermind.Db.Rocks; using Nethermind.Hive; using Nethermind.KeyStore.Config; @@ -147,7 +148,6 @@ private static void Run(string[] args) app.OnExecute(async () => { - _appClosed.Reset(); IConfigProvider configProvider = BuildConfigProvider(app, loggerConfigSource, logLevelOverride, configsDirectory, configFile); IInitConfig initConfig = configProvider.GetConfig(); IKeyStoreConfig keyStoreConfig = configProvider.GetConfig(); @@ -192,25 +192,57 @@ private static void Run(string[] args) INethermindApi nethermindApi = apiBuilder.Create(plugins.OfType()); ((List)nethermindApi.Plugins).AddRange(plugins); + _appClosed.Reset(); EthereumRunner ethereumRunner = new(nethermindApi); - await ethereumRunner.Start(_processCloseCancellationSource.Token).ContinueWith(x => + int exitCode = ExitCodes.Ok; + try { - if (x.IsFaulted && _logger.IsError) - _logger.Error("Error during ethereum runner start", x.Exception); - }); + await ethereumRunner.Start(_processCloseCancellationSource.Token); - _ = await Task.WhenAny(_cancelKeySource.Task, _processExit.Task); + _ = await Task.WhenAny(_cancelKeySource.Task, _processExit.Task); + } + catch (Exception e) + { + if (_logger.IsError) _logger.Error("Error during ethereum runner start", e); + if (e is IExceptionWithExitCode withExit) + { + exitCode = withExit.ExitCode; + } + else + { + exitCode = ExitCodes.GeneralError; + } + _processCloseCancellationSource.Cancel(); + } _logger.Info("Closing, please wait until all functions are stopped properly..."); await ethereumRunner.StopAsync(); _logger.Info("All done, goodbye!"); _appClosed.Set(); - return 0; + return exitCode; }); - _ = app.Execute(args); - _appClosed.Wait(); + try + { + Environment.ExitCode = app.Execute(args); + } + catch (Exception e) + { + if (e is IExceptionWithExitCode withExit) + { + Environment.ExitCode = withExit.ExitCode; + } + else + { + Environment.ExitCode = ExitCodes.GeneralError; + } + throw; + } + finally + { + _appClosed.Wait(); + } } private static IntPtr OnResolvingUnmanagedDll(Assembly _, string nativeLibraryName) diff --git a/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg index 17a9a7b899b..2b4256ae876 100644 --- a/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg @@ -38,4 +38,4 @@ "Merge": { "Enabled": true } -} \ No newline at end of file +}