Skip to content

Dependency-free WrapWorkerExecution retry seam on the base classes (#94) - #333

Merged
Chris-Wolfgang merged 1 commit into
vNext-plus-onefrom
feat/94-retry-seam
Jul 28, 2026
Merged

Dependency-free WrapWorkerExecution retry seam on the base classes (#94)#333
Chris-Wolfgang merged 1 commit into
vNext-plus-onefrom
feat/94-retry-seam

Conversation

@Chris-Wolfgang

Copy link
Copy Markdown
Owner

Closes #94.

Summary

Adds a dependency-free retry seamprotected virtual WrapWorkerExecution(...) — to ExtractorBase, LoaderBase, and TransformerBase, wrapped around every worker invocation (both the no-progress and with-progress paths). The default is a no-op, so existing behaviour is unchanged; an override runs the worker through a retry / resilience strategy.

protected virtual IAsyncEnumerable<TSource> WrapWorkerExecution(
    Func<CancellationToken, IAsyncEnumerable<TSource>> workerFactory, CancellationToken token)
    => workerFactory(token); // default: no wrapping

The override receives a re-invocable worker factory — calling it again produces a fresh worker run, which is what lets a strategy retry a transient failure.

Design decision — Option 1 (dep-free), per maintainer

The issue recommended Option A (inject Polly's ResiliencePipeline), but Abstractions is intentionally zero third-party runtime dependencies and roots 8 downstream packages, so taking a Polly dependency here would force it on the whole fleet. Chosen approach: ship only the seam; a ready-made Polly integration lands as a separate opt-in Wolfgang.Etl.Polly package — tracked in #332.

Scope

Verification

  • 426 unit tests pass (13 new RetrySeamTests: default no-op, seam-invoked-once on both paths, factory re-invocability, genuine transient-failure recovery, null guard — across all 3 bases).
  • Stryker 100.00 %, 0 survivors (full project).
  • Builds clean across all 11 TFMs; PublicAPI.Unshipped updated (3 protected-virtual entries, RS0017-validated).

Notes for release-prep (not in this PR)

…ses (#94)

Add a protected virtual WrapWorkerExecution hook to ExtractorBase, LoaderBase, and
TransformerBase, wrapped around every worker invocation (both the no-progress and
with-progress paths). The default implementation is a no-op, so behaviour is unchanged;
an override receives a re-invocable worker factory (call it again to retry a transient
failure) and stream-level semantics are documented on the method.

Kept dependency-free per the 0.20.0 decision (Option 1): Abstractions stays zero-dep and
roots 8 downstream packages, so no Polly dependency here. A ready-made Polly integration
will ship as a separate opt-in Wolfgang.Etl.Polly package (#332).

- 13 new RetrySeamTests (default no-op, seam-invoked-once on both paths, factory
  re-invocability, genuine transient-failure recovery, null guard) across all 3 bases.
  426 unit tests pass; Stryker 100.00% (0 survivors) on the full project.
- PublicAPI.Unshipped updated with the 3 protected virtual entries (RS0017-validated).
- CHANGELOG [Unreleased] Added entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 28, 2026 01:24

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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