fix(reliability): Remove cutoffTime filter to prevent data loss in outbox poller
CRITICAL: Previous cutoffTime logic (occurred_at >= now-5min) silently dropped messages older than 5 minutes forever, contradicting Outbox Pattern's guarantee of eventual delivery for stuck messages. Changes: - DapperOutboxMessageReader: Remove cutoffTime parameter, process ALL unpublished - OutboxPollerJob: Remove cutoffTime calculation, process all messages by occurred_at - Tests: Remove cutoff scenario (no longer applicable); keep normal + max-attempts - Comments: Document monitoring approach (alert if pending > 5 min) as separate concern AGENTS.md v16.0 Checklist: ✅ Safety: No partial success (no silent data loss) ✅ Audit: Evidence tracked (all messages eventually processed) ✅ Right Way: Root cause fixed (was processing-logic bug, not test-logic bug) Test results: 2/2 passing (normal path, max-attempts DQ) Validation gate: Outbox/Inbox crash-recovery ✅ RESTORED Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,6 @@ public sealed class DapperOutboxMessageReader(IDbConnectionFactory connectionFac
|
||||
attempt as Attempt
|
||||
from building_blocks.outbox_message
|
||||
where published_at is null
|
||||
and occurred_at >= @CutoffTime
|
||||
order by occurred_at asc
|
||||
limit @BatchSize
|
||||
""";
|
||||
@@ -36,7 +35,6 @@ public sealed class DapperOutboxMessageReader(IDbConnectionFactory connectionFac
|
||||
""";
|
||||
|
||||
public async Task<IReadOnlyList<OutboxMessageRow>> GetUnpublishedAsync(
|
||||
DateTimeOffset cutoffTime,
|
||||
int batchSize,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -44,7 +42,7 @@ public sealed class DapperOutboxMessageReader(IDbConnectionFactory connectionFac
|
||||
var messages = await connection.QueryAsync<OutboxMessageRow>(
|
||||
new CommandDefinition(
|
||||
SelectUnpublishedSql,
|
||||
new { CutoffTime = cutoffTime, BatchSize = batchSize },
|
||||
new { BatchSize = batchSize },
|
||||
cancellationToken: cancellationToken));
|
||||
return messages.ToList();
|
||||
}
|
||||
|
||||
@@ -43,9 +43,10 @@ public sealed class OutboxPollerJob(
|
||||
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var now = clock.UtcNow;
|
||||
var cutoffTime = now.AddMinutes(-5);
|
||||
|
||||
var messages = await reader.GetUnpublishedAsync(cutoffTime, DefaultBatchSize, cancellationToken);
|
||||
// Process all unpublished messages ordered by occurred_at (oldest first).
|
||||
// Monitoring: alert if any message pending > 5 min (see dashboard/alerts).
|
||||
var messages = await reader.GetUnpublishedAsync(DefaultBatchSize, cancellationToken);
|
||||
|
||||
var failureCount = 0;
|
||||
foreach (var message in messages)
|
||||
|
||||
Reference in New Issue
Block a user