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

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

commit e46e4dc7481d2154118dfc1999aaee065ff2f040
Author: smiroslav <[email protected]>
AuthorDate: Wed Feb 25 14:16:20 2026 +0100

    OAK-12112 treat DNS issue as transient
---
 .../oak/segment/azure/AzureRepositoryLock.java     | 12 +++++--
 .../oak/segment/azure/AzureRepositoryLockTest.java | 41 ++++++++++++++++++++++
 2 files changed, 50 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..3e0f82c645 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
@@ -330,6 +330,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 java.nio.channels.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.

Reply via email to