Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 57 additions & 146 deletions src/Nethermind/Nethermind.History.Test/HistoryPrunerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,116 +43,53 @@ public class HistoryPrunerTests
SnapSync = true
};

[Test]
public async Task Can_prune_blocks_older_than_specified_epochs()
{
const int blocks = 100;
const int cutoff = 36;

IHistoryConfig historyConfig = new HistoryConfig
{
Pruning = PruningModes.Rolling,
RetentionEpochs = 2,
PruningInterval = 0
};

using BasicTestBlockchain testBlockchain = await BasicTestBlockchain.Create(BuildContainer(historyConfig));

List<Hash256> blockHashes = [];
blockHashes.Add(testBlockchain.BlockTree.Head!.Hash!);
for (int i = 0; i < blocks; i++)
{
await testBlockchain.AddBlock();
blockHashes.Add(testBlockchain.BlockTree.Head!.Hash!);
}

Block head = testBlockchain.BlockTree.Head;
Assert.That(head, Is.Not.Null);
testBlockchain.BlockTree.SyncPivot = (blocks, Hash256.Zero);

HistoryPruner historyPruner = (HistoryPruner)testBlockchain.Container.Resolve<IHistoryPruner>();

CheckOldestAndCutoff(1, cutoff, historyPruner);

historyPruner.TryPruneHistory(CancellationToken.None);

CheckGenesisPreserved(testBlockchain, blockHashes[0]);
for (int i = 1; i <= blocks; i++)
{
if (i < cutoff)
{
CheckBlockPruned(testBlockchain, blockHashes, i);
}
else
{
CheckBlockPreserved(testBlockchain, blockHashes, i);
}
}

CheckHeadPreserved(testBlockchain, blocks);
CheckOldestAndCutoff(cutoff, cutoff, historyPruner);
}

[Test]
public async Task Can_prune_to_ancient_barriers()
private static IEnumerable<TestCaseData> PruningCases()
{
const int blocks = 100;

IHistoryConfig historyConfig = new HistoryConfig
{
Pruning = PruningModes.UseAncientBarriers,
RetentionEpochs = 100, // should have no effect
PruningInterval = 0
};
using BasicTestBlockchain testBlockchain = await BasicTestBlockchain.Create(BuildContainer(historyConfig));

List<Hash256> blockHashes = [];
blockHashes.Add(testBlockchain.BlockTree.Head!.Hash!);
for (int i = 0; i < blocks; i++)
{
await testBlockchain.AddBlock();
blockHashes.Add(testBlockchain.BlockTree.Head!.Hash!);
}

Block head = testBlockchain.BlockTree.Head;
Assert.That(head, Is.Not.Null);
testBlockchain.BlockTree.SyncPivot = (blocks, Hash256.Zero);

HistoryPruner historyPruner = (HistoryPruner)testBlockchain.Container.Resolve<IHistoryPruner>();

CheckOldestAndCutoff(1, BeaconGenesisBlockNumber, historyPruner);

historyPruner.TryPruneHistory(CancellationToken.None);

CheckGenesisPreserved(testBlockchain, blockHashes[0]);

for (int i = 1; i <= blocks; i++)
{
if (i < BeaconGenesisBlockNumber)
{
CheckBlockPruned(testBlockchain, blockHashes, i);
}
else
{
CheckBlockPreserved(testBlockchain, blockHashes, i);
}
}

CheckHeadPreserved(testBlockchain, blocks);
CheckOldestAndCutoff(BeaconGenesisBlockNumber, BeaconGenesisBlockNumber, historyPruner);
yield return new TestCaseData(
new HistoryConfig { Pruning = PruningModes.Rolling, RetentionEpochs = 2, PruningInterval = 0 },
/*syncPivot:*/ (long)blocks,
/*primeWithOldestRead:*/ true,
/*expectedPruneBelow:*/ 36L,
/*finalCutoff:*/ 36L
).SetName("Can_prune_blocks_older_than_specified_epochs");

yield return new TestCaseData(
new HistoryConfig { Pruning = PruningModes.Rolling, RetentionEpochs = 2, PruningInterval = 0 },
/*syncPivot:*/ (long)blocks,
/*primeWithOldestRead:*/ false, // regression: pruner must self-bootstrap without external OldestBlockHeader call
/*expectedPruneBelow:*/ 36L,
/*finalCutoff:*/ 36L
).SetName("Can_prune_without_prior_oldest_block_read");

yield return new TestCaseData(
new HistoryConfig { Pruning = PruningModes.UseAncientBarriers, RetentionEpochs = 100 /* no effect in UseAncientBarriers mode */, PruningInterval = 0 },
/*syncPivot:*/ (long)blocks,
/*primeWithOldestRead:*/ true,
/*expectedPruneBelow:*/ BeaconGenesisBlockNumber,
/*finalCutoff:*/ BeaconGenesisBlockNumber
).SetName("Can_prune_to_ancient_barriers");

yield return new TestCaseData(
new HistoryConfig { Pruning = PruningModes.UseAncientBarriers, PruningInterval = 0 },
/*syncPivot:*/ 20L, // below BeaconGenesisBlockNumber — sync pivot caps the prune boundary
/*primeWithOldestRead:*/ true,
/*expectedPruneBelow:*/ 20L,
/*finalCutoff:*/ BeaconGenesisBlockNumber
).SetName("Prunes_up_to_sync_pivot");
}

[Test]
public async Task Prunes_up_to_sync_pivot()
[TestCaseSource(nameof(PruningCases))]
public async Task Prunes_history(
IHistoryConfig historyConfig,
long syncPivot,
bool primeWithOldestRead,
long expectedPruneBelow,
long finalCutoff)
{
const int blocks = 100;
const long syncPivot = 20;

IHistoryConfig historyConfig = new HistoryConfig
{
Pruning = PruningModes.UseAncientBarriers,
PruningInterval = 0
};
using BasicTestBlockchain testBlockchain = await BasicTestBlockchain.Create(BuildContainer(historyConfig));

List<Hash256> blockHashes = [];
Expand All @@ -169,26 +106,22 @@ public async Task Prunes_up_to_sync_pivot()

HistoryPruner historyPruner = (HistoryPruner)testBlockchain.Container.Resolve<IHistoryPruner>();

CheckOldestAndCutoff(1, BeaconGenesisBlockNumber, historyPruner);
if (primeWithOldestRead)
CheckOldestAndCutoff(1, finalCutoff, historyPruner);

historyPruner.TryPruneHistory(CancellationToken.None);

CheckGenesisPreserved(testBlockchain, blockHashes[0]);

for (int i = 1; i <= blocks; i++)
{
if (i < syncPivot)
{
if (i < expectedPruneBelow)
CheckBlockPruned(testBlockchain, blockHashes, i);
}
else
{
CheckBlockPreserved(testBlockchain, blockHashes, i);
}
}

CheckHeadPreserved(testBlockchain, blocks);
CheckOldestAndCutoff(syncPivot, BeaconGenesisBlockNumber, historyPruner);
CheckOldestAndCutoff(expectedPruneBelow, finalCutoff, historyPruner);
}

[Test]
Expand Down Expand Up @@ -260,59 +193,37 @@ public async Task Does_not_prune_when_disabled()
CheckHeadPreserved(testBlockchain, blocks);
}

[Test]
public void Can_accept_valid_config()
[TestCase(0, 100000u, false)]
[TestCase(100, 10u, true)]
public void Validates_config(int minHistoryRetentionEpochs, uint retentionEpochs, bool shouldThrow)
{
IHistoryConfig validHistoryConfig = new HistoryConfig
{
Pruning = PruningModes.Rolling,
RetentionEpochs = 100000,
};

IDbProvider dbProvider = Substitute.For<IDbProvider>();
dbProvider.MetadataDb.Returns(new TestMemDb());

Assert.DoesNotThrow(() => new HistoryPruner(
Substitute.For<IBlockTree>(),
Substitute.For<IReceiptStorage>(),
Substitute.For<ISpecProvider>(),
Substitute.For<IChainLevelInfoRepository>(),
dbProvider,
validHistoryConfig,
BlocksConfig,
SyncConfig,
new ProcessExitSource(new()),
Substitute.For<IBackgroundTaskScheduler>(),
Substitute.For<IBlockProcessingQueue>(),
LimboLogs.Instance));
}

[Test]
public void Can_reject_invalid_config()
{
IHistoryConfig invalidHistoryConfig = new HistoryConfig
IHistoryConfig historyConfig = new HistoryConfig
{
Pruning = PruningModes.Rolling,
RetentionEpochs = 10,
RetentionEpochs = retentionEpochs,
};

ISpecProvider specProvider = new TestSpecProvider(new ReleaseSpec() { MinHistoryRetentionEpochs = 100 });
ISpecProvider specProvider = new TestSpecProvider(new ReleaseSpec { MinHistoryRetentionEpochs = minHistoryRetentionEpochs });
IDbProvider dbProvider = Substitute.For<IDbProvider>();
dbProvider.MetadataDb.Returns(new TestMemDb());

Assert.Throws<HistoryPruner.HistoryPrunerException>(() => new HistoryPruner(
TestDelegate action = () => new HistoryPruner(
Substitute.For<IBlockTree>(),
Substitute.For<IReceiptStorage>(),
specProvider,
Substitute.For<IChainLevelInfoRepository>(),
dbProvider,
invalidHistoryConfig,
historyConfig,
BlocksConfig,
SyncConfig,
new ProcessExitSource(new()),
Substitute.For<IBackgroundTaskScheduler>(),
Substitute.For<IBlockProcessingQueue>(),
LimboLogs.Instance));
LimboLogs.Instance);

if (shouldThrow)
Assert.Throws<HistoryPruner.HistoryPrunerException>(action);
else
Assert.DoesNotThrow(action);
}

private static void CheckGenesisPreserved(BasicTestBlockchain testBlockchain, Hash256 genesisHash)
Expand Down
1 change: 0 additions & 1 deletion src/Nethermind/Nethermind.History/HistoryPruner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,6 @@ internal void TryPruneHistory(CancellationToken cancellationToken)
{
if (_blockTree.Head is null ||
_blockTree.SyncPivot.BlockNumber == 0 ||
!_hasLoadedDeletePointer ||
!ShouldPruneHistory(out ulong? cutoffTimestamp))
{
SkipLocalPruning();
Expand Down
Loading