Add tests.analytics.parquet_indices toggle for analytics-engine IT coverage#5436
Open
ahkcs wants to merge 2 commits into
Open
Add tests.analytics.parquet_indices toggle for analytics-engine IT coverage#5436ahkcs wants to merge 2 commits into
ahkcs wants to merge 2 commits into
Conversation
5412901 to
7bb6fb3
Compare
4 tasks
Adds a small, opt-in test infrastructure slice so the PPL integration
test suite can run end-to-end against the analytics-engine backend
without per-test rewiring.
`-Dtests.analytics.parquet_indices=true` makes `TestUtils.createIndexByRestClient`
back every test-created index with single-shard composite/parquet storage:
index.pluggable.dataformat.enabled = true
index.pluggable.dataformat = "composite"
index.composite.primary_data_format = "parquet"
`RestUnifiedQueryAction.isAnalyticsIndex` (post-opensearch-project#5432) reads these settings
and routes any query against such indices to the analytics-engine planner
(DataFusion). No additional cluster setting or routing override required —
the production routing logic is the single source of truth.
Also adds `PPLIntegTestCase.isAnalyticsParquetIndicesEnabled()` as a
per-test predicate so individual tests can branch their assertions on
engine semantics (DataFusion follows different ordering and null-bucket
semantics than the legacy V2 and Calcite-DSL paths).
Bulk loads on parquet-backed indices use `refresh=true` because
`analytics-backend-lucene`'s `LuceneCommitter.getSafeCommitInfo` is a
`TODO:: with index deleter` stub that hangs `refresh=wait_for` until the
test framework request timeout (~60s).
`integ-test/build.gradle` forwards the property to `:integTestRemote` so
the gradle command line is the single knob.
Default behavior is unchanged — with the flag unset, every test-created
index is Lucene-backed and every IT runs through the existing V2 /
Calcite path exactly as before.
Signed-off-by: Kai Huang <ahkcs@amazon.com>
5bc0b2f to
a2a105a
Compare
When -Dtests.analytics.parquet_indices=true is set, every test-created index is parquet-backed and RestUnifiedQueryAction.isAnalyticsIndex routes all queries through the analytics-engine backend (DataFusion). Seven tests need analytics-specific assertions because DataFusion follows different null-bucket, SQL-spec, and TDigest interpolation semantics than the legacy V2 / Calcite-DSL paths: SQL-spec semantics (DataFusion follows the spec; legacy DSL returns 0): - testSumWithNull — SUM of all-null returns null on analytics; the existing isPushdownDisabled branch already handles the Calcite-no- pushdown case. OR analytics into that branch. - testStatsPercentileByNullValue + NonNullBucket — percentile of an all-null / empty group returns null on analytics. Same OR pattern. Non-deterministic head ordering: - testStatsWithLimit Q1+Q2 — head 5 over a 6-bucket result picks a different null-balance row than the legacy / Calcite-DSL path; same effect cascades into `head 2 from 1`. Reuse the size-only assertion branch already present for the no-pushdown case. TDigest interpolation divergence (genuine impl difference): - testStatsPercentileWithNull — DataFusion TDigest interpolates p50 to 35413 vs OpenSearch's 39225; both within compression-bound error. Per-engine expected value. - testStatsPercentileBySpan — same TDigest diff on the age=30 bucket (33194 vs 39225). Coordinated with opensearch-project/OpenSearch#21584 commit 5 which fixes the upstream planner-side type mismatch so the query reaches the data assertion at all. Semantic divergence pending team decision: - testDisableLegacyPreferred — under PPL_SYNTAX_LEGACY_PREFERRED=false, V2 / Calcite-DSL drop the null-age bucket (5 rows) while DataFusion keeps it (6 rows). Skipped on the analytics path via Assume.assumeFalse until the team decides which behaviour is the intended new-syntax semantics. Out of scope for this PR (intentionally left failing on the analytics path so the gap is visible in CI): - testStatsBySpanTimeWithNullBucket — span(@timestamp, 12h) multi-unit time interval unsupported in current SpanAdapter (only interval=1 is rewritten to DATE_TRUNC; multi-unit needs DataFusion's date_bin). Signed-off-by: Kai Huang <ahkcs@amazon.com>
a2a105a to
d3782dc
Compare
dai-chen
reviewed
May 13, 2026
| * settings (#5432) — no other test plumbing required. | ||
| */ | ||
| private static void makeParquetBacked(JSONObject jsonObject) { | ||
| JSONObject settings = |
Collaborator
There was a problem hiding this comment.
np: make both these index settings and refresh config into a test index config in single place. We have this problem in os-spark repo which has aoss conditional check and configs in many places.
Comment on lines
+516
to
+517
| // SUM of all-null is null, not 0. | ||
| Integer expectedValue = (isPushdownDisabled() || isAnalyticsParquetIndicesEnabled()) ? null : 0; |
Collaborator
There was a problem hiding this comment.
Just thinking does all these assertion changes means semantic/behavior change? Or we can simply fix the assertion in this way.
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
Adds a small, opt-in test infrastructure slice so the PPL integration test suite can run end-to-end against the analytics-engine backend without per-test rewiring. Default behavior is unchanged: with the flag unset, every IT runs through the existing V2 / Calcite path exactly as before.
The single knob is
-Dtests.analytics.parquet_indices=true. When set,TestUtils.createIndexByRestClientbacks every test-created index with single-shard composite/parquet storage.RestUnifiedQueryAction.isAnalyticsIndex(post-#5432) reads those settings and routes the query to the analytics-engine planner (DataFusion). No additional cluster setting, REST short-circuit, or routing override — production routing is the single source of truth.Commits
1.
Add tests.analytics.parquet_indices test toggle— the infra slice (3 files, +68 LOC):integ-test/.../TestUtils.javaANALYTICS_PARQUET_INDICES_PROPconstant;createIndexByRestClientinjects composite/parquet settings when the flag is on;loadDataByRestClientusesrefresh=trueto work aroundLuceneCommitter.getSafeCommitInfostubinteg-test/.../PPLIntegTestCase.javaisAnalyticsParquetIndicesEnabled()predicate for per-test branching on engine semanticsinteg-test/build.gradle-Dtests.analytics.parquet_indicesto:integTestRemote2.
Branch 7 stats tests on isAnalyticsParquetIndicesEnabled— demonstrates the helper (1 file, +40/-8 LOC):SQL-spec semantics (DataFusion follows the spec; legacy DSL returns 0)
testSumWithNullSUMof all-null returnsnullon analytics; legacy DSL pushdown returns0. OR analytics into the existingisPushdownDisabled()branchtestStatsPercentileByNullValue+NonNullBucketpercentileof empty/all-null group returnsnullon analytics. Same OR patternNon-deterministic head ordering
testStatsWithLimitQ1 + Q2head 5over a 6-bucket result picks(null,36)instead of(null,null)on analytics (DataFusion hash-bucket order differs from Calcite enumerable order). Size-only assertion via the existing pushdown branchTDigest interpolation divergence
testStatsPercentileWithNull35413vs OpenSearch's39225; both are valid approximations within TDigest's compression-bound error. Per-engine expected valuetestStatsPercentileBySpan33194vs39225). Coordinated with opensearch-project/OpenSearch#21584 commit 5 which fixes the upstream planner-side type mismatch so the query reaches the data assertion at allSemantic divergence pending team decision
testDisableLegacyPreferredPPL_SYNTAX_LEGACY_PREFERRED=false, V2 / Calcite-DSL drops the null-age bucket (5 rows) while DataFusion keeps it (6 rows). Skipped on the analytics path viaAssume.assumeFalseuntil the team decides which behavior is the intended new-syntax semantics.Why no force-routing cluster setting
An earlier revision of this PR added a
plugins.calcite.analytics.force_routingcluster setting plus a short-circuit inRestUnifiedQueryAction.isAnalyticsIndex. With #5432 merged (routing now keys onindex.pluggable.dataformat.enabled+pluggable.dataformat=composite), that override is redundant:parquet_indices=truemakes every test-created index carry the right settings.CalciteStatsCommandIT.init()callsenableCalcite(), so the Calcite path is active.isAnalyticsIndex()returnstruevia the index-setting check → query routes to analytics-engine.Verified end-to-end: removing the force-routing cluster setting / IT toggle / REST short-circuit gives the exact same pass-rate as the earlier revision.
Verification
Against an analytics-engine cluster (opensearch-project/OpenSearch#21584 with commit 5 + main rebase):
./gradlew :integ-test:integTestRemote \ -Dtests.rest.cluster=localhost:9200 -Dtests.cluster=localhost:9300 -Dtests.clustername=runTask \ -Dtests.analytics.parquet_indices=true \ --tests "org.opensearch.sql.calcite.remote.CalciteStatsCommandIT"CalciteStatsCommandITpass-ratetestStatsWithLimittestSumWithNulltestStatsPercentileByNullValuetestStatsPercentileByNullValueNonNullBuckettestStatsPercentileWithNulltestStatsPercentileBySpantestDisableLegacyPreferredtestStatsBySpanTimeWithNullBucketDefault behavior unchanged
With the flag unset, the cluster's persistent settings are untouched, indices stay Lucene-backed, and every IT runs through the existing V2 / Calcite path. CI that doesn't opt in sees zero behavior change.
Remaining 10 failures on
CalciteStatsCommandIT(analytics path)testStatsTimeSpandatetime output-cast formatCAST(timestamp AS VARCHAR)emits ISO 8601 withTseparator while PPL's Calcite path emits space-separated ANSI SQL format. Tracked at opensearch-project/sql#5420.testStatsBySpanTimeWithNullBucketmulti-unit time spanspan(@timestamp, 12h)falls throughSpanAdapter(onlyinterval=1is rewritten toDATE_TRUNC); needs DataFusion'sdate_bin. Left failing on the analytics path so the gap is visible.LogicalJoinplanner gapTest plan
./gradlew :integ-test:compileTestJavaclean.CalciteStatsCommandITagainst the analytics-engine cluster with only-Dtests.analytics.parquet_indices=true: 46 → 52 passes + 1 documented skip.