rangareddy commented on code in PR #19486:
URL: https://github.com/apache/hudi/pull/19486#discussion_r3817547088


##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/FileSystemBasedLockProvider.java:
##########
@@ -63,14 +63,29 @@
 @Slf4j
 public class FileSystemBasedLockProvider implements LockProvider<String>, 
Serializable {
   private static final String LOCK_FILE_NAME = "lock";
+  /**
+   * Guards this provider's lock-file operations.
+   *
+   * <p>These blocks used to synchronize on {@link #LOCK_FILE_NAME}. That is a 
compile-time String constant,
+   * so it is interned: any class anywhere in the JVM that synchronizes on the 
same {@code "lock"} literal
+   * contends on the very same monitor and silently couples itself to Hudi's 
lock acquisition. A private
+   * object cannot be aliased that way.
+   *
+   * <p>Kept static so the mutual-exclusion scope is unchanged by this fix.

Review Comment:
   Applied your suggestion verbatim. Agreed the old wording overclaimed.



##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/FileSystemBasedLockProvider.java:
##########
@@ -63,14 +63,29 @@
 @Slf4j
 public class FileSystemBasedLockProvider implements LockProvider<String>, 
Serializable {
   private static final String LOCK_FILE_NAME = "lock";
+  /**
+   * Guards this provider's lock-file operations.
+   *
+   * <p>These blocks used to synchronize on {@link #LOCK_FILE_NAME}. That is a 
compile-time String constant,
+   * so it is interned: any class anywhere in the JVM that synchronizes on the 
same {@code "lock"} literal
+   * contends on the very same monitor and silently couples itself to Hudi's 
lock acquisition. A private
+   * object cannot be aliased that way.
+   *
+   * <p>Kept static so the mutual-exclusion scope is unchanged by this fix.
+   */
+  private static final Object LOCK_FILE_MONITOR = new Object();

Review Comment:
   Fixed, the trailer now reads `Part of #16943` so the issue stays open for 
the remaining providers and the close-during-unlock semantics.



##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/FileSystemBasedLockProvider.java:
##########
@@ -63,14 +63,29 @@
 @Slf4j
 public class FileSystemBasedLockProvider implements LockProvider<String>, 
Serializable {
   private static final String LOCK_FILE_NAME = "lock";
+  /**
+   * Guards this provider's lock-file operations.
+   *
+   * <p>These blocks used to synchronize on {@link #LOCK_FILE_NAME}. That is a 
compile-time String constant,
+   * so it is interned: any class anywhere in the JVM that synchronizes on the 
same {@code "lock"} literal
+   * contends on the very same monitor and silently couples itself to Hudi's 
lock acquisition. A private
+   * object cannot be aliased that way.
+   *
+   * <p>Kept static so the mutual-exclusion scope is unchanged by this fix.
+   */
+  private static final Object LOCK_FILE_MONITOR = new Object();
   private final int lockTimeoutMinutes;
   private final transient HoodieStorage storage;
   private final transient StoragePath lockFile;
   protected LockConfiguration lockConfiguration;
   private final SimpleDateFormat sdf;
   private final LockInfo lockInfo;
+  /**
+   * Written while holding {@link #LOCK_FILE_MONITOR} in {@code tryLock}, but 
read through the generated
+   * getter without it, so the read needs to be volatile for the value to be 
visible to other threads.
+   */

Review Comment:
   Reworded to the public-API reason, since you are right that both in-repo 
readers are on the calling thread. Kept the modifier. Added the 
`serialVersionUID` change to the description with the two measured values.



##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/FileSystemBasedLockProvider.java:
##########
@@ -63,14 +63,29 @@
 @Slf4j
 public class FileSystemBasedLockProvider implements LockProvider<String>, 
Serializable {
   private static final String LOCK_FILE_NAME = "lock";
+  /**
+   * Guards this provider's lock-file operations.
+   *
+   * <p>These blocks used to synchronize on {@link #LOCK_FILE_NAME}. That is a 
compile-time String constant,
+   * so it is interned: any class anywhere in the JVM that synchronizes on the 
same {@code "lock"} literal
+   * contends on the very same monitor and silently couples itself to Hudi's 
lock acquisition. A private
+   * object cannot be aliased that way.
+   *
+   * <p>Kept static so the mutual-exclusion scope is unchanged by this fix.
+   */
+  private static final Object LOCK_FILE_MONITOR = new Object();
   private final int lockTimeoutMinutes;
   private final transient HoodieStorage storage;
   private final transient StoragePath lockFile;
   protected LockConfiguration lockConfiguration;
   private final SimpleDateFormat sdf;
   private final LockInfo lockInfo;
+  /**
+   * Written while holding {@link #LOCK_FILE_MONITOR} in {@code tryLock}, but 
read through the generated
+   * getter without it, so the read needs to be volatile for the value to be 
visible to other threads.
+   */
   @Getter
-  private String currentOwnerLockInfo;
+  private volatile String currentOwnerLockInfo;

Review Comment:
   All three corrected in the description. Verified locally: 
`DynamoDBBasedLockProviderBase:80` is `protected volatile LockItem lock`, so 
the contrast I drew was wrong, and the description now says so explicitly 
rather than quietly dropping it. Also folded in the expiry-path double-delete 
as the reason the monitor has to stay static.



##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/client/transaction/lock/FileSystemBasedLockProvider.java:
##########
@@ -120,7 +135,7 @@ public void close() {
   @Override
   public boolean tryLock(long time, TimeUnit unit) {
     try {
-      synchronized (LOCK_FILE_NAME) {
+      synchronized (LOCK_FILE_MONITOR) {

Review Comment:
   Fixed here rather than deferring, using your version. Added 
`testReloadCurrentOwnerLockInfoClearsWhenLockFileIsGone`, which fails against 
the old method with `FileNotFoundException` out of the reload.



##########
hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/lock/TestFileSystemBasedLockProvider.java:
##########
@@ -81,6 +86,60 @@ public void testAcquireAndReleaseLock() {
     }
   }
 
+  /**
+   * The lock-file operations used to synchronize on the {@code "lock"} String 
constant, which is interned
+   * and therefore shared JVM-wide with every other {@code "lock"} literal. 
Unrelated code holding that
+   * monitor blocked lock acquisition outright. {@code 
FileSystemBasedLockProviderTestClass} in this very
+   * repo declares {@code static final String LOCK = "lock"} and so aliases it.

Review Comment:
   Rewritten as `{@link}`. The sentence also had to change: the helper no 
longer aliases the literal after this revision, so it now reads as historical.



##########
hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/lock/TestFileSystemBasedLockProvider.java:
##########
@@ -81,6 +86,60 @@ public void testAcquireAndReleaseLock() {
     }
   }
 
+  /**
+   * The lock-file operations used to synchronize on the {@code "lock"} String 
constant, which is interned
+   * and therefore shared JVM-wide with every other {@code "lock"} literal. 
Unrelated code holding that
+   * monitor blocked lock acquisition outright. {@code 
FileSystemBasedLockProviderTestClass} in this very
+   * repo declares {@code static final String LOCK = "lock"} and so aliases it.
+   */
+  @Test
+  public void testAcquisitionIsNotBlockedByTheInternedLockLiteral() throws 
Exception {
+    StorageConfiguration<?> storageConf = 
HoodieTestUtils.getDefaultStorageConf();
+    FileSystemBasedLockProvider provider =
+        new FileSystemBasedLockProvider(lockConfiguration(lockDir("interned"), 
0), storageConf);
+    CountDownLatch holding = new CountDownLatch(1);
+    CountDownLatch release = new CountDownLatch(1);
+    // stands in for any other class in the JVM doing synchronized ("lock")
+    Thread unrelated = new Thread(() -> {
+      synchronized ("lock") {

Review Comment:
   Folded in. `LOCK` is split into a private `Object` monitor and a 
`LOCK_FILE_NAME` string, so there is no `"lock"` monitor left in the tree. No 
`notify`/`notifyAll` anywhere and `wait()` still targets the synchronized 
object, so it is monitor-for-monitor.



##########
hudi-client/hudi-client-common/src/test/java/org/apache/hudi/client/transaction/lock/TestFileSystemBasedLockProvider.java:
##########
@@ -81,6 +86,60 @@ public void testAcquireAndReleaseLock() {
     }
   }
 
+  /**
+   * The lock-file operations used to synchronize on the {@code "lock"} String 
constant, which is interned
+   * and therefore shared JVM-wide with every other {@code "lock"} literal. 
Unrelated code holding that
+   * monitor blocked lock acquisition outright. {@code 
FileSystemBasedLockProviderTestClass} in this very
+   * repo declares {@code static final String LOCK = "lock"} and so aliases it.
+   */
+  @Test
+  public void testAcquisitionIsNotBlockedByTheInternedLockLiteral() throws 
Exception {
+    StorageConfiguration<?> storageConf = 
HoodieTestUtils.getDefaultStorageConf();
+    FileSystemBasedLockProvider provider =
+        new FileSystemBasedLockProvider(lockConfiguration(lockDir("interned"), 
0), storageConf);
+    CountDownLatch holding = new CountDownLatch(1);
+    CountDownLatch release = new CountDownLatch(1);
+    // stands in for any other class in the JVM doing synchronized ("lock")
+    Thread unrelated = new Thread(() -> {
+      synchronized ("lock") {
+        holding.countDown();
+        try {
+          release.await();
+        } catch (InterruptedException e) {
+          Thread.currentThread().interrupt();
+        }
+      }
+    });
+    unrelated.setDaemon(true);
+    unrelated.start();
+    assertTrue(holding.await(10, TimeUnit.SECONDS), "the unrelated thread 
should hold the interned monitor");
+
+    try {

Review Comment:
   Applied. `start()` and the await are inside the try, so the latch always 
drops.



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