-
Notifications
You must be signed in to change notification settings - Fork 461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Safety check for full pruning #5550
Changes from 12 commits
9bedec0
bff7b79
0844cc1
a2a3c60
730542f
6f6d1f1
551e0b8
5ba8f00
f21fd71
584c580
4ee2619
3111f28
6820dc3
00408e9
6cb6252
b97321a
64d9f99
2bdef94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited | ||
// SPDX-License-Identifier: LGPL-3.0-only | ||
|
||
using FluentAssertions; | ||
using Nethermind.Core; | ||
using Nethermind.Core.Extensions; | ||
using NUnit.Framework; | ||
|
||
namespace Nethermind.Blockchain.Test; | ||
|
||
public class KnownChainSizesTests | ||
{ | ||
[Test] | ||
public void Update_known_chain_sizes() | ||
{ | ||
// Pruning size have to be updated frequently | ||
ChainSizes.CreateChainSizeInfo(BlockchainIds.Mainnet).PruningSize.Should().BeLessThan(200.GB()); | ||
ChainSizes.CreateChainSizeInfo(BlockchainIds.Sepolia).PruningSize.Should().BeLessThan(7.GB()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
// SPDX-License-Identifier: LGPL-3.0-only | ||
|
||
using System; | ||
using System.IO.Abstractions; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Nethermind.Config; | ||
|
@@ -28,6 +29,8 @@ public class FullPruner : IDisposable | |
private readonly IStateReader _stateReader; | ||
private readonly IProcessExitSource _processExitSource; | ||
private readonly ILogManager _logManager; | ||
private readonly IChainEstimations _chainEstimations; | ||
private readonly IDriveInfo _driveInfo; | ||
private IPruningContext? _currentPruning; | ||
private int _waitingForBlockProcessed = 0; | ||
private int _waitingForStateReady = 0; | ||
|
@@ -44,6 +47,8 @@ public FullPruner( | |
IBlockTree blockTree, | ||
IStateReader stateReader, | ||
IProcessExitSource processExitSource, | ||
IChainEstimations chainEstimations, | ||
IDriveInfo driveInfo, | ||
ILogManager logManager) | ||
{ | ||
_fullPruningDb = fullPruningDb; | ||
|
@@ -53,6 +58,8 @@ public FullPruner( | |
_stateReader = stateReader; | ||
_processExitSource = processExitSource; | ||
_logManager = logManager; | ||
_chainEstimations = chainEstimations; | ||
_driveInfo = driveInfo; | ||
_pruningTrigger.Prune += OnPrune; | ||
_logger = _logManager.GetClassLogger(); | ||
_minimumPruningDelay = TimeSpan.FromHours(_pruningConfig.FullPruningMinimumDelayHours); | ||
|
@@ -78,8 +85,13 @@ private void OnPrune(object? sender, PruningTriggerEventArgs e) | |
// If we are already pruning, we don't need to do anything | ||
else if (CanStartNewPruning()) | ||
{ | ||
// Check if we have enough disk space to run pruning | ||
if (!HaveEnoughDiskSpaceToRun() && _pruningConfig.AvailableSpaceCheckEnabled) | ||
{ | ||
e.Status = PruningStatus.NotEnoughDiskSpace; | ||
} | ||
// we mark that we are waiting for block (for thread safety) | ||
if (Interlocked.CompareExchange(ref _waitingForBlockProcessed, 1, 0) == 0) | ||
else if (Interlocked.CompareExchange(ref _waitingForBlockProcessed, 1, 0) == 0) | ||
{ | ||
// we don't want to start pruning in the middle of block processing, lets wait for new head. | ||
_blockTree.OnUpdateMainChain += OnUpdateMainChain; | ||
|
@@ -158,6 +170,28 @@ private void SetCurrentPruning(IPruningContext pruningContext) | |
|
||
private bool CanStartNewPruning() => _fullPruningDb.CanStartPruning; | ||
|
||
private const double ChainSizeThresholdFactor = 1.3; | ||
|
||
private bool HaveEnoughDiskSpaceToRun() | ||
{ | ||
long? currentChainSize = _chainEstimations.PruningSize; | ||
if (currentChainSize is null) | ||
{ | ||
if (_logger.IsWarn) _logger.Warn("Full Pruning: Chain size estimation is unavailable."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this user problem? |
||
return true; | ||
} | ||
|
||
long available = _driveInfo.AvailableFreeSpace; | ||
if (available < currentChainSize * ChainSizeThresholdFactor) | ||
{ | ||
if (_logger.IsWarn) | ||
_logger.Warn( | ||
$"Not enough disk space to run full pruning. Required {(currentChainSize * ChainSizeThresholdFactor) / 1.GB()} GB. Have {available / 1.GB()} GB"); | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
private void HandlePruningFinished(object? sender, PruningEventArgs e) | ||
{ | ||
switch (_pruningConfig.FullPruningCompletionBehavior) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,4 +31,9 @@ public enum PruningStatus | |
/// Full pruning was triggered and is starting. | ||
/// </summary> | ||
Starting, | ||
|
||
/// <summary> | ||
/// Pruning failed because of low disk space | ||
/// </summary> | ||
NotEnoughDiskSpace, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. very minor - thinking if we shoulnd't keep this enum in some order, so this would go after |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,46 +2,95 @@ | |
// SPDX-License-Identifier: LGPL-3.0-only | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using Nethermind.Core; | ||
using Nethermind.Core.Extensions; | ||
|
||
namespace Nethermind.Blockchain | ||
{ | ||
public static class Known | ||
public interface IChainEstimations | ||
{ | ||
public readonly struct SizeInfo | ||
long? StateSize { get; } | ||
long? PruningSize { get; } | ||
} | ||
|
||
public static class ChainSizes | ||
{ | ||
public class UnknownChain : IChainEstimations | ||
{ | ||
public long? StateSize => null; | ||
public long? PruningSize => null; | ||
|
||
public static readonly IChainEstimations Instance = new UnknownChain(); | ||
} | ||
|
||
private class ChainEstimations : IChainEstimations | ||
{ | ||
public SizeInfo( | ||
long sizeAtUpdateDate, | ||
long dailyGrowth, | ||
DateTime updateDate) | ||
private readonly LinearExtrapolation? _stateSizeEstimator; | ||
private readonly LinearExtrapolation? _prunedStateEstimator; | ||
|
||
public ChainEstimations(LinearExtrapolation? stateSizeEstimator = null, LinearExtrapolation? prunedStateEstimator = null) | ||
{ | ||
SizeAtUpdateDate = sizeAtUpdateDate; | ||
DailyGrowth = dailyGrowth; | ||
UpdateDate = updateDate; | ||
_stateSizeEstimator = stateSizeEstimator; | ||
_prunedStateEstimator = prunedStateEstimator; | ||
} | ||
|
||
public long SizeAtUpdateDate { get; } | ||
public long DailyGrowth { get; } | ||
public DateTime UpdateDate { get; } | ||
public long? StateSize => _stateSizeEstimator?.Estimate; | ||
public long? PruningSize => _prunedStateEstimator?.Estimate; | ||
} | ||
|
||
public long Current => SizeAtUpdateDate + (DateTime.UtcNow - UpdateDate).Days * DailyGrowth; | ||
private class LinearExtrapolation | ||
{ | ||
private readonly long _atUpdate; | ||
private readonly long _dailyGrowth; | ||
private readonly DateTime _updateDate; | ||
|
||
public LinearExtrapolation(long atUpdate, long dailyGrowth, DateTime updateDate) | ||
{ | ||
_atUpdate = atUpdate; | ||
_dailyGrowth = dailyGrowth; | ||
_updateDate = updateDate; | ||
} | ||
|
||
public LinearExtrapolation(long firstValue, DateTime firstDate, long secondValue, DateTime secondDate) | ||
{ | ||
_atUpdate = firstValue; | ||
_dailyGrowth = (long)((secondValue - firstValue) / (secondDate - firstDate).TotalDays); | ||
_updateDate = firstDate; | ||
} | ||
|
||
public long Estimate => _atUpdate + (DateTime.UtcNow - _updateDate).Days * _dailyGrowth; | ||
} | ||
|
||
/// <summary> | ||
/// Size in bytes, daily growth rate and the date of manual update | ||
/// </summary> | ||
public static Dictionary<ulong, SizeInfo> ChainSize = new() | ||
public static IChainEstimations CreateChainSizeInfo(ulong chainId) | ||
{ | ||
{ BlockchainIds.Goerli, new SizeInfo(8490.MB(), 15.MB(), new DateTime(2021, 12, 7)) }, | ||
{ BlockchainIds.Rinkeby, new SizeInfo(34700.MB(), 20.MB(), new DateTime(2021, 12, 7)) }, | ||
{ BlockchainIds.Ropsten, new SizeInfo(35900.MB(), 25.MB(), new DateTime(2021, 12, 7)) }, | ||
{ BlockchainIds.Mainnet, new SizeInfo(90000.MB(), 70.MB(), new DateTime(2022, 04, 7)) }, | ||
{ BlockchainIds.Gnosis, new SizeInfo(18000.MB(), 48.MB(), new DateTime(2021, 12, 7)) }, | ||
{ BlockchainIds.EnergyWeb, new SizeInfo(15300.MB(), 15.MB(), new DateTime(2021, 12, 7)) }, | ||
{ BlockchainIds.Volta, new SizeInfo(17500.MB(), 10.MB(), new DateTime(2021, 11, 7)) }, | ||
{ BlockchainIds.PoaCore, new SizeInfo(13900.MB(), 4.MB(), new DateTime(2021, 12, 7)) }, | ||
}; | ||
return chainId switch | ||
{ | ||
BlockchainIds.Goerli => new ChainEstimations( | ||
new LinearExtrapolation(8490.MB(), 15.MB(), new DateTime(2021, 12, 7))), | ||
BlockchainIds.Rinkeby => new ChainEstimations( | ||
new LinearExtrapolation(34700.MB(), 20.MB(), new DateTime(2021, 12, 7))), | ||
BlockchainIds.Ropsten => new ChainEstimations( | ||
new LinearExtrapolation(35900.MB(), 25.MB(), new DateTime(2021, 12, 7))), | ||
BlockchainIds.Mainnet => new ChainEstimations( | ||
new LinearExtrapolation(90000.MB(), 70.MB(), new DateTime(2022, 04, 7)), | ||
new LinearExtrapolation(163.GB(), new DateTime(2023, 4, 11, 19, 49, 0), | ||
177653453177, new DateTime(2023, 04, 29, 00, 50, 0))), | ||
BlockchainIds.Gnosis => new ChainEstimations( | ||
new LinearExtrapolation(18000.MB(), 48.MB(), new DateTime(2021, 12, 7))), | ||
BlockchainIds.EnergyWeb => new ChainEstimations( | ||
new LinearExtrapolation(15300.MB(), 15.MB(), new DateTime(2021, 12, 7))), | ||
BlockchainIds.Volta => new ChainEstimations( | ||
new LinearExtrapolation(17500.MB(), 10.MB(), new DateTime(2021, 11, 7))), | ||
BlockchainIds.PoaCore => new ChainEstimations( | ||
new LinearExtrapolation(13900.MB(), 4.MB(), new DateTime(2021, 12, 7))), | ||
BlockchainIds.Sepolia => new ChainEstimations(null, | ||
new LinearExtrapolation(2457.MB(), new DateTime(2023, 04, 9, 21, 11, 0), | ||
3699505976, new DateTime(2023, 04, 28, 20, 18, 0))), | ||
_ => UnknownChain.Instance | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kamilchodola can you update chain sizes post newest tests? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have problems with chain sizes, they are very unstable. I'm working on it right now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we should do that a bit in similar way as we had with Pivots. |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO.Abstractions; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Nethermind.Api; | ||
|
@@ -314,7 +315,10 @@ private static void InitializeFullPruning( | |
api.PruningTrigger.Add(pruningTrigger); | ||
} | ||
|
||
FullPruner pruner = new(fullPruningDb, api.PruningTrigger, pruningConfig, api.BlockTree!, stateReader, api.ProcessExit!, api.LogManager); | ||
IDriveInfo drive = api.FileSystem.GetDriveInfos(fullPruningDb.GetPath(initConfig.BaseDbPath))[0]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. StateDb Path should be used, this can be mapped to different drive. |
||
FullPruner pruner = new(fullPruningDb, api.PruningTrigger, pruningConfig, api.BlockTree!, | ||
stateReader, api.ProcessExit!, ChainSizes.CreateChainSizeInfo(api.ChainSpec!.ChainId), | ||
drive, api.LogManager); | ||
api.DisposeStack.Push(pruner); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would make this a
long
and make it in %, set it to 130, then when using it first multiply by it, then divide by 100. You keep everything integer and avoid floats.