Summary
Add a store-agnostic way to read projection dead-letter counts to JasperFx.Events, implemented in Marten and Polecat.
Motivation
JasperFx.Events 2.0 made SkipApplyErrors = true the default: a projection Apply() failure is recorded as a JasperFx.Events.Daemon.DeadLetterEvent and the shard keeps advancing rather than stalling. The accumulation of those rows is now the primary "this projection is unhealthy" signal.
As of 2.0.0-rc.2 the abstraction layer only lets you write dead letters:
IEventDatabase.StoreDeadLetterEventAsync(...)
ISubscriptionAgent.RecordDeadLetterEventAsync(...)
ISubscriptionController.RecordDeadLetterEventAsync(...)
There is no abstraction to read/count them. Consumers (e.g. CritterWatch's Wolverine.CritterWatch client package, which must depend only on JasperFx.Events) are forced to reflect into Marten's IDocumentStore and Query<DeadLetterEvent>() — Marten-only and reflection-based.
Proposal
Add a read method to JasperFx.Events.IEventDatabase, e.g.:
// per-shard
Task<long> CountDeadLetterEventsAsync(ShardName shard, CancellationToken token = default);
// or bulk, one row per (ProjectionName, ShardKey)
Task<IReadOnlyList<DeadLetterShardCount>> FetchDeadLetterCountsAsync(CancellationToken token = default);
(DeadLetterShardCount ≈ record(string ProjectionName, string ShardKey, long Count), keyed to align with ShardName.Name / ShardName.ShardKey as set by the DeadLetterEvent constructor.)
- Marten: implement via the existing
DeadLetterEvent document (Query<DeadLetterEvent>() grouped by ProjectionName/ShardName).
- Polecat: implement the equivalent so Polecat-backed stores light up too.
Mirrors the existing IEventDatabase.AllProjectionProgress shape ("give me every progression row"), which both stores already implement.
Downstream
Unblocks JasperFx/CritterWatch removing its reflection-based ProjectionDeadLetterProbe stopgap (see the companion CritterWatch issue). CritterWatch will consume this via the graceful-no-op GetServices<IEventDatabase>() pattern.
Summary
Add a store-agnostic way to read projection dead-letter counts to
JasperFx.Events, implemented in Marten and Polecat.Motivation
JasperFx.Events 2.0 made
SkipApplyErrors = truethe default: a projectionApply()failure is recorded as aJasperFx.Events.Daemon.DeadLetterEventand the shard keeps advancing rather than stalling. The accumulation of those rows is now the primary "this projection is unhealthy" signal.As of
2.0.0-rc.2the abstraction layer only lets you write dead letters:IEventDatabase.StoreDeadLetterEventAsync(...)ISubscriptionAgent.RecordDeadLetterEventAsync(...)ISubscriptionController.RecordDeadLetterEventAsync(...)There is no abstraction to read/count them. Consumers (e.g. CritterWatch's
Wolverine.CritterWatchclient package, which must depend only onJasperFx.Events) are forced to reflect into Marten'sIDocumentStoreandQuery<DeadLetterEvent>()— Marten-only and reflection-based.Proposal
Add a read method to
JasperFx.Events.IEventDatabase, e.g.:(
DeadLetterShardCount≈record(string ProjectionName, string ShardKey, long Count), keyed to align withShardName.Name/ShardName.ShardKeyas set by theDeadLetterEventconstructor.)DeadLetterEventdocument (Query<DeadLetterEvent>()grouped byProjectionName/ShardName).Mirrors the existing
IEventDatabase.AllProjectionProgressshape ("give me every progression row"), which both stores already implement.Downstream
Unblocks JasperFx/CritterWatch removing its reflection-based
ProjectionDeadLetterProbestopgap (see the companion CritterWatch issue). CritterWatch will consume this via the graceful-no-opGetServices<IEventDatabase>()pattern.