Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retry tool #490

Open
gissuebot opened this issue Oct 31, 2014 · 22 comments
Open

Retry tool #490

gissuebot opened this issue Oct 31, 2014 · 22 comments

Comments

@gissuebot
Copy link

Original issue created by Nerther on 2010-12-03 at 02:00 AM


Hello

I implement a retry tool which can retry task(wrapped in Callable) easily, whether it's valuable to be added in guava library?

Only 2 classes: Retry, RetryBuilder.

Example:

Retryer retryer = new RetryerBuilder()
   .times(3)
   .interval(10, SECONDS)
   .when(timeout())
   .build();

Response response = retryer.callWithRetry(new Callable<Response>() {
   public Response call() throws Exception {
      return removeService.upload(request);
   }
});

response.doSomething();
Retry condition:

private static Predicate<Exception> timeout() {
   return new Predicate<Exception>() {
      public boolean apply(Exception exception) {
         if (exception instanceof TimeoutException) {
            return exception.getMessage().startsWith("Retryable");
         }
         return false;
      }
   };
}

Project url : http://code.google.com/p/google-guava-retryer/

All java sources & test has been attached.

@gissuebot
Copy link
Author

Original comment posted by Java2Enterprise on 2010-12-03 at 02:14 AM


retryer.retry is more concise than retryer.callWithRetry :)

@gissuebot
Copy link
Author

Original comment posted by Nerther on 2010-12-03 at 02:28 AM


I refer to the interface TimeLimiter of guava when design the signature, the usage and structure of which is similar to Retryer, the method is named callWithTimeout, it is simple and clear.

@gissuebot
Copy link
Author

Original comment posted by kevinb@google.com on 2011-01-12 at 10:37 PM


FYI, we are experimenting with one or two approaches internally at the moment.


Status: Accepted
Labels: Type-Enhancement

@gissuebot
Copy link
Author

Original comment posted by kevinb@google.com on 2011-07-13 at 06:18 PM


(No comment entered for this change.)


Status: Triaged

@gissuebot
Copy link
Author

Original comment posted by kevinb@google.com on 2011-07-16 at 08:32 PM


(No comment entered for this change.)


Status: Accepted

@gissuebot
Copy link
Author

Original comment posted by Nerther on 2011-08-18 at 09:09 AM


Update sources!

@gissuebot
Copy link
Author

Original comment posted by cgdecker on 2011-11-20 at 04:30 PM


Issue #797 has been merged into this issue.

@gissuebot
Copy link
Author

Original comment posted by fry@google.com on 2011-12-10 at 03:58 PM


(No comment entered for this change.)


Labels: Package-Concurrent

@gissuebot
Copy link
Author

Original comment posted by jnizet on 2011-12-25 at 08:42 AM


Sorry for the successive posts. My earlier design had serious issues, so I'm reposting it.

I had the same idea, and built a solution which is a bit more complex, but also more configurable. The differences are:
 - configurable stop strategy (number of times, delay, or custom)
 - configurable wait strategy (fixed delay, random delay, incrementing delay, or custom)
 - different exception handling

Usage example:

Retryer<Response> retryer =
    RetryerBuilder.<Response>newBuilder()
                  .withStopStrategy(StopStrategies.stopAfterAttempt(4))
                  .withWaitStrategy(WaitStrategies.fixedWait(1L, TimeUnit.SECONDS))
                  .retryIfRuntimeException()
                  .retryIfExceptionOfType(IOException.class)
                  .retryIfResult(Predicates.<Response>isNull())
                  .build();
try {
    return retryer.call(callable);
}
catch (ExecutionException e) {
    // the call threw a checked exception which didn't cause a retry
    // encapsulated in the execution exception
    Throwables.propagateIfPossible(e.getCause(), SomeCheckedException.class);
    throw new RuntimeException("unexpected", e.getCause());
}
catch (RetryException e) {
    // the retry was aborted because the call didn't succeed
    // it's also possible to get the last attempt result or exception
    // from the RetryException
    throw new RuntimeException("Call never succeeded", e);
}

@gissuebot
Copy link
Author

Original comment posted by wasserman.louis on 2011-12-25 at 12:42 PM


This is clearly a nontrivial problem. I am curious if this is the best way to solve it...

@gissuebot
Copy link
Author

Original comment posted by adrian.f.cole on 2012-01-04 at 10:09 PM


We are currently using this for years:
https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/predicates/RetryablePredicate.java
https://github.com/jclouds/jclouds/blob/master/core/src/test/java/org/jclouds/predicates/RetryablePredicateTest.java

It has limitations including no support for non-trivial shapes (ex. wait initially 10 seconds, then increase period over time), and sometimes you want to receive both the boolean result as well the last result tested. The latter functionality is sketched here:

https://github.com/jclouds/jclouds/blob/master/core/src/main/java/org/jclouds/predicates/Retryables.java
https://github.com/jclouds/jclouds/blob/master/core/src/test/java/org/jclouds/predicates/RetryablesTest.java

Clearly, we'd prefer something like this in guava as opposed to our codebase, so really excited about progress we can make here.

@gissuebot
Copy link
Author

Original comment posted by adrian.f.cole on 2012-01-04 at 10:11 PM


personally, I like the customizability of comment 11

@gissuebot
Copy link
Author

Original comment posted by raymond.rishty on 2012-01-18 at 09:09 PM


It might be nice if the "Retryer" were a RetryingExecutorService and the response a ListenableFuture

@gissuebot
Copy link
Author

Original comment posted by cpovirk@google.com on 2012-01-18 at 09:19 PM


FYI, internally, we have:

  • RetryingCallable (similar to the original suggestion and comment 11)
  • RetryingFuture (repeated calls to a Supplier<Future>)
  • Retry (bytecode magic to turn an object into a retrying proxy; unlikely to be open-sourced due to its dependencies)
  • RetryingExecutor (still under review; similar to comment 15)

There is clearly demand, but there are a lot of possible approaches and a lot of work left to do, so I recommend using one of the existing solutions in the meantime.

@gissuebot
Copy link
Author

Original comment posted by adrian.f.cole on 2012-05-15 at 06:33 AM


Here's an addition to add the list, resulting from jclouds proliferation of LoadingCache :)

   // deal with eventual consistency delay between bucket resources and their acls
   CacheLoader<String, AccessControlList> loader = RetryingCacheLoaderDecorator.newDecorator()
        .on(ResourceNotFoundException.class).exponentiallyBackoff()
        .decorate(
            new CacheLoader<String, AccessControlList>() {
               @Override
               public AccessControlList load(String bucketName) {
                  return client.getBucketACL(bucketName);
               }

               @Override
               public String toString() {
                  return "getBucketAcl()";
               }
            });

@gissuebot
Copy link
Author

gissuebot commented Oct 31, 2014

Original comment posted by em...@soldal.org on 2012-05-15 at 07:02 AM


+1 for comment 11 and 17's approach. I'm not sure I like the particulars of the builder pattern though, I'd like to save/serialize the builder so I could use it later.

.withStopStrategy() and .withWaitStrategy() could probably have variants directly in the builder, although having the ability to wire in your own strategies is very useful.

@gissuebot
Copy link
Author

Original comment posted by kevinb@google.com on 2012-05-30 at 07:43 PM


(No comment entered for this change.)


Labels: -Type-Enhancement, Type-Addition

@gissuebot
Copy link
Author

Original comment posted by schnitzi on 2013-02-04 at 05:15 AM


This would be useful for me too, but so as not to make this a "me, too" post, here are a few links to others who have implemented this sort of thing, which can be used reference for a Guava version (which is what I'd prefer):

https://github.com/rholder/guava-retrying - not Guava but built on top of Guava; hence the name

http://grepcode.com/file/repo1.maven.org/maven2/org.springframework.batch/spring-batch-infrastructure/1.0.0.FINAL/org/springframework/batch/retry/ - one from the Spring framework

@gissuebot
Copy link
Author

Original comment posted by toellrich on 2013-05-03 at 06:15 AM


There's also a .Net API called the transient fault handling application block which does the same thing:
http://msdn.microsoft.com/en-us/library/hh680905(v=pandp.50).aspx

@gissuebot
Copy link
Author

gissuebot commented Nov 1, 2014

Original comment posted by zolyfar...@yahoo.com on 2013-05-11 at 12:43 AM


I have implemented both approaches:

  1. simple: http://code.google.com/p/spf4j/source/browse/trunk/src/main/java/org/spf4j/base/Callables.java

this implementation allows you to retry a callable based on Exception or returned result, and execute a callable before retry.

public static <T> T executeWithRetry(final Callable<T> what, final Callable<Boolean> doBeforeRetry,
        final Predicate<? super T> retryOnReturnVal, final Predicate<Exception> retryOnException)
        throws InterruptedException 
  1. sophisticated:http://code.google.com/p/spf4j/source/browse/trunk/src/main/java/org/spf4j/concurrent/RetryExecutor.java

this is a executor based implementation that allows you to use your threads more efficiently.

@gissuebot
Copy link
Author

Original comment posted by loisel.jerome on 2013-06-05 at 07:14 AM


I have tried an implementation too, trying to keep it simple:

https://github.com/jloisel/retrying-callable

I like the approach in #11 but i felt the need to separate concerns between retry on exception and retry on returned result.

@akormushin
Copy link

Aren't you gonna merge this one? Waiting since 31 Oct 2014 and highly anticipated

@raghsriniv raghsriniv added the P3 label Jun 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants