This is an automated email from the ASF dual-hosted git repository.

jackie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 6669f065c1a Upserts Pruning Optimization - Do not clone 
MutableRoaringBitmap for each segment  (#17920)
6669f065c1a is described below

commit 6669f065c1a1ab816109a7cb864e8622d2540aeb
Author: Chaitanya Deepthi <[email protected]>
AuthorDate: Mon Mar 23 11:30:53 2026 -0700

    Upserts Pruning Optimization - Do not clone MutableRoaringBitmap for each 
segment  (#17920)
---
 .../pinot/core/query/pruner/SegmentPrunerService.java  | 18 ++++++------------
 .../apache/pinot/segment/local/upsert/UpsertUtils.java | 12 ++++++++++++
 .../index/mutable/ThreadSafeMutableRoaringBitmap.java  |  4 ++++
 3 files changed, 22 insertions(+), 12 deletions(-)

diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/query/pruner/SegmentPrunerService.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/query/pruner/SegmentPrunerService.java
index 084a00f9351..5707ef698a0 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/query/pruner/SegmentPrunerService.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/query/pruner/SegmentPrunerService.java
@@ -32,7 +32,6 @@ import org.apache.pinot.segment.local.upsert.UpsertUtils;
 import org.apache.pinot.segment.spi.IndexSegment;
 import org.apache.pinot.spi.trace.InvocationScope;
 import org.apache.pinot.spi.trace.Tracing;
-import org.roaringbitmap.buffer.MutableRoaringBitmap;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -142,27 +141,22 @@ public class SegmentPrunerService {
    *                 emptiness is determined from them; otherwise {@link 
IndexSegment#getValidDocIds()} is used.
    * @return the new list with filtered elements. This is the list that have 
to be used.
    */
-  private static List<IndexSegment> removeEmptySegments(List<IndexSegment> 
segments, @Nullable QueryContext query) {
+  private static List<IndexSegment> removeEmptySegments(List<IndexSegment> 
segments, QueryContext query) {
     int selected = 0;
+    boolean skipUpsert = 
QueryOptionsUtils.isSkipUpsert(query.getQueryOptions());
     for (IndexSegment segment : segments) {
-      if (!isEmptySegment(segment, query)) {
+      if (!isEmptySegment(segment, skipUpsert)) {
         segments.set(selected++, segment);
       }
     }
     return segments.subList(0, selected);
   }
 
-  private static boolean isEmptySegment(IndexSegment segment, @Nullable 
QueryContext query) {
+  private static boolean isEmptySegment(IndexSegment segment, boolean 
skipUpsert) {
     if (segment.getSegmentMetadata().getTotalDocs() == 0) {
       return true;
     }
-    // For upsert tables, treat segments with 0 docs to query as empty only 
when the query does not skip upsert.
-    // When skipUpsert=true, the query returns all docs (including replaced), 
so the segment contributes rows.
-    // Use query options map directly: _skipUpsert is only set on the server; 
the broker has options in the map.
-    if (query != null && query.getQueryOptions() != null && 
QueryOptionsUtils.isSkipUpsert(query.getQueryOptions())) {
-      return false;
-    }
-    MutableRoaringBitmap queryableDocIds = 
UpsertUtils.getQueryableDocIdsSnapshotFromSegment(segment);
-    return queryableDocIds != null && queryableDocIds.isEmpty();
+    // Check if the segment has 0 queryable docIds while skipUpsert=false
+    return !skipUpsert && UpsertUtils.hasNoQueryableDocs(segment);
   }
 }
diff --git 
a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/UpsertUtils.java
 
b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/UpsertUtils.java
index b508a02aeb0..990dadb9074 100644
--- 
a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/UpsertUtils.java
+++ 
b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/UpsertUtils.java
@@ -57,6 +57,18 @@ public class UpsertUtils {
         : (useEmptyForNull ? new MutableRoaringBitmap() : null);
   }
 
+  /**
+   * Returns whether the segment has no queryable documents, when no delete 
record column we look into validDocIds
+   * for an upsert table
+   */
+  public static boolean hasNoQueryableDocs(IndexSegment segment) {
+    ThreadSafeMutableRoaringBitmap queryableDocIds = 
segment.getQueryableDocIds();
+    if (queryableDocIds != null) {
+      return queryableDocIds.isEmpty();
+    }
+    ThreadSafeMutableRoaringBitmap validDocIds = segment.getValidDocIds();
+    return validDocIds != null && validDocIds.isEmpty();
+  }
 
   public static void doReplaceDocId(ThreadSafeMutableRoaringBitmap validDocIds,
       @Nullable ThreadSafeMutableRoaringBitmap queryableDocIds, int oldDocId, 
int newDocId, RecordInfo recordInfo) {
diff --git 
a/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/index/mutable/ThreadSafeMutableRoaringBitmap.java
 
b/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/index/mutable/ThreadSafeMutableRoaringBitmap.java
index 05ab86628d5..470913fd4ce 100644
--- 
a/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/index/mutable/ThreadSafeMutableRoaringBitmap.java
+++ 
b/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/index/mutable/ThreadSafeMutableRoaringBitmap.java
@@ -60,4 +60,8 @@ public class ThreadSafeMutableRoaringBitmap {
   public synchronized MutableRoaringBitmap getMutableRoaringBitmap() {
     return _mutableRoaringBitmap.clone();
   }
+
+  public synchronized boolean isEmpty() {
+    return _mutableRoaringBitmap.isEmpty();
+  }
 }


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

Reply via email to