feat(cayenne): default-on adaptive cold layout from observed filters (F4)#11973
Open
lukekim wants to merge 3 commits into
Open
feat(cayenne): default-on adaptive cold layout from observed filters (F4)#11973lukekim wants to merge 3 commits into
lukekim wants to merge 3 commits into
Conversation
…(F4) When sort_columns is empty (the production default), record filter columns on every scan and sort compaction / sort-and-rewrite by the hottest observed columns so zone maps prune selective queries without spicepod setup. Explicit cayenne_sort_columns still wins; cold-tier Z-order falls back through observations before the primary key. Includes unit tests, integration tests, and a layout read-amp bench (16× fewer bytes vs append layout at 5% selectivity).
Contributor
✅ Pull with Spice Passed🏷️ Auto-applied labels:
Passing checks:
|
Contributor
There was a problem hiding this comment.
Pull request overview
Implements Cayenne “F4” default-on adaptive cold-layout selection: when sort_columns is unset/empty, Cayenne observes which columns appear in scan pushdown filters and uses the hottest observed columns to drive rewrite/compaction sorting (improving zone-map pruning without requiring spicepod config).
Changes:
- Add per-table filter-column observation histogram and record filter columns on
TableProvider::scan. - Use “effective” rewrite sort columns (configured
sort_columnsfirst, else top observed columns) for sort-and-rewrite and compaction rewrite paths, and prefer observations over PK fallback for cold-tier clustering when unset. - Add integration tests + a criterion bench to validate/anchor the default-on adaptive layout behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/cayenne/tests/adaptive_layout_test.rs | New integration tests covering learning from scans, configured override, and rewrite correctness. |
| crates/cayenne/src/provider/table.rs | Plumbs shared observations into the provider; uses effective sort columns for compaction/rewrite; records filters during scans. |
| crates/cayenne/src/provider/predicate_stats.rs | New bounded histogram implementation + unit tests for column extraction/ranking. |
| crates/cayenne/src/provider/mod.rs | Wires in the new predicate_stats module. |
| crates/cayenne/Cargo.toml | Registers new layout_amp_default_on benchmark target. |
| crates/cayenne/benches/layout_amp_default_on.rs | Adds read-amplification anchor bench and filter-column extraction microbench. |
phillipleblanc
previously approved these changes
Jul 22, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
crates/cayenne/src/provider/table.rs:11391
- The doc comments above
sort_stream_by_columnslook like they still include a leftover# Errorssentence from the oldsort_streamimplementation, and now duplicate / slightly misdescribe the current behavior. Please simplify to a single description + single# Errorssection so the docs stay accurate.
- predicate_stats: deterministic eviction victim (break ties by column name) so the observed set — and therefore auto-layout — no longer depends on randomized HashMap iteration order under column churn - table.rs: fix the duplicated/misleading `sort_stream_by_columns` `# Errors` doc; missing/invalid sort columns are skipped with a warning and the stream returned unsorted, not surfaced as an error - docs/cayenne: document the cold-tier clustering-key precedence (clustering cols -> sort cols -> hot observed filters -> PK), update the cheat-sheet default, and add a Document changelog row - align new adaptive_layout_test / layout_amp_default_on license headers to the 2024-2026 repo standard - rustfmt this PR's new code
Comment on lines
11400
to
11410
| use datafusion_execution::TaskContext; | ||
|
|
||
| if sort_columns.is_empty() { | ||
| return Ok(stream); | ||
| } | ||
|
|
||
| // Create a task context with default memory pool and runtime settings | ||
| // This will use the configured spill directory and compression settings | ||
| let task_ctx = Arc::new(TaskContext::default()); | ||
|
|
||
| tracing::debug!( |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
sort_columns/ cold clustering are empty (production default), Cayenne records which columns appear in scan filters and uses the hottest ones to sort compaction / sort-and-rewrite output.maintained_aggregatesorcayenne_sort_columns.sort_columnsalways wins; cold-tier Z-order resolution prefers observations over PK fallback when clustering is unset.How it works
FilterColumnObservations(bounded histogram, max 32 columns) on each table.TableProvider::scanrecords distinct filter column names (nested AND/OR supported).effective_sort_columns_for_rewrite()→ configured sort columns, else top-2 observed columns in schema.sort_and_rewrite_datasort by that list when non-empty.Validation
cargo test -p cayenne --lib predicate_statscargo test -p cayenne --test adaptive_layout_testcargo clippy -p cayenne --no-deps --lib --tests --bench layout_amp_default_on -- -D warningscargo bench -p cayenne --bench layout_amp_default_onBench anchor (100k rows, 64 files, ~5% selective)
Test plan
files_scanneddrops post-compact