Skip to content

Release v0.19.0 — overflow-safe progress counters - #337

Merged
Chris-Wolfgang merged 10 commits into
mainfrom
vNext
Jul 29, 2026
Merged

Release v0.19.0 — overflow-safe progress counters#337
Chris-Wolfgang merged 10 commits into
mainfrom
vNext

Conversation

@Chris-Wolfgang

Copy link
Copy Markdown
Owner

Release: Wolfgang.Etl.Abstractions 0.19.0 — overflow-safe progress counters

Minor release. Bundles the 0.19.0 line from vNextmain.

What's in it

Release-prep done in this PR (commit 853ee46)

  • PackageValidationBaselineVersion 0.18.0 → 0.18.1; pack + Package Validation green against the published 0.18.1.
  • CHANGELOG [Unreleased][0.19.0] - 2026-07-28.
  • Report timing ctor promoted PublicAPI.UnshippedShipped (ordinal); Unshipped reset.
  • 413 tests pass; all 11 TFMs build clean.

⚠️ Timing — do NOT tag until after 8 PM EDT tonight

Merging this PR is safe anytimerelease.yaml fires only on release:published, not on push to main. But do not create the v0.19.0 tag/release until after 2026-07-29T00:00Z (≥ 8:00 PM EDT) so it lands on a NuGet UTC date (2026-07-29) distinct from 0.18.1's (2026-07-28). See the scheduled reminder.

Tag v0.19.0 (bare), release title e.g. v0.19.0 — overflow-safe progress counters.

No protected files in the diff → no admin bypass needed. The 0.20.0 stack (#333/#334/#336 on vNext-plus-one) is separate and not part of this release.

Chris-Wolfgang and others added 7 commits July 26, 2026 22:07
ExtractedItemCount / LoadedItemCount / ErrorItemCount are now long, so a
long-running pipeline can report more than int.MaxValue (~2.1B) records without
overflow. Changes the record's getters, positional ctor, and Deconstruct.

- EtlRunState tracking fields widened to long; internal usages widen implicitly.
- PublicAPI.Shipped updated; CompatibilitySuppressions.xml regenerated vs the new
  0.18.0 baseline (55 CP0002 waivers for the int->long members) + justification.
- Version 0.18.0 -> 0.19.0, baseline 0.17.0 -> 0.18.0. AssemblyVersion stays 1.0.0.0.
- Added an overflow-safety test (counters hold > int.MaxValue).

Breaking, pre-1.0. Full solution builds clean; 399 tests pass; pack passes Package Validation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Widen EtlPipelineProgress counters int -> long (#285)
#328)

The residual Stryker survivors were all in the timer machinery — SystemProgressTimer
(Start/StopTimer/Dispose guards) and the base CreateProgressTimer's timer.Start —
and were only reachable by real-timer *timing* tests (flaky; System.Threading.Timer
is post-dispose-safe so the guards read as equivalent).

Introduce an internal ITimerCore seam:
- SystemProgressTimer drives an ITimerCore (production = a thin System.Threading.Timer
  wrapper); an internal test ctor injects a fake core.
- The 3 base classes gain an internal TimerCoreFactory so CreateProgressTimer builds
  its timer over an injectable core.

TimerSeamTests use a FakeTimerCore that RECORDS Change/Dispose calls, making the
Start/StopTimer/Dispose contract and each base's CreateProgressTimer.Start
deterministically observable — no wall-clock ticks. All internal (no public API change).

Mutation score 97.23% -> 100.00% (0 survivors); 409 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Kill the 10 timer mutation survivors via a deterministic seam (#328)
# Conflicts:
#	src/Wolfgang.Etl.Abstractions/Wolfgang.Etl.Abstractions.csproj
- PackageValidationBaselineVersion 0.18.0 -> 0.18.1 (validate against the now-published
  0.18.1); CompatibilitySuppressions still cover the int->long break vs 0.18.1 (pack green).
- Promote CHANGELOG [Unreleased] -> [0.19.0] (2026-07-28); baseline note updated to 0.18.1.
- Promote the Report timing ctor PublicAPI.Unshipped -> Shipped (ordinal); reset Unshipped.

No code changes. 413 tests pass; pack + Package Validation green vs 0.18.1.

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

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.

@github-actions github-actions Bot 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.

BenchmarkDotNet

Details
Benchmark suite Current: b89a625 Previous: 3a14ba9 Ratio
Wolfgang.Etl.Abstractions.Benchmarks.ExtractorBenchmarks.Extract_NoProgress(RecordCount: 1000) 32153.659149169922 ns (± 271.3716530526001) 31930.534067789715 ns (± 658.2371578009119) 1.01
Wolfgang.Etl.Abstractions.Benchmarks.ExtractorBenchmarks.Extract_WithProgress(RecordCount: 1000) 33935.63491821289 ns (± 120.3086629043112) 34010.91799926758 ns (± 129.0833785595842) 1.00
Wolfgang.Etl.Abstractions.Benchmarks.ExtractorBenchmarks.Extract_NoProgress(RecordCount: 100000) 3202251.9049479165 ns (± 9609.219461803606) 3106942.7135416665 ns (± 3078.2601995964997) 1.03
Wolfgang.Etl.Abstractions.Benchmarks.ExtractorBenchmarks.Extract_WithProgress(RecordCount: 100000) 3420486.3059895835 ns (± 19797.47197654614) 3369861.2369791665 ns (± 3214.804757548345) 1.02
Wolfgang.Etl.Abstractions.Benchmarks.PipelineBenchmarks.FluentPipeline(RecordCount: 1000) 29940.04736836751 ns (± 123.36518324624471) 29861.78623453776 ns (± 94.08997512637652) 1.00
Wolfgang.Etl.Abstractions.Benchmarks.PipelineBenchmarks.ManualComposition(RecordCount: 1000) 29013.722813924152 ns (± 41.61275689471991) 29908.161814371746 ns (± 70.64600581703343) 0.97
Wolfgang.Etl.Abstractions.Benchmarks.PipelineBenchmarks.BaseClassComposition(RecordCount: 1000) 67647.85827636719 ns (± 225.2945243238395) 68483.69946289062 ns (± 285.6650714675182) 0.99
Wolfgang.Etl.Abstractions.Benchmarks.PipelineBenchmarks.FluentPipeline(RecordCount: 100000) 2824819.7578125 ns (± 1034.5833105776833) 2854886.4140625 ns (± 1462.1467840974312) 0.99
Wolfgang.Etl.Abstractions.Benchmarks.PipelineBenchmarks.ManualComposition(RecordCount: 100000) 2914988.14453125 ns (± 2706.130020031926) 2870112.7330729165 ns (± 326.26898943322766) 1.02
Wolfgang.Etl.Abstractions.Benchmarks.PipelineBenchmarks.BaseClassComposition(RecordCount: 100000) 6693342.674479167 ns (± 37616.747084649534) 6728828.515625 ns (± 4243.902731655375) 0.99

This comment was automatically generated by workflow using github-action-benchmark.

…#338)

Add an internal ITimeSource seam (StartedAt/Elapsed and the throughput metrics derived
from them) so timing can be driven from a fake clock in tests, unblocking ETL-Test-Kit#262.

- Internal ITimeSource + SystemTimeSource (default = DateTimeOffset.UtcNow + Stopwatch,
  net462-safe; NOT TimeProvider which is net8+).
- ExtractorBase/LoaderBase/TransformerBase read StartedAt/Elapsed through an internal
  TimeSource field (mirrors the internal TimerCoreFactory seam); EtlRunState reads elapsed
  through an injected ITimeSource (internal ctor). Default behaviour identical (real clock).
- InternalsVisibleTo("Wolfgang.Etl.TestKit") so Test-Kit doubles can inject a fake.

NO public API change (all internal) — pack + Package Validation green vs 0.18.1. 419 tests
(6 new ClockSeamTests); Stryker 100.00% (0 survivors); all 11 TFMs clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Chris-Wolfgang and others added 2 commits July 28, 2026 11:55
Injectable internal time source for deterministic Report timing (#338)
…elease day)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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