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


##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/customobject/PercentileTDigestAccumulator.java:
##########
@@ -756,12 +841,14 @@ private byte[] toVerboseBytes() {
 
   private byte[] toCapacityPreservingBytes() {
     checkCapacityPreservingCentroidCount();
-    int mainCapacity = Math.min(Short.MAX_VALUE, Math.max(_numCentroids, 
_serializedMainCapacity));
-    long defaultBufferCapacity = 
Math.multiplyExact(DEFAULT_MERGE_BUFFER_MULTIPLIER, (long) mainCapacity);
+    int mainCapacity = Math.min(Short.MAX_VALUE,
+        Math.max(_centroidCapacity, Math.max(_numCentroids, 
_serializedMainCapacity)));
+    long defaultBufferCapacity = 
Math.multiplyExact(TDigestUtils.DEFAULT_MERGE_BUFFER_MULTIPLIER, (long) 
mainCapacity);
     int bufferCapacity = Math.toIntExact(Math.min(Short.MAX_VALUE,
         Math.max(Math.max((long) _serializedBufferCapacity, mainCapacity + 
1L), defaultBufferCapacity)));
-    ByteBuffer buffer = ByteBuffer.allocate(SMALL_HEADER_SIZE + 
SMALL_CENTROID_SIZE * _numCentroids);
-    buffer.putInt(SMALL_ENCODING);
+    ByteBuffer buffer = ByteBuffer.allocate(
+        TDigestUtils.SMALL_HEADER_SIZE + TDigestUtils.SMALL_CENTROID_SIZE * 
_numCentroids);
+    buffer.putInt(TDigestUtils.SMALL_ENCODING);
     buffer.putDouble(_min);
     buffer.putDouble(_max);
     buffer.putFloat((float) _compression);

Review Comment:
   **[code-review]** toCapacityPreservingBytes narrows means/weights to float 
with no exact-representability fallback, so the accumulator's own asSmallBytes 
round trip degrades to NaN for huge values
   
   add(1e39); add(2e39); asSmallBytes(buf); deserialize(buf) -> centroid means 
narrow to Infinity under finite double header extrema, inspectSerialized sets 
needsLegacyFallback, and quantile(0.5) returns NaN (verified by running the 
compiled classes: before-serialization q(0.5)=2.0E39, after round trip NaN). 
makeLegacyCompatible guards the same narrowing with `(double)(float)mean != 
mean -> reduceVerboseCentroids`, but asSmallBytes/smallByteSize bypass that 
guard; a weight > Float.MAX_VALUE additionally throws 'Invalid TDigest centroid 
weight' on read.



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/customobject/PercentileTDigestAccumulator.java:
##########
@@ -1243,19 +1292,12 @@ private void ensureRawBuffer() {
   }
 
   private static int getRawBufferSize(long roundedCompression) {
-    return (int) Math.min(MAX_RAW_BUFFER_SIZE, Math.max(MIN_RAW_BUFFER_SIZE, 
2L * roundedCompression));
-  }
-
-  private static int getCentroidCapacity(double compression) {
-    double normalizedCompression = Math.max(MIN_COMPRESSION, compression);
-    int padding = normalizedCompression < 30.0 ? 
LOW_COMPRESSION_CAPACITY_PADDING
-        : DEFAULT_CENTROID_CAPACITY_PADDING;
-    return Math.toIntExact((long) Math.ceil(2.0 * normalizedCompression + 
padding));
+    return (int) Math.min(MAX_RAW_BUFFER_SIZE, Math.max(MIN_RAW_BUFFER_SIZE, 
2.0 * roundedCompression));
   }
 
   private static int getPendingCentroidCapacity(int centroidCapacity) {
-    return Math.min(MAX_RAW_BUFFER_SIZE,
-        Math.max(MIN_RAW_BUFFER_SIZE, 
Math.multiplyExact(DEFAULT_MERGE_BUFFER_MULTIPLIER, centroidCapacity)));
+    return (int) Math.min(MAX_RAW_BUFFER_SIZE,
+        Math.max(MIN_RAW_BUFFER_SIZE, (long) 
TDigestUtils.DEFAULT_MERGE_BUFFER_MULTIPLIER * centroidCapacity));
   }
 
   private int getPendingInputLimit() {

Review Comment:
   **[code-review]** getPendingInputLimit() collapses to 1 at high compression, 
degrading ingestion to one full K1 merge per added value (O(n^2))
   
   _pendingCentroidCapacity is capped at MAX_RAW_BUFFER_SIZE=10000 while 
_numCentroids at working compression can exceed it. SQL only validates 
compressionFactor >= 0 (AggregationFunctionFactory), so PERCENTILETDIGEST(col, 
20000) is legal. Measured on the compiled classes with 1M values: compression 
100 -> 59 ms; compression 20000 -> 62,371 ms (limit stuck at 1, ~900x). The 
weighted/merge path (working compression 2x) collapses from configured 
compression >= ~8000. Results stay correct and loops terminate, but 
query/ingestion stalls by 2-3 orders of magnitude.



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/customobject/PercentileTDigestAccumulator.java:
##########
@@ -912,13 +928,8 @@ private void flushIncoming(double compression) {
     double[] incomingMeans = _incomingMeans;
     double[] incomingWeights = _incomingWeights;
     if (!_incomingCentroidsSorted) {
-      ensureIncomingSortCapacity(incomingCount);
-      Sort.stableSort(_incomingOrder, _incomingMeans, incomingCount);
-      for (int i = 0; i < incomingCount; i++) {
-        int sourceIndex = _incomingOrder[i];
-        _sortedIncomingMeans[i] = _incomingMeans[sourceIndex];
-        _sortedIncomingWeights[i] = _incomingWeights[sourceIndex];
-      }
+      ensureSortedIncomingCapacity(incomingCount);
+      stableSortIncoming(incomingCount);
       incomingMeans = _sortedIncomingMeans;
       incomingWeights = _sortedIncomingWeights;
     }

Review Comment:
   **[code-review]** flushIncoming clears the incoming buffers before 
mergeSorted can throw, silently dropping buffered mass on the exception path
   
   `_numIncomingCentroids = 0; _incomingWeight = 0.0;` run before mergeSorted, 
whose checkTotalWeight(newTotalWeight) can still throw: the incremental 
pre-checks evaluate ((_totalWeight + _incomingWeight) + ...) + weight while the 
merge-time check evaluates a differently-associated sum, and near 
Double.MAX_VALUE these round differently (MAX + 5e291 == MAX per add, but MAX + 
1e292 == Infinity at flush). A caller that catches and reuses the accumulator 
then serves quantiles excluding the dropped mass. flush(double) already orders 
this correctly (zeroes _numRawValues after the merge) - move the clears after 
mergeSorted to match.



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