You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by mi...@apache.org on 2026/02/25 15:25:47 UTC

(jackrabbit-oak) branch trunk updated: Issue/oak 12112 unresolved address (#2766)

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

miroslav pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new a4b6d55be7 Issue/oak 12112 unresolved address (#2766)
a4b6d55be7 is described below

commit a4b6d55be7a30b1dc77578db0a94b5990c1fcb38
Author: Miroslav Smiljanic <sm...@gmail.com>
AuthorDate: Wed Feb 25 16:25:41 2026 +0100

    Issue/oak 12112 unresolved address (#2766)
    
    * OAK-12112 treat DNS issue as transient
    ---------
    
    Co-authored-by: smiroslav <mi...@apache.com>
---
 .../oak/segment/azure/AzureRepositoryLock.java     | 12 +++++--
 .../oak/segment/azure/AzureRepositoryLockTest.java | 42 ++++++++++++++++++++++
 2 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureRepositoryLock.java b/oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureRepositoryLock.java
index a34193f742..1594b3b52a 100644
--- a/oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureRepositoryLock.java
+++ b/oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/AzureRepositoryLock.java
@@ -28,8 +28,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.nio.channels.UnresolvedAddressException;
 import java.time.Duration;
 import java.util.Set;
+import java.util.concurrent.TimeoutException;
 
 public class AzureRepositoryLock implements RepositoryLock {
 
@@ -222,15 +224,19 @@ public class AzureRepositoryLock implements RepositoryLock {
      * Per Azure SDK documentation, the timeout parameter causes a RuntimeException to be raised.
      * Reactor-core throws IllegalStateException with message "Timeout on blocking read" when
      * the timeout expires (see BlockingSingleSubscriber.blockingGet in reactor-core).
+     * <p>
+     * {@link java.nio.channels.UnresolvedAddressException} (extends IllegalArgumentException)
+     * can occur when DNS resolution temporarily fails.
      * @param e the exception to check
      * @return true if this is a transient exception that should be retried
      */
     private boolean isTransientClientSideException(Exception e) {
         Throwable current = e;
         while (current != null) {
-            if (current instanceof java.util.concurrent.TimeoutException ||
-                current instanceof java.io.IOException ||
-                current instanceof IllegalStateException) {
+            if (current instanceof TimeoutException ||
+                current instanceof IOException ||
+                current instanceof IllegalStateException ||
+                current instanceof UnresolvedAddressException) {
                 return true;
             }
             current = current.getCause();
diff --git a/oak-segment-azure/src/test/java/org/apache/jackrabbit/oak/segment/azure/AzureRepositoryLockTest.java b/oak-segment-azure/src/test/java/org/apache/jackrabbit/oak/segment/azure/AzureRepositoryLockTest.java
index ae7504ea76..10e8370d13 100644
--- a/oak-segment-azure/src/test/java/org/apache/jackrabbit/oak/segment/azure/AzureRepositoryLockTest.java
+++ b/oak-segment-azure/src/test/java/org/apache/jackrabbit/oak/segment/azure/AzureRepositoryLockTest.java
@@ -45,6 +45,7 @@ import reactor.core.publisher.Mono;
 
 import java.io.IOException;
 import java.net.URISyntaxException;
+import java.nio.channels.UnresolvedAddressException;
 import java.security.InvalidKeyException;
 import java.time.Duration;
 import java.util.concurrent.Semaphore;
@@ -330,6 +331,47 @@ public class AzureRepositoryLockTest {
         }
     }
 
+    @Test
+    public void testUnresolvedAddressExceptionIsRecoverable() throws Exception {
+        BlockBlobClient blockBlobClient = readBlobContainerClient.getBlobClient("oak/repo.lock").getBlockBlobClient();
+        BlockBlobClient noRetryBlockBlobClient = noRetryBlobContainerClient.getBlobClient("oak/repo.lock").getBlockBlobClient();
+        BlobLeaseClient blobLeaseClient = new BlobLeaseClientBuilder().blobClient(noRetryBlockBlobClient).buildClient();
+
+        BlockBlobClient blobMocked = Mockito.spy(blockBlobClient);
+        BlobLeaseClient blobLeaseMocked = Mockito.spy(blobLeaseClient);
+
+        // Simulate DNS resolution failure wrapped in a RuntimeException (as Netty/Reactor does)
+        RuntimeException dnsError = new RuntimeException(
+                "Connection failed",
+                new UnresolvedAddressException());
+
+        // Track if shutdown hook was called
+        AtomicBoolean shutdownCalled = new AtomicBoolean(false);
+        Runnable shutdownHook = () -> shutdownCalled.set(true);
+
+        // Instrument the mock to throw the UnresolvedAddressException twice, then succeed
+        Mockito.doThrow(dnsError)
+                .doThrow(dnsError)
+                .doCallRealMethod()
+                .when(blobLeaseMocked).renewLeaseWithResponse((RequestConditions) any(), any(), any());
+
+        WriteAccessController writeAccessController = new WriteAccessController();
+
+        AzureRepositoryLock lock = new AzureRepositoryLock(blobMocked, blobLeaseMocked, shutdownHook, writeAccessController);
+        try {
+            lock.lock();
+
+            // Wait for at least 3 calls (2 failures + 1 success) with a timeout
+            Mockito.verify(blobLeaseMocked, Mockito.timeout(10000).atLeast(3))
+                    .renewLeaseWithResponse((RequestConditions) any(), any(), any());
+
+            // Verify that shutdown hook was NOT called - the UnresolvedAddressException should be treated as recoverable
+            assertFalse("Shutdown hook should not be called for UnresolvedAddressException", shutdownCalled.get());
+        } finally {
+            lock.unlock();
+        }
+    }
+
     /**
      * HTTP pipeline policy that injects delays into specific requests.
      * Used to cause real client-side timeouts in tests.