POC: incremental materialized view β derived data format + transparent read path - #212
Open
alchemist51 wants to merge 8 commits into
Open
POC: incremental materialized view β derived data format + transparent read path#212alchemist51 wants to merge 8 commits into
alchemist51 wants to merge 8 commits into
Conversation
Proves per-segment materialized-view state files as a composite-engine secondary data format, end to end in a cluster IT: - MV state files (DataFusion Partial-mode aggregate output, sorted by group key) built at flush from the primary's parquet file; tracked as first-class format files in the catalog snapshot - Row-parity exemption for derived formats: DataFormat.exemptFromRowParity() gating the CatalogSnapshotManager parity check (POC-hardcoded name set) and the CompositeMergeExecutor row-equality check - Hardcoded search (Final fold over state files across segments) returns exact golden answers; duplicate groups across segments combine correctly - Standalone round-trip test proving the state contract (Partial -> parquet -> Final; merge-fold -> Final) POC scope: one hardcoded definition (COUNT(*) GROUP BY service), merges disabled, private tokio runtime per build call, no memory-pool enrollment. See kb mv-poc-results.md for lessons and the hardcode-replacement list. Signed-off-by: Arpit Bandejiya <abandeji@amazon.com>
Replaces the derived-at-flush build (re-reading the primary's parquet)
with the forward-buffer / background-state model:
- MVDocumentInput captures the MV's referenced column from the composite
broadcast (no capability claim needed - all formats see all fields)
- MVWriter buffers values into a small VSR; rotation (4096 rows, always
BEFORE append so rollback only truncates the buffer) exports via Arrow
C-Data and folds into a native sorted background state (BTreeMap for
the POC count case; sorted-runs + k-way fold is the general design)
- flush = final rotation + finalize: background state written as the
sorted MV state parquet. Zero flush-time aggregation work, no read of
the primary file, no path-convention coupling
Same observable contract (sorted state file, schema, catalog
integration, golden answers) - the IT passes unchanged except the log
line ('streamed' vs 'built'). The derived build (mv_poc.rs mv_build_poc)
remains as the future merge/backfill path.
Signed-off-by: Arpit Bandejiya <abandeji@amazon.com>
Extends the POC view to
SELECT service, status, COUNT(*), SUM(latency_ms), MIN(latency_ms),
MAX(latency_ms) GROUP BY service, status
and replaces the hand-rolled BTreeMap fold with DataFusion's own state
algebra (hash aggregation until flush + one sort at flush):
- feed: Partial-mode aggregation over the fed batch (raw -> state rows),
accumulated; held state bounded by PartialReduce compaction
(state (+) state -- the engine operator, nothing hand-rolled)
- finalize: final PartialReduce fold -> single lexsort by the group-key
columns (cost ~ groups) -> sorted state parquet
- state schema comes from the plan; Java knows only group keys + search
template. min/max state columns are name[value] (probed via test)
- MVDocumentInput captures (service, status, latency_ms); forward buffer
is a 3-vector VSR; rotation contract unchanged
- search v2: SQL template folding all four aggregates over state files
- noted-not-built optimization recorded in mv_writer.rs: sorted runs +
SortPreservingMerge + GroupOrdered fold for high cardinality
IT: 6 state rows across 2 segments; goldens incl. a group split across
segments and min/max winners in different segments -- all exact.
Signed-off-by: Arpit Bandejiya <abandeji@amazon.com>
Expose per-generation coverage on MVReaderManager.MVReader: coveredGenerations(), stateFilesByGeneration(), covers(gen). The writer generation is engine-owned and shared across formats for one flush, so the read-path coverage split is a set intersection between the MV reader's generations and the primary format's β both from the same catalog snapshot, making the split atomic with the snapshot. First work item (W1) of the search-side integration plan; consumed by the shard-side coverage split when the second scan source lands. Also applies spotless formatting to the POC files. Signed-off-by: Arpit Bandejiya <abandeji@amazon.com>
Match aggregate queries against materialized view definitions and record rewrite options, without changing any plan: - MVRegistry: planner-facing lookup (index -> eligible MV definitions in canonical post-decompose form). Production default is EMPTY (the phase is a zero-cost no-op) until MV metadata lands. - MVRewritePhase: runs between decomposeAggregates and mark, so the query tree and stored definitions are identically canonicalized and the marking/CBO/split phases see today's exact shapes. v0 scope: Aggregate over (trivial Project over) TableScan, simple group type, no DISTINCT/FILTER calls; group-by exact set match; query aggregates a verbatim subset of the MV's. Self-joined tables are skipped. - MVRewriteAnnotation lives in a PlannerContext side-channel keyed by table identity (not on the RelNode tree - survives rel copies), to be consumed at shard-fragment emission; the shard exercises the option per segment based on snapshot coverage. Work items W2/W3 of mv-search-side-integration-plan.md (decisions D1/D2). The DAG-layer binding (W4) and the shard-side coverage split (W5/W6) follow. Signed-off-by: Arpit Bandejiya <abandeji@amazon.com>
Completes the search read path for incremental materialized views (W4/W5/W6 of the search-side integration plan): - ShardScanInstructionNode carries an optional MV binding (mvId + state schema fingerprint) as optional fields, not a new instruction type, so instruction-list readers never see an unknown enum ordinal. - FragmentConversionDriver threads the planner's side-channel annotations into instruction assembly; the binding travels only on a plain shard scan whose fragment has a partial aggregate β row-id (QTF) fragments and delegated-predicate fragments never bind (D3). - ShardScanInstructionHandler computes the coverage split from the acquired reader's catalog snapshot (MV generations intersected with parquet generations β one snapshot, so the split is atomic) and attaches state-file paths + covered raw file names to the native session via a new df_session_attach_mv FFM call. - Rust mv_read: at prepare_partial_plan time the stripped Partial plan becomes UNION(scan of state files positionally aliased to the partial output names, Partial over the raw scan narrowed to uncovered files). Fallback-first: any mismatch (plan shape, schema shape, scan leaf shape) keeps the raw-only plan β never wrong, only slower. TopK fragments are excluded in v0. - MVRegistryHolder: POC static wiring of the planner registry until MV metadata provides per-query cluster-state resolution. Proven by tests/mv_read_union_test.rs: mixed coverage equals the full raw answer (narrowing failure would double-count), all-covered serves entirely from state files, and a mismatched state schema falls back to the untouched raw plan. Signed-off-by: Arpit Bandejiya <abandeji@amazon.com>
MVReadPathIT drives a SQL aggregate on the source index through the complete chain: MVRewritePhase annotation (POC registry via MVRegistryHolder) -> fragment binding -> shard-side coverage attach -> native union over state files -> coordinator FINAL. Assertions are layered so a silent fallback FAILS the test: - MockLogAppender expects the shard handler's 'mv-binding attached' INFO event during the MV-enabled query (the log only fires after df_session_attach_mv returns success). - Differential: MV-enabled results equal raw-path results, both equal golden values. - Negative control: a query whose group-by differs from the MV definition doesn't bind and still answers correctly. Findings encoded in the test setup: - Single-shard fragments run SINGLE-mode aggregation (no Partial node), so the MV binding never applies there β the IT uses 2 shards to get the partial/final split. v0 scope gap tracked in the plan doc. - The POC MV writer fails flush row-parity on near-empty per-shard batches; docs are routed to one shard (the empty shard still exercises the no-coverage path end to end). Also promotes the attach/union success logs to INFO for operability. Signed-off-by: Arpit Bandejiya <abandeji@amazon.com>
POC verification mode (system property mv.poc.strict_read) that inverts the read path's fallback-first contract to fail-closed: - Every fallback in mv_read (plan shape, state schema, scan leaf, binding not applied) becomes a hard error instead of a silent raw fallback; an attach failure in the shard handler also fails closed. - Every raw file must be MV-covered; any uncovered file is an error. - The produced plan is the positionally-aliased state-file scan ALONE β no raw scan node exists in the plan, so raw parquet physically cannot be read. A successful strict query is therefore proof the answer came exclusively from MV state files. Proof: - Rust (6/6): strict all-covered plan's display contains no raw-dir path and folds to golden answers; strict with an uncovered file errors; strict with a mismatched state schema errors; the three non-strict tests are unchanged. - MVReadPathIT (3/3): new strict test sets the property, asserts the handler's '[STRICT MV-only]' attach event via MockLogAppender, and gets golden results β with fallbacks hard-failing, success proves MV-only serving end to end through the live query path. Testing/POC only; production keeps the fallback-first contract. Signed-off-by: Arpit Bandejiya <abandeji@amazon.com>
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.
What this proves
The incremental-MV design (per-segment aggregate state files as a composite-engine secondary data format) works end to end in a live cluster β both halves:
MVDataFormatPocIT)MVReadPathIT)Write path (commits 1β3)
For the hardcoded view
SELECT service, status, COUNT(*), SUM(latency_ms), MIN(latency_ms), MAX(latency_ms) FROM payments GROUP BY service, status:MVDocumentInputcaptures the referenced columns from the composite broadcast β forward VSR buffer (4096-row rotation, rotate-before-append for rollback safety) β Arrow C-Data β DataFusion-maintained native state (Partial-mode accumulate +PartialReducecompaction) β flush finalizes into a group-key-sorted state parquet. No primary re-read, no flush-time aggregation spike.Segment's format map β pinning, snapshot membership, per-format cleanup all free. Row-parity exemption (DataFormat.exemptFromRowParity()) lets a segment carry 8 raw rows next to 3 state rows.Read path (commits 4β7)
The full transparent-rewrite chain, star-tree style (gate once per request, substitute per segment, zero new downstream types):
Fallback-first everywhere: any mismatch (plan shape, state schema, scan leaf shape, attach failure) keeps today's raw-only plan. The only possible failure mode is "no speedup", never a wrong answer.
MV-only proof (strict mode): a POC verification switch (
mv.poc.strict_read) inverts the fallback contract to fail-closed β every fallback becomes a hard error, every raw file must be covered, and the native plan is the state-file scan alone (no raw scan node exists, raw parquet physically cannot be read).testStrictModeProvesQueryServedFromMVOnlyruns the aggregate through the live cluster in this mode and gets the golden answers β constructive proof the query was served exclusively from the MV.Proof (assertions layered so a silent fallback FAILS the test):
MVReadPathIT(2 shards): MockLogAppender expects the shard handler'smv-binding β¦ attachedevent during the MV-enabled query; differential β MV-enabled results β‘ raw-path results β‘ goldens; negative control β a non-matching group-by doesn't bind and still answers correctly. Observed chain in one run: annotation βbind=trueβattached 2 state files, 2 covered raw filesβmv_read: bound 2 state files; raw branch narrowed by 2 covered files.tests/mv_read_union_test.rs(6 tests): mixed coverage equals the full-raw answer (a narrowing failure would double-count and fail), all-covered serves entirely from state files, mismatched state schema returns the untouched plan; strict variants β the strict plan's display contains no raw-dir path, an uncovered file errors, a mismatched schema errors.Reading order
server/β¦/DataFormat.java+CatalogSnapshotManager.javaβ parity-exemption core change (small)sandbox/plugins/mv-data-format/β the format plugin (MVWriteris the write-path heart;MVReaderManagernow exposes per-generation coverage)analytics-engine β¦/planner/mv/βMVRewritePhase+MVRegistry(the planner gate)analytics-backend-datafusion β¦/ShardScanInstructionHandler.java+rust/src/mv_read.rsβ the shard-side split + native union surgeryMVDataFormatPocIT(write path) andMVReadPathIT(read path) β the demos:POC hardcodes / known v0 gaps (not production)
MVRegistryHolderstatic wiring (replaced by cluster-state MV metadata at M0/M1); state-fingerprint carried on the wire but enforced only via the positional schema checkDesign decisions (D1βD8: annotation transport, surgery ordering, wire transport, coverage split, schema alignment, registry wiring) + full work log live in the KB:
mv-search-side-integration-plan.md.Not for merge β design-validation artifact for the incremental-MV track, now covering the complete writeβread loop.