Skip to content

Add V7 raw forward index format for codec pipelines - #19307

Open
xiangfu0 wants to merge 1 commit into
xiangfu0/codex/codec-stack/04-t64-gorillafrom
xiangfu0/codex/codec-stack/05-v7-format
Open

Add V7 raw forward index format for codec pipelines#19307
xiangfu0 wants to merge 1 commit into
xiangfu0/codex/codec-stack/04-t64-gorillafrom
xiangfu0/codex/codec-stack/05-v7-format

Conversation

@xiangfu0

@xiangfu0 xiangfu0 commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Stacked on #19306 (xiangfu0/codex/codec-stack/04-t64-gorilla). Part of the split of #18229.

Summary

Adds the V7 on-disk raw forward index format — the first format that can persist a codec
pipeline — plus the factory routing that decides, per codecSpec, whether a column can keep
using the legacy chunk formats or needs V7:

  • FixedByteChunkForwardIndexWriterV7 (new, io.writer.impl): chunked SV fixed-byte writer
    for INT/LONG. File header carries version=7, an explicit FORMAT_MAGIC (0xC0DEC0DE)
    discriminator, chunk geometry, and the canonical codec spec (UTF-8) followed by a long[]
    chunk-offset table; each chunk stores encodedSize/decodedSize and the pipeline-encoded
    payload. The writer validates the pipeline's composed encoded-size bound and cumulative work
    bound up front so it never emits a file it could not read back.
  • FixedByteChunkSVForwardIndexReaderV7 (new, readers.forward): self-describing reader
    that re-creates the CodecPipelineExecutor from the header string and fully validates the
    header (chunk geometry consistency, spec length, offset-table monotonicity/bounds, per-chunk
    sizes against the composed pipeline bound) before any allocation sized from file data.
  • FixedByteValueWriter (new interface) + retrofit on FixedByteChunkForwardIndexWriter, so
    SingleValueFixedByteRawIndexCreator holds a single writer reference and gains a V7
    constructor taking a CodecPipelineExecutor.
  • CodecSpecUtils (new, public, io.codec): classification helper —
    toLegacyChunkCompressionType(...) (compression-only specs that map 1:1 to a legacy
    ChunkCompressionType, e.g. LZ4, ZSTD(3)) and hasTransform(...). Alongside
    CodecPipelineExecutor this is the only public entry into the package-private codec runtime.
  • ForwardIndexCreatorFactory: codecSpec routing — legacy-mappable compression-only specs
    go to the existing raw writers (byte-identical files to a legacy compressionCodec config);
    transforms/chains/non-default options go to the V7 writer (SV INT/LONG only, with
    defense-in-depth checks).
  • ForwardIndexReaderFactory: V7 dispatch keyed on the second-int FORMAT_MAGIC check
    BEFORE the version >= 4 power-of-2 fallback
    (legacy fixed-byte writers accept arbitrary
    versions ≥ 4, so the version integer alone can never identify V7), plus a truncation check
    before reading the version int.
  • SPI: ForwardIndexConfig.CODEC_PIPELINE_WRITER_VERSION = 7 and
    ForwardIndexReader.getCodecSpec() default method (V7 readers return the canonical header
    spec; every other reader returns null).
  • ForwardIndexHandler guard (~6 lines): in shouldChangeRawCompressionType, if the
    existing reader reports getCodecSpec() != null, return false before the
    existingCompressionType != null precondition. V7 segments are never rewritten yet (the next
    PR owns reload/rewrite). The computeOperations gate already rejects codecSpec configs;
    this guard protects against V7 segments on disk (e.g. tooling-created) paired with a
    non-codec config transition.

Canonicalization (frozen into on-disk headers)

The V7 writer embeds executor.getCanonicalSpec() — never ForwardIndexConfig's stored string,
which is only a structural normalization (toDslString) and can differ (e.g. ZSTANDARD vs
ZSTD(3)). The reader re-creates the executor from the header string, so the header is always
in executor-canonical form (covered by testCanonicalSpecStoredInHeader).

Gate relocation (deliberate, explicit)

The codec stack's closed feature gate (codecSpec is not supported yet for column: %s) was
planted at every config/creator/reader/reload surface in #19284. This PR removes exactly two
factory-level gates
because it makes those code paths real:

  • ForwardIndexCreatorFactory.createIndexCreator — now routes instead of throwing.
  • ForwardIndexReaderFactory.createIndexReader (both overrides) — now reads instead of throwing.

All config-surface gates remain: ForwardIndexType.validate,
ForwardIndexType.shouldCreateIndex, ForwardIndexType.createMutableIndex,
ForwardIndexHandler.computeOperations (rejectUnsupportedCodecSpecs), and the OPEN_STRUCT
sites. Net effect: no table config carrying codecSpec can validate, build a segment, create
a mutable index, or reload
— so no V7 segment is creatable from config — but the format code
itself and its direct-construction tests work end to end.

Deliberately excluded (next PR: 06-reload-enable)

  • Opening the feature gate (removing the remaining rejectUnsupportedCodecSpec sites) and
    table-config validation of codecSpec column types (validateCodecSpec).
  • ForwardIndexHandler rewrite support for codec-pipeline segments (codecSpec change
    detection via canonical-spec comparison, legacy↔V7 conversions on reload).
  • Mutable/realtime forward index support for codecSpec.

Why master stays safe with only this merged

  • The gate keeps codecSpec unusable from any table config, so the new writer/reader can only
    be reached by direct construction (tests/tools). No production config can produce a V7 file.
  • V7 reader dispatch triggers only on the 0xC0DEC0DE second-int magic, which no legacy writer
    emits; all legacy version ≥ 4 files keep their exact previous routing (locked by
    ForwardIndexReaderFactoryBackwardCompatTest, including golden base64 fixtures produced
    before this code existed).
  • The handler guard only short-circuits columns whose on-disk index already reports a codec
    spec, i.e. V7 segments that could not exist via config today; legacy columns follow the exact
    previous logic.

Verification

./mvnw -q -T 1C install -DskipTests -Ppinot-fastdev -pl pinot-segment-spi,pinot-segment-local -am
./mvnw -q test -Ppinot-fastdev -pl pinot-segment-local \
  -Dtest=CodecPipelineForwardIndexTest,FixedByteChunkSVForwardIndexReaderV7CorruptionTest,\
ForwardIndexCreatorFactoryTest,ForwardIndexReaderFactoryTest,ForwardIndexReaderFactoryBackwardCompatTest,\
CodecSpecUtilsTest,CompressionCodecCorruptInputTest,ForwardIndexHandlerTest \
  -Dsurefire.failIfNoSpecifiedTests=false
# precommit
./mvnw spotless:apply license:format -pl pinot-segment-spi,pinot-segment-local
./mvnw checkstyle:check license:check -pl pinot-segment-spi,pinot-segment-local

Stack (split of #18229)

  1. Fix forward index reader context lifecycle #19281 — Fix forward index reader context lifecycle (merged)
  2. Fix delta forward index chunk caching #19282 — Fix delta forward index chunk caching (merged)
  3. Add codec spec DSL and configuration plumbing #19284 — Codec spec DSL and configuration plumbing
  4. Add bounded codec runtime and compression handlers #19285 — Bounded codec runtime and compression handlers
  5. Add DELTA and DELTADELTA transform codecs #19305 — DELTA and DELTADELTA transform codecs
  6. Add T64 and GORILLA packing transform codecs #19306 — T64 and GORILLA packing transform codecs
  7. Add V7 raw forward index format for codec pipelines #19307 — V7 raw forward index format ← this PR
  8. Support codecSpec on segment reload and enable codecSpec end to end #19308 — Reload/rewrite support, gate removal (enabling PR)
  9. Add codec pipeline integration tests and design doc #19309 — Integration tests and design doc

Each PR is based on its predecessor's branch; GitHub retargets the next PR as each merges.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant