xiangfu0 commented on code in PR #19795:
URL: https://github.com/apache/pinot/pull/19795#discussion_r4246616155


##########
pinot-core/src/main/java/org/apache/pinot/core/query/aggregation/function/PercentileSmartTDigestAggregationFunction.java:
##########
@@ -172,7 +172,7 @@ private void aggregateIntoValueList(int length, 
AggregationResultHolder aggregat
     } else {
       double[][] doubleValues = blockValSet.getDoubleValuesMV();
       forEachNotNull(length, blockValSet, (from, toEx) -> {
-        for (int i = 0; i < length; i++) {
+        for (int i = from; i < toEx; i++) {

Review Comment:
   **Bundled behavior fix — please call it out in the PR description.**
   
   Changing `for (int i = 0; i < length; i++)` to `for (int i = from; i < toEx; 
i++)` is a genuine correctness fix (the old loop re-added the entire MV block 
once per non-null range, duplicating values and including null rows when null 
handling is enabled), and it now has a dedicated regression test. But it 
changes `PERCENTILE_SMART_TDIGEST` results for MV columns with null handling 
independently of the t-digest replacement, and nothing in the PR/commit message 
mentions it. Without a callout:
   - upgrade validation may misattribute the result change to the digest 
rewrite, and
   - a wholesale revert of this dependency-removal PR would silently lose the 
fix.



##########
pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileTDigestAggregationFunctionTest.java:
##########
@@ -1185,21 +1352,49 @@ private static byte[][] createSerializedDigests(int 
numDigests, int valuesPerDig
       IntPredicate useSmallEncoding) {
     byte[][] serializedDigests = new byte[numDigests][];
     for (int digestIndex = 0; digestIndex < numDigests; digestIndex++) {
-      TDigest digest = TDigestUtils.createMergingDigest(compression);
+      TDigest digest = 
PercentileTDigestAccumulator.forLegacyAggregation(compression);
       for (int valueIndex = 0; valueIndex < valuesPerDigest; valueIndex++) {
         digest.add(serializedValue(digestIndex, valueIndex, numDigests, 
valuesPerDigest));
       }
       if (useSmallEncoding.test(digestIndex)) {
-        ByteBuffer buffer = ByteBuffer.allocate(digest.smallByteSize());
-        digest.asSmallBytes(buffer);
-        serializedDigests[digestIndex] = buffer.array();
+        serializedDigests[digestIndex] = compactFixture(digest);
       } else {
         serializedDigests[digestIndex] = 
ObjectSerDeUtils.TDIGEST_SER_DE.serialize(digest);
       }
     }
     return serializedDigests;
   }
 
+  // Legacy input fixtures keep compact decoding coverage without exposing a 
compact-writing digest API.
+  private static byte[] compactFixture(TDigest digest) {
+    byte[] serialized = digest.serialize();
+    ByteBuffer input = ByteBuffer.wrap(serialized);
+    if (input.getInt() == TDigestCodec.SMALL_ENCODING) {
+      return serialized;
+    }
+    double min = input.getDouble();
+    double max = input.getDouble();
+    double compression = input.getDouble();
+    int count = input.getInt();
+    int mainCapacity = 
Math.max(TDigestCodec.getDefaultCentroidCapacity(compression), count);
+    long bufferCapacity = 5L * mainCapacity;
+    if ((double) (float) compression != compression || count > Short.MAX_VALUE 
|| bufferCapacity > Short.MAX_VALUE) {
+      return serialized;
+    }
+    ByteBuffer output = ByteBuffer.allocate(30 + 8 * count);
+    
output.putInt(TDigestCodec.SMALL_ENCODING).putDouble(min).putDouble(max).putFloat((float)
 compression);
+    output.putShort((short) mainCapacity).putShort((short) 
bufferCapacity).putShort((short) count);
+    for (int i = 0; i < count; i++) {
+      float weight = (float) input.getDouble();
+      float mean = (float) input.getDouble();
+      if (!(weight > 0.0f) || !Float.isFinite(weight) || 
!Float.isFinite(mean)) {
+        return serialized;
+      }
+      output.putFloat(weight).putFloat(mean);
+    }
+    return output.array();
+  }
+
   private static byte[] createVerboseUnitCentroidDigest(int numCentroids, 
double compression) {

Review Comment:
   **Duplicate fixture builders — reuse the shared helpers.**
   
   This private `createVerboseUnitCentroidDigest` is a byte-for-byte copy of 
the package-private 
`PercentileRawTDigestAggregationFunctionTest.createVerboseUnitCentroidDigest` 
(line 145, already documented as shared with the Smart test). Likewise, 
`compactFixture` (line 1369 here) is duplicated nearly verbatim in 
`LegacyTDigestCompatibilityTest` (line 151), both hand-rolling the SMALL 
encoding with the same `5L * mainCapacity` / float-representability logic.
   
   If one copy is later fixed (e.g., a header or capacity tweak), the tests 
stop exercising the same wire format with nothing failing to signal the 
divergence. Suggest calling the existing package-private helper here and 
hoisting `compactFixture` into a shared test utility.



##########
compatibility-verifier/tdigest-compatibility/run.sh:
##########
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Run after LegacyTDigestCompatibilityTest. Legacy jars are test-tool 
artifacts, not Pinot dependencies.
+set -euo pipefail
+# Match the unit-test workflow's Maven Central retry and connection settings, 
including standalone runs.
+export MAVEN_OPTS="${MAVEN_OPTS:-} 
-Dmaven.wagon.httpconnectionManager.ttlSeconds=25 \
+-Dmaven.wagon.http.retryHandler.count=30 -Dhttp.keepAlive=false 
-Dmaven.wagon.http.pool=false"
+TDIGEST_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
+TDIGEST_FIXTURES="$TDIGEST_ROOT/pinot-segment-local/target/tdigest-compat-fixtures"
+TDIGEST_WORK="$TDIGEST_ROOT/pinot-segment-local/target/tdigest-legacy-reader"
+if [[ ! -s "$TDIGEST_FIXTURES/manifest.tsv" ]]; then

Review Comment:
   **CI gate coupling — fragile under test filtering/reshuffles (acknowledged, 
flagging for visibility).**
   
   This gate hard-fails when `LegacyTDigestCompatibilityTest` did not run 
earlier in the same job (fixtures come from its side effect), and it downloads 
`com.tdunning:t-digest` 3.2/3.3 from Maven Central at verify time. The workflow 
comments show this is intentional and fail-closed, which is reasonable — but 
note two hazards:
   - any surefire filtering (`-Dtest=...`), flaky-quarantine exclusion, or 
future test-set reshuffle that drops the test from set 2 fails the whole 
unit-test job with an error unrelated to the code under test;
   - the gate depends on Maven Central availability for an artifact Pinot no 
longer declares anywhere, so repo caching for this job needs to stay in place.
   
   A fallback that invokes the fixture-exporting test directly (e.g., `mvnw -pl 
pinot-segment-local test -Dtest=LegacyTDigestCompatibilityTest`) when the 
manifest is missing would decouple the gate from test-set composition.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to