Copilot commented on code in PR #13400:
URL: https://github.com/apache/gravitino/pull/13400#discussion_r4069188691


##########
core/src/main/java/org/apache/gravitino/cache/SegmentedLock.java:
##########
@@ -34,8 +34,15 @@ public class SegmentedLock {
 
   private final Striped<Lock> stripedLocks;
 
-  /** CountDownLatch for global operations - null when no operation is in 
progress */
-  private final AtomicReference<CountDownLatch> globalOperationLatch = new 
AtomicReference<>();
+  /**
+   * Gates segment operations against global operations: segment operations 
hold the read lock
+   * across their whole critical section, global operations hold the write 
lock, so a global
+   * operation excludes every segment operation, including ones already in 
flight when it starts.
+   */
+  private final ReentrantReadWriteLock globalGate = new 
ReentrantReadWriteLock();

Review Comment:
   The gate uses the default non-fair mode, so readers can barge ahead of a 
queued writer. Once `withGlobalLock` is waiting for an in-flight reader, a 
continuous stream of `withLock` calls can keep acquiring the read lock and 
indefinitely starve the global action, leaving cache clears blocked. Use a fair 
read/write lock (or otherwise prevent readers from entering once a writer is 
queued).



##########
core/src/test/java/org/apache/gravitino/cache/TestSegmentedLock.java:
##########
@@ -380,4 +381,58 @@ void testConcurrentGlobalClearing() {
               });
         });
   }
+
+  @Test
+  @Timeout(30)
+  void testGlobalClearingWaitsForInFlightOperations() throws 
InterruptedException {
+    SegmentedLock lock = new SegmentedLock(4);
+    CountDownLatch insideSegmentOp = new CountDownLatch(1);
+    CountDownLatch releaseSegmentOp = new CountDownLatch(1);
+    CountDownLatch globalActionDone = new CountDownLatch(1);
+    AtomicBoolean overlapped = new AtomicBoolean(false);
+
+    Thread segmentOpThread =
+        new Thread(
+            () ->
+                lock.withLock(
+                    "key1",
+                    () -> {
+                      insideSegmentOp.countDown();
+                      try {
+                        releaseSegmentOp.await();
+                      } catch (InterruptedException e) {
+                        Thread.currentThread().interrupt();
+                      }
+                    }));
+    segmentOpThread.start();
+    assertTrue(insideSegmentOp.await(5, TimeUnit.SECONDS), "segment operation 
never started");
+
+    Thread globalThread =
+        new Thread(
+            () ->
+                lock.withGlobalLock(
+                    () -> {
+                      // The global action must never observe the in-flight 
segment
+                      // operation still holding its critical section.
+                      if (releaseSegmentOp.getCount() > 0) {
+                        overlapped.set(true);
+                      }

Review Comment:
   `releaseSegmentOp` only signals that the segment action may return; it is 
counted down before the lambda returns and before `withLock` releases its 
segment/read locks. A regression that releases the global gate before the 
action exits could therefore run the global action after this latch is released 
and still pass. Add a separate completion latch counted down after the segment 
action finishes, and assert that latch in the global action to verify exclusion 
across the full critical section.



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

Reply via email to