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


##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/customobject/PercentileTDigestAccumulator.java:
##########
@@ -638,10 +686,17 @@ public int byteSize() {
     return serialize().length;
   }
 
+  /// Returns the small-encoding size for valid statistics. Historical 
degraded state retains its original encoding
+  /// until modified, then uses verbose bytes so poisoned values and precise 
weights survive without narrowing.
   @Override
   public int smallByteSize() {

Review Comment:
   smallByteSize() now calls the writer's same signed-short centroid-count 
guard, so size and write reject oversized compact output consistently. The 
32768-centroid regression checks both failures. The degraded original/verbose 
encoding exception is documented on both SPI methods.
   
   Addressed in 
[33c0b75b60](https://github.com/apache/pinot/commit/33c0b75b60f5ed7561cbdc186e241167992c0a2e).
 All 491 selected tests, the 322-case compatibility matrix and 74 real-reader 
cases pass.



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/customobject/PercentileTDigestAccumulator.java:
##########
@@ -1029,34 +1032,64 @@ private void finishMerge(int numOutputCentroids, double 
totalWeight, double comp
 
   private void ensureIncomingCapacity(int capacity) {
     if (_incomingMeans == null || capacity > _incomingMeans.length) {
-      int newCapacity = _incomingMeans == null ? Math.max(capacity, 
_centroidCapacity)
-          : Math.max(capacity, Math.min(_pendingCentroidCapacity,
-              Math.multiplyExact(_incomingMeans.length, 
DEFAULT_MERGE_BUFFER_MULTIPLIER)));
+      int newCapacity = _incomingMeans == null
+          ? Math.max(capacity, Math.min(_centroidCapacity, 
_pendingCentroidCapacity))
+          : Math.max(capacity, (int) Math.min(_pendingCentroidCapacity,
+              (long) _incomingMeans.length * 
TDigestUtils.DEFAULT_MERGE_BUFFER_MULTIPLIER));
       _incomingMeans = _incomingMeans == null ? new double[newCapacity] : 
Arrays.copyOf(_incomingMeans, newCapacity);
       _incomingWeights =
           _incomingWeights == null ? new double[newCapacity] : 
Arrays.copyOf(_incomingWeights, newCapacity);
     }
   }
 
-  private void ensureIncomingSortCapacity(int capacity) {
-    ensureSortedIncomingCapacity(capacity);
-    if (_incomingOrder == null || capacity > _incomingOrder.length) {
-      int newCapacity = _incomingOrder == null ? Math.max(capacity, 
_centroidCapacity)
-          : Math.max(capacity, Math.multiplyExact(_incomingOrder.length, 2));
-      _incomingOrder = new int[newCapacity];
+  private void stableSortIncoming(int count) {

Review Comment:
   This was already fixed in the previous published revision: 
stableSortIncoming directly merges primitive mean/weight pairs into the 
existing scratch arrays. There is no order array, capturing comparator/swapper, 
identity initialization or gather pass on that hot path.
   
   Verified at 
[33c0b75b60](https://github.com/apache/pinot/commit/33c0b75b60f5ed7561cbdc186e241167992c0a2e).
 All 491 selected tests, the 322-case compatibility matrix and 74 real-reader 
cases pass.



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/customobject/PercentileTDigestAccumulator.java:
##########
@@ -696,34 +744,60 @@ public double getMin() {
   @Override
   public double getMax() {
     if (_pendingSerializedTDigest != null) {
-      return 
ByteBuffer.wrap(_pendingSerializedTDigest).getDouble(Integer.BYTES + 
Double.BYTES);
+      return _pendingSerializedMetadata.max();
     }
     flush();
     return _max;
   }
 
-  byte[] serialize() {
+  public byte[] serialize() {
+    if (_legacyDegraded) {
+      if (_pendingSerializedTDigest != null) {
+        return _pendingSerializedTDigest.clone();
+      }
+      if (!_legacySerializedDirty) {
+        return _legacySerializedBytes.clone();
+      }
+      // A merge with a historical invalid distribution remains unknown. Keep 
that explicit in the legacy
+      // encoding instead of passing its invalid centroids into the normal 
sorted K1 merge.
+      return toLegacyDegradedBytes();
+    }
     if (_pendingSerializedTDigest != null) {
-      getSerializedTotalWeight(_pendingSerializedTDigest);
-      if (ByteBuffer.wrap(_pendingSerializedTDigest).getInt() == 
VERBOSE_ENCODING) {
-        byte[] serialized = 
TDigestUtils.makeLegacyCompatible(_pendingSerializedTDigest);
-        return serialized == _pendingSerializedTDigest ? serialized.clone() : 
serialized;
+      if (hasWeightedBoundaryCentroids(_pendingSerializedTDigest, 
_pendingSerializedMetadata.centroidCount())) {
+        materializePendingSerializedTDigest();
+      } else {
+        if (ByteBuffer.wrap(_pendingSerializedTDigest).getInt() == 
TDigestUtils.VERBOSE_ENCODING) {
+          byte[] serialized = 
TDigestUtils.makeLegacyCompatible(_pendingSerializedTDigest, 
_pendingSerializedMetadata);
+          return serialized == _pendingSerializedTDigest ? serialized.clone() 
: serialized;
+        }
+        return _pendingSerializedTDigest.clone();
       }
-      return _pendingSerializedTDigest.clone();
     }
     compress();
+    normalizeBoundaryCentroids();
     return TDigestUtils.makeLegacyCompatible(toVerboseBytes());
   }
 
-  TDigest toTDigest() {
-    if (_pendingSerializedTDigest != null) {
-      return TDigestUtils.deserialize(_pendingSerializedTDigest);
+  private static boolean hasWeightedBoundaryCentroids(byte[] bytes, int count) 
{

Review Comment:
   Boundary weights and zero-weight presence are now computed during the 
existing codec scan and cached in SerializedTDigestMetadata. Pending 
serialization consumes those flags; the second byte-level boundary parser is 
removed.
   
   Addressed in 
[33c0b75b60](https://github.com/apache/pinot/commit/33c0b75b60f5ed7561cbdc186e241167992c0a2e).
 All 491 selected tests, the 322-case compatibility matrix and 74 real-reader 
cases pass.



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/aggregator/PercentileTDigestValueAggregator.java:
##########
@@ -152,128 +150,112 @@ private static NonFiniteAwareTDigest 
asNonFiniteAware(TDigest value) {
   }
 
   private void updateMaxByteSize(TDigest value) {

Review Comment:
   Added a nonabstract maxSerializedByteSize() contract with 
implementation-owned native/wrapper bounds. The aggregator delegates to it and 
registers the bound before emission; native state does not flush. Infinity 
split counts come from the same helper used by the writer. Regressions cover 
verbose expansion, unobserved state, and degraded compact mutations without 
under-allocation.
   
   Addressed in 
[33c0b75b60](https://github.com/apache/pinot/commit/33c0b75b60f5ed7561cbdc186e241167992c0a2e).
 All 491 selected tests, the 322-case compatibility matrix and 74 real-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