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

xiangfu0 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 a986489af49 Preserve queryableDocIds tracking when preloading upsert 
segment with no valid docs (#18527)
a986489af49 is described below

commit a986489af495d8d41f9816a62f7defea5dc2bfc1
Author: Chaitanya Deepthi <[email protected]>
AuthorDate: Mon May 18 20:30:50 2026 -0700

    Preserve queryableDocIds tracking when preloading upsert segment with no 
valid docs (#18527)
    
    * Make sure the queryable doc ids are not null when the valid doc ids are 
empty
    
    * Add unit tests for the regression
    
    * Simplify the code
---
 .../upsert/BasePartitionUpsertMetadataManager.java |  4 +-
 ...rrentMapPartitionUpsertMetadataManagerTest.java | 60 ++++++++++++++++++++++
 2 files changed, 63 insertions(+), 1 deletion(-)

diff --git 
a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/BasePartitionUpsertMetadataManager.java
 
b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/BasePartitionUpsertMetadataManager.java
index 6df010ae0e8..f0c8f850b89 100644
--- 
a/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/BasePartitionUpsertMetadataManager.java
+++ 
b/pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/BasePartitionUpsertMetadataManager.java
@@ -437,7 +437,9 @@ public abstract class BasePartitionUpsertMetadataManager 
implements PartitionUps
     if (validDocIds.isEmpty()) {
       _logger.info("Skip preloading segment: {} without valid doc, current 
primary key count: {}",
           segment.getSegmentName(), getNumPrimaryKeys());
-      segment.enableUpsert(this, new ThreadSafeMutableRoaringBitmap(), null);
+      ThreadSafeMutableRoaringBitmap queryableDocIds =
+          (_deleteRecordColumn == null) ? null : new 
ThreadSafeMutableRoaringBitmap();
+      segment.enableUpsert(this, new ThreadSafeMutableRoaringBitmap(), 
queryableDocIds);
       return;
     }
     if (isTTLEnabled() && !_comparisonColumns.isEmpty()) {
diff --git 
a/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/upsert/ConcurrentMapPartitionUpsertMetadataManagerTest.java
 
b/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/upsert/ConcurrentMapPartitionUpsertMetadataManagerTest.java
index bbba657b985..926af5e34ce 100644
--- 
a/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/upsert/ConcurrentMapPartitionUpsertMetadataManagerTest.java
+++ 
b/pinot-segment-local/src/test/java/org/apache/pinot/segment/local/upsert/ConcurrentMapPartitionUpsertMetadataManagerTest.java
@@ -71,6 +71,7 @@ import org.apache.pinot.spi.utils.ReadMode;
 import org.apache.pinot.spi.utils.builder.TableConfigBuilder;
 import org.apache.pinot.spi.utils.builder.TableNameBuilder;
 import org.apache.pinot.util.TestUtils;
+import org.mockito.ArgumentCaptor;
 import org.mockito.MockedConstruction;
 import org.roaringbitmap.buffer.MutableRoaringBitmap;
 import org.testng.annotations.AfterClass;
@@ -85,6 +86,7 @@ import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.*;
 
@@ -1356,6 +1358,64 @@ public class 
ConcurrentMapPartitionUpsertMetadataManagerTest {
     }
   }
 
+  /**
+   * Regression test: when preloading an immutable segment whose validDocIds 
snapshot is empty and the table is
+   * configured with a deleteRecordColumn, doPreloadSegment's fast path must 
initialize queryableDocIds to an
+   * empty bitmap (not null). Passing null would set _queryableDocIds = null 
on the segment, causing subsequent
+   * doTakeSnapshot rounds to skip persisting the queryableDocIds bitmap file 
and leaving the on-disk snapshot
+   * frozen at its pre-restart contents.
+   */
+  @Test
+  public void testPreloadSegmentEmptyValidDocIdsWithDeleteColumn() {
+    
_contextBuilder.setEnableSnapshot(true).setDeleteRecordColumn(DELETE_RECORD_COLUMN);
+    ConcurrentMapPartitionUpsertMetadataManager upsertMetadataManager =
+        new ConcurrentMapPartitionUpsertMetadataManager(REALTIME_TABLE_NAME, 
0, _contextBuilder.build());
+
+    ImmutableSegmentImpl segment = mock(ImmutableSegmentImpl.class);
+    when(segment.getSegmentName()).thenReturn("emptyValidDocIdsSegment");
+    
when(segment.loadDocIdsFromSnapshot(V1Constants.VALID_DOC_IDS_SNAPSHOT_FILE_NAME)).thenReturn(
+        new MutableRoaringBitmap());
+
+    upsertMetadataManager.preloadSegment(segment);
+
+    ArgumentCaptor<ThreadSafeMutableRoaringBitmap> validDocIdsCaptor =
+        ArgumentCaptor.forClass(ThreadSafeMutableRoaringBitmap.class);
+    ArgumentCaptor<ThreadSafeMutableRoaringBitmap> queryableDocIdsCaptor =
+        ArgumentCaptor.forClass(ThreadSafeMutableRoaringBitmap.class);
+    verify(segment).enableUpsert(eq(upsertMetadataManager), 
validDocIdsCaptor.capture(),
+        queryableDocIdsCaptor.capture());
+    assertNotNull(validDocIdsCaptor.getValue());
+    
assertTrue(validDocIdsCaptor.getValue().getMutableRoaringBitmap().isEmpty());
+    assertNotNull(queryableDocIdsCaptor.getValue(),
+        "queryableDocIds must be non-null when deleteRecordColumn is 
configured");
+    
assertTrue(queryableDocIdsCaptor.getValue().getMutableRoaringBitmap().isEmpty());
+  }
+
+  /**
+   * Companion to {@link #testPreloadSegmentEmptyValidDocIdsWithDeleteColumn}: 
when deleteRecordColumn is not
+   * configured, the fast path should still pass null for queryableDocIds 
since queryable tracking is disabled.
+   */
+  @Test
+  public void testPreloadSegmentEmptyValidDocIdsWithoutDeleteColumn() {
+    _contextBuilder.setEnableSnapshot(true);
+    ConcurrentMapPartitionUpsertMetadataManager upsertMetadataManager =
+        new ConcurrentMapPartitionUpsertMetadataManager(REALTIME_TABLE_NAME, 
0, _contextBuilder.build());
+
+    ImmutableSegmentImpl segment = mock(ImmutableSegmentImpl.class);
+    when(segment.getSegmentName()).thenReturn("emptyValidDocIdsSegment");
+    
when(segment.loadDocIdsFromSnapshot(V1Constants.VALID_DOC_IDS_SNAPSHOT_FILE_NAME)).thenReturn(
+        new MutableRoaringBitmap());
+
+    upsertMetadataManager.preloadSegment(segment);
+
+    ArgumentCaptor<ThreadSafeMutableRoaringBitmap> queryableDocIdsCaptor =
+        ArgumentCaptor.forClass(ThreadSafeMutableRoaringBitmap.class);
+    verify(segment).enableUpsert(eq(upsertMetadataManager), 
any(ThreadSafeMutableRoaringBitmap.class),
+        queryableDocIdsCaptor.capture());
+    assertNull(queryableDocIdsCaptor.getValue(),
+        "queryableDocIds must be null when deleteRecordColumn is not 
configured");
+  }
+
   @Test
   public void testAddRecordWithDeleteColumn()
       throws IOException {


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

Reply via email to