Part of #5118 (compliance wave growth), epic #5110.
AsyncDaemonCompliance is deliberately a smoke test: one async snapshot, one rebuild. The error path — a projection that throws, the event being dead-lettered, the shard continuing — is the behavior that actually costs users when it drifts, and it is declared on shared interfaces:
JasperFx.Events.Daemon.DeadLetterEvent, DeadLetterShardCount
IEventDatabase.CountDeadLetterEventsAsync(ShardName), QueryDeadLetterEventsAsync(ShardName, tenantId), FetchDeadLetterCountsAsync() and its per-tenant overload
IEventStore.AllDatabases() → IReadOnlyList<IEventDatabase>
IEventFailureContext
Seam additions required
Small. AllDatabases() is already on the shared IEventStore the fixture exposes, so the dead-letter queries are reachable today. What is missing is a way for a suite to register a projection that fails deterministically and to configure the error policy:
ComplianceStoreConfig needs an error-handling knob — at minimum "skip poison events after N attempts and record a dead letter", which both products spell differently (Marten OnException(...).SkipEvent() policies on DaemonSettings; Polecat's equivalent). Follows the MaxConcurrentRebuildsPerDatabase precedent: fixture-owned property, not registrar-routed.
- Nothing else — the failing projection itself is shared source.
What each store tests today
| Marten |
Polecat |
DaemonTests error-handling and skip tests |
Daemon/dead_letter_count_tests.cs |
marking_events_as_skipped_as_Guid_identified.cs |
Daemon/skipped_events_count_augmenter_tests.cs |
marking_events_as_skipped_as_string_identified.cs |
Daemon/shard_failure_progression_columns_tests.cs |
|
Events/event_failure_context_tests.cs |
Scope
New DeadLetterCompliance suite:
- A projection that throws on one event type dead-letters that event and the shard keeps advancing
CountDeadLetterEventsAsync / FetchDeadLetterCountsAsync report the shard and the count
QueryDeadLetterEventsAsync returns the event body, the exception type and the shard name
IEventFailureContext correlates the failure with the dead-letter row
- A rebuild clears prior dead letters for the shard
- Marking events as skipped, for Guid and string stream identity
Explicitly out of scope: pause/resume policies, circuit breakers, multi-node failure handling — daemon-topology behavior that stays product-specific.
Acceptance
- One config knob added; suite enrolled in both stores
- Originals retired for the absorbed behavior in both repos
Part of #5118 (compliance wave growth), epic #5110.
AsyncDaemonComplianceis deliberately a smoke test: one async snapshot, one rebuild. The error path — a projection that throws, the event being dead-lettered, the shard continuing — is the behavior that actually costs users when it drifts, and it is declared on shared interfaces:JasperFx.Events.Daemon.DeadLetterEvent,DeadLetterShardCountIEventDatabase.CountDeadLetterEventsAsync(ShardName),QueryDeadLetterEventsAsync(ShardName, tenantId),FetchDeadLetterCountsAsync()and its per-tenant overloadIEventStore.AllDatabases()→IReadOnlyList<IEventDatabase>IEventFailureContextSeam additions required
Small.
AllDatabases()is already on the sharedIEventStorethe fixture exposes, so the dead-letter queries are reachable today. What is missing is a way for a suite to register a projection that fails deterministically and to configure the error policy:ComplianceStoreConfigneeds an error-handling knob — at minimum "skip poison events after N attempts and record a dead letter", which both products spell differently (MartenOnException(...).SkipEvent()policies onDaemonSettings; Polecat's equivalent). Follows theMaxConcurrentRebuildsPerDatabaseprecedent: fixture-owned property, not registrar-routed.What each store tests today
DaemonTestserror-handling and skip testsDaemon/dead_letter_count_tests.csmarking_events_as_skipped_as_Guid_identified.csDaemon/skipped_events_count_augmenter_tests.csmarking_events_as_skipped_as_string_identified.csDaemon/shard_failure_progression_columns_tests.csEvents/event_failure_context_tests.csScope
New
DeadLetterCompliancesuite:CountDeadLetterEventsAsync/FetchDeadLetterCountsAsyncreport the shard and the countQueryDeadLetterEventsAsyncreturns the event body, the exception type and the shard nameIEventFailureContextcorrelates the failure with the dead-letter rowExplicitly out of scope: pause/resume policies, circuit breakers, multi-node failure handling — daemon-topology behavior that stays product-specific.
Acceptance