fix(historian): dead-letter poison events after maxAttempts (finding 002)
This commit is contained in:
+32
@@ -268,6 +268,37 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable
|
||||
sink.CurrentBackoff.ShouldBe(TimeSpan.FromSeconds(60));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regression for finding 002: a permanently-malformed (poison) event that the
|
||||
/// writer can only ever map to <see cref="HistorianWriteOutcome.RetryPlease"/>
|
||||
/// must NOT retry forever. After <c>maxAttempts</c> retry-please drains the row
|
||||
/// is dead-lettered so the queue head can never stall on it indefinitely.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task RetryPlease_dead_letters_row_after_MaxAttempts()
|
||||
{
|
||||
var writer = new FakeWriter { DefaultOutcome = HistorianWriteOutcome.RetryPlease };
|
||||
using var sink = new SqliteStoreAndForwardSink(
|
||||
_dbPath, writer, _log, maxAttempts: 3);
|
||||
|
||||
await sink.EnqueueAsync(Event("poison"), CancellationToken.None);
|
||||
|
||||
// Drain 1 + 2: still retrying — the row stays queued, not yet dead-lettered.
|
||||
await sink.DrainOnceAsync(CancellationToken.None);
|
||||
sink.GetStatus().QueueDepth.ShouldBe(1, "after 1 attempt the row is still queued");
|
||||
sink.GetStatus().DeadLetterDepth.ShouldBe(0, "not yet dead-lettered after 1 attempt");
|
||||
|
||||
await sink.DrainOnceAsync(CancellationToken.None);
|
||||
sink.GetStatus().QueueDepth.ShouldBe(1, "after 2 attempts the row is still queued");
|
||||
sink.GetStatus().DeadLetterDepth.ShouldBe(0, "not yet dead-lettered after 2 attempts");
|
||||
|
||||
// Drain 3: the 3rd attempt hits the cap — dead-letter it.
|
||||
await sink.DrainOnceAsync(CancellationToken.None);
|
||||
var status = sink.GetStatus();
|
||||
status.QueueDepth.ShouldBe(0, "row left the live queue once max attempts exceeded");
|
||||
status.DeadLetterDepth.ShouldBe(1, "poison row dead-lettered at the max-attempts cap");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that NullAlarmHistorianSink reports disabled status.</summary>
|
||||
[Fact]
|
||||
public void NullAlarmHistorianSink_reports_disabled_status()
|
||||
@@ -295,6 +326,7 @@ public sealed class SqliteStoreAndForwardSinkTests : IDisposable
|
||||
Should.Throw<ArgumentNullException>(() => new SqliteStoreAndForwardSink(_dbPath, w, null!));
|
||||
Should.Throw<ArgumentOutOfRangeException>(() => new SqliteStoreAndForwardSink(_dbPath, w, _log, batchSize: 0));
|
||||
Should.Throw<ArgumentOutOfRangeException>(() => new SqliteStoreAndForwardSink(_dbPath, w, _log, capacity: 0));
|
||||
Should.Throw<ArgumentOutOfRangeException>(() => new SqliteStoreAndForwardSink(_dbPath, w, _log, maxAttempts: 0));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a disposed sink rejects enqueue operations.</summary>
|
||||
|
||||
Reference in New Issue
Block a user