Files
CBDD/tests/CBDD.Tests.Benchmark/CompactionBenchmarks.cs
Joseph Doherty 3ffd468c79
All checks were successful
NuGet Publish / build-and-pack (push) Successful in 45s
NuGet Publish / publish-to-gitea (push) Successful in 52s
Fix audit findings for coverage, architecture checks, and XML docs
2026-02-20 15:43:25 -05:00

151 lines
4.4 KiB
C#

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using ZB.MOM.WW.CBDD.Bson;
using ZB.MOM.WW.CBDD.Core.Collections;
using ZB.MOM.WW.CBDD.Core.Storage;
namespace ZB.MOM.WW.CBDD.Tests.Benchmark;
[SimpleJob]
[InProcess]
[MemoryDiagnoser]
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[HtmlExporter]
[JsonExporterAttribute.Full]
public class CompactionBenchmarks
{
/// <summary>
/// Gets or sets the number of documents used per benchmark iteration.
/// </summary>
[Params(2_000)]
public int DocumentCount { get; set; }
private string _dbPath = string.Empty;
private string _walPath = string.Empty;
private StorageEngine _storage = null!;
private BenchmarkTransactionHolder _transactionHolder = null!;
private DocumentCollection<Person> _collection = null!;
private List<ObjectId> _insertedIds = [];
/// <summary>
/// Prepares benchmark state and seed data for each iteration.
/// </summary>
[IterationSetup]
public void Setup()
{
var id = Guid.NewGuid().ToString("N");
_dbPath = Path.Combine(AppContext.BaseDirectory, $"bench_compaction_{id}.db");
_walPath = Path.ChangeExtension(_dbPath, ".wal");
_storage = new StorageEngine(_dbPath, PageFileConfig.Small);
_transactionHolder = new BenchmarkTransactionHolder(_storage);
_collection = new DocumentCollection<Person>(_storage, _transactionHolder, new PersonMapper());
_insertedIds.Clear();
for (var i = 0; i < DocumentCount; i++)
{
var person = CreatePerson(i);
var insertedId = _collection.Insert(person);
_insertedIds.Add(insertedId);
}
_transactionHolder.CommitAndReset();
_storage.Checkpoint();
for (var i = _insertedIds.Count - 1; i >= _insertedIds.Count / 3; i--)
{
_collection.Delete(_insertedIds[i]);
}
_transactionHolder.CommitAndReset();
_storage.Checkpoint();
}
/// <summary>
/// Cleans up benchmark resources and temporary files after each iteration.
/// </summary>
[IterationCleanup]
public void Cleanup()
{
_transactionHolder?.Dispose();
_storage?.Dispose();
if (File.Exists(_dbPath)) File.Delete(_dbPath);
if (File.Exists(_walPath)) File.Delete(_walPath);
}
/// <summary>
/// Benchmarks reclaimed file bytes reported by offline compaction.
/// </summary>
/// <returns>The reclaimed file byte count.</returns>
[Benchmark(Baseline = true)]
[BenchmarkCategory("Compaction_Offline")]
public long OfflineCompact_ReclaimedBytes()
{
var stats = _storage.Compact(new CompactionOptions
{
OnlineMode = false,
EnableTailTruncation = true,
DefragmentSlottedPages = true,
NormalizeFreeList = true
});
return stats.ReclaimedFileBytes;
}
/// <summary>
/// Benchmarks tail bytes truncated by offline compaction.
/// </summary>
/// <returns>The truncated tail byte count.</returns>
[Benchmark]
[BenchmarkCategory("Compaction_Offline")]
public long OfflineCompact_TailBytesTruncated()
{
var stats = _storage.Compact(new CompactionOptions
{
OnlineMode = false,
EnableTailTruncation = true,
DefragmentSlottedPages = true,
NormalizeFreeList = true
});
return stats.TailBytesTruncated;
}
private static Person CreatePerson(int i)
{
return new Person
{
Id = ObjectId.NewObjectId(),
FirstName = $"First_{i}",
LastName = $"Last_{i}",
Age = i % 90,
Bio = BuildPayload(i),
CreatedAt = DateTime.UnixEpoch.AddSeconds(i),
Balance = i * 1.5m,
HomeAddress = new Address
{
Street = $"{i} Long Street",
City = "Compaction City",
ZipCode = "90000"
}
};
}
private static string BuildPayload(int seed)
{
var builder = new System.Text.StringBuilder(2500);
for (var i = 0; i < 80; i++)
{
builder.Append("compact-");
builder.Append(seed.ToString("D6"));
builder.Append('-');
builder.Append(i.ToString("D3"));
builder.Append('|');
}
return builder.ToString();
}
}