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

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


The following commit(s) were added to refs/heads/master by this push:
     new 4a5d5b0e0410 fix(hive-sync): drop the unreachable HMS lock 
timeout-recovery path (#19371)
4a5d5b0e0410 is described below

commit 4a5d5b0e0410b1e2313fb7ad42b049bc6e36e417
Author: Vova Kolmakov <[email protected]>
AuthorDate: Mon Jul 27 20:31:32 2026 +0700

    fix(hive-sync): drop the unreachable HMS lock timeout-recovery path (#19371)
    
    * fix(hive-sync): drop the unreachable HMS lock timeout-recovery path
    
    * addressed review comments: stub checkLock and pin the abandoned late grant
    
    ---------
    
    Co-authored-by: Vova Kolmakov <[email protected]>
---
 .../lock/HiveMetastoreBasedLockProvider.java       | 18 +----
 ...veMetastoreBasedLockProviderAcquireTimeout.java | 90 ++++++++++++++++++++++
 2 files changed, 92 insertions(+), 16 deletions(-)

diff --git 
a/hudi-sync/hudi-hive-sync/src/main/java/org/apache/hudi/hive/transaction/lock/HiveMetastoreBasedLockProvider.java
 
b/hudi-sync/hudi-hive-sync/src/main/java/org/apache/hudi/hive/transaction/lock/HiveMetastoreBasedLockProvider.java
index 91d4062ecc3b..a10302e1bedd 100644
--- 
a/hudi-sync/hudi-hive-sync/src/main/java/org/apache/hudi/hive/transaction/lock/HiveMetastoreBasedLockProvider.java
+++ 
b/hudi-sync/hudi-hive-sync/src/main/java/org/apache/hudi/hive/transaction/lock/HiveMetastoreBasedLockProvider.java
@@ -215,29 +215,15 @@ public class HiveMetastoreBasedLockProvider implements 
LockProvider<LockResponse
 
   private void acquireLockInternal(long time, TimeUnit unit, LockComponent 
lockComponent)
       throws InterruptedException, ExecutionException, TimeoutException, 
TException {
-    LockRequest lockRequest = null;
     lockLostRemotely = false;
     try {
       // TODO : FIX:Using the parameterized constructor throws MethodNotFound
       final LockRequestBuilder builder = new LockRequestBuilder();
-      lockRequest = 
builder.addLockComponent(lockComponent).setUser(System.getProperty("user.name")).build();
+      final LockRequest lockRequest = 
builder.addLockComponent(lockComponent).setUser(System.getProperty("user.name")).build();
       lockRequest.setUserIsSet(true);
-      final LockRequest lockRequestFinal = lockRequest;
-      this.lock = executor.submit(() -> hiveClient.lock(lockRequestFinal))
+      this.lock = executor.submit(() -> hiveClient.lock(lockRequest))
           .get(time, unit);
       scheduleHeartbeat();
-    } catch (InterruptedException | TimeoutException e) {
-      if (this.lock == null || this.lock.getState() != LockState.ACQUIRED) {
-        LockResponse lockResponse = 
this.hiveClient.checkLock(lockRequest.getTxnid());
-        if (lockResponse.getState() == LockState.ACQUIRED) {
-          this.lock = lockResponse;
-          // The lock was granted server-side even though the client timed out 
waiting on the
-          // future; it still needs a heartbeat, otherwise a long-running 
commit lets HMS expire it.
-          scheduleHeartbeat();
-        } else {
-          throw e;
-        }
-      }
     } finally {
       // it is better to release WAITING lock, otherwise hive lock will hang 
forever
       // Snapshot the lock: the heartbeat thread clears it as soon as the 
metastore reports it gone.
diff --git 
a/hudi-sync/hudi-hive-sync/src/test/java/org/apache/hudi/hive/transaction/lock/TestHiveMetastoreBasedLockProviderAcquireTimeout.java
 
b/hudi-sync/hudi-hive-sync/src/test/java/org/apache/hudi/hive/transaction/lock/TestHiveMetastoreBasedLockProviderAcquireTimeout.java
new file mode 100644
index 000000000000..750e8406da1b
--- /dev/null
+++ 
b/hudi-sync/hudi-hive-sync/src/test/java/org/apache/hudi/hive/transaction/lock/TestHiveMetastoreBasedLockProviderAcquireTimeout.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.hudi.hive.transaction.lock;
+
+import org.apache.hudi.exception.HoodieLockException;
+
+import org.apache.hadoop.hive.metastore.IMetaStoreClient;
+import org.apache.hadoop.hive.metastore.api.NoSuchLockException;
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for what {@link HiveMetastoreBasedLockProvider} reports when the 
metastore does not
+ * answer a lock request in time, with a mocked {@link IMetaStoreClient} and 
no live metastore or
+ * ZooKeeper.
+ */
+class TestHiveMetastoreBasedLockProviderAcquireTimeout extends 
HiveMetastoreBasedLockProviderTestBase {
+
+  private static final long LOCK_ID = 42L;
+  private static final long ACQUIRE_TIMEOUT_MS = 200L;
+
+  @Test
+  void acquireTimeoutIsReportedAsATimeout() throws Exception {
+    IMetaStoreClient client = mock(IMetaStoreClient.class);
+    CountDownLatch metastoreAnswers = new CountDownLatch(1);
+    CountDownLatch lockReturned = new CountDownLatch(1);
+    when(client.lock(any())).thenAnswer(invocation -> {
+      metastoreAnswers.await();
+      lockReturned.countDown();
+      return acquiredLock(LOCK_ID);
+    });
+    // What a real metastore answers for the txn id 0 that the timed-out 
request carried. Never
+    // reached now, it is here so that restoring the removed lookup fails this 
test on the cause of
+    // the exception rather than on a bare NPE from an unstubbed call.
+    when(client.checkLock(anyLong())).thenThrow(new NoSuchLockException("No 
such lock 0"));
+
+    HiveMetastoreBasedLockProvider provider = new 
HiveMetastoreBasedLockProvider(lockConfiguration, client);
+    try {
+      HoodieLockException thrown = assertThrows(HoodieLockException.class,
+          () -> provider.tryLock(ACQUIRE_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+      // The metastore never answered, and that is what the writer has to be 
told. Looking the lock
+      // up afterwards cannot help: the request that timed out never returned 
a lock id.
+      assertInstanceOf(TimeoutException.class, thrown.getCause());
+      verify(client, never()).checkLock(anyLong());
+      assertNull(provider.getLock());
+    } finally {
+      metastoreAnswers.countDown();
+      provider.close();
+    }
+
+    // A lock granted after the client gave up is abandoned, not released: the 
timed-out get() never
+    // assigned it, so neither the acquire path nor close() knows of a lock to 
unlock or heartbeat.
+    // It stays held at the metastore until hive.txn.timeout reaps it.
+    assertTrue(lockReturned.await(30, TimeUnit.SECONDS));
+    verify(client, never()).unlock(anyLong());
+    verify(client, never()).heartbeat(anyLong(), anyLong());
+  }
+}

Reply via email to