Skip to content

feat: per-item error handling / dead-lettering (multi-stream) (#11) - #239

Merged
Chris-Wolfgang merged 1 commit into
vNextfrom
feat/11-dead-letter
Aug 9, 2026
Merged

feat: per-item error handling / dead-lettering (multi-stream) (#11)#239
Chris-Wolfgang merged 1 commit into
vNextfrom
feat/11-dead-letter

Conversation

@Chris-Wolfgang

Copy link
Copy Markdown
Owner

First feature of the 0.5.0 cycle. Closes #11.

Ports the parked #234 error-handling implementation off the 0.22 stack onto 0.21main already ships Abstractions 0.21 / TestKit 0.14 (via #236), and the ErrorPolicy hooks + Wolfgang.Etl.ErrorPolicies package are 0.21 features, so this needs no 0.22.

What

  • XmlMultiStreamExtractor<T> / XmlMultiStreamLoader<T> honour the assignable ErrorPolicy inherited from the Abstractions base: assign ItemErrorPolicy.Skip / SkipAndLog / SkipAndDeadLetter / SkipDeadLetterAndLog to skip or dead-letter a stream/record that fails to (de)serialize and keep going. Count surfaced on CurrentErrorItemCount.
  • Default stays fail-fast. Single-stream classes stay fail-fast (a shared streaming document can't resume mid-record).
  • Wolfgang.Etl.ErrorPolicies pinned to 0.21.0 (tests + examples).

Verified locally

Base: vNext.

XmlMultiStreamExtractor<T> and XmlMultiStreamLoader<T> now honour the
assignable ErrorPolicy inherited from the Abstractions base (0.21+): a
Skip / SkipAndDeadLetter / …-and-log policy skips or dead-letters a
stream/record that fails to (de)serialize and keeps going, with the
count on CurrentErrorItemCount. Default stays fail-fast; single-stream
classes stay fail-fast (a shared document can't resume mid-record).

Ported from the parked 0.22 stack onto 0.21 (main already ships 0.21 via
#236), so this ships in 0.5.0 without waiting on 0.22. ErrorPolicies
package pinned to 0.21.0. 285 tests pass; per-class coverage ≥90%.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 9, 2026 19:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds per-item error handling / dead-lettering support to the multi-stream XML extractor and loader by routing (de)serialization failures through the assignable ErrorPolicy provided by the Abstractions base stages. This enables skip and dead-letter workflows for independent stream/record processing while keeping default behavior fail-fast.

Changes:

  • Updated XmlMultiStreamExtractor<T> and XmlMultiStreamLoader<T> to route per-item failures through ErrorPolicy and continue when policies choose to skip/dead-letter.
  • Added unit tests for multi-stream error-policy behavior and pinned Wolfgang.Etl.ErrorPolicies 0.21.0 in tests/examples.
  • Documented the new capability in README.md and noted it in CHANGELOG.md, plus added a runnable example.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/Wolfgang.Etl.Xml.Tests.Unit/XmlMultiStreamErrorPolicyTests.cs New test coverage for multi-stream error-policy behavior (extractor/loader).
tests/Wolfgang.Etl.Xml.Tests.Unit/Wolfgang.Etl.Xml.Tests.Unit.csproj Adds Wolfgang.Etl.ErrorPolicies dependency for unit tests.
src/Wolfgang.Etl.Xml/XmlMultiStreamLoader.cs Routes serialization failures through ErrorPolicy during multi-stream loading.
src/Wolfgang.Etl.Xml/XmlMultiStreamExtractor.cs Routes deserialization failures through ErrorPolicy during multi-stream extraction.
README.md Adds usage docs and example snippet for per-item error handling & dead-lettering.
examples/Wolfgang.Etl.Xml.Examples/Wolfgang.Etl.Xml.Examples.csproj Adds Wolfgang.Etl.ErrorPolicies dependency for the example app.
examples/Wolfgang.Etl.Xml.Examples/Program.cs Adds a runnable dead-lettering demo for the multi-stream extractor.
CHANGELOG.md Documents the new feature under Unreleased.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +183 to 186
if (await SerializeItemOrHandleErrorAsync(item, itemNumber, streamIndex, token).ConfigureAwait(false))
{
XmlLogMessages.StreamFactoryReturnedNull(_logger, streamIndex, null);
throw new InvalidOperationException($"Stream factory returned null for item at index {streamIndex}.");
streamIndex++;
}
Comment thread README.md
Comment on lines +228 to +230
using Wolfgang.Etl.ErrorPolicies;

var deadLetters = new List<ItemErrorContext>();
Comment on lines +124 to +125


@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

PR benchmark delta

Benchmark Base mean HEAD mean Δ mean Base alloc HEAD alloc Δ alloc
ExtractAsync(ItemCount: 10) 31.40 µs 32.34 µs +3.0% 67.05 KB 67.81 KB +1.1%
ExtractAsync(ItemCount: 100) 317.80 µs 327.62 µs +3.1% 667.60 KB 674.69 KB +1.1%
ExtractAsync(ItemCount: 1000) 3.14 ms 3.21 ms +2.2% 6679.32 KB 6749.69 KB +1.1%
LoadAsync(ItemCount: 10) 13.79 µs 14.37 µs +4.2% 82.92 KB 82.92 KB +0.0%
LoadAsync(ItemCount: 100) 131.20 µs 139.00 µs +5.9% 827.22 KB 827.22 KB +0.0%
LoadAsync(ItemCount: 1000) 1.32 ms 1.57 ms +19.5% 8273.31 KB 8273.31 KB +0.0%
ExtractAsync(ItemCount: 10) 18.78 µs 18.98 µs +1.1% 111.79 KB 111.79 KB +0.0%
ExtractAsync(ItemCount: 100) 152.90 µs 154.90 µs +1.3% 204.28 KB 204.28 KB +0.0%
ExtractAsync(ItemCount: 1000) 1.50 ms 1.50 ms -0.2% 1128.90 KB 1128.90 KB +0.0%
LoadAsync(ItemCount: 10) 9.33 µs 9.52 µs +2.0% 74.80 KB 74.80 KB +0.0%
LoadAsync(ItemCount: 100) 72.78 µs 76.07 µs +4.5% 181.30 KB 181.30 KB +0.0%
LoadAsync(ItemCount: 1000) 899.76 µs 907.56 µs +0.9% 1093.51 KB 1093.51 KB +0.0%
LoadNoIndentAsync(ItemCount: 10) 8.03 µs 8.29 µs +3.3% 74.88 KB 74.88 KB +0.0%
LoadNoIndentAsync(ItemCount: 100) 59.79 µs 63.08 µs +5.5% 149.36 KB 149.36 KB +0.0%
LoadNoIndentAsync(ItemCount: 1000) 772.26 µs 793.73 µs +2.8% 1093.60 KB 1093.60 KB +0.0%

Job: Short (fast, ~5% variance). Filter: * (all benchmarks). Gate mode: informational (no pass/fail threshold).

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.

2 participants