From 2db1e74654d9df971c91b92d2bb36fa20be165b8 Mon Sep 17 00:00:00 2001 From: Marcin Sobczak <77129288+marcindsobczak@users.noreply.github.com> Date: Mon, 26 Aug 2024 10:40:57 +0200 Subject: [PATCH 1/8] Remove obsolete flag `FastBlocks` from configs (#7363) --- src/Nethermind/Nethermind.Runner/configs/energyweb.cfg | 3 +-- src/Nethermind/Nethermind.Runner/configs/exosama.cfg | 3 +-- src/Nethermind/Nethermind.Runner/configs/gnosis.cfg | 3 +-- src/Nethermind/Nethermind.Runner/configs/holesky.cfg | 1 - src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg | 3 +-- src/Nethermind/Nethermind.Runner/configs/mainnet.cfg | 3 +-- src/Nethermind/Nethermind.Runner/configs/poacore.cfg | 1 - 7 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg b/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg index 5c0a575be8d3..6048278d7d8f 100644 --- a/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg @@ -14,7 +14,6 @@ "PivotNumber": 31410000, "PivotHash": "0x78669655c10ef82908de41e4d9da4b7a1c10128bc57b18372923aa1ff3c8022a", "PivotTotalDifficulty": "10688269144986677137384596419431839521456903773", - "FastBlocks": true, "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 }, @@ -30,4 +29,4 @@ "Merge": { "Enabled": false } -} \ No newline at end of file +} diff --git a/src/Nethermind/Nethermind.Runner/configs/exosama.cfg b/src/Nethermind/Nethermind.Runner/configs/exosama.cfg index e7d9261dc4fb..fb66951f8b26 100644 --- a/src/Nethermind/Nethermind.Runner/configs/exosama.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/exosama.cfg @@ -14,7 +14,6 @@ "PivotNumber": 11500000, "PivotHash": "0x85569bf21a77a72a0e55b6cb4f49883181081c4c0f25de932f481559b866e9b2", "PivotTotalDifficulty": "3913247219590792329828807985465334431387853285", - "FastBlocks": true, "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 }, @@ -30,4 +29,4 @@ "Merge": { "Enabled": false } -} \ No newline at end of file +} diff --git a/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg b/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg index 5d245c1f4090..d95cd68ebae4 100644 --- a/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg @@ -16,7 +16,6 @@ "PivotNumber": 35520000, "PivotHash": "0x3bcec470f363f3cff3cfab3b8046b6e093b6506d495ab111169005ec5d8abafb", "PivotTotalDifficulty": "8626000110427538733349499292577475819600160930", - "FastBlocks": true, "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 }, @@ -40,4 +39,4 @@ 16 ] } -} \ No newline at end of file +} diff --git a/src/Nethermind/Nethermind.Runner/configs/holesky.cfg b/src/Nethermind/Nethermind.Runner/configs/holesky.cfg index 9a83a51cd805..c662bd62c594 100644 --- a/src/Nethermind/Nethermind.Runner/configs/holesky.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/holesky.cfg @@ -11,7 +11,6 @@ "Sync": { "FastSync": true, "SnapSync": true, - "FastBlocks": true, "FastSyncCatchUpHeightDelta": "10000000000" }, "Metrics": { diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg index 0a02b0942eaa..560ed0406293 100644 --- a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg @@ -11,7 +11,6 @@ "Sync": { "FastSync": true, "SnapSync": true, - "FastBlocks": true, "PivotNumber": 12860000, "PivotHash": "0x91fd87281b38f12604d8b3b89c6ba886bdc98d33bdca44301ea18b129016cb98", "PivotTotalDifficulty": "25662111" @@ -28,4 +27,4 @@ "Merge": { "Enabled": false } -} \ No newline at end of file +} diff --git a/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg index ecc91322ba64..35aeb1cad88d 100644 --- a/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg @@ -12,7 +12,6 @@ "PivotNumber": 20550000, "PivotHash": "0xf59ee916897eb3de726413f80153f8cd8ef3f19be27f33afd299d9c279307156", "PivotTotalDifficulty": "58750003716598352816469", - "FastBlocks": true, "FastSyncCatchUpHeightDelta": "10000000000" }, "EthStats": { @@ -36,4 +35,4 @@ "Merge": { "Enabled": true } -} \ No newline at end of file +} diff --git a/src/Nethermind/Nethermind.Runner/configs/poacore.cfg b/src/Nethermind/Nethermind.Runner/configs/poacore.cfg index 5420dc7e983b..94ed3102b2bd 100644 --- a/src/Nethermind/Nethermind.Runner/configs/poacore.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/poacore.cfg @@ -14,7 +14,6 @@ "PivotNumber": 31220000, "PivotHash": "0x50c4faecaaec0b40ab0fa4d98cbf70108a92654205c734b87ac70ac1799b1e67", "PivotTotalDifficulty": "10623615495271698829326555244019803561289774486", - "FastBlocks": true, "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 }, From ddd327fc178fa69f24d08b16dee92a7dc9700aa9 Mon Sep 17 00:00:00 2001 From: "core-repository-dispatch-app[bot]" <173070810+core-repository-dispatch-app[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 10:58:42 +0200 Subject: [PATCH 2/8] Update Fast Sync configuration in Nethermind repository (#7358) Co-authored-by: LukaszRozmej Co-authored-by: Lukasz Rozmej --- src/Nethermind/Nethermind.Runner/configs/chiado.cfg | 4 ++-- src/Nethermind/Nethermind.Runner/configs/energyweb.cfg | 6 +++--- src/Nethermind/Nethermind.Runner/configs/exosama.cfg | 6 +++--- src/Nethermind/Nethermind.Runner/configs/gnosis.cfg | 4 ++-- src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg | 6 +++--- src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg | 6 +++--- src/Nethermind/Nethermind.Runner/configs/mainnet.cfg | 4 ++-- src/Nethermind/Nethermind.Runner/configs/sepolia.cfg | 4 ++-- src/Nethermind/Nethermind.Runner/configs/volta.cfg | 6 +++--- 9 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/Nethermind/Nethermind.Runner/configs/chiado.cfg b/src/Nethermind/Nethermind.Runner/configs/chiado.cfg index c8e4790e450b..9cb0e5e09f46 100644 --- a/src/Nethermind/Nethermind.Runner/configs/chiado.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/chiado.cfg @@ -16,8 +16,8 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 11330000, - "PivotHash": "0x3a5a98cf89110e899d328d61d2f33c4f2e68872b4044317ade40be33e1f2f394", + "PivotNumber": 11450000, + "PivotHash": "0xee4ca6bde9f004fefa0fca9c60ef7d96135a21b6d447b794b6f710ddaf88013b", "PivotTotalDifficulty": "231708131825107706987652208063906496124457284", "FastSyncCatchUpHeightDelta": "10000000000", "UseGethLimitsInFastBlocks": false diff --git a/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg b/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg index 6048278d7d8f..fd8c8702dbf5 100644 --- a/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/energyweb.cfg @@ -11,9 +11,9 @@ }, "Sync": { "FastSync": true, - "PivotNumber": 31410000, - "PivotHash": "0x78669655c10ef82908de41e4d9da4b7a1c10128bc57b18372923aa1ff3c8022a", - "PivotTotalDifficulty": "10688269144986677137384596419431839521456903773", + "PivotNumber": 31520000, + "PivotHash": "0xd1940880fdfee2658d86943085110480a65511dcac2166603ab150d1cb846803", + "PivotTotalDifficulty": "10725700205347980368365567626249334024716831467", "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 }, diff --git a/src/Nethermind/Nethermind.Runner/configs/exosama.cfg b/src/Nethermind/Nethermind.Runner/configs/exosama.cfg index fb66951f8b26..240b0d593007 100644 --- a/src/Nethermind/Nethermind.Runner/configs/exosama.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/exosama.cfg @@ -11,9 +11,9 @@ }, "Sync": { "FastSync": true, - "PivotNumber": 11500000, - "PivotHash": "0x85569bf21a77a72a0e55b6cb4f49883181081c4c0f25de932f481559b866e9b2", - "PivotTotalDifficulty": "3913247219590792329828807985465334431387853285", + "PivotNumber": 11620000, + "PivotHash": "0xacde38b09384b716197fe06ce5af3483b86ffd011dd21e091b5b371cba7ff8d6", + "PivotTotalDifficulty": "3954081103621304945444412938357146616762333285", "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 }, diff --git a/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg b/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg index d95cd68ebae4..15370f1285e2 100644 --- a/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/gnosis.cfg @@ -13,8 +13,8 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 35520000, - "PivotHash": "0x3bcec470f363f3cff3cfab3b8046b6e093b6506d495ab111169005ec5d8abafb", + "PivotNumber": 35640000, + "PivotHash": "0x9a45a58812240defeff37c4529b017aa6755761a65692a0f70531c94fcd70ded", "PivotTotalDifficulty": "8626000110427538733349499292577475819600160930", "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg index 560ed0406293..cf4d27d5b0fc 100644 --- a/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/joc-mainnet.cfg @@ -11,9 +11,9 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 12860000, - "PivotHash": "0x91fd87281b38f12604d8b3b89c6ba886bdc98d33bdca44301ea18b129016cb98", - "PivotTotalDifficulty": "25662111" + "PivotNumber": 12980000, + "PivotHash": "0xf93c58186b2b95a0173655b4f92ad8ecdb81455350c6e604174f9391aacc339a", + "PivotTotalDifficulty": "25869289" }, "Metrics": { "NodeName": "JOC-Mainnet" diff --git a/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg b/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg index cd81005c30e4..e01ea1d08114 100644 --- a/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/joc-testnet.cfg @@ -11,9 +11,9 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 6470000, - "PivotHash": "0x66fa9bb86047e2a05025010e5f8f0bd4b8a58ca8ae7f9558ab59e7dcefac0d39", - "PivotTotalDifficulty": "12222228" + "PivotNumber": 6590000, + "PivotHash": "0xc51c48adff9e07b17162b8dcd9da38d3308c86f7a0956e4481faea8759f85059", + "PivotTotalDifficulty": "12413994" }, "Metrics": { "NodeName": "JOC-Testnet" diff --git a/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg b/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg index 35aeb1cad88d..1819dd19c551 100644 --- a/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/mainnet.cfg @@ -9,8 +9,8 @@ "Sync": { "FastSync": true, "SnapSync": true, - "PivotNumber": 20550000, - "PivotHash": "0xf59ee916897eb3de726413f80153f8cd8ef3f19be27f33afd299d9c279307156", + "PivotNumber": 20600000, + "PivotHash": "0x4804cee837fd95195099e56fc5ed546c5982c751d137c4ec3dfed763c9bb491e", "PivotTotalDifficulty": "58750003716598352816469", "FastSyncCatchUpHeightDelta": "10000000000" }, diff --git a/src/Nethermind/Nethermind.Runner/configs/sepolia.cfg b/src/Nethermind/Nethermind.Runner/configs/sepolia.cfg index b5d511366714..d1a86544ccd4 100644 --- a/src/Nethermind/Nethermind.Runner/configs/sepolia.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/sepolia.cfg @@ -17,8 +17,8 @@ "FastSync": true, "SnapSync": true, "UseGethLimitsInFastBlocks": true, - "PivotNumber": 6519000, - "PivotHash": "0xdadbf382c47fdc82398a43725291f4974ba43749522e6200730c3f4b86cf6639", + "PivotNumber": 6564000, + "PivotHash": "0x6d545e42a90b8ba66b3186ad784393aef51c8b16e14eb27a0b82340414f30312", "PivotTotalDifficulty": "17000018015853232", "FastSyncCatchUpHeightDelta": "10000000000" }, diff --git a/src/Nethermind/Nethermind.Runner/configs/volta.cfg b/src/Nethermind/Nethermind.Runner/configs/volta.cfg index 5a1446680602..55e19cf497fd 100644 --- a/src/Nethermind/Nethermind.Runner/configs/volta.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/volta.cfg @@ -15,9 +15,9 @@ }, "Sync": { "FastSync": true, - "PivotNumber": 28950000, - "PivotHash": "0xee10b80896ce2ab038ddfdaeab6e8b63dfbc08d04ad4fa6c507c3de3acfb9efd", - "PivotTotalDifficulty": "9851174522361168517264694885149689721277612944", + "PivotNumber": 29040000, + "PivotHash": "0x6dd97c51e4d0285d5a3666e08dffe4647f024c1c86b4f66897b1598e0ebac2cb", + "PivotTotalDifficulty": "9881799935384052978976398599818548860308439398", "UseGethLimitsInFastBlocks": false, "FastSyncCatchUpHeightDelta": 10000000000 }, From e187cbdbbcfb17f81f6ae46593037989591e4006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Chodo=C5=82a?= <43241881+kamilchodola@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:55:11 +0200 Subject: [PATCH 3/8] Fix assertor docker image creation trigger (#7364) --- .github/workflows/test-assertoor.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test-assertoor.yml b/.github/workflows/test-assertoor.yml index 9d25b165b3e6..101e61fe2194 100644 --- a/.github/workflows/test-assertoor.yml +++ b/.github/workflows/test-assertoor.yml @@ -68,9 +68,7 @@ jobs: fi if - [[ "$ref" == "refs/heads/master" ]] && - [[ "${{ steps.extract_dockerfile.outputs.dockerfile }}" == "Dockerfile" ]] && - [[ "${{ steps.extract_dockerfile.outputs.build-config }}" == "release" ]]; then + [[ "$ref" == "refs/heads/master" ]]; then echo "skip_docker_build=true" >> $GITHUB_OUTPUT else echo "skip_docker_build=false" >> $GITHUB_OUTPUT From ebdf27ac23faa6381f37c75f3b716c85e8e1c26b Mon Sep 17 00:00:00 2001 From: Lukasz Rozmej Date: Mon, 26 Aug 2024 15:30:03 +0200 Subject: [PATCH 4/8] Removes custom args validation and uses library one (#7359) --- src/Nethermind/Nethermind.Config/ExitCodes.cs | 5 +- src/Nethermind/Nethermind.Runner/Program.cs | 114 ++++++------------ 2 files changed, 42 insertions(+), 77 deletions(-) diff --git a/src/Nethermind/Nethermind.Config/ExitCodes.cs b/src/Nethermind/Nethermind.Config/ExitCodes.cs index c6a4e1c7eec5..dd6a4381bee7 100644 --- a/src/Nethermind/Nethermind.Config/ExitCodes.cs +++ b/src/Nethermind/Nethermind.Config/ExitCodes.cs @@ -15,8 +15,9 @@ public static class ExitCodes public const int TooLongExtraData = 102; public const int ConflictingConfigurations = 103; public const int LowDiskSpace = 104; - public const int DuplicatedArguments = 105; - public const int UnrecognizedArgument = 106; + public const int DuplicatedOption = 105; + public const int UnrecognizedOption = 106; + public const int ForbiddenOptionValue = 107; // Posix exit code // https://tldp.org/LDP/abs/html/exitcodes.html diff --git a/src/Nethermind/Nethermind.Runner/Program.cs b/src/Nethermind/Nethermind.Runner/Program.cs index e400cb17ce82..e6d0b5780a04 100644 --- a/src/Nethermind/Nethermind.Runner/Program.cs +++ b/src/Nethermind/Nethermind.Runner/Program.cs @@ -45,7 +45,7 @@ namespace Nethermind.Runner; -public static class Program +public static partial class Program { private const string FailureString = "Failure"; private const string DefaultConfigsDirectory = "configs"; @@ -152,8 +152,6 @@ private static void Run(string[] args) BuildOptionsFromConfigFiles(app); - if (!ValidateArguments(args, app)) return; - app.OnExecute(async () => { IConfigProvider configProvider = BuildConfigProvider(app, loggerConfigSource, logLevelOverride, configsDirectory, configFile); @@ -232,6 +230,41 @@ private static void Run(string[] args) { Environment.ExitCode = app.Execute(args); } + catch (UnrecognizedCommandParsingException e) + { + string[] matches = e.NearestMatches.Take(3).ToArray(); + string suggestion = matches.Length switch + { + 0 => "", + 1 => $" Did you mean {matches[0]}", + _ => $" Did you mean one of: {string.Join(", ", matches)}" + }; + _logger.Error($"{e.Message}.{suggestion}"); + Environment.ExitCode = ExitCodes.UnrecognizedOption; + } + catch (CommandParsingException e) + { + Regex regex = GetUnexpectedConfigValueRegex(); + Match match = regex.Match(e.Message); + if (match.Success) + { + string option = match.Groups["Name"].Value; + CommandOption? optionInfo = app.GetOptions().FirstOrDefault(o => o.ShortName == option || o.LongName == option); + switch (optionInfo?.OptionType) + { + case CommandOptionType.SingleValue or CommandOptionType.SingleOrNoValue: + _logger.Error($"Duplicated option '{option}'"); + Environment.ExitCode = ExitCodes.DuplicatedOption; + return; + case CommandOptionType.NoValue: + _logger.Error($"Value {match.Groups["Value"].Value} passed for value-less option '{option}'"); + Environment.ExitCode = ExitCodes.ForbiddenOptionValue; + return; + } + } + + _logger.Error($"{e.Message}"); + } catch (Exception e) { if (e is IExceptionWithExitCode withExit) @@ -250,6 +283,9 @@ private static void Run(string[] args) } } + [GeneratedRegex("^Unexpected value '(?.+)' for option '(?.+)'", RegexOptions.Singleline)] + private static partial Regex GetUnexpectedConfigValueRegex(); + private static IntPtr OnResolvingUnmanagedDll(Assembly _, string nativeLibraryName) { var alternativePath = nativeLibraryName switch @@ -560,76 +596,4 @@ private static string GetProductInfo() return info.ToString(); } - - private static bool ValidateArguments(string[] args, CommandLineApplication app) - { - static HashSet> GetValidArguments(CommandLineApplication app) - { - HashSet> validArguments = new(new MemoryContentsComparer()); - foreach (var option in app.GetOptions()) - { - if (!string.IsNullOrEmpty(option.LongName)) - { - validArguments.Add(option.LongName.AsMemory()); - } - if (!string.IsNullOrEmpty(option.ShortName)) - { - validArguments.Add(option.ShortName.AsMemory()); - } - } - - return validArguments; - } - - static ReadOnlyMemory GetArgumentName(string arg) => arg.StartsWith("--") ? arg.AsMemory(2) : arg.StartsWith('-') ? arg.AsMemory(1) : ReadOnlyMemory.Empty; - static IEnumerable> GetArgumentNames(IEnumerable args) - { - bool lastWasArgument = false; - foreach (ReadOnlyMemory potentialArgument in args.Select(GetArgumentName)) - { - if (!lastWasArgument) - { - bool isCurrentArgument = lastWasArgument = !potentialArgument.IsEmpty; - if (isCurrentArgument) - { - yield return potentialArgument; - } - } - else - { - lastWasArgument = false; - } - } - } - - static IEnumerable> GetDuplicateArguments(IEnumerable> argumentsNames) => - argumentsNames.GroupBy(n => n, new MemoryContentsComparer()) - .Where(g => g.Count() > 1) - .Select(g => g.Key); - - // Get all valid options from the configuration files - HashSet> validArguments = GetValidArguments(app); - - IEnumerable> argumentsNamesProvided = GetArgumentNames(args); - foreach (ReadOnlyMemory argumentName in argumentsNamesProvided) - { - // Check if the argument provided is a valid option/argument - if (!validArguments.Contains(argumentName)) - { - _logger.Error($"Failed due to unrecognized argument - [{argumentName}].\nRun --help for a list of available options and commands."); - Environment.ExitCode = ExitCodes.UnrecognizedArgument; - return false; - } - } - - string duplicateArgumentsList = string.Join(", ", GetDuplicateArguments(argumentsNamesProvided)); - if (!string.IsNullOrEmpty(duplicateArgumentsList)) - { - _logger.Error($"Failed due to duplicated arguments - [{duplicateArgumentsList}] passed while execution"); - Environment.ExitCode = ExitCodes.DuplicatedArguments; - return false; - } - - return true; - } } From 12b70c562e3f9afeb4855192d026a36dd34e073d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Chodo=C5=82a?= <43241881+kamilchodola@users.noreply.github.com> Date: Mon, 26 Aug 2024 18:28:12 +0200 Subject: [PATCH 5/8] Add merge_group trigger condition to build (#7365) --- .github/workflows/build-solutions.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-solutions.yml b/.github/workflows/build-solutions.yml index f339c0f29204..78744f250fd8 100644 --- a/.github/workflows/build-solutions.yml +++ b/.github/workflows/build-solutions.yml @@ -5,6 +5,7 @@ on: branches: [master] push: branches: [master] + merge_group: jobs: build: From bd18f290a60335efeafa61d0ced5c203b1d780c1 Mon Sep 17 00:00:00 2001 From: Ruben Buniatyan Date: Mon, 26 Aug 2024 21:23:05 +0200 Subject: [PATCH 6/8] Upgrade to RocksDB v9.4.0 (#7300) --- src/Nethermind/Directory.Packages.props | 2 +- src/Nethermind/Nethermind.Db.Test/RocksDbTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Directory.Packages.props b/src/Nethermind/Directory.Packages.props index ff38b0df203e..6b405f92b29f 100644 --- a/src/Nethermind/Directory.Packages.props +++ b/src/Nethermind/Directory.Packages.props @@ -66,7 +66,7 @@ - + diff --git a/src/Nethermind/Nethermind.Db.Test/RocksDbTests.cs b/src/Nethermind/Nethermind.Db.Test/RocksDbTests.cs index 2ce6af1f31b3..750b7050abf6 100644 --- a/src/Nethermind/Nethermind.Db.Test/RocksDbTests.cs +++ b/src/Nethermind/Nethermind.Db.Test/RocksDbTests.cs @@ -11,5 +11,5 @@ namespace Nethermind.Db.Test; internal static class RocksDbTests { [Test] - public static void Should_have_required_version() => DbOnTheRocks.GetRocksDbVersion().Should().Be("8.10.0"); + public static void Should_have_required_version() => DbOnTheRocks.GetRocksDbVersion().Should().Be("9.4.0"); } From abde87243f7614e11b6a1048d24bcebc0623f334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Chodo=C5=82a?= <43241881+kamilchodola@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:19:20 +0200 Subject: [PATCH 7/8] Fix permissions for other workflow trigger (#7366) --- .github/workflows/sync-supported-chains.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/sync-supported-chains.yml b/.github/workflows/sync-supported-chains.yml index 8c074dd17b66..aaa90de2b539 100644 --- a/.github/workflows/sync-supported-chains.yml +++ b/.github/workflows/sync-supported-chains.yml @@ -232,6 +232,7 @@ jobs: with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + repositories: "nethermind,post-merge-smoke-tests" - name: Trigger notification action on test repo if: github.event.workflow_run.head_branch == 'master' || startsWith(github.event.workflow_run.head_branch, 'release/') From 29f2a2083342eaefaa58bc227ec237a8998b5b21 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 29 Aug 2024 16:16:14 +0100 Subject: [PATCH 8/8] Reduce collection resizing costs (#7360) --- .../Processing/BlockCachePreWarmer.cs | 2 + .../Processing/BlockProcessor.cs | 6 ++ .../Processing/IBlockCachePreWarmer.cs | 1 + ...yTests.cs => ConcurrentDictionaryTests.cs} | 12 ++- .../Nethermind.Core/Caching/ClockCache.cs | 7 +- .../Nethermind.Core/Caching/ClockKeyCache.cs | 7 +- .../Collections/CollectionExtensions.cs | 64 ++++++++++++ .../Resettables/ResettableHashSet.cs | 92 ----------------- .../Nethermind.State/IWorldState.cs | 3 + .../PartialStorageProviderBase.cs | 82 +++++++--------- .../PersistentStorageProvider.cs | 42 ++++---- .../Nethermind.State/PreBlockCaches.cs | 50 +++++++--- .../Nethermind.State/StateProvider.cs | 98 ++++++++----------- src/Nethermind/Nethermind.State/WorldState.cs | 6 +- .../Nethermind.Trie/PreCachedTrieStore.cs | 3 +- .../Nethermind.Trie/Pruning/TrieStore.cs | 64 +++++++----- src/Nethermind/Nethermind.Trie/TrieNode.cs | 2 +- 17 files changed, 281 insertions(+), 260 deletions(-) rename src/Nethermind/Nethermind.Core.Test/Collections/{LockableConcurrentDictionaryTests.cs => ConcurrentDictionaryTests.cs} (73%) delete mode 100644 src/Nethermind/Nethermind.Core/Resettables/ResettableHashSet.cs diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs index c972b86f9d29..3f4e8d08b163 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs @@ -48,6 +48,8 @@ public Task PreWarmCaches(Block suggestedBlock, Hash256? parentStateRoot, Cancel public void ClearCaches() => targetWorldState?.ClearCache(); + public Task ClearCachesInBackground() => targetWorldState?.ClearCachesInBackground() ?? Task.CompletedTask; + private void PreWarmCachesParallel(Block suggestedBlock, Hash256 parentStateRoot, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) return; diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 434e213ddd1f..0908e40db324 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -51,6 +51,7 @@ public partial class BlockProcessor : IBlockProcessor /// to any block-specific tracers. /// protected BlockReceiptsTracer ReceiptsTracer { get; set; } + private readonly Func _clearCaches; public BlockProcessor( ISpecProvider? specProvider, @@ -78,6 +79,7 @@ public BlockProcessor( _preWarmer = preWarmer; _beaconBlockRootHandler = new BeaconBlockRootHandler(); ReceiptsTracer = new BlockReceiptsTracer(); + _clearCaches = _ => _preWarmer.ClearCachesInBackground(); } public event EventHandler? BlockProcessed; @@ -123,6 +125,10 @@ the previous head state.*/ : _preWarmer?.PreWarmCaches(suggestedBlock, preBlockStateRoot!, cancellationTokenSource.Token); (Block processedBlock, TxReceipt[] receipts) = ProcessOne(suggestedBlock, options, blockTracer); // Block is processed, we can cancel the prewarm task + if (preWarmTask is not null) + { + preWarmTask = preWarmTask.ContinueWith(_clearCaches).Unwrap(); + } cancellationTokenSource.Cancel(); processedBlocks[i] = processedBlock; diff --git a/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs b/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs index 609e802d44c2..aa6d9769005e 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs @@ -12,4 +12,5 @@ public interface IBlockCachePreWarmer { Task PreWarmCaches(Block suggestedBlock, Hash256 parentStateRoot, CancellationToken cancellationToken = default); void ClearCaches(); + Task ClearCachesInBackground(); } diff --git a/src/Nethermind/Nethermind.Core.Test/Collections/LockableConcurrentDictionaryTests.cs b/src/Nethermind/Nethermind.Core.Test/Collections/ConcurrentDictionaryTests.cs similarity index 73% rename from src/Nethermind/Nethermind.Core.Test/Collections/LockableConcurrentDictionaryTests.cs rename to src/Nethermind/Nethermind.Core.Test/Collections/ConcurrentDictionaryTests.cs index 3ac80bec60f5..d4cc7fee4ac6 100644 --- a/src/Nethermind/Nethermind.Core.Test/Collections/LockableConcurrentDictionaryTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Collections/ConcurrentDictionaryTests.cs @@ -10,7 +10,7 @@ namespace Nethermind.Core.Test.Collections; -public class LockableConcurrentDictionaryTests +public class ConcurrentDictionaryTests { [Test] public void Locks() @@ -28,4 +28,14 @@ public void Locks() updateTask.Wait(); dictionary.ContainsKey(3).Should().BeTrue(); } + + [Test] + public void NoResizeClear() + { + // Tests that the reflection works + ConcurrentDictionary dictionary = new(new Dictionary { { 0, 0 }, { 1, 1 }, { 2, 2 } }); + dictionary.NoResizeClear(); + + dictionary.Count.Should().Be(0); + } } diff --git a/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs b/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs index 3afa9e03660f..8d30ef44fcee 100644 --- a/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs +++ b/src/Nethermind/Nethermind.Core/Caching/ClockCache.cs @@ -7,14 +7,17 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using Nethermind.Core.Collections; using Nethermind.Core.Threading; +using CollectionExtensions = Nethermind.Core.Collections.CollectionExtensions; + namespace Nethermind.Core.Caching; public sealed class ClockCache(int maxCapacity) : ClockCacheBase(maxCapacity) where TKey : struct, IEquatable { - private readonly ConcurrentDictionary _cacheMap = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _cacheMap = new(CollectionExtensions.LockPartitions, maxCapacity); private readonly McsLock _lock = new(); public TValue Get(TKey key) @@ -154,7 +157,7 @@ public bool Delete(TKey key) using var lockRelease = _lock.Acquire(); base.Clear(); - _cacheMap.Clear(); + _cacheMap.NoResizeClear(); } public bool Contains(TKey key) diff --git a/src/Nethermind/Nethermind.Core/Caching/ClockKeyCache.cs b/src/Nethermind/Nethermind.Core/Caching/ClockKeyCache.cs index df3ee975dca7..ad5dec88ccad 100644 --- a/src/Nethermind/Nethermind.Core/Caching/ClockKeyCache.cs +++ b/src/Nethermind/Nethermind.Core/Caching/ClockKeyCache.cs @@ -7,14 +7,17 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using Nethermind.Core.Collections; using Nethermind.Core.Threading; +using CollectionExtensions = Nethermind.Core.Collections.CollectionExtensions; + namespace Nethermind.Core.Caching; public sealed class ClockKeyCache(int maxCapacity) : ClockCacheBase(maxCapacity) where TKey : struct, IEquatable { - private readonly ConcurrentDictionary _cacheMap = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _cacheMap = new(CollectionExtensions.LockPartitions, maxCapacity); private readonly McsLock _lock = new(); public bool Get(TKey key) @@ -128,7 +131,7 @@ public bool Delete(TKey key) using var lockRelease = _lock.Acquire(); base.Clear(); - _cacheMap.Clear(); + _cacheMap.NoResizeClear(); } public bool Contains(TKey key) diff --git a/src/Nethermind/Nethermind.Core/Collections/CollectionExtensions.cs b/src/Nethermind/Nethermind.Core/Collections/CollectionExtensions.cs index 51bb83fda0d6..7996b5f8b5ba 100644 --- a/src/Nethermind/Nethermind.Core/Collections/CollectionExtensions.cs +++ b/src/Nethermind/Nethermind.Core/Collections/CollectionExtensions.cs @@ -1,12 +1,19 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; +using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; namespace Nethermind.Core.Collections { public static class CollectionExtensions { + public static int LockPartitions { get; } = Environment.ProcessorCount * 16; + public static void AddRange(this ICollection list, IEnumerable items) { foreach (T item in items) @@ -22,5 +29,62 @@ public static void AddRange(this ICollection list, params T[] items) list.Add(items[index]); } } + + public static bool NoResizeClear(this ConcurrentDictionary? dictionary) + where TKey : notnull + { + if (dictionary is null || dictionary.IsEmpty) + { + return false; + } + + using var handle = dictionary.AcquireLock(); + + ClearCache.Clear(dictionary); + return true; + } + + private static class ClearCache where TKey : notnull + { + public static readonly Action> Clear = CreateNoResizeClearExpression(); + + private static Action> CreateNoResizeClearExpression() + { + // Parameters + var dictionaryParam = Expression.Parameter(typeof(ConcurrentDictionary), "dictionary"); + + // Access _tables field + var tablesField = typeof(ConcurrentDictionary).GetField("_tables", BindingFlags.NonPublic | BindingFlags.Instance); + var tablesAccess = Expression.Field(dictionaryParam, tablesField!); + + // Access _buckets and _countPerLock fields + var tablesType = tablesField!.FieldType; + var bucketsField = tablesType.GetField("_buckets", BindingFlags.NonPublic | BindingFlags.Instance); + var countPerLockField = tablesType.GetField("_countPerLock", BindingFlags.NonPublic | BindingFlags.Instance); + + var bucketsAccess = Expression.Field(tablesAccess, bucketsField!); + var countPerLockAccess = Expression.Field(tablesAccess, countPerLockField!); + + // Clear arrays using Array.Clear + var clearMethod = typeof(Array).GetMethod("Clear", new[] { typeof(Array), typeof(int), typeof(int) }); + + var clearBuckets = Expression.Call(clearMethod!, + bucketsAccess, + Expression.Constant(0), + Expression.ArrayLength(bucketsAccess)); + + var clearCountPerLock = Expression.Call(clearMethod!, + countPerLockAccess, + Expression.Constant(0), + Expression.ArrayLength(countPerLockAccess)); + + // Block to execute both clears + var block = Expression.Block(clearBuckets, clearCountPerLock); + + // Compile the expression into a lambda + return Expression.Lambda>>(block, dictionaryParam).Compile(); + } + } } } + diff --git a/src/Nethermind/Nethermind.Core/Resettables/ResettableHashSet.cs b/src/Nethermind/Nethermind.Core/Resettables/ResettableHashSet.cs deleted file mode 100644 index 4db6c75bd799..000000000000 --- a/src/Nethermind/Nethermind.Core/Resettables/ResettableHashSet.cs +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace Nethermind.Core.Resettables -{ - public class ResettableHashSet : ICollection, IReadOnlyCollection - { - private int _currentCapacity; - private readonly int _startCapacity; - private readonly int _resetRatio; - - private HashSet _wrapped; - - public ResettableHashSet(int startCapacity = Resettable.StartCapacity, int resetRatio = Resettable.ResetRatio) - { - _wrapped = new HashSet(startCapacity); - _startCapacity = startCapacity; - _resetRatio = resetRatio; - _currentCapacity = _startCapacity; - } - - public IEnumerator GetEnumerator() - { - return _wrapped.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public void Add(T item) - { - _wrapped.Add(item); - } - - public void Clear() - { - _wrapped.Clear(); - } - - public bool Contains(T item) - { - return _wrapped.Contains(item); - } - - public void CopyTo(T[] array, int arrayIndex) - { - _wrapped.CopyTo(array, arrayIndex); - } - - public bool Remove(T item) - { - return _wrapped.Remove(item); - } - - public int Count => _wrapped.Count; - public bool IsReadOnly => false; - - public void Reset(bool resizeCollections = true) - { - if (_wrapped.Count == 0) - { - return; - } - if (!resizeCollections) - { - _wrapped.Clear(); - return; - } - - if (_wrapped.Count < _currentCapacity / _resetRatio && _currentCapacity != _startCapacity) - { - _currentCapacity = Math.Max(_startCapacity, _currentCapacity / _resetRatio); - _wrapped = new HashSet(_currentCapacity); - } - else - { - while (_wrapped.Count > _currentCapacity) - { - _currentCapacity *= _resetRatio; - } - - _wrapped.Clear(); - } - } - } -} diff --git a/src/Nethermind/Nethermind.State/IWorldState.cs b/src/Nethermind/Nethermind.State/IWorldState.cs index 9c78813ce93b..a7b0aa82cc76 100644 --- a/src/Nethermind/Nethermind.State/IWorldState.cs +++ b/src/Nethermind/Nethermind.State/IWorldState.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Threading.Tasks; using Nethermind.Core; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; @@ -114,4 +115,6 @@ public interface IWorldState : IJournal, IReadOnlyStateProvider ArrayPoolList? GetAccountChanges(); bool ClearCache() => false; + + Task ClearCachesInBackground() => Task.CompletedTask; } diff --git a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs index ec0e284d260d..69a3c2fa4335 100644 --- a/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs +++ b/src/Nethermind/Nethermind.State/PartialStorageProviderBase.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using Nethermind.Core; using Nethermind.Core.Collections; using Nethermind.Core.Resettables; @@ -16,14 +17,10 @@ namespace Nethermind.State /// internal abstract class PartialStorageProviderBase { - protected readonly ResettableDictionary> _intraBlockCache = new(); - + protected readonly Dictionary> _intraBlockCache = new(); protected readonly ILogger _logger; - - private const int StartCapacity = Resettable.StartCapacity; - private int _capacity = StartCapacity; - protected Change?[] _changes = new Change[StartCapacity]; - protected int _currentPosition = Resettable.EmptyPosition; + protected readonly List _changes = new(Resettable.StartCapacity); + private readonly List _keptInCache = new(); // stack of snapshot indexes on changes for start of each transaction // this is needed for OriginalValues for new transactions @@ -63,13 +60,14 @@ public void Set(in StorageCell storageCell, byte[] newValue) /// Snapshot index public int TakeSnapshot(bool newTransactionStart) { - if (_logger.IsTrace) _logger.Trace($"Storage snapshot {_currentPosition}"); - if (newTransactionStart && _currentPosition != Resettable.EmptyPosition) + int position = _changes.Count - 1; + if (_logger.IsTrace) _logger.Trace($"Storage snapshot {position}"); + if (newTransactionStart && position != Resettable.EmptyPosition) { - _transactionChangesSnapshots.Push(_currentPosition); + _transactionChangesSnapshots.Push(position); } - return _currentPosition; + return position; } /// @@ -81,45 +79,44 @@ public void Restore(int snapshot) { if (_logger.IsTrace) _logger.Trace($"Restoring storage snapshot {snapshot}"); - if (snapshot > _currentPosition) + int currentPosition = _changes.Count - 1; + if (snapshot > currentPosition) { - throw new InvalidOperationException($"{GetType().Name} tried to restore snapshot {snapshot} beyond current position {_currentPosition}"); + throw new InvalidOperationException($"{GetType().Name} tried to restore snapshot {snapshot} beyond current position {currentPosition}"); } - if (snapshot == _currentPosition) + if (snapshot == currentPosition) { return; } - List keptInCache = new(); - - for (int i = 0; i < _currentPosition - snapshot; i++) + for (int i = 0; i < currentPosition - snapshot; i++) { - Change change = _changes[_currentPosition - i]; + Change change = _changes[currentPosition - i]; StackList stack = _intraBlockCache[change!.StorageCell]; if (stack.Count == 1) { if (_changes[stack.Peek()]!.ChangeType == ChangeType.JustCache) { int actualPosition = stack.Pop(); - if (actualPosition != _currentPosition - i) + if (actualPosition != currentPosition - i) { - throw new InvalidOperationException($"Expected actual position {actualPosition} to be equal to {_currentPosition} - {i}"); + throw new InvalidOperationException($"Expected actual position {actualPosition} to be equal to {currentPosition} - {i}"); } - keptInCache.Add(change); + _keptInCache.Add(change); _changes[actualPosition] = null; continue; } } int forAssertion = stack.Pop(); - if (forAssertion != _currentPosition - i) + if (forAssertion != currentPosition - i) { - throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {_currentPosition} - {i}"); + throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {currentPosition} - {i}"); } - _changes[_currentPosition - i] = null; + _changes[currentPosition - i] = null; if (stack.Count == 0) { @@ -127,14 +124,17 @@ public void Restore(int snapshot) } } - _currentPosition = snapshot; - foreach (Change kept in keptInCache) + CollectionsMarshal.SetCount(_changes, snapshot + 1); + currentPosition = _changes.Count - 1; + foreach (Change kept in _keptInCache) { - _currentPosition++; - _changes[_currentPosition] = kept; - _intraBlockCache[kept.StorageCell].Push(_currentPosition); + currentPosition++; + _changes.Add(kept); + _intraBlockCache[kept.StorageCell].Push(currentPosition); } + _keptInCache.Clear(); + while (_transactionChangesSnapshots.TryPeek(out int lastOriginalSnapshot) && lastOriginalSnapshot > snapshot) { _transactionChangesSnapshots.Pop(); @@ -174,7 +174,7 @@ public ChangeTrace(byte[]? after) /// State tracer public void Commit(IStorageTracer tracer, bool commitStorageRoots = true) { - if (_currentPosition == Snapshot.EmptyPosition) + if (_changes.Count == 0) { if (_logger.IsTrace) _logger.Trace("No storage changes to commit"); } @@ -201,8 +201,8 @@ protected virtual void CommitStorageRoots() /// Storage tracer protected virtual void CommitCore(IStorageTracer tracer) { - Resettable.Reset(ref _changes, ref _capacity, ref _currentPosition); - _intraBlockCache.Reset(); + _changes.Clear(); + _intraBlockCache.Clear(); _transactionChangesSnapshots.Clear(); } @@ -213,10 +213,9 @@ public virtual void Reset(bool resizeCollections = true) { if (_logger.IsTrace) _logger.Trace("Resetting storage"); + _changes.Clear(); _intraBlockCache.Clear(); _transactionChangesSnapshots.Clear(); - _currentPosition = -1; - Array.Clear(_changes, 0, _changes.Length); } /// @@ -255,17 +254,8 @@ protected bool TryGetCachedValue(in StorageCell storageCell, out byte[]? bytes) private void PushUpdate(in StorageCell cell, byte[] value) { StackList stack = SetupRegistry(cell); - IncrementChangePosition(); - stack.Push(_currentPosition); - _changes[_currentPosition] = new Change(ChangeType.Update, cell, value); - } - - /// - /// Increment position and size (if needed) of _changes - /// - protected void IncrementChangePosition() - { - Resettable.IncrementPosition(ref _changes, ref _capacity, ref _currentPosition); + stack.Push(_changes.Count); + _changes.Add(new Change(ChangeType.Update, cell, value)); } /// @@ -274,7 +264,7 @@ protected void IncrementChangePosition() /// protected StackList SetupRegistry(in StorageCell cell) { - ref StackList? value = ref _intraBlockCache.GetValueRefOrAddDefault(cell, out bool exists); + ref StackList? value = ref CollectionsMarshal.GetValueRefOrAddDefault(_intraBlockCache, cell, out bool exists); if (!exists) { value = new StackList(); diff --git a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs index f39a9bdb507f..2e154a6074cc 100644 --- a/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs +++ b/src/Nethermind/Nethermind.State/PersistentStorageProvider.cs @@ -11,7 +11,6 @@ using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; -using Nethermind.Core.Resettables; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.State.Tracing; @@ -30,15 +29,15 @@ internal sealed class PersistentStorageProvider : PartialStorageProviderBase private readonly StateProvider _stateProvider; private readonly ILogManager? _logManager; internal readonly IStorageTreeFactory _storageTreeFactory; - private readonly ResettableDictionary _storages = new(); + private readonly Dictionary _storages = new(); private readonly HashSet _toUpdateRoots = new(); /// /// EIP-1283 /// - private readonly ResettableDictionary _originalValues = new(); + private readonly Dictionary _originalValues = new(); - private readonly ResettableHashSet _committedThisRound = new(); + private readonly HashSet _committedThisRound = new(); private readonly Dictionary> _blockCache = new(4_096); private readonly ConcurrentDictionary? _preBlockCache; private readonly Func _loadFromTree; @@ -73,7 +72,7 @@ public override void Reset(bool resizeCollections = true) { base.Reset(); _blockCache.Clear(); - _storages.Reset(resizeCollections); + _storages.Clear(); _originalValues.Clear(); _committedThisRound.Clear(); _toUpdateRoots.Clear(); @@ -123,14 +122,14 @@ protected override void CommitCore(IStorageTracer tracer) { if (_logger.IsTrace) _logger.Trace("Committing storage changes"); - if (_changes[_currentPosition] is null) + int currentPosition = _changes.Count - 1; + if (currentPosition < 0) { - throw new InvalidOperationException($"Change at current position {_currentPosition} was null when commiting {nameof(PartialStorageProviderBase)}"); + return; } - - if (_changes[_currentPosition + 1] is not null) + if (_changes[currentPosition] is null) { - throw new InvalidOperationException($"Change after current position ({_currentPosition} + 1) was not null when commiting {nameof(PartialStorageProviderBase)}"); + throw new InvalidOperationException($"Change at current position {currentPosition} was null when committing {nameof(PartialStorageProviderBase)}"); } HashSet
toUpdateRoots = new(); @@ -142,9 +141,9 @@ protected override void CommitCore(IStorageTracer tracer) trace = new Dictionary(); } - for (int i = 0; i <= _currentPosition; i++) + for (int i = 0; i <= currentPosition; i++) { - Change change = _changes[_currentPosition - i]; + Change change = _changes[currentPosition - i]; if (!isTracing && change!.ChangeType == ChangeType.JustCache) { continue; @@ -173,9 +172,9 @@ protected override void CommitCore(IStorageTracer tracer) } int forAssertion = _intraBlockCache[change.StorageCell].Pop(); - if (forAssertion != _currentPosition - i) + if (forAssertion != currentPosition - i) { - throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {_currentPosition} - {i}"); + throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {currentPosition} - {i}"); } switch (change.ChangeType) @@ -218,8 +217,8 @@ protected override void CommitCore(IStorageTracer tracer) } base.CommitCore(tracer); - _originalValues.Reset(); - _committedThisRound.Reset(); + _originalValues.Clear(); + _committedThisRound.Clear(); if (isTracing) { @@ -329,13 +328,13 @@ public void CommitTrees(long blockNumber) _toUpdateRoots.Clear(); // only needed here as there is no control over cached storage size otherwise - _storages.Reset(); - _preBlockCache?.Clear(); + _storages.Clear(); + _preBlockCache?.NoResizeClear(); } private StorageTree GetOrCreateStorage(Address address) { - ref StorageTree? value = ref _storages.GetValueRefOrAddDefault(address, out bool exists); + ref StorageTree? value = ref CollectionsMarshal.GetValueRefOrAddDefault(_storages, address, out bool exists); if (!exists) { value = _storageTreeFactory.Create(address, _trieStore.GetTrieStore(address.ToAccountPath), _stateProvider.GetStorageRoot(address), StateRoot, _logManager); @@ -422,10 +421,9 @@ private byte[] LoadFromTreeStorage(StorageCell storageCell) private void PushToRegistryOnly(in StorageCell cell, byte[] value) { StackList stack = SetupRegistry(cell); - IncrementChangePosition(); - stack.Push(_currentPosition); _originalValues[cell] = value; - _changes[_currentPosition] = new Change(ChangeType.JustCache, cell, value); + stack.Push(_changes.Count); + _changes.Add(new Change(ChangeType.JustCache, cell, value)); } private static void ReportChanges(IStorageTracer tracer, Dictionary trace) diff --git a/src/Nethermind/Nethermind.State/PreBlockCaches.cs b/src/Nethermind/Nethermind.State/PreBlockCaches.cs index 39a5b39e0285..8967c4466619 100644 --- a/src/Nethermind/Nethermind.State/PreBlockCaches.cs +++ b/src/Nethermind/Nethermind.State/PreBlockCaches.cs @@ -5,28 +5,54 @@ using System.Collections.Concurrent; using System.Numerics; using System.Runtime.InteropServices; - +using System.Threading.Tasks; using Nethermind.Core; -using Nethermind.Core.Crypto; +using Nethermind.Core.Collections; using Nethermind.Trie; +using CollectionExtensions = Nethermind.Core.Collections.CollectionExtensions; + namespace Nethermind.State; public class PreBlockCaches { - public ConcurrentDictionary StorageCache { get; } = new(Environment.ProcessorCount * 2, 4096 * 4); - public ConcurrentDictionary StateCache { get; } = new(Environment.ProcessorCount * 2, 4096 * 4); - public ConcurrentDictionary RlpCache { get; } = new(Environment.ProcessorCount * 2, 4096 * 4); - public ConcurrentDictionary, bool)> PrecompileCache { get; } = new(Environment.ProcessorCount * 2, 4096 * 4); + private const int InitialCapacity = 4096 * 8; + private static int LockPartitions => CollectionExtensions.LockPartitions; + + private readonly Func[] _clearCaches; + private readonly Action _clearAllCaches; + + private readonly ConcurrentDictionary _storageCache = new(LockPartitions, InitialCapacity); + private readonly ConcurrentDictionary _stateCache = new(LockPartitions, InitialCapacity); + private readonly ConcurrentDictionary _rlpCache = new(LockPartitions, InitialCapacity); + private readonly ConcurrentDictionary, bool)> _precompileCache = new(LockPartitions, InitialCapacity); + + public PreBlockCaches() + { + _clearCaches = + [ + _storageCache.NoResizeClear, + _stateCache.NoResizeClear, + _rlpCache.NoResizeClear, + _precompileCache.NoResizeClear + ]; + + _clearAllCaches = () => ClearImmediate(); + } + + public ConcurrentDictionary StorageCache => _storageCache; + public ConcurrentDictionary StateCache => _stateCache; + public ConcurrentDictionary RlpCache => _rlpCache; + public ConcurrentDictionary, bool)> PrecompileCache => _precompileCache; + + public Task ClearCachesInBackground() => Task.Run(_clearAllCaches); - public bool Clear() + public bool ClearImmediate() { - bool isDirty = !StorageCache.IsEmpty || !StateCache.IsEmpty || !RlpCache.IsEmpty; - if (isDirty) + bool isDirty = false; + foreach (Func clearCache in _clearCaches) { - StorageCache.Clear(); - StateCache.Clear(); - RlpCache.Clear(); + isDirty |= clearCache(); } return isDirty; diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 81cd907963b0..abcd0c150219 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -22,9 +22,8 @@ namespace Nethermind.State { internal class StateProvider { - private const int StartCapacity = Resettable.StartCapacity; - private readonly ResettableDictionary> _intraTxCache = new(); - private readonly ResettableHashSet _committedThisRound = new(); + private readonly Dictionary> _intraTxCache = new(); + private readonly HashSet _committedThisRound = new(); private readonly HashSet _nullAccountReads = new(); // Only guarding against hot duplicates so filter doesn't need to be too big // Note: @@ -38,9 +37,7 @@ internal class StateProvider private readonly ILogger _logger; private readonly IKeyValueStore _codeDb; - private int _capacity = StartCapacity; - private Change?[] _changes = new Change?[StartCapacity]; - private int _currentPosition = Resettable.EmptyPosition; + private List _changes = new(Resettable.StartCapacity); internal readonly StateTree _tree; private readonly Func _getStateFromTrie; @@ -317,35 +314,37 @@ public void DeleteAccount(Address address) public int TakeSnapshot() { - if (_logger.IsTrace) _logger.Trace($"State snapshot {_currentPosition}"); - return _currentPosition; + int currentPosition = _changes.Count - 1; + if (_logger.IsTrace) _logger.Trace($"State snapshot {currentPosition}"); + return currentPosition; } public void Restore(int snapshot) { - if (snapshot > _currentPosition) + int currentPosition = _changes.Count - 1; + if (snapshot > currentPosition) { - throw new InvalidOperationException($"{nameof(StateProvider)} tried to restore snapshot {snapshot} beyond current position {_currentPosition}"); + throw new InvalidOperationException($"{nameof(StateProvider)} tried to restore snapshot {snapshot} beyond current position {currentPosition}"); } if (_logger.IsTrace) _logger.Trace($"Restoring state snapshot {snapshot}"); - if (snapshot == _currentPosition) + if (snapshot == currentPosition) { return; } - for (int i = 0; i < _currentPosition - snapshot; i++) + for (int i = 0; i < currentPosition - snapshot; i++) { - Change change = _changes[_currentPosition - i]; + Change change = _changes[currentPosition - i]; Stack stack = _intraTxCache[change!.Address]; if (stack.Count == 1) { if (change.ChangeType == ChangeType.JustCache) { int actualPosition = stack.Pop(); - if (actualPosition != _currentPosition - i) + if (actualPosition != currentPosition - i) { - throw new InvalidOperationException($"Expected actual position {actualPosition} to be equal to {_currentPosition} - {i}"); + throw new InvalidOperationException($"Expected actual position {actualPosition} to be equal to {currentPosition} - {i}"); } _keptInCache.Add(change); @@ -354,11 +353,11 @@ public void Restore(int snapshot) } } - _changes[_currentPosition - i] = null; // TODO: temp, ??? + _changes[currentPosition - i] = null; // TODO: temp, ??? int forChecking = stack.Pop(); - if (forChecking != _currentPosition - i) + if (forChecking != currentPosition - i) { - throw new InvalidOperationException($"Expected checked value {forChecking} to be equal to {_currentPosition} - {i}"); + throw new InvalidOperationException($"Expected checked value {forChecking} to be equal to {currentPosition} - {i}"); } if (stack.Count == 0) @@ -367,12 +366,13 @@ public void Restore(int snapshot) } } - _currentPosition = snapshot; + CollectionsMarshal.SetCount(_changes, snapshot + 1); + currentPosition = _changes.Count - 1; foreach (Change kept in _keptInCache) { - _currentPosition++; - _changes[_currentPosition] = kept; - _intraTxCache[kept.Address].Push(_currentPosition); + currentPosition++; + _changes.Add(kept); + _intraTxCache[kept.Address].Push(currentPosition); } _keptInCache.Clear(); @@ -431,21 +431,17 @@ public ChangeTrace(Account? after) public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer stateTracer, bool isGenesis = false) { - if (_currentPosition == -1) + var currentPosition = _changes.Count - 1; + if (currentPosition < 0) { if (_logger.IsTrace) _logger.Trace(" no state changes to commit"); return; } - if (_logger.IsTrace) _logger.Trace($"Committing state changes (at {_currentPosition})"); - if (_changes[_currentPosition] is null) + if (_logger.IsTrace) _logger.Trace($"Committing state changes (at {currentPosition})"); + if (_changes[currentPosition] is null) { - throw new InvalidOperationException($"Change at current position {_currentPosition} was null when commiting {nameof(StateProvider)}"); - } - - if (_changes[_currentPosition + 1] is not null) - { - throw new InvalidOperationException($"Change after current position ({_currentPosition} + 1) was not null when commiting {nameof(StateProvider)}"); + throw new InvalidOperationException($"Change at current position {currentPosition} was null when committing {nameof(StateProvider)}"); } bool isTracing = stateTracer.IsTracingState; @@ -455,9 +451,9 @@ public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer stateTracer, bool trace = new Dictionary(); } - for (int i = 0; i <= _currentPosition; i++) + for (int i = 0; i <= currentPosition; i++) { - Change change = _changes[_currentPosition - i]; + Change change = _changes[currentPosition - i]; if (!isTracing && change!.ChangeType == ChangeType.JustCache) { continue; @@ -482,9 +478,9 @@ public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer stateTracer, bool Stack stack = _intraTxCache[change.Address]; int forAssertion = stack.Pop(); - if (forAssertion != _currentPosition - i) + if (forAssertion != currentPosition - i) { - throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {_currentPosition} - {i}"); + throw new InvalidOperationException($"Expected checked value {forAssertion} to be equal to {currentPosition} - {i}"); } _committedThisRound.Add(change.Address); @@ -572,10 +568,10 @@ public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer stateTracer, bool } } - Resettable.Reset(ref _changes, ref _capacity, ref _currentPosition, StartCapacity); - _committedThisRound.Reset(); + _changes.Clear(); + _committedThisRound.Clear(); _nullAccountReads.Clear(); - _intraTxCache.Reset(); + _intraTxCache.Clear(); if (isTracing) { @@ -776,27 +772,20 @@ private void Push(ChangeType changeType, Address address, Account? touchedAccoun return; } - IncrementChangePosition(); - stack.Push(_currentPosition); - _changes[_currentPosition] = new Change(changeType, address, touchedAccount); + stack.Push(_changes.Count); + _changes.Add(new Change(changeType, address, touchedAccount)); } private void PushNew(Address address, Account account) { Stack stack = SetupCache(address); - IncrementChangePosition(); - stack.Push(_currentPosition); - _changes[_currentPosition] = new Change(ChangeType.New, address, account); - } - - private void IncrementChangePosition() - { - Resettable.IncrementPosition(ref _changes, ref _capacity, ref _currentPosition); + stack.Push(_changes.Count); + _changes.Add(new Change(ChangeType.New, address, account)); } private Stack SetupCache(Address address) { - ref Stack? value = ref _intraTxCache.GetValueRefOrAddDefault(address, out bool exists); + ref Stack? value = ref CollectionsMarshal.GetValueRefOrAddDefault(_intraTxCache, address, out bool exists); if (!exists) { value = new Stack(); @@ -850,11 +839,10 @@ public void Reset(bool resizeCollections = true) { if (_logger.IsTrace) _logger.Trace("Clearing state provider caches"); _blockCache.Clear(); - _intraTxCache.Reset(resizeCollections); - _committedThisRound.Reset(resizeCollections); + _intraTxCache.Clear(); + _committedThisRound.Clear(); _nullAccountReads.Clear(); - _currentPosition = Resettable.EmptyPosition; - Array.Clear(_changes, 0, _changes.Length); + _changes.Clear(); _needsStateRootUpdate = false; } @@ -866,7 +854,7 @@ public void CommitTree(long blockNumber) } _tree.Commit(blockNumber); - _preBlockCache?.Clear(); + _preBlockCache?.NoResizeClear(); } public static void CommitBranch() diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index 60f5492cd601..d83f2d7c71ed 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using Nethermind.Core; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; @@ -272,6 +272,8 @@ public void CreateAccountIfNotExists(Address address, in UInt256 balance, in UIn PreBlockCaches? IPreBlockCaches.Caches => PreBlockCaches; - public bool ClearCache() => PreBlockCaches?.Clear() == true; + public bool ClearCache() => PreBlockCaches?.ClearImmediate() == true; + + public Task ClearCachesInBackground() => PreBlockCaches?.ClearCachesInBackground() ?? Task.CompletedTask; } } diff --git a/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs b/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs index 564d91675473..08d580b1de52 100644 --- a/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Numerics; using Nethermind.Core; +using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Trie.Pruning; @@ -41,7 +42,7 @@ public void CommitNode(long blockNumber, Hash256? address, in NodeCommitInfo nod public void FinishBlockCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) { _inner.FinishBlockCommit(trieType, blockNumber, address, root, writeFlags); - _preBlockCache.Clear(); + _preBlockCache.NoResizeClear(); } public bool IsPersisted(Hash256? address, in TreePath path, in ValueHash256 keccak) diff --git a/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs index 9d7972ec11c4..24bbf58dcfca 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs @@ -22,6 +22,8 @@ namespace Nethermind.Trie.Pruning { using Nethermind.Core.Cpu; + using CollectionExtensions = Core.Collections.CollectionExtensions; + /// /// Trie store helps to manage trie commits block by block. /// If persistence and pruning are needed they have a chance to execute their behaviour on commits. @@ -109,11 +111,10 @@ void Trace(TrieNode trieNode) } } - private static readonly int _concurrencyLevel = HashHelpers.GetPrime(Environment.ProcessorCount * 4); private static readonly int _initialBuckets = HashHelpers.GetPrime(Math.Max(31, Environment.ProcessorCount * 16)); - private readonly ConcurrentDictionary _byKeyObjectCache = new(_concurrencyLevel, _initialBuckets); - private readonly ConcurrentDictionary _byHashObjectCache = new(_concurrencyLevel, _initialBuckets); + private readonly ConcurrentDictionary _byKeyObjectCache = new(CollectionExtensions.LockPartitions, _initialBuckets); + private readonly ConcurrentDictionary _byHashObjectCache = new(CollectionExtensions.LockPartitions, _initialBuckets); public bool IsNodeCached(in Key key) { @@ -205,8 +206,8 @@ public void Dump() public void Clear() { - _byHashObjectCache.Clear(); - _byKeyObjectCache.Clear(); + _byHashObjectCache.NoResizeClear(); + _byKeyObjectCache.NoResizeClear(); Interlocked.Exchange(ref _count, 0); Metrics.CachedNodesCount = 0; _trieStore.MemoryUsedByDirtyCache = 0; @@ -285,7 +286,7 @@ public readonly void Dispose() // Track ALL of the recently re-committed persisted nodes. This is so that we don't accidentally remove // recommitted persisted nodes (which will not get re-persisted). - private readonly NonBlocking.ConcurrentDictionary _persistedLastSeens = new(); + private readonly ConcurrentDictionary _persistedLastSeen = new(CollectionExtensions.LockPartitions, 4 * 4096); private bool _lastPersistedReachedReorgBoundary; private Task _pruningTask = Task.CompletedTask; @@ -410,13 +411,13 @@ public void CommitNode(long blockNumber, Hash256? address, in NodeCommitInfo nod ThrowUnknownPackage(blockNumber, node); } - if (node!.LastSeen.HasValue) + if (node!.LastSeen > 0) { ThrowNodeHasBeenSeen(blockNumber, node); } node = SaveOrReplaceInDirtyNodesCache(address, nodeCommitInfo, node); - node.LastSeen = Math.Max(blockNumber, node.LastSeen ?? 0); + node.LastSeen = Math.Max(blockNumber, node.LastSeen); if (!_pruningStrategy.PruningEnabled) { @@ -667,7 +668,7 @@ public void Prune() // persisted node have a pretty good hit rate and tend to conflict with the persisted // nodes (address,path) entry on second PruneCache. So pruning them ahead of time // really helps increase nodes that can be removed. - PruneCache(true); + PruneCache(skipRecalculateMemory: true); SaveSnapshot(); @@ -748,11 +749,11 @@ _pastPathHash is not null && AnnounceReorgBoundaries(); deleteTask.Wait(); - foreach (KeyValuePair keyValuePair in _persistedLastSeens) + foreach (KeyValuePair keyValuePair in _persistedLastSeen) { if (IsNoLongerNeeded(keyValuePair.Value)) { - _persistedLastSeens.Remove(keyValuePair.Key, out _); + _persistedLastSeen.Remove(keyValuePair.Key, out _); } } @@ -785,7 +786,7 @@ bool CanRemove(in ValueHash256 address, TinyTreePath path, in TreePath fullPath, !IsNoLongerNeeded(node)) return false; // We don't have it in cache, but we know it was re-committed, so if it is still needed, don't remove - if (_persistedLastSeens.TryGetValue(new(address, in path, in keccak), out long commitBlock) && + if (_persistedLastSeen.TryGetValue(new(address, in path, in keccak), out long commitBlock) && !IsNoLongerNeeded(commitBlock)) return false; return true; @@ -858,7 +859,7 @@ private void PruneCurrentSet() /// removing ones that are either no longer referenced or already persisted. /// /// - private void PruneCache(bool skipRecalculateMemory = false) + private void PruneCache(bool skipRecalculateMemory = false, KeyValuePair[]? allNodes = null) { if (_logger.IsDebug) _logger.Debug($"Pruning nodes {MemoryUsedByDirtyCache / 1.MB()} MB , last persisted block: {LastPersistedBlockNumber} current: {LatestCommittedBlockNumber}."); Stopwatch stopwatch = Stopwatch.StartNew(); @@ -878,7 +879,7 @@ private void PruneCache(bool skipRecalculateMemory = false) Interlocked.Add(ref newMemory, node.GetMemorySize(false) + _dirtyNodes.KeyMemoryUsage); }); - foreach ((DirtyNodesCache.Key key, TrieNode node) in _dirtyNodes.AllNodes) + foreach ((DirtyNodesCache.Key key, TrieNode node) in (allNodes ?? _dirtyNodes.AllNodes)) { if (node.IsPersisted) { @@ -924,7 +925,7 @@ private void PruneCache(bool skipRecalculateMemory = false) pruneAndRecalculateAction.Completion.Wait(); trackNodesAction?.Completion.Wait(); - if (!skipRecalculateMemory) MemoryUsedByDirtyCache = newMemory + _persistedLastSeens.Count * 48; + if (!skipRecalculateMemory) MemoryUsedByDirtyCache = newMemory + _persistedLastSeen.Count * 48; Metrics.CachedNodesCount = _dirtyNodes.Count; stopwatch.Stop(); @@ -937,14 +938,14 @@ private void TrackPrunedPersistedNodes(in DirtyNodesCache.Key key, TrieNode node TinyTreePath treePath = new(key.Path); // Persisted node with LastSeen is a node that has been re-committed, likely due to processing // recalculated to the same hash. - if (node.LastSeen is not null) + if (node.LastSeen > 0) { // Update _persistedLastSeen to later value. - _persistedLastSeens.AddOrUpdate( + _persistedLastSeen.AddOrUpdate( new(key.Address, in treePath, key.Keccak), (_, newValue) => newValue, (_, newValue, currentLastSeen) => Math.Max(newValue, currentLastSeen), - node.LastSeen.Value); + node.LastSeen); } // This persisted node is being removed from cache. Keep it in mind in case of an update to the same @@ -1075,7 +1076,7 @@ private void PersistNode(Hash256? address, in TreePath path, TrieNode currentNod if (currentNode.Keccak is not null) { - Debug.Assert(currentNode.LastSeen.HasValue, $"Cannot persist a dangling node (without {(nameof(TrieNode.LastSeen))} value set)."); + Debug.Assert(currentNode.LastSeen > 0, $"Cannot persist a dangling node (without {(nameof(TrieNode.LastSeen))} value set)."); // Note that the LastSeen value here can be 'in the future' (greater than block number // if we replaced a newly added node with an older copy and updated the LastSeen value. // Here we reach it from the old root so it appears to be out of place but it is correct as we need @@ -1084,7 +1085,7 @@ private void PersistNode(Hash256? address, in TreePath path, TrieNode currentNod if (_logger.IsTrace) _logger.Trace($"Persisting {nameof(TrieNode)} {currentNode} in snapshot {blockNumber}."); writeBatch.Set(address, path, currentNode.Keccak, currentNode.FullRlp, writeFlags); currentNode.IsPersisted = true; - currentNode.LastSeen = Math.Max(blockNumber, currentNode.LastSeen ?? 0); + currentNode.LastSeen = Math.Max(blockNumber, currentNode.LastSeen); PersistedNodesCount++; } else @@ -1219,6 +1220,7 @@ private void PersistOnShutdown() #endregion + ConcurrentDictionary? _wasPersisted; public void PersistCache(CancellationToken cancellationToken) { @@ -1253,6 +1255,7 @@ void ClearCommitSetQueue() if (_logger.IsInfo) _logger.Info($"Saving all commit set took {stopwatch.Elapsed} for {commitSetCount} commit sets."); stopwatch.Restart(); + ConcurrentDictionary wasPersisted; lock (_dirtyNodes) { using (_dirtyNodes.AcquireMapLock()) @@ -1261,10 +1264,12 @@ void ClearCommitSetQueue() ClearCommitSetQueue(); // This should clear most nodes. For some reason, not all. - PruneCache(); + PruneCache(skipRecalculateMemory: true); KeyValuePair[] nodesCopy = _dirtyNodes.AllNodes.ToArray(); - NonBlocking.ConcurrentDictionary wasPersisted = new(); + wasPersisted = Interlocked.Exchange(ref _wasPersisted, null) ?? + new(CollectionExtensions.LockPartitions, nodesCopy.Length); + void PersistNode(TrieNode n, Hash256? address, TreePath path) { if (n.Keccak is null) return; @@ -1283,7 +1288,7 @@ void PersistNode(TrieNode n, Hash256? address, TreePath path) Hash256? address = key.AddressAsHash256; nodesCopy[i].Value.CallRecursively(PersistNode, address, ref path, GetTrieStore(address), false, _logger, false); }); - PruneCache(); + PruneCache(allNodes: nodesCopy); if (_dirtyNodes.Count != 0) { @@ -1292,9 +1297,20 @@ void PersistNode(TrieNode n, Hash256? address, TreePath path) } } - _persistedLastSeens.Clear(); + _persistedLastSeen.NoResizeClear(); _pastPathHash?.Clear(); if (_logger.IsInfo) _logger.Info($"Clear cache took {stopwatch.Elapsed}."); + + if (wasPersisted is not null) + { + // Clear in background outside of lock to not block + Task.Run(() => + { + wasPersisted.NoResizeClear(); + // Set back to be reused + _wasPersisted = wasPersisted; + }); + } } // Used to serve node by hash diff --git a/src/Nethermind/Nethermind.Trie/TrieNode.cs b/src/Nethermind/Nethermind.Trie/TrieNode.cs index b85a88dc5df8..46165abd4302 100644 --- a/src/Nethermind/Nethermind.Trie/TrieNode.cs +++ b/src/Nethermind/Nethermind.Trie/TrieNode.cs @@ -89,7 +89,7 @@ public ValueRlpStream RlpStream public bool IsBranch => NodeType == NodeType.Branch; public bool IsExtension => NodeType == NodeType.Extension; - public long? LastSeen { get; set; } + public long LastSeen { get; set; } public byte[]? Key {