Skip to content

Unify bucketing contract between sort/join/group by algorithms #2543

Merged
norberttech merged 7 commits into
1.xfrom
bucketing-contract
Jul 22, 2026
Merged

Unify bucketing contract between sort/join/group by algorithms #2543
norberttech merged 7 commits into
1.xfrom
bucketing-contract

Conversation

@norberttech

@norberttech norberttech commented Jul 22, 2026

Copy link
Copy Markdown
Member

Change Log


Added

  • flow-php/etl - Standalone Flow\ETL\Bucketing storage backend with Memory, Filesystem and PSR-16 implementations.
  • flow-php/etl - BucketingProcessor with pluggable strategies (HashBucketing, SortedRunBucketing).
  • flow-php/etl - Algorithm builder DSL functions: memory_sort(), external_sort(), hash_join(), hash_group_by().
  • flow-php/etl - Floe Options value object with floe_options() DSL and validateData option.

Fixed

  • flow-php/etl - DateInterval ordering in sort now works via type-specialized Rows::sortBy.

Changed

  • flow-php/etl - External sort rebuilt on the bucketing engine.
  • flow-php/etl - Grace hash join rebuilt on the bucketing engine, building the hash table from the smaller bucket.
  • flow-php/etl - Mixed join expressions with equalities now run as hash join instead of nested loop.
  • flow-php/etl - Group-by runs on the bucketing engine with spill column pruning.
  • flow-php/etl - Sort, join and group-by configuration is now algorithm-scoped via ConfigBuilder::sort()/join()/groupBy().
  • flow-php/etl - Equal/Identical join comparisons follow SQL null semantics.
  • flow-php/etl - FloeWriter/FloeStreamWriter take schema in constructor and build the encoder once.

Removed

  • flow-php/etl - DataFrame::pivot() - pivot is declared between groupBy() and aggregate().
  • flow-php/etl - Root ConfigBuilder externalSort*/join*/grouping* delegators.
  • flow-php/etl - Legacy Sort\ExternalSort and BucketsCache family.
  • flow-php/etl - SortAlgorithms enum replaced by SortSteps processor dispatch.

Deprecated

Security


Benchmarks:

  ┌─────────────────────┬────────┬────────┬────────┬────────┐                                                                                                                                                                                                                                                 
  │      Benchmark      │ Before │ After  │ Δ time │ Δ mem  │                                                                                                                                                                                                                                                 
  ├─────────────────────┼────────┼────────┼────────┼────────┤                                                                                                                                                                                                                                                 
  │ group_by            │ 3.709s │ 1.361s │ −63.3% │ −16.7% │                                                                                                                                                                                                                                                 
  ├─────────────────────┼────────┼────────┼────────┼────────┤                                                                                                                                                                                                                                                 
  │ group_by_aggregate  │ 4.193s │ 1.842s │ −56.1% │ −16.6% │                                                                                                                                                                                                                                                 
  ├─────────────────────┼────────┼────────┼────────┼────────┤                                                                                                                                                                                                                                                 
  │ join_left_anti      │ 3.941s │ 2.523s │ −36.0% │ —      │                                                                                                                                                                                                                                                 
  ├─────────────────────┼────────┼────────┼────────┼────────┤                                                                                                                                                                                                                                                 
  │ join_left           │ 4.397s │ 3.009s │ −31.6% │ —      │                                                                                                                                                                                                                                                 
  ├─────────────────────┼────────┼────────┼────────┼────────┤                                                                                                                                                                                                                                                 
  │ join_inner          │ 4.395s │ 3.076s │ −30.0% │ —      │                                                                                                                                                                                                                                                 
  ├─────────────────────┼────────┼────────┼────────┼────────┤                                                                                                                                                                                                                                                 
  │ join_right          │ 4.500s │ 3.164s │ −29.7% │ —      │                                                                                                                                                                                                                                                 
  ├─────────────────────┼────────┼────────┼────────┼────────┤                                                                                                                                                                                                                                                 
  │ sort_orders         │ 4.068s │ 2.503s │ −38.5% │ −5.6%  │                                                                                                                                                                                                                                                 
  ├─────────────────────┼────────┼────────┼────────┼────────┤                                                                                                                                                                                                                                                 
  │ cache_write         │ 2.642s │ 2.062s │ −22.0% │ —      │                                                                                                                                                                                                                                                 
  ├─────────────────────┼────────┼────────┼────────┼────────┤                                                                                                                                                                                                                                                 
  │ floe_write (php)    │ 4.362s │ 3.748s │ −14.1% │ —      │                                                                                                                                                                                                                                                 
  ├─────────────────────┼────────┼────────┼────────┼────────┤                                                                                                                                                                                                                                                 
  │ floe_write (native) │ 2.510s │ 1.930s │ −23.1% │ —      │                                                                                                                                                                                                                                                 
  ├─────────────────────┼────────┼────────┼────────┼────────┤                                                                                                                                                                                                                                                 
  │ schema_inference    │ 2.086s │ 1.555s │ −25.5% │ —      │                                                                                                                                                                                                                                                 
  └─────────────────────┴────────┴────────┴────────┴────────┘        

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

 Flow\ETL\Bucketing namespace: BucketsStorage contract,
Bucket/BucketStats value objects, Buckets repository
- Memory, Filesystem, and PSR-16 cache storage backends
- array-backed PSR-16 test fake and bucket ObjectMothers
- BucketingProcessor: barrier processor spilling rows into buckets,
tagging _bucket_id
- pluggable strategies: HashBucketing (hash-mod) and SortedRunBucketing
(external-sort runs)
- single-pass key extraction + value-based batch Hasher (KeyValues,
NativeHasher)
- per-bucket stats (rows/chunks/min/max/nullCount/capped-distinct) via
BucketStatsCollector
… on the bucketing engin

- BucketingStrategy spills to BucketsStorage itself and yields
Bucket(id, totalRows) VOs; BucketChunk, BucketYield and the
stats-collector machinery removed
- BucketingProcessor reduced to an adapter: registers buckets, yields
one metadata Rows per bucket (BucketShape: _bucket_id,
_bucket_total_rows)
- external sort rebuilt on the engine: SortingProcessor/ExternalSort
family replaced by MemorySortProcessor + SortedRunBucketing +
MergeSortProcessor, k-way merge internals moved to Sort\Merge
- sorted runs written with set() (write-and-close), hash buckets with
append() - storage verb owned by the strategy
- external-sort storage and tuning moved from CacheConfig to SortConfig
(public Config::builder() API unchanged)
…imization

- grace hash join rebuilt on Buckets/BucketsStorage, config swapped from
BucketsCache, BucketSpiller/BucketedHashJoin removed
- SQL null semantics for Equal/Identical, null join keys dropped before
spilling
- hash table built from the smaller bucket of each pair (Bucket gains
partition index)
- equalities extracted from mixed join expressions, All(Equal, Any) runs
as hash join instead of nested loop (~31x)
- hashing consolidated: positional KeyValues + single NativeHasher,
JoinKeys reduced to refs VO, HashTable to hash->rows multimap
- BucketsStorage::get() yields batches (-16% joins), Rows::sortBy via
type-specialized C-level sort (-10% external sort, also fixes
never-working DateInterval ordering)
…n pruning

- GroupBy runs on HashBucketing/BucketingProcessor; ExternalAggregation
deleted
- aggregate() registers pipeline segments via GroupBySteps
(BucketingProcessor -> GroupByAggregationProcessor, or PivotProcessor)
- AggregatingFunction::references() enables pruning spilled rows to
grouping + aggregation columns (~-40% wall time); missing columns
normalized to null entries
- GroupingConfig swaps BucketsCache for BucketsStorage
(groupingStorage())
- DataFrame::pivot() removed - pivot is declared between groupBy() and
aggregate()
- HashBucketing/KeyValues gain nullOnMissing flag
- ConfigBuilder::sort()/join()/groupBy() take algorithm builders
(memory_sort(), external_sort(), hash_join(), hash_group_by()); root
externalSort*/join*/grouping* delegators removed
- per-algorithm configs share one BucketingConfig{storage, bucketsCount,
batchSize}; Config::$sort is MemorySortConfig|ExternalSortConfig,
JoinConfig/GroupingConfig become HashJoinConfig/HashGroupByConfig
- SortAlgorithms enum replaced by SortSteps processor dispatch (mirrors
GroupBySteps); sort run knob renamed bucketSize -> runSize
- unused legacy Sort\ExternalSort\BucketsCache family deleted
- sort/join/group-by docs and upgrade notes rewritten to the new API
… join and group-by

- JoinSteps factory; DataFrame::join delegates like sortBy/aggregate
- bucket lifecycle unified: shared Buckets registry, ids from metadata
rows, plain finally cleanup; dead Buckets/ExternalSortConfig API removed
- RowsBuffer extracted for size-bounded batch emission; RowKey deleted,
HashBucketing pairs rows with hashes positionally
- group-by output batch size wired from config; GroupBy\Bucket renamed
to Group
- sort spill ids namespaced (sort-{run}-{index}); strategies take
list<Reference>
- int<1, max> guard convention applied to all bucketing constructors
@norberttech
norberttech merged commit 32bbeab into 1.x Jul 22, 2026
50 checks passed
@norberttech
norberttech deleted the bucketing-contract branch July 22, 2026 13:10
@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.71795% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.45%. Comparing base (91a9da6) to head (396c2f3).
⚠️ Report is 5 commits behind head on 1.x.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@             Coverage Diff              @@
##                1.x    #2543      +/-   ##
============================================
+ Coverage     86.32%   86.45%   +0.13%     
- Complexity    23233    23329      +96     
============================================
  Files          1761     1772      +11     
  Lines         70803    71001     +198     
============================================
+ Hits          61119    61384     +265     
+ Misses         9684     9617      -67     
Components Coverage Δ
etl 90.38% <98.71%> (+0.69%) ⬆️
cli 89.40% <ø> (ø)
lib-array-dot 81.44% <ø> (ø)
lib-azure-sdk 64.44% <ø> (ø)
lib-doctrine-dbal-bulk 93.61% <ø> (ø)
lib-filesystem 86.64% <ø> (ø)
lib-types 91.15% <ø> (ø)
lib-parquet 70.23% <ø> (ø)
lib-parquet-viewer 82.26% <ø> (ø)
lib-snappy 89.38% <ø> (-0.45%) ⬇️
lib-dremel 0.00% <ø> (ø)
lib-postgresql 88.62% <ø> (ø)
lib-telemetry 86.58% <ø> (ø)
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.41% <ø> (ø)
bridge-symfony-http-foundation-telemetry 92.85% <ø> (ø)
bridge-symfony-filesystem-bundle 91.85% <ø> (ø)
bridge-symfony-filesystem-cache 98.18% <ø> (ø)
bridge-symfony-postgresql-bundle 93.70% <ø> (ø)
bridge-symfony-postgresql-cache 94.41% <ø> (ø)
bridge-symfony-postgresql-messenger 98.80% <ø> (ø)
bridge-symfony-postgresql-session 93.65% <ø> (ø)
bridge-symfony-telemetry-bundle 90.10% <ø> (ø)
adapter-chartjs 84.05% <ø> (ø)
adapter-csv 89.46% <ø> (ø)
adapter-doctrine 90.75% <ø> (ø)
adapter-google-sheet 91.56% <ø> (ø)
adapter-http 73.23% <ø> (ø)
adapter-json 87.30% <ø> (ø)
adapter-logger 50.00% <ø> (ø)
adapter-parquet 88.69% <ø> (ø)
adapter-text 92.59% <ø> (ø)
adapter-xml 83.56% <ø> (ø)
adapter-avro 0.00% <ø> (ø)
adapter-excel 86.72% <ø> (ø)
adapter-postgresql 90.84% <ø> (ø)
adapter-seal 83.26% <ø> (ø)
bridge-phpunit-postgresql 75.30% <ø> (ø)
bridge-phpunit-telemetry 87.32% <ø> (ø)
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant