Skip to content

feat(batching): IsolateBatchMembers error policy for opaque batch failures (GH-3289 Phase 3b)#3311

Merged
jeremydmiller merged 1 commit into
mainfrom
feat-batching-phase3b-probe-3289
Jul 5, 2026
Merged

feat(batching): IsolateBatchMembers error policy for opaque batch failures (GH-3289 Phase 3b)#3311
jeremydmiller merged 1 commit into
mainfrom
feat-batching-phase3b-probe-3289

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Phase 3b of the #3289 batch-processing plan: item 3 (IsolateBatchMembers). (Phases 0/1/2/3a merged as #3299/#3300/#3301/#3302.)

The feature

Item 8 (ApplyItemException, #3302) handles failures the handler can name. This handles the opaque case — the batch handler throws but can't attribute it to a specific item. The new exception-type-keyed error policy isolates the culprit anyway:

// A deterministic error isolates the offending member...
opts.Policies.OnException<ValidationException>().IsolateBatchMembers();

// ...while a transient error still retries the whole batch (the policies compose by exception type).
opts.Policies.OnException<SqlException>().RetryWithCooldown(100.Milliseconds(), 1.Seconds());

IsolateBatchMembers() re-runs each member of the failed batch as its own size-1 batch (via the shared BatchReplay primitive from 3a), so only the member that actually reproduces the failure is dead-lettered while the healthy members succeed.

Implementation

  • ProbeIndividuallyContinuation (+Source) — splits a batch (>1 member) into singletons; a size-≤1 batch is dead-lettered. That size-≤1 guard is the terminating/base case: a probed singleton that fails again just DLQs (no infinite re-probe) and it's also the "no effect on non-batched messages" path.
  • IsolateBatchMembers() verb — added to IFailureActions / FailureActions / PolicyExpression, installed as an every-attempt ContinueWith. Composes with the retry verbs purely by exception-type matching (separate FailureRules), exactly as the plan's flagship example shows.

Tests (DB-free)

src/Testing/CoreTests/Acceptance/batch_isolate_members.cs:

  • opaque-failure isolation — batch [good1, bad, good2] throws → probed into singletons → the two healthy items succeed and only bad is dead-lettered (captured via an IDeadLetterInterceptor, which runs under NullMessageStore);
  • non-batched fallback — a solo message with the policy just dead-letters (nothing to isolate).

Both green; 253 existing error-handling/batch tests still pass. dotnet build wolverine.slnx -c Release clean.

Remaining plan follow-ups

🤖 Generated with Claude Code

…lures (GH-3289 Phase 3b)

Phase 3b of the #3289 batch-processing plan: item 3. For an OPAQUE batch failure
(the handler throws but can't name the bad item), the new error policy
  opts.Policies.OnException<ValidationException>().IsolateBatchMembers();
re-runs each member of the failed batch as its own size-1 batch (via 3a's shared
BatchReplay primitive), so only the member that actually reproduces the failure is
dead-lettered while the healthy members succeed.

- New ProbeIndividuallyContinuation(+Source): splits a batch (>1 member) into
  singletons; a size-<=1 batch is dead-lettered (the terminating/base case, so a
  probed singleton that fails again just DLQs).
- New IsolateBatchMembers() verb on IFailureActions / FailureActions / PolicyExpression,
  installed as an every-attempt ContinueWith. Composes with the retry verbs by
  exception type: a transient SqlException can RetryWithCooldown the whole batch while a
  deterministic ValidationException isolates.

Tests (DB-free): opaque-failure isolation (only the bad item dead-letters, healthy
members succeed, captured via IDeadLetterInterceptor) + non-batched fallback to plain
dead-lettering. 253 error-handling/batch tests still green.

Follow-ups (called out in docs): count-based ProbeIndividuallyAfter(N) (item 2) and
poisoning every member that collapsed into a coalesced CoalesceBy key.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeremydmiller
jeremydmiller merged commit e678a6b into main Jul 5, 2026
26 checks passed
This was referenced Jul 9, 2026
This was referenced Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant