Add end-to-end Parquet VARIANT support - #19101
Open
xiangfu0 wants to merge 3 commits into
Open
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #19101 +/- ##
============================================
+ Coverage 65.53% 65.70% +0.16%
+ Complexity 1423 1322 -101
============================================
Files 3432 3445 +13
Lines 218106 220086 +1980
Branches 34665 35021 +356
============================================
+ Hits 142941 144597 +1656
- Misses 63603 63747 +144
- Partials 11562 11742 +180
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:
|
xiangfu0
force-pushed
the
xiangfu0/variant-e2e
branch
2 times, most recently
from
July 28, 2026 11:14
3e4dc18 to
acfba94
Compare
xiangfu0
marked this pull request as ready for review
July 28, 2026 16:03
xiangfu0
force-pushed
the
xiangfu0/variant-e2e
branch
from
July 29, 2026 08:01
acfba94 to
ca98faf
Compare
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
Add an end-to-end
VARIANTuser journey for Apache Pinot:VARIANTdimension that is retained in Pinot's raw forward indexVARIANT(1)values, including unshredded and shredded layoutsRaw Variant values are deliberately not orderable, comparable, or generally aggregatable. Pinot allows
COUNT(raw_variant)but rejects raw comparison, grouping, distinct, ordering, join-key, set-operation, and other aggregate uses with guidance to extract a typed scalar first.The persisted format, null semantics, ownership boundaries, compatibility
contract, and activation/rollback gates are recorded in
pinot-spi/VARIANT_DESIGN.md.Try it
Build Pinot and launch the dedicated batch quickstart:
The quickstart:
variantEventsschema and offline tableVARIANT(1)fixtureThe complete sample is under
pinot-tools/src/main/resources/examples/batch/variantEvents.Supported scope
VARIANT(1)columnsVARIANTdimension stored without a dictionarySET enableNullHandling=trueNested/repeated Variant columns, streaming ingestion, quoted path keys, and mixed-version queries over active Variant tables are outside this initial verified scope.
The automatic Parquet reader selection keeps the existing Avro-metadata
precedence. Files that contain Avro metadata must explicitly select the native
Parquet reader to ingest a Variant column.
Production safety
PVARframing and validation inVariantEnvelope, freezes the version-1 bytes with a golden test, and assigns a new protobuf wire value without renumbering existing typesCOUNTraw aggregatesBinaryvalues into the finalPVARenvelope without a full-payload intermediate copy and without changing source buffer positions or limitsparseJson/tryParseJsoninputs once per query in both engines and specializes ingestion-time Variant functions with constant paths/types into compiled, cursor-reusing evaluatorsBigIntegerallocation for JSON integers that fit primitive ranges"null"across Arrow, JSON, HTTP JDBC, and gRPC JDBC resultsparquet-variantinpinot-common; Parquet column/schema dependencies remain isolated to the input pluginjapicmpreports no binary-incompatible changes forpinot-spiorpinot-segment-spi. The published Java 11 artifacts retain classfile majorversion 55.
Compatibility and rollout
Existing schemas, segments, and data types keep their current behavior. Older
components reject an unknown Variant wire type deterministically, but they
cannot operate on an active Variant table. Upgrade controllers, brokers,
servers, clients, and external ingestion jobs/plugins before creating a Variant
schema; do not activate Variant during a rolling mixed-version window.
Verification
acfba945dd1d, covering unit, integration, quickstart, linter, dependency, compatibility, binary compatibility, and container-security jobsVariantTypeTestintegration scenarios across both query enginesBUILD SUCCESS, 11m24s)pinot-perfpackage/test reactor with benchmark unit coverage (BUILD SUCCESS, 6m38s)git diff --checkPerformance: 2M-row JSON versus VARIANT
Commit
acfba945dd1dadds a reproducible end-to-end benchmark underpinot-perf. It generates paired Parquet files from the same deterministic logical events, builds four local Pinot segments per representation, validates every query against independently generated ground truth, and alternates build order across four rounds. Source generation is excluded from ingestion timing.Environment: Apple M2 Max (12 cores), 32 GB RAM, macOS 26.5.2, Temurin 25+36, AC power. Each query result below is the median of the four round-level p50 values; every round uses two warmups followed by 10 measured sequential executions.
Ingestion and storage
The raw VARIANT Parquet source is 2.22% smaller than JSON. The resulting raw Pinot VARIANT segment is 1.61% larger than raw JSON. Adding the JSON index makes the JSON segment 4.26x the raw JSON size and is intentionally excluded from the raw-format ingestion conclusion.
Query latency
eventTypecount (control)The neutral materialized-column control and the stable paired round ratios support attributing the raw nested-query gap to parsing/extraction work rather than general broker/server overhead.
Run after packaging
pinot-perf:/path/to/jdk-25/bin/java -Xms4G -Xmx8G \ -Dpinot.perf.jsonVariant.rows=2000000 \ -Dpinot.perf.jsonVariant.segments=4 \ -Dpinot.perf.jsonVariant.rounds=4 \ -Dpinot.perf.jsonVariant.warmups=2 \ -Dpinot.perf.jsonVariant.samples=10 \ -Dpinot.perf.jsonVariant.includeLegacy=false \ -Dpinot.perf.jsonVariant.cleanup=false \ -Dpinot.perf.jsonVariant.workDir=/tmp/pinot-json-variant-benchmark \ -cp 'pinot-perf/target/pinot-perf-pkg/lib/*' \ org.apache.pinot.perf.BenchmarkJsonVariantComparisonLimitations: this is a local, in-process, single-stage, sequential warm-cache server/broker comparison over four segments. It does not model network transfer, multi-stage execution, concurrent clients, distributed scheduling, or production hardware. The workstation was interactive and had a long uptime, so paired alternating rounds and the materialized control are used to bound environmental noise.
Review notes
This is a design and implementation draft, not a merge-ready ownership
approval. It changes public SPI (
FieldSpec.DataType.VARIANT,VariantEnvelope, protobuf/DataSchema mappings) and requires explicitcompatibility sign-off before merge:
The draft is kept end-to-end so those owners can review one complete activation
contract: the public type, its only supported ingestion path, the raw-value
safety guards, and the executable acceptance test. No partial subset is safe to
activate: the public type without ingestion is unusable, ingestion without all
query guards exposes byte-layout semantics, and any subset without mixed-version
gates is unsafe to roll out. The design document records the dependency order
and the rule that VARIANT remains unactivatable until all safety pieces land.
If maintainers prefer smaller rollback units after design review, this draft can
be converted into that documented dependency-ordered stack: foundation and wire
contract; segment and Parquet ingestion; query functions and engines; clients
and DDL; then quickstart, integration, and benchmarks.