diff --git a/server/src/main/java/org/apache/druid/rpc/StandardRetryPolicy.java b/server/src/main/java/org/apache/druid/rpc/StandardRetryPolicy.java index 71097ef6d890..07d753c7f3bc 100644 --- a/server/src/main/java/org/apache/druid/rpc/StandardRetryPolicy.java +++ b/server/src/main/java/org/apache/druid/rpc/StandardRetryPolicy.java @@ -141,7 +141,8 @@ public boolean retryHttpResponse(final HttpResponse response) return code == HttpResponseStatus.BAD_GATEWAY.getCode() || code == HttpResponseStatus.SERVICE_UNAVAILABLE.getCode() || code == HttpResponseStatus.GATEWAY_TIMEOUT.getCode() - + // 401 can happen from things like expiration and might be retryable + || code == HttpResponseStatus.UNAUTHORIZED.getCode() // Technically shouldn't retry this last one, but servers sometimes return HTTP 500 for retryable errors. || code == HttpResponseStatus.INTERNAL_SERVER_ERROR.getCode(); } diff --git a/server/src/test/java/org/apache/druid/rpc/ServiceClientImplTest.java b/server/src/test/java/org/apache/druid/rpc/ServiceClientImplTest.java index 7346edd5cf6b..0b2bb7fe2640 100644 --- a/server/src/test/java/org/apache/druid/rpc/ServiceClientImplTest.java +++ b/server/src/test/java/org/apache/druid/rpc/ServiceClientImplTest.java @@ -715,6 +715,23 @@ private OngoingStubbing ); } + @Test + public void test_request_authErrorRetry() throws Exception + { + final RequestBuilder requestBuilder = new RequestBuilder(HttpMethod.GET, "/foo"); + final ImmutableMap expectedResponseObject = ImmutableMap.of("foo", "bar"); + + // Unauthorized response from SERVER1, then OK response. + stubLocatorCall(locations(SERVER1, SERVER2)); + expectHttpCall(requestBuilder, SERVER1) + .thenReturn(errorResponse(HttpResponseStatus.UNAUTHORIZED, null, "unauthorized")) + .thenReturn(valueResponse(expectedResponseObject)); + + serviceClient = makeServiceClient(StandardRetryPolicy.unlimited()); + final Map response = doRequest(serviceClient, requestBuilder); + Assert.assertEquals(expectedResponseObject, response); + } + private void stubLocatorCall(final ServiceLocations locations) { stubLocatorCall(Futures.immediateFuture(locations));