Add codec spec DSL and configuration plumbing - #19284
Conversation
There was a problem hiding this comment.
Pull request overview
Adds the codec-spec DSL and configuration plumbing while keeping codec execution disabled.
Changes:
- Adds bounded parsing and immutable codec AST types.
- Adds
codecSpecto forward-index configuration. - Adds fail-closed validation across creation, loading, preprocessing, and realtime paths.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
ForwardIndexConfig.java |
Adds codec-spec configuration support. |
CodecSpecParser.java |
Parses and canonicalizes the DSL. |
CodecPipeline.java |
Represents ordered codec stages. |
CodecInvocation.java |
Represents individual codec calls. |
ForwardIndexType.java |
Reconciles configuration and rejects activation. |
ForwardIndexReaderFactory.java |
Adds reader-side rejection. |
ForwardIndexCreatorFactory.java |
Adds creator-side rejection. |
ForwardIndexHandler.java |
Rejects preprocessing with codec specs. |
ForwardIndexConfigTest.java |
Tests configuration behavior. |
CodecSpecParserTest.java |
Tests parsing and limits. |
CodecPipelineTest.java |
Tests pipeline immutability. |
CodecInvocationTest.java |
Tests invocation validation. |
TableConfigUtilsTest.java |
Tests table validation. |
ForwardIndexHandlerTest.java |
Tests preprocessing rejection. |
ForwardIndexTypeTest.java |
Tests reconciliation and mutable indexes. |
ForwardIndexReaderFactoryTest.java |
Tests reader rejection. |
ForwardIndexCreatorFactoryTest.java |
Tests creator rejection. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #19284 +/- ##
============================================
+ Coverage 67.00% 67.07% +0.06%
Complexity 1424 1424
============================================
Files 3463 3467 +4
Lines 221671 222321 +650
Branches 34954 34994 +40
============================================
+ Hits 148524 149111 +587
+ Misses 61310 61304 -6
- Partials 11837 11906 +69
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
e83ed0e to
1c5df46
Compare
1c5df46 to
ad52533
Compare
|
Reviewed against the split plan. One thing worth fixing before this merges, since this PR freezes the codecSpec grammar: Minor: |
|
Pushed `e00e837b5c` addressing review findings on the DSL surface. Leading-zero arguments are now rejected. `ZSTD(03)` and `ZSTD(3)` previously canonicalized to two different strings. Since the canonical spec is frozen into V7 segment headers and compared by equality on reload, two spellings of the same value would eventually mean spurious rewrites and a parser that has to accept both forever. The grammar is now `arg ::= "0" | [1-9][0-9]*`, enforced in both `CodecSpecParser.parseArg` and the `CodecInvocation` constructor (programmatic construction bypasses the parser). A bare `0` remains valid. `MAX_SPEC_LENGTH` tightened 64KB to 4KB to bound header bloat. Note this now wins over the combinatorial maximum of the per-stage limits (32 stages x 128-char names is ~4.1KB); any realistic spec is far below it. Tests pin both boundaries — exactly `MAX_SPEC_LENGTH` parses, one over is rejected. `REMOVED_WRAPPER_NAME` widened to public so every layer enforcing the `CODEC` reservation shares one definition instead of re-spelling the literal (the registry in #19285 now references it). All of these tighten acceptance before first release, which is the reversible direction — accepting now and rejecting later would not be. Verification: `CodecSpecParserTest`, `CodecInvocationTest`, `CodecPipelineTest`, `ForwardIndexConfigTest` (pinot-segment-spi) plus `ForwardIndexTypeTest`, `TableConfigUtilsTest`, `ForwardIndexCreatorFactoryTest`, `OpenStructIndexTypeTest` (pinot-segment-local) all green; spotless/checkstyle/license clean on both modules. |
|
Note: pushed a master sync to this branch (merge commit, no code changes of its own) so the downstream stack includes the #19282 chunk-caching fix that the reload tests in #19308 depend on. The full stack continuing this PR: #19305 (DELTA/DELTADELTA) → #19306 (T64/GORILLA) → #19307 (V7 format) → #19308 (reload + enable) → #19309 (integration tests + docs). Each is based on its predecessor's branch. |
6dac83b to
b34537f
Compare
Jackie-Jiang
left a comment
There was a problem hiding this comment.
Let's remove all the unnecessary config check
b34537f to
ba1a944
Compare
Define a bounded, dependency-stable grammar and canonical configuration representation that later codec execution and V7 segment-header layers can share. This avoids binding persisted codec strings to Calcite behavior and keeps legacy compression configuration interoperable. This is a deliberately stacked boundary: it adds only the unreleased parser and configuration contract. Codec execution and on-disk formats remain unchanged until the dependent runtime PR lands, so codecSpec must not be used on an intermediate build.
The canonical codec spec is later frozen into V7 segment headers, so every argument value must have exactly one spelling: ZSTD(03) and ZSTD(3) must not produce different canonical strings. Reject leading zeros in both the parser and the CodecInvocation constructor (programmatic construction bypasses the parser). Also tighten MAX_SPEC_LENGTH from 64KB to 4KB to bound V7 header bloat; the cap still admits any realistic spec, and where it intersects the combinatorial maximum of the per-stage limits, the length cap wins. Widen REMOVED_WRAPPER_NAME to public so every layer enforcing the reservation shares one definition.
The canonical spec produced here is frozen into segment headers and compared for rewrite detection by later layers in this stack, but CodecInvocation and CodecPipeline had no equals/hashCode coverage, and the documented structural limits were only exercised on the reject side. Add tests that pin the frozen contract: - a parsed invocation/pipeline equals the programmatically built equivalent and agrees on hashCode, argument-less invocations canonicalize alike, and stage order is significant - every documented limit (identifier length, argument length, arguments per invocation, pipeline stages) is checked at its accept boundary as well as one over, through both the parser and the AST constructors, which are independent entry points Test-only; no production behavior changes.
The DSL is function-call shaped, so reviewers reasonably ask why it does not reuse Pinot's expression parsing. Record the answer where it is discoverable rather than leaving it to be re-derived. CalciteSqlParser is unreachable from pinot-segment-spi: it lives in pinot-common, which already depends on this module, so calling into it would close a module cycle. Calcite's own SqlParser is on the classpath but is deliberately unused, chiefly because the canonical form produced here is frozen into segment headers and compared by string equality, which would tie on-disk segment compatibility to Calcite's casing, quoting, and literal formatting across upgrades. Also records the cost of that choice: unsigned integer arguments only, so negative and keyword arguments are not expressible today. Javadoc-only; no behavior change.
Remove temporary runtime rejection guards from the stacked configuration layer, tighten the public codec DSL API, and document builder selection semantics.
Share structural limits and token validation between the parser and programmatic AST construction without expanding the public SPI.
ba1a944 to
29eddbd
Compare
Code Review — Codec spec DSL and configuration plumbingReviewed the full diff. This is a clean, well-scoped configuration-layer PR that adds the codec DSL AST/parser and threads a nullable A few observations below — none blocking. Correctness
Serialization
Parser / DSL
Minor / nits
Risk assessmentLow. The PR is inert by design — Nice work — happy to see this merged as the stack root once the minor disabled-path asymmetry is considered. |
Context
This is the configuration layer extracted from #18229 and the root of the codec-pipeline PR stack.
What changed
DELTA,ZSTD(3).indexes.forward.codecSpecsupport toForwardIndexConfig, including JSON, builder/copy, equality, RAW-only validation, and mutual exclusion with legacycompressionCodec.noDictionaryand top-level compression settings with the nested forward-index config.CODEC(...)wrapper reserved and rejected with a precise diagnostic.Stack boundary and compatibility
This PR deliberately does not execute codecs or change on-disk data.
codecSpecis accepted and structurally normalized here. #19285-#19306 add the runtime codecs; #19307 is the first slice that consumescodecSpecfor forward-index creation/loading; #19308 adds semantic table-config validation and codec-aware reload/rewrite behavior. Do not configure this unreleased property on an intermediate build of the stack.The dedicated bounded parser is intentional: reusing Pinot's SQL parser would create a module cycle through
pinot-common, and Calcite canonicalization is not a suitable dependency for strings later frozen into segment headers.Verification
ForwardIndexConfig, and forward-index configuration-reconciliation tests.Stack
Review and merge parent-first; GitHub will retarget each child as its parent merges.