Skip to content

Branching Loader - aggregating / sorting / grouping transformations bug - #2599

Merged
norberttech merged 10 commits into
1.xfrom
transformation-loader-bug
Aug 17, 2026
Merged

Branching Loader - aggregating / sorting / grouping transformations bug#2599
norberttech merged 10 commits into
1.xfrom
transformation-loader-bug

Conversation

@norberttech

Copy link
Copy Markdown
Member

Change Log


Added

  • flow-php/etl - to_branch() accepts a Transformation as an optional third argument
  • flow-php/etl - retry_any_throwable_except() DSL and AnyThrowableExcept retry strategy
  • flow-php/etl-adapter-doctrine - TransactionalDbalLoader::loaders() exposes wrapped loaders
  • flow-php/etl-adapter-postgresql - TransactionalPostgreSqlLoader::loaders() exposes wrapped loaders

Fixed

  • flow-php/etl - transformation-wrapped loader is closed once per pipeline, not once per batch
  • flow-php/etl - loader wrappers forward closure() to the loaders they wrap
  • flow-php/etl - operations inside a Transformation answer for the whole stream, not per batch
  • flow-php/etl - to_branch()->withTransformation() spans the whole filtered stream
  • flow-php/etl - batch size optimization sees through loader wrappers
  • flow-php/etl - save mode no longer leaks between frames sharing one Config
  • flow-php/etl - no SplObjectStorage deprecations on PHP 8.5
  • flow-php/etl-adapter-doctrine - to_dbal_transaction() runs closure() inside a transaction
  • flow-php/etl-adapter-postgresql - to_pgsql_transaction() runs closure() inside a transaction

Changed

  • flow-php/etl - write_with_retries() no longer retries InvalidLogicException by default
  • flow-php/etl - write_with_retries() refuses wrapping an armed Transformation
  • flow-php/etl - a Transformation triggering its own frame (fetch()/count()/schema()) throws InvalidLogicException
  • flow-php/etl - unpartitioned window functions no longer inject collect() and sortBy() stages

Removed

  • flow-php/etl - Extractor\SwappableRowsExtractor
  • flow-php/etl - Config::filesystemStreams() and the FilesystemStreams constructor parameter

Deprecated

Security

- drive the wrapped loader from `->get()` instead of terminating the
nested frame with `->load()->run()`
- wrapped loader now receives the outer FlowContext
- SpyLoader implements Loader\Closure so the close storm is observable
- regression coverage for 9 stateful loaders across 7 packages
- drop dead private helpers from ParquetTest
- RetryLoader, TransactionalDbalLoader and TransactionalPostgreSqlLoader now implement Closure and OverridingLoader
- fixes silent data loss under overwrite(), where the destination file was never published and a temp file was orphaned
- closing stays outside the retry loop, a failed commit is not resumable
- documentation no longer recommends wrapping file loaders in write_with_retries()
…er Config

- save mode and open write streams no longer leak between DataFrames sharing a Config
- remove Config::filesystemStreams() and its constructor parameter
- document the per-DataFrame scope in save-mode.md and upgrading.md
…a time

- explain the Processor vs Transformer rule, list the affected and safe operations
- call out offset() and cache() as data loss, not just wrong ordering
- give the outer ->collect() / ->batchSize(-1) workaround
- add characterization tests pinning today's batch-local behaviour
…whole stream

- drive the nested pipeline once per run inside a Fiber, fed batch by batch
- replace the public SwappableRowsExtractor with an internal FeedExtractor
- route a failed drive through the ErrorHandler and refuse retrying a Transformation
- exclude InvalidLogicException from the default retry strategy
- drop the redundant collect() and sortBy() from withEntry()'s window branch
- walk the loader tree in BatchSizeOptimization::isFor()
- add LoaderTree, first consumer of OverridingLoader::loaders()
- benchmark a wrapped Doctrine write
…ltered stream

- TransformationDrive extracted from TransformerLoader; BranchingLoader shares it instead of rebuilding a DataFrame per batch
- FeedExtractor moved to the Pipeline namespace
- new ReplayAware contract; RetryLoader refuses transformation drives at any nesting depth
- ATTR_LOADING_ROWS on to_branch now counts input rows
- docs: default retry strategy and the wrap refusal documented, upgrading entries extended
- test hygiene: narrowed exception captures, optimizer test dropped its DbalLoader dependency, transformation benchmarks added
- to_dbal_transaction() / to_pgsql_transaction() open one final transaction around the forwarded closure() drain, so blocking transformations commit atomically and roll back on failure
- a failing rollBack() never masks the original load/drain error
- withIsolationLevel() applies to every transaction the wrapper opens
- composition tests in both adapters + nested-transformation pins in core
- docs: new doctrine Transactional Loading section, postgresql rewrite, retry/transaction cross-references, upgrading entry 10
- pgsql adapter DSL functions added to the dsl.json generator paths
…ument

- BranchingLoader takes the optional Transformation in its constructor; withTransformation() replaces it
- mark LoaderTree as @internal like its drive siblings
- landing: new data_writing/branch example routing rows to different destinations by condition
- regenerate DSL json dump and CodeMirror completions
- document the new signature in transformations.md
…tream

- move FeedExtractor from Flow\ETL\Pipeline to Flow\ETL\Extractor
- LoaderTree: offsetExists()/offsetSet() instead of contains()/attach(), deprecated on PHP 8.5
- phpunit: fail on first-party deprecations (failOnDeprecation + ignoreIndirectDeprecations)
- trim narration comments in wrapper loaders
@norberttech norberttech added this to the 0.44.0 milestone Aug 17, 2026
@codecov

codecov Bot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.45355% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 86.51%. Comparing base (afe4309) to head (9b6257d).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@             Coverage Diff              @@
##                1.x    #2599      +/-   ##
============================================
+ Coverage     86.45%   86.51%   +0.06%     
- Complexity    23309    23988     +679     
============================================
  Files          1834     1837       +3     
  Lines         72331    72461     +130     
============================================
+ Hits          62531    62688     +157     
+ Misses         9800     9773      -27     
Components Coverage Δ
etl 91.47% <100.00%> (+0.27%) ⬆️
cli 89.40% <ø> (ø)
lib-array-dot 81.44% <ø> (ø)
lib-azure-sdk 64.44% <ø> (ø)
lib-doctrine-dbal-bulk 93.61% <ø> (ø)
lib-filesystem 86.67% <ø> (ø)
lib-types 92.26% <ø> (ø)
lib-parquet 70.33% <ø> (ø)
lib-parquet-viewer 82.26% <ø> (ø)
lib-snappy 89.38% <ø> (-0.45%) ⬇️
lib-dremel 0.00% <ø> (ø)
lib-postgresql 87.74% <ø> (ø)
lib-telemetry 86.66% <ø> (+0.02%) ⬆️
bridge-filesystem-async-aws 92.74% <ø> (ø)
bridge-filesystem-azure 90.45% <ø> (ø)
bridge-monolog-http 96.82% <ø> (ø)
bridge-monolog-telemetry 94.79% <ø> (ø)
bridge-openapi-specification 92.07% <ø> (ø)
symfony-http-foundation 78.57% <ø> (ø)
bridge-psr18-telemetry 100.00% <ø> (ø)
bridge-psr3-telemetry 98.95% <ø> (ø)
bridge-psr7-telemetry 100.00% <ø> (ø)
bridge-telemetry-otlp 90.11% <ø> (ø)
bridge-symfony-http-foundation-telemetry 92.85% <ø> (ø)
bridge-symfony-filesystem-bundle 91.85% <ø> (ø)
bridge-symfony-filesystem-cache 98.18% <ø> (ø)
bridge-symfony-postgresql-bundle 93.57% <ø> (ø)
bridge-symfony-postgresql-cache 94.41% <ø> (ø)
bridge-symfony-postgresql-messenger 98.80% <ø> (ø)
bridge-symfony-postgresql-session 93.65% <ø> (ø)
bridge-symfony-telemetry-bundle 90.72% <ø> (ø)
adapter-chartjs 84.05% <ø> (ø)
adapter-csv 89.46% <ø> (ø)
adapter-doctrine 91.10% <94.44%> (+0.34%) ⬆️
adapter-google-sheet 91.56% <ø> (ø)
adapter-http 75.79% <ø> (ø)
adapter-json 87.65% <ø> (ø)
adapter-logger 50.00% <ø> (ø)
adapter-parquet 90.40% <ø> (ø)
adapter-text 92.59% <ø> (ø)
adapter-xml 84.45% <ø> (ø)
adapter-avro 0.00% <ø> (ø)
adapter-excel 87.20% <ø> (ø)
adapter-postgresql 90.98% <100.00%> (+0.14%) ⬆️
adapter-seal 83.61% <ø> (ø)
bridge-phpunit-postgresql 75.30% <ø> (ø)
bridge-phpunit-telemetry 87.36% <ø> (ø)
bridge-phpstan-types 0.00% <ø> (ø)
bridge-postgresql-valinor 100.00% <ø> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@norberttech
norberttech merged commit af74b91 into 1.x Aug 17, 2026
49 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Roadmap Aug 17, 2026
@norberttech
norberttech deleted the transformation-loader-bug branch August 17, 2026 10:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant