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 <[email protected]>
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 <[email protected]>
---
.../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.