Jackie-Jiang commented on code in PR #19028:
URL: https://github.com/apache/pinot/pull/19028#discussion_r3619876045


##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/upsert/BasePartitionUpsertMetadataManager.java:
##########
@@ -951,27 +952,39 @@ protected void doTakeSnapshot() {
         numUnchangedSegments++;
         continue;
       }
-      // Try to acquire the segmentLock when taking snapshot for the segment 
because the segment directory can be
-      // modified, e.g. a new snapshot file can be added to the directory. If 
not taking the lock, the Helix task
-      // thread replacing the segment could fail. For example, we found 
FileUtils.cleanDirectory() failed due to
+      // Acquire the segmentLock when taking snapshot for the segment because 
the segment directory can be modified,
+      // e.g. a new snapshot file can be added to the directory. If not taking 
the lock, the Helix task thread
+      // replacing the segment could fail. For example, we found 
FileUtils.cleanDirectory() failed due to
       // DirectoryNotEmptyException because a new snapshot file got added into 
the segment directory just between two
       // major cleanup steps in the cleanDirectory() method.
+      // Blocking on the segmentLock cannot deadlock because this thread holds 
no lock that segmentLock holders can
+      // wait on. Contention should also be rare: the most common segment 
replacement is for the just committed
+      // consuming segment, but the tracked segment remains the mutable one 
(thus skipped above) until the immutable
+      // segment is swapped in at the end of the replacement, so the wait is 
bounded by the metadata replacement
+      // instead of the segment download/build. A concurrent segment 
reload/refresh can hold the segmentLock for its
+      // full duration though, which delays the snapshot and the start of 
consumption.
+      // Lock interruptibly so that stopping the consumer thread is not 
blocked by a long-held segmentLock.
       String segmentName = segment.getSegmentName();
       Lock segmentLock = tableDataManager.getSegmentLock(segmentName);
-      boolean locked = segmentLock.tryLock();
-      if (!locked) {
-        // Try to get the segmentLock in a non-blocking manner to avoid 
deadlock. The Helix task thread takes
-        // segmentLock first and then the snapshot RLock when replacing a 
segment. However, the consuming thread has
-        // already acquired the snapshot WLock when reaching here, and if it 
has to wait for segmentLock, it may
-        // enter deadlock with the Helix task threads waiting for snapshot 
RLock.
-        // If we can't get the segmentLock, we'd better skip taking snapshot 
for this tracked segment, because its
-        // validDocIds/queryableDocIds might become stale or wrong when the 
segment is being processed by another
-        // thread right now.
-        _logger.warn("Could not get segmentLock to take snapshot for segment: 
{}, skipping", segmentName);
+      try {
+        segmentLock.lockInterruptibly();

Review Comment:
   The wait is effectively bounded for the common cases:
   - Segment commit/replacement: during the segment download/build, the tracked 
segment is still the mutable one, which is skipped before touching the 
segmentLock. The immutable segment object only enters `_trackedSegments` at the 
end of the replacement, so the wait is bounded by the upsert metadata swap, not 
the download/build.
   - Segment removal holds the lock for a bounded metadata cleanup.
   
   The one genuinely long hold is a concurrent segment reload/refresh, which is 
rare. The previous behavior wasn't free in that case either: skipping left a 
stale snapshot on disk and suppressed the second loop, so all the segments 
waiting for their first snapshot got deferred as well, hurting restart/preload. 
Delaying the start of consumption in that rare case buys snapshot completeness.
   
   The interrupt escape hatch is exactly the consumer stop path 
(`RealtimeSegmentDataManager.stop()` interrupts the consumer thread), so 
shutdown/segment stop is not blocked by the wait.
   
   If this shows up delaying consumption in practice, we can switch to 
`tryLock(timeout)` with a generous timeout that degrades to the old skip 
behavior, but I'd rather not reintroduce the coverage loss for a rare case.
   



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