From 2445ed07d6b28ed136d4c25cdf833465786e9c36 Mon Sep 17 00:00:00 2001 From: fergalmcl Date: Thu, 13 Feb 2025 17:14:06 +0000 Subject: [PATCH 1/3] logging when max retries hit --- .../templates/ApiClient.mustache | 13 ++++++++ .../tests/ApiClientRetryTest.java | 31 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/resources/sdk/purecloudjava/templates/ApiClient.mustache b/resources/sdk/purecloudjava/templates/ApiClient.mustache index 661d87304..54c2782b4 100644 --- a/resources/sdk/purecloudjava/templates/ApiClient.mustache +++ b/resources/sdk/purecloudjava/templates/ApiClient.mustache @@ -829,6 +829,10 @@ public class ApiClient implements AutoCloseable { } return new ApiResponseWrapper<>(statusCode, reasonPhrase, headers, body, entity); } + else if (statusCode == 429) { + String message = "Max number of retries exceeded"; + throw new ApiException(statusCode, message, headers, response.readBody()); + } else { String message = "error"; String body = response.readBody(); @@ -889,6 +893,10 @@ public class ApiClient implements AutoCloseable { if (e.getStatusCode() == 401 && shouldRefreshAccessToken) { handleExpiredAccessToken(); return getAPIResponse(request, returnType, isAuthRequest); + } else if (e.getStatusCode() == 429 && retry.maxRetriesExceeded) { + Map responseHeaderCopy = new HashMap<>(e.getHeaders()); + logger.error(connectorRequest.getMethod(), connectorRequest.getUrl(), connectorRequest.readBody(), e.getRawBody(), e.getStatusCode(), requestHeaderCopy, responseHeaderCopy); + throw e; } else if (e.getStatusCode() < 200 || e.getStatusCode() >= 300) { Map responseHeaderCopy = new HashMap<>(e.getHeaders()); logger.error(connectorRequest.getMethod(), connectorRequest.getUrl(), connectorRequest.readBody(), e.getRawBody(), e.getStatusCode(), requestHeaderCopy, responseHeaderCopy); @@ -1544,6 +1552,7 @@ public class ApiClient implements AutoCloseable { private long retryAfterMs; private Stopwatch stopwatch = null; private long defaultMaxRetry = 180000L; + private boolean maxRetriesExceeded; private final List statusCodes = Arrays.asList(429, 502, 503, 504); @@ -1553,6 +1562,7 @@ public class ApiClient implements AutoCloseable { this.maxRetryTimeSec = retryConfiguration.maxRetryTimeSec; this.maxRetriesBeforeBackoff = retryConfiguration.maxRetriesBeforeBackoff; this.retryMax = retryConfiguration.retryMax; + this.maxRetriesExceeded = false; stopwatch = Stopwatch.createStarted(); } @@ -1580,6 +1590,9 @@ public class ApiClient implements AutoCloseable { return waitBeforeRetry(getWaitTimeExp(Math.min(3, Math.floor(stopwatch.elapsed(TimeUnit.MILLISECONDS) / backoffIntervalMs) + 1))); } + else if (retryCount > retryMax) { + maxRetriesExceeded = true; + } stopwatch.stop(); return false; } diff --git a/resources/sdk/purecloudjava/tests/ApiClientRetryTest.java b/resources/sdk/purecloudjava/tests/ApiClientRetryTest.java index cbe4b3003..581874900 100644 --- a/resources/sdk/purecloudjava/tests/ApiClientRetryTest.java +++ b/resources/sdk/purecloudjava/tests/ApiClientRetryTest.java @@ -120,6 +120,37 @@ public void invokeTestWith_429_And_No_MaxRetryTime() throws IOException { } } + @Test + public void invokeTestWith_429_MaxRetriesExceeded() throws IOException { + ApiClient.RetryConfiguration retryConfiguration = new ApiClient.RetryConfiguration(); + + retryConfiguration.setMaxRetryTimeSec(6); + retryConfiguration.setRetryAfterDefaultMs(100); + retryConfiguration.setRetryMax(0); + retryConfiguration.setMaxRetriesBeforeBackoff(2); + + apiClient = getApiClient(retryConfiguration); + + mockResponse = getMockCloseableHttpResponse(429); + + Header header = mock(Header.class); + when(header.getName()).thenReturn("Retry-After"); + when(header.getValue()).thenReturn("3"); + + when(mockResponse.getAllHeaders()).thenReturn(new Header[]{header}); + doReturn(mockResponse).when(spyClient).execute(any(HttpUriRequest.class)); + + try { + stopwatch = Stopwatch.createStarted(); + ApiResponse response = apiClient.invoke(getConnectorRequest(), getReturnType()); + } catch (ApiException ex) { + Assert.assertEquals(429, ex.getStatusCode()); + Assert.assertEquals("Max number of retries exceeded", ex.getMessage()); + Assert.assertTrue(stopwatch.elapsed(TimeUnit.MILLISECONDS) >= 3000 && stopwatch.elapsed(TimeUnit.MILLISECONDS) < 3100, "Since retryMax is 0, after one retry exception is thrown"); + stopwatch.stop(); + } + } + @Test public void invokeTestWith_502() throws IOException { ApiClient.RetryConfiguration retryConfiguration = new ApiClient.RetryConfiguration(); From d2a0f7c2eea21be5c2f8ed6a152a265c613bdde8 Mon Sep 17 00:00:00 2001 From: fergalmcl Date: Fri, 14 Feb 2025 11:35:27 +0000 Subject: [PATCH 2/3] remove boolean --- resources/sdk/purecloudjava/templates/ApiClient.mustache | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/resources/sdk/purecloudjava/templates/ApiClient.mustache b/resources/sdk/purecloudjava/templates/ApiClient.mustache index 54c2782b4..d8d926110 100644 --- a/resources/sdk/purecloudjava/templates/ApiClient.mustache +++ b/resources/sdk/purecloudjava/templates/ApiClient.mustache @@ -893,7 +893,7 @@ public class ApiClient implements AutoCloseable { if (e.getStatusCode() == 401 && shouldRefreshAccessToken) { handleExpiredAccessToken(); return getAPIResponse(request, returnType, isAuthRequest); - } else if (e.getStatusCode() == 429 && retry.maxRetriesExceeded) { + } else if (e.getStatusCode() == 429) { Map responseHeaderCopy = new HashMap<>(e.getHeaders()); logger.error(connectorRequest.getMethod(), connectorRequest.getUrl(), connectorRequest.readBody(), e.getRawBody(), e.getStatusCode(), requestHeaderCopy, responseHeaderCopy); throw e; @@ -1552,7 +1552,6 @@ public class ApiClient implements AutoCloseable { private long retryAfterMs; private Stopwatch stopwatch = null; private long defaultMaxRetry = 180000L; - private boolean maxRetriesExceeded; private final List statusCodes = Arrays.asList(429, 502, 503, 504); @@ -1562,7 +1561,6 @@ public class ApiClient implements AutoCloseable { this.maxRetryTimeSec = retryConfiguration.maxRetryTimeSec; this.maxRetriesBeforeBackoff = retryConfiguration.maxRetriesBeforeBackoff; this.retryMax = retryConfiguration.retryMax; - this.maxRetriesExceeded = false; stopwatch = Stopwatch.createStarted(); } @@ -1590,9 +1588,6 @@ public class ApiClient implements AutoCloseable { return waitBeforeRetry(getWaitTimeExp(Math.min(3, Math.floor(stopwatch.elapsed(TimeUnit.MILLISECONDS) / backoffIntervalMs) + 1))); } - else if (retryCount > retryMax) { - maxRetriesExceeded = true; - } stopwatch.stop(); return false; } From 3e61718c8d7537c84e9b6628ee5f651eff9365a4 Mon Sep 17 00:00:00 2001 From: fergalmcl Date: Fri, 14 Feb 2025 15:56:46 +0000 Subject: [PATCH 3/3] log when rate limit hit and remove redundancy --- .../sdk/purecloudjava/templates/ApiClient.mustache | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/resources/sdk/purecloudjava/templates/ApiClient.mustache b/resources/sdk/purecloudjava/templates/ApiClient.mustache index d8d926110..59599392c 100644 --- a/resources/sdk/purecloudjava/templates/ApiClient.mustache +++ b/resources/sdk/purecloudjava/templates/ApiClient.mustache @@ -829,12 +829,8 @@ public class ApiClient implements AutoCloseable { } return new ApiResponseWrapper<>(statusCode, reasonPhrase, headers, body, entity); } - else if (statusCode == 429) { - String message = "Max number of retries exceeded"; - throw new ApiException(statusCode, message, headers, response.readBody()); - } else { - String message = "error"; + String message = (statusCode == 429) ? "Max number of retries exceeded" : "error"; String body = response.readBody(); throw new ApiException(statusCode, message, headers, body); } @@ -893,10 +889,6 @@ public class ApiClient implements AutoCloseable { if (e.getStatusCode() == 401 && shouldRefreshAccessToken) { handleExpiredAccessToken(); return getAPIResponse(request, returnType, isAuthRequest); - } else if (e.getStatusCode() == 429) { - Map responseHeaderCopy = new HashMap<>(e.getHeaders()); - logger.error(connectorRequest.getMethod(), connectorRequest.getUrl(), connectorRequest.readBody(), e.getRawBody(), e.getStatusCode(), requestHeaderCopy, responseHeaderCopy); - throw e; } else if (e.getStatusCode() < 200 || e.getStatusCode() >= 300) { Map responseHeaderCopy = new HashMap<>(e.getHeaders()); logger.error(connectorRequest.getMethod(), connectorRequest.getUrl(), connectorRequest.readBody(), e.getRawBody(), e.getStatusCode(), requestHeaderCopy, responseHeaderCopy); @@ -1594,6 +1586,7 @@ public class ApiClient implements AutoCloseable { private boolean waitBeforeRetry(long retryAfterMs){ try { + System.out.printf("Rate limit encountered. Retry the request in [%d] seconds\n", retryAfterMs/1000L); Thread.sleep(retryAfterMs); } catch (InterruptedException e) { Thread.currentThread().interrupt();