perf(historian): group-commit batch append on the historization outbox

This commit is contained in:
Joseph Doherty
2026-08-15 21:40:04 -04:00
parent debd44c213
commit 6ff64ca067
5 changed files with 138 additions and 8 deletions
@@ -733,6 +733,37 @@ public sealed class ContinuousHistorizationRecorderTests : TestKit
return ValueTask.CompletedTask;
}
public ValueTask AppendBatchAsync(IReadOnlyList<HistorizationOutboxEntry> entries, CancellationToken ct)
{
if (entries.Count == 0)
{
return ValueTask.CompletedTask;
}
if (_yieldOnAppend)
{
return AppendBatchYieldingAsync(entries);
}
foreach (HistorizationOutboxEntry entry in entries)
{
AppendCore(entry);
}
return ValueTask.CompletedTask;
}
private async ValueTask AppendBatchYieldingAsync(IReadOnlyList<HistorizationOutboxEntry> entries)
{
// Same pool-thread-completion requirement as AppendYieldingAsync below — a group commit
// fsyncs once for the batch, so the yield happens once, before the entries are recorded.
await Task.Delay(1).ConfigureAwait(false);
foreach (HistorizationOutboxEntry entry in entries)
{
AppendCore(entry);
}
}
private async ValueTask AppendYieldingAsync(HistorizationOutboxEntry entry)
{
// Must complete on a POOL thread, carrying no synchronization context — that is what a real
@@ -245,6 +245,9 @@ public sealed class ServiceCollectionExtensionsTests
public long DroppedCount => 0;
/// <summary>No-op append.</summary>
public ValueTask AppendAsync(HistorizationOutboxEntry entry, CancellationToken ct) => ValueTask.CompletedTask;
/// <summary>No-op batch append.</summary>
public ValueTask AppendBatchAsync(IReadOnlyList<HistorizationOutboxEntry> entries, CancellationToken ct)
=> ValueTask.CompletedTask;
/// <summary>Always returns an empty batch.</summary>
public ValueTask<IReadOnlyList<HistorizationOutboxEntry>> PeekBatchAsync(int max, CancellationToken ct)
=> ValueTask.FromResult<IReadOnlyList<HistorizationOutboxEntry>>(Array.Empty<HistorizationOutboxEntry>());