Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • May 17 02:29

    jhalterman on mvn-modules

    (compare)

  • May 13 20:27

    jhalterman on master

    Do not skip staging repo close (compare)

  • May 13 20:14

    jhalterman on master

    [maven-release-plugin] prepare … (compare)

  • May 13 20:14

    jhalterman on failsafe-parent-3.2.4

    (compare)

  • May 13 20:14

    jhalterman on master

    [maven-release-plugin] prepare … (compare)

  • May 13 18:06

    jhalterman on master

    Update changelog for 3.2.4 (compare)

  • May 07 03:19

    jhalterman on master

    Fix javadoc typo (compare)

  • Apr 19 10:10
    bertbaron commented #341
  • Apr 19 09:18
    whiskeysierra commented #341
  • Apr 19 09:18
    whiskeysierra commented #341
  • Apr 19 07:55
    bertbaron edited #342
  • Apr 19 07:54
    bertbaron commented #341
  • Apr 19 07:44
    bertbaron opened #342
  • Apr 17 04:38
    jhalterman labeled #341
  • Apr 14 17:33
    bertbaron commented #341
  • Apr 14 17:20
    jhalterman commented #341
  • Apr 14 17:18
    jhalterman commented #341
  • Apr 14 17:18
    jhalterman commented #341
  • Apr 14 10:21
    bertbaron opened #341
  • Mar 29 03:08

    jhalterman on master

    Synchronize on execution when c… (compare)

Succy
@Succy
thanks a lot for your help, I almost kown how to use failsafe in my project. to be honest, failsafe's API designed is very level! I'll continue to learn Java8 development and English from it. @jhalterman
Succy
@Succy
I have a question, If a large amounts of concurrency at a certain moment, the forkjoin common pool parallelism are cpu core number, Will it occur OOM?
Jonathan Halterman
@jhalterman
@Succy It could, yea. That's true whether you're using Failsafe or not though.
Josh Deichmann
@jcdeichmann

Hello! Using Failsafe to help with failures during a database migration. It's been great so far. I am having an issue with listner.getFailure() being null in a fallback policy though. I'm curious if someone could help me out here. Here's my code:

Fallback<String> exceptionFallback = Fallback
      .of(
        () -> {
          System.out.println("Used fallback...");
          return "fallback result";
        }
      )
      .handle(Exception.class)
      .onFailure(listener -> System.out.println("error " + listener.getFailure()));

    String result = Failsafe
      .with(exceptionFallback)
      .get(
        () -> {
          throw new RuntimeException("Main exception");
        }
      );
    System.out.println("Result was: " + result);

The console output of this is:

Used fallback...
error null
Result was: fallback result

I'm very confused why listener.getFailure() is returning null. I'd expect it to be the throwable produced by the runtime exception that I threw. Is that a correct assumption? (using failsafe 2.3.1)

3 replies
Isabelle Phan
@iphan

Hello,
I am researching retry frameworks, and found Failsafe. It is a great lightweight library.
Would Failsafe be able to handle following use case?

  • Upon start up, my service will connect to a data store to retrieve data
  • If the connection fails, I would like to connect to a back-up data store and let the service proceed with back-up connection.
  • While the back-up connection is in use, I would like to keep retrying to connect to the original data store, and if the connection is successful, replace the back-up connection.

The policy composition handles the execution sequentially. Failsafe.with(fallback, retryPolicy) would retry first, then use the fallback. How to keep retrying while fallback is in use? Or is there any other approach for this use case?

Thanks in advance for your advice

Jonathan Halterman
@jhalterman
Sure. The unusual thing about this one is that you want to return one result, but then keep trying to return another result later on. We'll need a separate variable for this, to store whatever the most recent result is. Here's one example:
// Stores a reference to the connection to use
AtomicReference<Connection> connectionRef = new AtomicReference<>();

RetryPolicy<Void> retryPolicy = new RetryPolicy<Void>()
  .withDelay(Duration.ofSeconds(30))
  .handleResult(null);
Fallback<Void> fallback = Fallback.<Void>of(e -> {
  if (e.getAttemptCount() == 1)
    connectionRef.set(connectToBackup());
}).handleResult(null);

Failsafe.with(retryPolicy, fallback).runAsync(() -> {
  connectionRef.set(connectToPrimary());
});
With this we try to connect to primary and set the result in the external var. If that fails the fallback will try to set the backup connection in the external var on the first attempt only, but since it returns null (Void), the outer retry policy, which is configured to handleResult(null) will keep retrying every 30 seconds. Your other code can just grab whatever the current connection is from the connectionRef, which may be the backup or primary connection.
11 replies
Jonathan Halterman
@jhalterman
@iphan
Jonathan Halterman
@jhalterman

@/all

I just moved some things around on the website to hopefully improve the docs:

As ever, if you like Failsafe, please spread the word. Blogs, social media, etc. are all great.

Also if your company is using Failsafe and aren't on this list yet, please click the link at the top of the page to add them: https://failsafe.dev/whos-using/
ardel
@ardel

I just started using failsafe for retries and wonder what's the best approach in my case.
I need 5 threads retrieving webpages from the list in parallel.
I tried using

policy.with(new ForkJoinPool(5)).runAsync(
// retrieve the page
)

But if I have thousands of webpages in the list then in case of a failure the threads won't get their slot to retry. I believe the code above creates a thread per page and only five of them can run at the same time.

Jonathan Halterman
@jhalterman
@ardel Can you clarify what you mean by "the threads won't get their slot to retry"? If your threadpool only has 5 threads, indeed those are all that would be available for initial executions and retries.
5 replies
Tymur Berezhnoi
@t-jd

Hi there
I have the following prepared executor

var fallback = Fallback.ofException(event -> {
        log.error(event.getLastFailure().getMessage());
        return new CommissionsException(INTEGRATION_EXCEPTION, "Can't perform operation");
});

var retryPolicy = new RetryPolicy<>()
                .handle(InvalidTokenException.class, AuthenticationException.class)
                .onRetry(event -> tokenManager.refreshToken())
                .withMaxRetries(1);

retryExecutor = Failsafe.with(fallback, retryPolicy);

and tokenManager.refreshToken() can throw an exception as well, is there a way to handle such exception in the fallback?

Jonathan Halterman
@jhalterman
@t-jd Exceptions thrown from within event listeners are ignored: https://failsafe.dev/event-listeners/#alternative-execution-results so unfortunately the fallback won't receive these
Tymur Berezhnoi
@t-jd
@jhalterman Yep, I saw the doc, thanks anyway 🙏
Jonathan Halterman
@jhalterman
Sure no problem!
Succy
@Succy
@jhalterman Hi, I wrote the retry code in the for loop, it seems there is no problem, but the variable in the onSuccess method is always the value of the first call, the value in getAsync will follow the loop to change, may I ask in this case How should I adjust my code?
Succy
@Succy
for (File zip : zips) {
           failsafeExecutor.onSuccess(e -> {
// in here, the zip variable always the first value of zips. When retry successfully I want to update the status, but the "zip" value is not match 
               log.info("certNo:{}", CcsApiUtil.getCertNo(zip));
                        setSubmitResult(zip, 0);
               }).onFailure(e -> {
                        log.error("retry 3 times still failure!!");
                        setSubmitResult(zip, -2);
                    }).getAsync(context -> CcsApiUtil.uploadFile(zip));
                }
Georgi Donskoy
@gyudin
Hi! I'm a bit confused. Is Failsafe supposed to execute retries on handled exceptions?
Jonathan Halterman
@jhalterman

@gyudin Yes, by default a RetryPolicy will retry an execution up to two times if any exception is thrown. Else you can configure it to handle other types of failures, which will cause retries as configured:

https://failsafe.dev/policies/
https://failsafe.dev/retry/

Jonathan Halterman
@jhalterman
@Succy Sorry I missed this earlier. Which variable in onSuccess is always the value of the first call? Are you able to provide a small reproducer for what you're seeing?
Jonathan Halterman
@jhalterman

FYI - Failsafe 3.0 is about to be released. The changelog describes what is coming: https://github.com/failsafe-lib/failsafe/blob/master/CHANGELOG.md#30

Also the website / docs have some updates that will be published after the release: https://github.com/failsafe-lib/failsafe.dev/tree/3.0

Jonathan Halterman
@jhalterman

@/all Failsafe 3.0 has been released. The website has been updated and the changelog describes what's new:

https://failsafe.dev/
https://github.com/failsafe-lib/failsafe/blob/master/CHANGELOG.md#30

Jonathan Halterman
@jhalterman
A new section in the docs describes in a bit more detail how function composition works: https://failsafe.dev/policies/#executing-a-policy-composition
Jonathan Halterman
@jhalterman
Released 3.0.1, with a few minor improvements: https://github.com/failsafe-lib/failsafe/blob/master/CHANGELOG.md#301
Jonathan Halterman
@jhalterman
Jonathan Halterman
@jhalterman
Released 3.1, which includes a new RateLimiter policy: https://failsafe.dev/rate-limiter/
Jonathan Halterman
@jhalterman
Released 3.2, which includes a new Bulkhead policy: https://failsafe.dev/bulkhead/
Jonathan Halterman
@jhalterman
Released 3.2.1, with some improvements to RateLimiter https://github.com/failsafe-lib/failsafe/blob/master/CHANGELOG.md#321
Jonathan Halterman
@jhalterman

Released 3.2.2, with new OkHttp and Retrofit modules!

https://failsafe.dev/okhttp/
https://failsafe.dev/retrofit/

Jay Patel
@jaympatel1893_twitter
Hello, I have implemented a fallback but it goes into onSuccess of fallback even if fallback was not executed.
If I understand the onSuccess if written at policy level should only be executed if that policy is executed..

@jhalterman ^

Thanks for an amazing library btw!

Jonathan Halterman
@jhalterman

@jaympatel1893_twitter Hey you're welcome! Yea, onSuccess is called in two cases:

  • if the incoming result or exception is not considered a failure by the policy
  • if the incoming result or exception is considered a failure by the policy, but the policy handles it successfully

This is mentioned in the docs:
https://failsafe.dev/javadoc/core/dev/failsafe/PolicyListeners.html#onSuccess-dev.failsafe.event.EventListener-
https://failsafe.dev/event-listeners/#determining-success

So in the case of a fallback, that means onSuccess would be called if the fallback doesn't consider the incoming result/exception a failure. Do you think it should work differently or be clarified better in the docs?

Magnus Lundström
@OxygeneIV

Hi!

I've used the (timeout,retrypolicy)-order with the 2.x-version and the wrapping timeout
has successfully terminated the retrying unless success within the timeout.

With 3.x I can't succeed at all with a wrapping timeout (using the builder for both Timeout and retryPolicy).
The retries keep on going forever not taking into account the Timeout. I've tried with a simple method throwing an Exception.
Any hints here...

2.x-code (terminating after 6 secs):

        RetryPolicy<Object> objectRetryPolicy = new RetryPolicy<>()
                .withDelay(Duration.ofSeconds(1))
                .withMaxRetries(-1);

        Timeout<Object> timeout = Timeout.of(Duration.ofSeconds(6));
        Failsafe.with(timeout,objectRetryPolicy).get(() -> getMe());

3.x code (tried to remove "withInterrupt" w/o success)

       RetryPolicy<Object> retryPolicy = RetryPolicy.builder()
               .withDelay(Duration.ofSeconds(1))
               .withMaxRetries(-1)
               .build();

         Timeout<Object> timeout = Timeout.builder(Duration.ofSeconds(6)).withInterrupt().build();
         Failsafe.with(timeout,retryPolicy).get(() -> getMe());
Jonathan Halterman
@jhalterman
@OxygeneIV At first glance this certainly looks like a regression. Will follow up...
Magnus Lundström
@OxygeneIV
Ok, thanks.
However, I note that adding ".withMaxDuration" with the RetryPolicy seems to work as stopper for regular Exceptions.
The timeout seems to work for InterruptedException when used as "leftmost policy" (as in the examples above).
Perhaps I'll switch the policy-order to Failsafe.with(retryPolicy,timeout) and use ".withMaxDuration". Just did some small midnight tests, looks promising.
Jonathan Halterman
@jhalterman
Sounds good. One other thing worth mentioning, FWIW, is this problem only affects sync executions. Async (getAsync) are not affected. I hope to have a fix very soon and will cut a release immediately after.
Nathan Reynolds
@numeralnathan
Bitbucket Server has rate limiting. https://confluence.atlassian.com/bitbucketserver/improving-instance-stability-with-rate-limiting-976171954.html. The rate limit starts with a bucket of say 50 requests and the bucket refills at 4 requests per second. I setup a smooth rate limiter with 4 requests per second. Sadly, this means I can never take advantage of the 50 requests. Besides creating a new class that implements Policy, is there a way to setup the existing RateLimiter to take advantage of the bucket of 50 requests?
Nathan Reynolds
@numeralnathan
I really like the design of the classes. This makes it super simple to extend.
Jonathan Halterman
@jhalterman
@numeralnathan Thanks. Failsafe's smooth rate limiter uses a leaky bucket approach which is different from BitBucket's token bucket approach. We'd need to enhance Failsafe's RateLimiter to support something like that. If you're interested, please file an issue for that along with how you think it should work. But in the meantime, yea, you can hopefully extend RateLimiter to work however you need.
Nathan Reynolds
@numeralnathan
@jhalterman Thank you for letting me know that Failsafe can't do this. I created this ticket. failsafe-lib/failsafe#334
Jonathan Halterman
@jhalterman
I saw it - thanks!
Alexandre Gattiker
@algattik
My application needs to send many HTTP requests to distinct servers from a database. Is it possible to use failsafe in this scenario? Perhaps saving state, per request, to the database.
Jonathan Halterman
@jhalterman
@algattik Saving state to a database is outside the scope of what Failsafe does. Failsafe will help you handle failures that might result from the requests, but making the requests and saving state is separate.
Alexandre Gattiker
@algattik
@jhalterman yes got that. Is there a way to extract the state (backoff and circuit breaker) and reinject it?
Jonathan Halterman
@jhalterman
@algattik Not at the moment, no. For RetryPolicy, the state would mostly involve how many failed attempts there have been. For CircuitBreaker, it would include a record of particular failures. If you want, we can consider enhancing Failsafe to better support your use case if you can describe more of what you want to do and why. Opening a Github issue to discuss it would probably be best.
Jonathan Halterman
@jhalterman
Failsafe 3.2.3 has been released, which includes a bug fix relating to Timeout/RetryPolicy combinations.
@OxygeneIV check out 3.2.3 - you should be good now. Thanks for reporting this!
Magnus Lundström
@OxygeneIV
@jhalterman Great, thanks! I'll look into it this afternoon.