Files
CBDD/tests/CBDD.Tests.Benchmark/MixedWorkloadBenchmarks.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

186 lines
5.5 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.Compression;
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 MixedWorkloadBenchmarks
{
/// <summary>
/// Gets or sets whether periodic online compaction is enabled.
/// </summary>
[Params(false, true)]
public bool PeriodicCompaction { get; set; }
/// <summary>
/// Gets or sets the number of operations per benchmark iteration.
/// </summary>
[Params(800)]
public int Operations { 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 readonly List<ObjectId> _activeIds = [];
private int _nextValueSeed;
/// <summary>
/// Prepares benchmark storage and seed data for each iteration.
/// </summary>
[IterationSetup]
public void Setup()
{
var id = Guid.NewGuid().ToString("N");
_dbPath = Path.Combine(AppContext.BaseDirectory, $"bench_mixed_{id}.db");
_walPath = Path.ChangeExtension(_dbPath, ".wal");
var options = new CompressionOptions
{
EnableCompression = true,
MinSizeBytes = 256,
MinSavingsPercent = 0,
Codec = CompressionCodec.Brotli
};
_storage = new StorageEngine(_dbPath, PageFileConfig.Default, options);
_transactionHolder = new BenchmarkTransactionHolder(_storage);
_collection = new DocumentCollection<Person>(_storage, _transactionHolder, new PersonMapper());
_activeIds.Clear();
_nextValueSeed = 0;
for (var i = 0; i < 300; i++)
{
var idValue = _collection.Insert(CreatePerson(_nextValueSeed++));
_activeIds.Add(idValue);
}
_transactionHolder.CommitAndReset();
}
/// <summary>
/// Cleans up benchmark resources for 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 a mixed insert/update/delete workload.
/// </summary>
[Benchmark(Baseline = true)]
[BenchmarkCategory("MixedWorkload")]
public int InsertUpdateDeleteMix()
{
var random = new Random(12345);
for (var i = 1; i <= Operations; i++)
{
var mode = i % 5;
if (mode is 0 or 1)
{
var id = _collection.Insert(CreatePerson(_nextValueSeed++));
_activeIds.Add(id);
}
else if (mode is 2 or 3)
{
if (_activeIds.Count > 0)
{
var idx = random.Next(_activeIds.Count);
var id = _activeIds[idx];
var current = _collection.FindById(id);
if (current != null)
{
current.Age += 1;
current.Bio = BuildPayload(_nextValueSeed++);
_collection.Update(current);
}
}
}
else
{
if (_activeIds.Count > 100)
{
var idx = random.Next(_activeIds.Count);
var id = _activeIds[idx];
_collection.Delete(id);
_activeIds.RemoveAt(idx);
}
}
if (i % 50 == 0)
{
_transactionHolder.CommitAndReset();
}
if (PeriodicCompaction && i % 200 == 0)
{
_storage.RunOnlineCompactionPass(new CompactionOptions
{
OnlineMode = true,
OnlineBatchPageLimit = 8,
OnlineBatchDelay = TimeSpan.FromMilliseconds(1),
MaxOnlineDuration = TimeSpan.FromMilliseconds(120),
EnableTailTruncation = true
});
}
}
_transactionHolder.CommitAndReset();
return _collection.Count();
}
private static Person CreatePerson(int seed)
{
return new Person
{
Id = ObjectId.NewObjectId(),
FirstName = $"First_{seed}",
LastName = $"Last_{seed}",
Age = 18 + (seed % 60),
Bio = BuildPayload(seed),
CreatedAt = DateTime.UnixEpoch.AddSeconds(seed),
Balance = seed,
HomeAddress = new Address
{
Street = $"{seed} Mixed Ave",
City = "Workload City",
ZipCode = "10101"
}
};
}
private static string BuildPayload(int seed)
{
var builder = new System.Text.StringBuilder(1800);
for (var i = 0; i < 64; i++)
{
builder.Append("mixed-");
builder.Append(seed.ToString("D6"));
builder.Append('-');
builder.Append(i.ToString("D3"));
builder.Append('|');
}
return builder.ToString();
}
}