|
26 | 26 | import com.palantir.conjure.java.api.errors.RemoteException;
|
27 | 27 | import com.palantir.conjure.java.client.config.ClientConfiguration;
|
28 | 28 | import com.palantir.logsafe.SafeArg;
|
| 29 | +import com.palantir.logsafe.UnsafeArg; |
| 30 | +import com.palantir.logsafe.exceptions.SafeIoException; |
29 | 31 | import java.io.IOException;
|
30 | 32 | import java.io.InterruptedIOException;
|
31 | 33 | import java.time.Duration;
|
@@ -175,25 +177,35 @@ public void onFailure(Call call, IOException exception) {
|
175 | 177 | // Fail call if backoffs are exhausted or if no retry URL can be determined.
|
176 | 178 | Optional<Duration> backoff = backoffStrategy.nextBackoff();
|
177 | 179 | if (!backoff.isPresent()) {
|
178 |
| - callback.onFailure(call, new IOException("Failed to complete the request due to an " |
179 |
| - + "IOException", exception)); |
| 180 | + callback.onFailure(call, new SafeIoException( |
| 181 | + "Failed to complete the request due to an IOException", |
| 182 | + exception, |
| 183 | + UnsafeArg.of("requestUrl", call.request().url().toString()))); |
180 | 184 | return;
|
181 | 185 | }
|
| 186 | + |
182 | 187 | Optional<HttpUrl> redirectTo = urls.redirectToNext(request().url());
|
183 | 188 | if (!redirectTo.isPresent()) {
|
184 |
| - callback.onFailure(call, new IOException("Failed to determine valid failover URL for '" |
185 |
| - + request().url() + "' and base URLs " + urls.getBaseUrls())); |
| 189 | + callback.onFailure(call, new SafeIoException( |
| 190 | + "Failed to determine valid failover URL", |
| 191 | + exception, |
| 192 | + UnsafeArg.of("requestUrl", call.request().url().toString()), |
| 193 | + UnsafeArg.of("baseUrls", urls.getBaseUrls()))); |
186 | 194 | return;
|
187 | 195 | }
|
188 | 196 |
|
| 197 | + log.info("Retrying call after failure", |
| 198 | + SafeArg.of("backoffMillis", backoff.get().toMillis()), |
| 199 | + UnsafeArg.of("redirectToUrl", redirectTo.get()), |
| 200 | + exception); |
189 | 201 | Request redirectedRequest = request().newBuilder()
|
190 | 202 | .url(redirectTo.get())
|
191 | 203 | .build();
|
192 | 204 | RemotingOkHttpCall retryCall =
|
193 | 205 | client.newCallWithMutableState(redirectedRequest, backoffStrategy, maxNumRelocations - 1);
|
194 |
| - log.debug("Rescheduling call after backoff", SafeArg.of("backoffMillis", backoff.get().toMillis()), |
195 |
| - exception); |
196 |
| - scheduleExecution(() -> retryCall.enqueue(callback), backoff.get()); |
| 206 | + scheduleExecution( |
| 207 | + () -> retryCall.enqueue(callback), |
| 208 | + backoff.get()); |
197 | 209 | }
|
198 | 210 |
|
199 | 211 | @Override
|
@@ -258,65 +270,89 @@ private QosException.Visitor<Void> createQosVisitor(Callback callback, Call call
|
258 | 270 | public Void visit(QosException.Throttle exception) {
|
259 | 271 | Optional<Duration> nonAdvertizedBackoff = backoffStrategy.nextBackoff();
|
260 | 272 | if (!nonAdvertizedBackoff.isPresent()) {
|
261 |
| - callback.onFailure(call, new IOException("Failed to reschedule call since " |
262 |
| - + "the number of configured backoffs are exhausted", exception)); |
| 273 | + callback.onFailure(call, new SafeIoException( |
| 274 | + "Failed to complete the request due to QosException.Throttle", |
| 275 | + exception, |
| 276 | + UnsafeArg.of("requestUrl", call.request().url().toString()))); |
263 | 277 | return null;
|
264 | 278 | }
|
265 | 279 |
|
266 | 280 | Duration backoff = exception.getRetryAfter().orElse(nonAdvertizedBackoff.get());
|
267 |
| - log.debug("Rescheduling call after backoff", SafeArg.of("backoffMillis", backoff.toMillis()), |
| 281 | + log.debug("Rescheduling call after receiving QosException.Throttle", |
| 282 | + SafeArg.of("backoffMillis", backoff.toMillis()), |
268 | 283 | exception);
|
269 |
| - scheduleExecution(() -> doClone().enqueue(callback), backoff); |
| 284 | + scheduleExecution( |
| 285 | + () -> doClone().enqueue(callback), |
| 286 | + backoff); |
270 | 287 | return null;
|
271 | 288 | }
|
272 | 289 |
|
273 | 290 | @Override
|
274 | 291 | public Void visit(QosException.RetryOther exception) {
|
275 | 292 | if (maxNumRelocations <= 0) {
|
276 |
| - callback.onFailure(call, new IOException("Exceeded the maximum number of allowed redirects for " |
277 |
| - + "initial URL: " + call.request().url())); |
278 |
| - } else { |
279 |
| - // Redirect to the URL specified by the exception. |
280 |
| - Optional<HttpUrl> redirectTo = urls.redirectTo(request().url(), |
281 |
| - exception.getRedirectTo().toString()); |
282 |
| - if (!redirectTo.isPresent()) { |
283 |
| - callback.onFailure(call, new IOException("Failed to determine valid redirect URL for '" |
284 |
| - + exception.getRedirectTo() + "' and base URLs " + urls.getBaseUrls())); |
285 |
| - } else { |
286 |
| - Request redirectedRequest = request().newBuilder() |
287 |
| - .url(redirectTo.get()) |
288 |
| - .build(); |
289 |
| - client.newCallWithMutableState(redirectedRequest, backoffStrategy, maxNumRelocations - 1) |
290 |
| - .enqueue(callback); |
291 |
| - } |
| 293 | + callback.onFailure(call, new SafeIoException( |
| 294 | + "Exceeded the maximum number of allowed redirects", |
| 295 | + exception, |
| 296 | + UnsafeArg.of("requestUrl", call.request().url().toString()))); |
| 297 | + return null; |
| 298 | + } |
| 299 | + |
| 300 | + // Redirect to the URL specified by the exception. |
| 301 | + Optional<HttpUrl> redirectTo = urls.redirectTo(request().url(), exception.getRedirectTo().toString()); |
| 302 | + if (!redirectTo.isPresent()) { |
| 303 | + callback.onFailure(call, new SafeIoException( |
| 304 | + "Failed to determine valid redirect URL after receiving QosException.RetryOther", |
| 305 | + exception, |
| 306 | + UnsafeArg.of("requestUrl", call.request().url().toString()), |
| 307 | + UnsafeArg.of("redirectToUrl", exception.getRedirectTo().toString()), |
| 308 | + UnsafeArg.of("baseUrls", urls.getBaseUrls()))); |
| 309 | + return null; |
292 | 310 | }
|
| 311 | + |
| 312 | + log.debug("Retrying call after receiving QosException.RetryOther", |
| 313 | + UnsafeArg.of("requestUrl", call.request().url()), |
| 314 | + UnsafeArg.of("redirectToUrl", redirectTo.get()), |
| 315 | + exception); |
| 316 | + Request redirectedRequest = request().newBuilder() |
| 317 | + .url(redirectTo.get()) |
| 318 | + .build(); |
| 319 | + client.newCallWithMutableState(redirectedRequest, backoffStrategy, maxNumRelocations - 1) |
| 320 | + .enqueue(callback); |
293 | 321 | return null;
|
294 | 322 | }
|
295 | 323 |
|
296 | 324 | @Override
|
297 | 325 | public Void visit(QosException.Unavailable exception) {
|
298 | 326 | Optional<Duration> backoff = backoffStrategy.nextBackoff();
|
299 | 327 | if (!backoff.isPresent()) {
|
300 |
| - log.debug("Max number of retries exceeded, failing call"); |
301 |
| - callback.onFailure(call, |
302 |
| - new IOException("Failed to complete the request due to a " |
303 |
| - + "server-side QoS condition: 503", exception)); |
304 |
| - } else { |
305 |
| - log.debug("Rescheduling call after backoff", |
306 |
| - SafeArg.of("backoffMillis", backoff.get().toMillis()), exception); |
307 |
| - // Redirect to the "next" URL, whichever that may be, after backing off. |
308 |
| - Optional<HttpUrl> redirectTo = urls.redirectToNext(request().url()); |
309 |
| - if (!redirectTo.isPresent()) { |
310 |
| - callback.onFailure(call, new IOException("Failed to determine valid redirect URL for base " |
311 |
| - + "URLs " + urls.getBaseUrls())); |
312 |
| - } else { |
313 |
| - Request redirectedRequest = request().newBuilder() |
314 |
| - .url(redirectTo.get()) |
315 |
| - .build(); |
316 |
| - scheduleExecution(() -> client.newCallWithMutableState(redirectedRequest, backoffStrategy, |
317 |
| - maxNumRelocations).enqueue(callback), backoff.get()); |
318 |
| - } |
| 328 | + callback.onFailure(call, new SafeIoException( |
| 329 | + "Failed to complete the request due to QosException.Unavailable", |
| 330 | + exception, |
| 331 | + UnsafeArg.of("requestUrl", call.request().url().toString()))); |
| 332 | + return null; |
| 333 | + } |
| 334 | + |
| 335 | + // Redirect to the "next" URL, whichever that may be, after backing off. |
| 336 | + Optional<HttpUrl> redirectTo = urls.redirectToNext(request().url()); |
| 337 | + if (!redirectTo.isPresent()) { |
| 338 | + callback.onFailure(call, new SafeIoException( |
| 339 | + "Failed to determine valid redirect URL after receiving QosException.Unavailable", |
| 340 | + UnsafeArg.of("requestUrl", call.request().url().toString()), |
| 341 | + UnsafeArg.of("baseUrls", urls.getBaseUrls()))); |
| 342 | + return null; |
319 | 343 | }
|
| 344 | + |
| 345 | + log.debug("Retrying call after receiving QosException.Unavailable", |
| 346 | + SafeArg.of("backoffMillis", backoff.get().toMillis()), |
| 347 | + UnsafeArg.of("redirectToUrl", redirectTo.get()), |
| 348 | + exception); |
| 349 | + Request redirectedRequest = request().newBuilder() |
| 350 | + .url(redirectTo.get()) |
| 351 | + .build(); |
| 352 | + scheduleExecution( |
| 353 | + () -> client.newCallWithMutableState(redirectedRequest, backoffStrategy, maxNumRelocations) |
| 354 | + .enqueue(callback), |
| 355 | + backoff.get()); |
320 | 356 | return null;
|
321 | 357 | }
|
322 | 358 | };
|
|
0 commit comments