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


##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/customobject/PercentileTDigestAccumulator.java:
##########
@@ -1041,22 +976,56 @@ private void ensureIncomingCapacity(int capacity) {
   private void ensureIncomingSortCapacity(int capacity) {
     ensureSortedIncomingCapacity(capacity);
     if (_incomingOrder == null || capacity > _incomingOrder.length) {
-      int newCapacity = _incomingOrder == null ? Math.max(capacity, 
_centroidCapacity)
+      int newCapacity = _incomingOrder == null
+          ? Math.max(capacity, Math.min(_centroidCapacity, 
_pendingCentroidCapacity))
           : Math.max(capacity, Math.multiplyExact(_incomingOrder.length, 2));
       _incomingOrder = new int[newCapacity];
+      _incomingOrderScratch = new int[newCapacity];
     }
   }
 
+  // Stable, primitive merge sort bounds the work to O(n log n), including 
repeated and infinite means.
+  private void stableSortIncoming(int count) {

Review Comment:
   Replaced the handwritten sort with fastutil stable mergeSort and removed the 
retained second sort-index scratch array. Equal means keep stable order, 
including signed zero.
   
   Addressed in 
[1fc37ba77c](https://github.com/apache/pinot/commit/1fc37ba77c631c574df59a6e211bb978954fb2b2).
 All 463 targeted tests and 322 legacy reader cases pass.



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/utils/TDigestUtils.java:
##########
@@ -48,35 +45,34 @@ public final class TDigestUtils {
   private static final int SMALL_HEADER_SIZE = 30;
   private static final int SMALL_CENTROID_SIZE = 8;
   private static final int DEFAULT_MERGE_BUFFER_MULTIPLIER = 5;
+  private static final double MAX_COMPRESSION = 1_000_000.0;
 
   private TDigestUtils() {
   }
 
-  /// Creates a merging digest with Pinot's accuracy-preserving scale function.
+  /// Creates a digest using Pinot's accuracy-preserving K1 implementation.
   public static TDigest createMergingDigest(double compression) {
-    return configureScaleFunction(new MergingDigest(compression));
+    return PercentileTDigestAccumulator.forLegacyAggregation(compression);
   }
 
-  /// Creates a merging digest with the temporary-buffer footprint used by 
t-digest 3.2.
-  ///
-  /// Segment aggregation can keep many digests live at once. T-digest 3.3 
more than doubled the default temporary
-  /// buffer to support two-level compression, adding about 11 KiB to every 
compression-100 digest. This factory keeps
-  /// the 3.3 main-buffer capacity and Pinot's K1 policy, but restores the 
smaller temporary buffer for that
-  /// high-cardinality use case.
+  /// Creates Pinot's buffered digest for segment aggregation.
   public static TDigest createMergingDigestWithLegacyBuffer(double 
compression) {
     return createMergingDigestWithLegacyBuffer(compression, 0);
   }
 
-  /// Creates a reduced-buffer merging digest whose main array can hold at 
least `minimumMainCapacity` centroids.
+  /// Creates a digest whose dynamically grown arrays can hold the requested 
centroid capacity.
   public static TDigest createMergingDigestWithLegacyBuffer(double 
compression, int minimumMainCapacity) {

Review Comment:
   Removed the misleading legacy-buffer aliases and discarded 
minimumMainCapacity argument. Wrapper copy uses the single factory without 
forcing centroidCount() for unused pre-sizing.
   
   Addressed in 
[1fc37ba77c](https://github.com/apache/pinot/commit/1fc37ba77c631c574df59a6e211bb978954fb2b2).
 All 463 targeted tests and 322 legacy reader cases pass.



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/customobject/TDigest.java:
##########
@@ -0,0 +1,70 @@
+/**
+ * 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.
+ */
+package org.apache.pinot.segment.local.customobject;
+
+import java.nio.ByteBuffer;
+import java.util.Collection;
+import java.util.List;
+
+/// Pinot's mutable t-digest contract using the existing K1 aggregation 
behavior and legacy byte encodings.
+///
+/// Implementations are not safe for concurrent mutation; callers must 
serialize access to each aggregation state.
+public abstract class TDigest {
+  public static TDigest createMergingDigest(double compression) {

Review Comment:
   Removed the static factory from the TDigest contract and migrated all 
callers to TDigestUtils.createMergingDigest, leaving one factory for this 
behavior.
   
   Addressed in 
[1fc37ba77c](https://github.com/apache/pinot/commit/1fc37ba77c631c574df59a6e211bb978954fb2b2).
 All 463 targeted tests and 322 legacy reader cases pass.



##########
pom.xml:
##########
@@ -253,7 +253,6 @@
     
<hadoop-shaded-protobuf_3_25.version>1.5.0</hadoop-shaded-protobuf_3_25.version>
     <clearspring-stream-lib.version>2.9.8</clearspring-stream-lib.version>
     <datasketches-java.version>9.0.0</datasketches-java.version>
-    <t-digest.version>3.3</t-digest.version>

Review Comment:
   Added backward-incompat and an explicit migration/release note in the PR 
description. Java plugins must migrate their Pinot-facing signatures to the new 
TDigest type and recompile. Independent tdunning usage requires a plugin-owned 
dependency; bundling it alone does not restore old Pinot method signatures. 
Legacy BYTES/type 10 remain supported.
   
   Addressed in 
[1fc37ba77c](https://github.com/apache/pinot/commit/1fc37ba77c631c574df59a6e211bb978954fb2b2).
 All 463 targeted tests and 322 legacy reader cases pass.



##########
pinot-core/src/test/java/org/apache/pinot/core/query/aggregation/function/PercentileTDigestAggregationFunctionTest.java:
##########
@@ -1005,59 +1006,29 @@ public void testReducerMergeStructureAndAccuracy(int 
fanIn, int compression, Red
         sourceValues[valueIndex] = value;
       }
       sources[digestIndex] = createReducerSource(sourceValues, compression, 
reducerInput);
-      referenceSources[digestIndex] = 
createReferenceReducerSource(sourceValues, compression, reducerInput);
     }
 
     PercentileTDigestAggregationFunction function =
         new PercentileTDigestAggregationFunction(EXPRESSION, 75.0, 
compression, false);
     TDigest result = TDigest.createMergingDigest(compression);
-    TDigest referenceResult = TDigest.createMergingDigest(compression);
-    AssertionError referenceFailure = null;
     for (int sourceIndex : mergeOrder.sourceIndexes(fanIn)) {
       result = function.merge(result, sources[sourceIndex]);
-      if (referenceFailure == null) {
-        try {
-          if (referenceResult.size() == 0L) {
-            referenceResult = referenceSources[sourceIndex];
-          } else {
-            referenceResult.add(referenceSources[sourceIndex]);
-          }
-        } catch (AssertionError e) {
-          // t-digest 3.3 can violate its own singleton-endpoint assertion 
when many duplicate-valued digests merge.
-          referenceFailure = e;
-        }
-      }
     }
 
     Arrays.sort(rawValues);
     String caseDescription =
         fanIn + "/" + compression + "/" + distribution + "/" + mergeOrder + 
"/" + reducerInput;
-    // Interpolation between large equal-valued centroid runs can land inside 
the empirical CDF jump. Keep a fixed
-    // envelope for that discontinuous distribution instead of requiring every 
merge order to match the reference's
-    // particular centroid partition.
-    double frozenRankError = compression == 20 || distribution == 
ReducerDistribution.DUPLICATE_HEAVY ? 0.06 : 0.02;
-    double[] referenceRankErrors = null;
-    if (referenceFailure == null) {
-      try {
-        referenceRankErrors = getReducerRankErrors(referenceResult, rawValues, 
caseDescription + "/reference");
-      } catch (AssertionError e) {
-        referenceFailure = e;
-      }
-    }
-    double[] maxRankErrors = new double[6];
-    if (referenceFailure == null) {
-      for (int i = 0; i < referenceRankErrors.length; i++) {
-        maxRankErrors[i] = Math.max(frozenRankError, referenceRankErrors[i] + 
1e-12);
-      }
-    } else {
-      Arrays.fill(maxRankErrors, compression == 20 ? 0.25 : compression == 100 
? 0.06 : 0.02);
-    }
-    assertValidReducerResult(result, rawValues, caseDescription, 
maxRankErrors);
+    // Validate against the empirical CDF. The former 3.3 reference allowed 
0.25 after duplicate-boundary
+    // assertions at compression 20; preserve that historical envelope for 
this discontinuous distribution.
+    double maxRankError = compression == 20 && distribution == 
ReducerDistribution.DUPLICATE_HEAVY

Review Comment:
   Added an independent real t-digest 3.3 corpus across all 270 seeded reducer 
cases, with per-quantile accuracy guards, and frozen independent 
custom-compression P75 values. Capacity tests now enforce the actual 3.2 limit 
and check weighted quantiles, so skipping reduction is detected. The broad 0.25 
allowance is removed.
   
   Addressed in 
[1fc37ba77c](https://github.com/apache/pinot/commit/1fc37ba77c631c574df59a6e211bb978954fb2b2).
 All 463 targeted tests and 322 legacy reader cases pass.



-- 
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