I'm struggling with understanding #Retryable. What I need is to retry 3 times when I get 5xx Exception and if retry also fails then throw a custom exception in the recovery method. And if some other exception is thrown then catch it and throw a custom exception.
#Retryable(value = HttpServerErrorException.class, maxAttempts = 3, backoff = #Backoff(delay = 3000))
public String callToService(String key) {
String response;
try {
response = //assume a service call here
}catch (Exception ex) {
throw new customException("some message");
}
return response;
}
#Recover
public void retryFailed(HttpServerErrorException httpServerErrorException) {
throw new customException("some message");
}
In your case as you have added:
#Retryable(value = HttpServerErrorException.class, maxAttempts = 3, backoff = #Backoff(delay = 3000))
The #Retryable is used with:
value = HttpServerErrorException.class, so your method will be retried only if HttpServerErrorException is occure/thrown from your method code, and Note: if any other exception thrown retry will not be done, and recover method will also not be invoked, as recover method is only invoked with exception mentioned in value in #Retryable.
maxAttempts = 3, so it will retry executing your method 3 times by maximum
backoff = #Backoff(delay = 3000), so it will keep a delay of 3000ms in between retry.
And after retrying 3 times, if your method still not working, your method with #Recover will be envoked with the HttpServerErrorException
I hope it make sense and help yo understand the concept of #Retryable
Now to implement what you want you need to implement it as below:
#Retryable(value = HttpServerErrorException.class, maxAttempts = 3, backoff = #Backoff(delay = 3000))
public String callToService(String key) {
String response;
try {
response = //assume a service call here
} catch (HttpServerErrorException httpServerErrorException) {
throw httpServerErrorException;
} catch (Exception ex) {
throw new CustomException("some message");
}
return response;
}
#Recover
public void retryFailed(HttpServerErrorException httpServerErrorException) {
//do whatever you want here, when HttpServerErrorException occured more than 3 times
}
Related
So I'm trying to retry for specific exceptions and created a bean which has shouldRetry(Throwable t) function. The function returns true if exception has to be retried, otherwise false.
But What I'm observing is shouldRetry(Throwable t) is executing twice(log is printing twice) for one retry attempt, however serviceImpl from where exception is being thrown is executing only once for one retry attempt.
Could someone please let me know if I'm doing something wrong here, or is it the default behavior/bug with spring retry itself.
#Component("dbRecoverableExceptionHandler")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
#Slf4j
public class DBRecoverableExceptionHandler {
private final Environment environment;
private final MultiTaggedCounter exceptionRetryCounter;
public Boolean isRetryable(Throwable t) {
String[] recoverableExceptionClasses = environment
.getRequiredProperty("db-recoverable-exception-classes", String[].class);
for (String s1 : recoverableExceptionClasses) {
if (t.getClass().getSimpleName().contains(s1)) {
exceptionRetryCounter.increment(1, s1);
log.warn("Retrying for exception " + t.toString());
return true;
}
}
return false;
}
}
#Retryable(exceptionExpression = "#{#dbRecoverableExceptionHandler.isRetryable(#root)}",
maxAttemptsExpression = "#{${max-attempts}}",
backoff = #Backoff(delayExpression = "#{${retry-backoff-delay-time}}",
multiplierExpression = "#{${retry-backoff-multiplier}}"))
It is as expected.
The method will be called by the RetryTemplate twice for each execution...
while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Retry: count=" + context.getRetryCount());
}
// Reset the last exception, so if we are successful
// the close interceptors will not think we failed...
lastException = null;
return retryCallback.doWithRetry(context);
}
catch (Throwable e) {
lastException = e;
try {
registerThrowable(retryPolicy, state, context, e);
}
catch (Exception ex) {
throw new TerminatedRetryException("Could not register throwable",
ex);
}
finally {
doOnErrorInterceptors(retryCallback, context, e);
}
if (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
...
The first call to canRetry() (in the while loop) is skipped on the very first call since there is no exception yet, on subsequent iterations, when the method throws an exception, it is called twice.
try {
reponseType = retryTemplate.execute((RetryCallback<X, RetryException>) context -> {
try {
log.error("Calling api attempt #" + context.getRetryCount());
HttpEntity<x> xResponse = httpRestTemplate.exchange(requestUrl, HttpMethod.POST, entity, x.class);
return xResponse.getBody();
} catch (HttpStatusCodeException e) {
if (e.getStatusCode().is5xxServerError()) {
throw new RetryException("api returned Server Error", e);
}
return null;
}
});
} catch (RetryException e) {
throw e;
Defined retryTemplate policy in configuration file and httpRestTemplate is normal template
#Bean
RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();
FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(30000);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
retryTemplate.setRetryPolicy(new CustomRetryPolicy(3));
return retryTemplate;
}
But not able to call retryTemplate again after time given.
Can anyone help me, i think me getting confused in exception game.
This will only retry if e.getStatusCode().is5xxServerError(). Otherwise you are returning null, which is "success" from the retry template's perspective. The template will only retry when an exception is thrown.
You can classify which exceptions are retryable in the retry policy.
Does RetryOperations required when I use retry-spring ?
or is it enough to have the annotation over my method?
#Retryable(value = {SQLException.class, Exception.class,RuntimeException.class, RetryException.class, ExhaustedRetryException.class}, //retry will be attempted only if the method throws an SQLException.
maxAttempts = 3, backoff = #Backoff (5000))
public boolean sendJmsMessage(final String xml) {
jmsTemplate.send.........()
// logic to send jms
}
#Recover
public void ExhaustedRetryException(ExhaustedRetryException re) {
LOG.error("Failed to deliver the message using the retry");
}
Hi I m trying to use httpcomponents5 beta to make persistent connection, I have tried the example given in their site, the code is as follows,
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom().setSoTimeout(Timeout.ofSeconds(45)).setSelectInterval(10000).setSoReuseAddress(true).setSoKeepAlive(true).build();
final SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new TrustAllStrategy()).build();
final PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create().setConnectionTimeToLive(TimeValue.of(1, TimeUnit.DAYS)).setTlsStrategy(new H2TlsStrategy(sslContext, NoopHostnameVerifier.INSTANCE)).build();
client = HttpAsyncClients.createMinimal(protocol, H2Config.DEFAULT, null, ioReactorConfig, connectionManager);
client.start();
final org.apache.hc.core5.http.HttpHost target = new org.apache.hc.core5.http.HttpHost("localhost", 8000, "https");
Future<AsyncClientEndpoint> leaseFuture = client.lease(target, null);
AsyncClientEndpoint asyncClientEndpoint = leaseFuture.get(60, TimeUnit.SECONDS);
final CountDownLatch latch = new CountDownLatch(1);
final AsyncRequestProducer requestProducer = AsyncRequestBuilder.post(target.getSchemeName()+"://"+target.getHostName()+":"+target.getPort()+locationposturl).addParameter(new BasicNameValuePair("info", requestData)).setEntity(new StringAsyncEntityProducer("json post data will go here", ContentType.APPLICATION_JSON)).setHeader("Pragma", "no-cache").setHeader("from", "http5").setHeader("Custom", customheaderName).setHeader("Secure", secureHeader).build();
locEndPoint.execute(requestProducer, SimpleResponseConsumer.create(), new FutureCallback<SimpleHttpResponse>() {
#Override
public void completed(final SimpleHttpResponse response) {
if (response != null) {
if (response.getCode() > -1) {
try {
System.out.println("http5:: COMPLETED : RESPONSE "+response.getBodyText());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
latch.countDown();
}
#Override
public void failed(final Exception ex) {
System.out.println("http5:: FAILED : "+target+locationposturl);
LoggerUtil.printStackTrace(ex);
System.out.println("http5::Exception Request failed "+LoggerUtil.getStackTrace(ex));
latch.countDown();
}
#Override
public void cancelled() {
System.out.println("http5:: CANCELLED : "+target+locationposturl);
System.out.println(http5::Exception Request cancelled");
latch.countDown();
}
});
latch.await();
This code works without a problem for the first time,but when I send a subsequent requests it throws an exception as follows,
http5:: Exception occured java.lang.IllegalStateException: Endpoint is
not connected at
org.apache.hc.core5.util.Asserts.check(Asserts.java:38) at
org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager$InternalConnectionEndpoint.getValidatedPoolEntry(PoolingAsyncClientConnectionManager.java:497)
at
org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager$InternalConnectionEndpoint.execute(PoolingAsyncClientConnectionManager.java:552)
at
org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient$InternalAsyncClientEndpoint.execute(MinimalHttpAsyncClient.java:405)
at
org.apache.hc.core5.http.nio.AsyncClientEndpoint.execute(AsyncClientEndpoint.java:81)
at
org.apache.hc.core5.http.nio.AsyncClientEndpoint.execute(AsyncClientEndpoint.java:114)
What may be the problem with endpoint, I m forcing endpoint to keep alive for a day, kindly shed some light on this
If an Exception is thrown while persisting an entity X, in the catch block can a call be made to a different method to persist some entity Y?
#Transactional(propagation = Propagation.REQUIRED)
private X addNewX(X transientX) {
X x = null;
try {
x = xDao.makePersistent(transientX); // A DB constraint will be violated and Hibernate throws PersistenceException
} catch (RuntimeException e) {
createErrorRecord(transientX, e.getMessage());
}
return x;
}
Save extra information about the error:
#Transactional(propagation = Propagation.REQUIRED)
private void createErrorRecord(X x, String errMsg) {
try {
ImportError error = new ImportError(x.getBlah(), x.moreBlah(),
errMsg);
impErrDao.makePersistent(error);
} catch (RuntimeException re) {
logger.error(re.toString());
}
}
However, ImportError never gets persisted. I tried noRollbackFor for PersistenceException but to no avail.
Is there something that can be done here?
Thanks
Using AOP for logging such exceptions seems to be the right option.