Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • 04:23

    jhalterman on 2.5.0

    Re-work Failsafe execution inte… (compare)

  • 04:20

    jhalterman on exec-with-logging

    WIP (compare)

  • 04:20

    jhalterman on 2.5.0

    Add record methods to Execution… Add type param/args to a few ad… Re-work Failsafe execution inte… (compare)

  • Sep 18 02:47

    jhalterman on master

    Update changelog for 2.4.4 (compare)

  • Sep 18 02:42
    jhalterman commented #296
  • Sep 18 02:32

    jhalterman on master

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

  • Sep 18 02:32

    jhalterman on failsafe-2.4.4

    (compare)

  • Sep 18 02:32

    jhalterman on master

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

  • Sep 18 02:30

    jhalterman on master

    Fix Fallback.onFailedAttempt F… (compare)

  • Sep 18 02:27
    jhalterman closed #298
  • Sep 18 02:27

    jhalterman on master

    Fix Fallback.onFailedAttempt F… (compare)

  • Sep 18 01:36
    jhalterman opened #298
  • Sep 17 18:08
    jhalterman labeled #297
  • Sep 17 18:08
    jhalterman opened #297
  • Sep 13 04:44

    jhalterman on exec-with-logging

    WIP (compare)

  • Sep 13 04:34

    jhalterman on new-exec-apis

    (compare)

  • Sep 13 04:33

    jhalterman on exec-with-logging

    Add record methods to Execution… Add type param/args to a few ad… wip2 wip (compare)

  • Sep 12 23:20

    jhalterman on new-exec-apis

    wip2 (compare)

  • Sep 12 23:18

    jhalterman on new-exec-apis

    wip2 (compare)

  • Sep 10 02:05
    jhalterman commented #294
Thomas Santana
@nebulorum
I have an example of this not working. Should I create a issue on the project issue tracker?
Jonathan Halterman
@jhalterman
Yes please, if you have a small reproducer you can include that would be great
Jens Bannmann
@bannmann

I use Failsafe to add timeout (2 seconds) and retry (2 retries) behavior for requests made with Java 11 HTTPClient. the application makes several thousands of requests (mostly sequentially) to several servers. mostly, things work fine, but sometimes after a few minutes, the future returned by Failsafe exceeds the additional timeout of 20 seconds I passed to Future.get(long, TimeUnit). I tried changing the order of the retry/timeout policies and even ran with only the timeout policy, but the problem still appears.

I suspected an interaction of HTTPClient and Failsafe as both default to using the common fork/join pools, but giving both their own executors did not help.

I'm not sure whether I can create a minimal reproducer.

@jhalterman, do you have any other suggestions how to locate the cause?

Jonathan Halterman
@jhalterman
@bannmann do you have reason to believe the execution is actually being completed but Failsafe is not completing the future for some reason? Or do you think the execution is actually timing out or stalling?
Thomas Santana
@nebulorum
@jhalterman I created the following issue for my case: jhalterman/failsafe#268
We can discuss there if you think it's better. I added several variations of the failures. We can discuss on the issue.
William Johnson S. Okano
@williamokano

Hey team, this might not be the right place to talk about it, but I'm trying to upgrade from failsafe 1.1.0 to 2.4.0 and I noticed that some API slightly changed. On 1.1.0 I used to use RetryPolicy with retryOn with given conditions. Internally it would add the condition to the retryConditions array and check for isRetryable on the executor and execute once more.

On 2.4.0, I tried using handleIf, as the changelog says, but it returns as complete and don't retry my condition. I wrote a test I can paste here in case anyone is willing to help me with that, but basically I'm trying to make an http call with retrofit, that returns the call in which I can test the HTTP status code, and ask for retry (after some delay) when it's 5xx.

Can someone help me with that?

Also, I don't understand what the RetryPolicy#withMaxDuration does. I thought it was some kind of timeout, but when I simulate a first call (that I want to be retried) "sleeping" for more the define on the this threshold, it doesn't timeout, but rather finishes after the supplier delay.
Jonathan Halterman
@jhalterman
Hey @williamokano - if you have a failing test for the retryIf situation you mentioned, feel free to open an issue and include it there. Probably better than pasting here.
withMaxDuration is a passive timeout, so to speak, and will stop retrying after the maxDuration across all attempts has been exceeded. It will not though interrupt any execution attempt that's in progress. For that you should use the TImeout policy withCancel(true)
William Johnson S. Okano
@williamokano

Hey @jhalterman thanks for the explanation. There's no error on the lib then, just my dumbness not knowing how it works. I was setting maxDuration with 2 seconds and making the first call 4 seconds long. So even though I put maxAttempts(2), it would never call the second and return the response from the first request.

Thanks for clarification and sorry for asking just dumb question 😂

the-mod
@the-mod
Hi, I am currently facing Problems regarding the Event Listeners of a Circuit Breaker. It seams the passed CheckedRunnable isn't completely executed. I am using a Failsafe Executor with an own ScheduledExecutorService. I know the Documentation stated Exceptions within this Runnables are ignored. But any change to debug it?
Jonathan Halterman
@jhalterman
@the-mod At the moment there is not. If we did allow some sort of exception handling for event listeners, how might it work? If you have any good suggestions, feel free to open an issue.
Alex Popescu
@al3xandru
Where should I look to understand why the following code ends up in 12 retries (rather than at most 3):
RetryPolicy<Object> policy= new RetryPolicy<>()
                .withMaxRetries(3)
                .withBackoff(1, 30, ChronoUnit.SECONDS, 2)
                .withMaxDuration(Duration.ofSeconds(30));
I take that back... I had another "native" retry inside the Failsafe retry
Alex Popescu
@al3xandru
Are handle and handleIf additive or "or-ed"? (as in handle(Exception).handleI(Predicate) => if (exception && Predicate) vs if (exception || Predicate) --- yes, my pseudo is really bad)
Alex Popescu
@al3xandru
Quick test shows that they are ORed (if the exception is of the type or the condition is met than handle the failure)
Alex Popescu
@al3xandru
And the javadoc confirms that: "If multiple handle or handleResult conditions are specified, any matching condition can allow a retry"
Jens Bannmann
@bannmann

:point_up: 27. Februar 2021 20:40

@jhalterman, thank you for your pointers!

I finally got around to do some more detailed testing and I think I can answer your question now: the original execution seems to be stalling. Does the methodology outlined below look valid to you? If yes, what do you suggest trying next?

Setup:

  • changed the supplier passed into getStageAsync() so that it wraps the original CompletableFuture by adding a whenComplete() handler which logs the completion
  • added logging by registering a global onComplete executor listener and all listeners offered by the timeout and retry policies

Results:
Logging happens exactly as intended, but only for a few minutes. Then the problem occurs and none of my listeners seem to be called: the only messages I see are the one I log immediately before calling Failsafe.with() followed by the one from my class calling future.get(long, TimeUnit) on the future returned by Failsafe. The timestamp delta of these log messages equals the timeout passed to that future.get call.

Alex Popescu
@al3xandru
Can someone help me understand the generic type of RetryPolicy? When is it something else than Object?
Jens Bannmann
@bannmann
@al3xandru if the code that you run via Failsafe (e.g. the this::connect in this example) returns e.g. a HttpResponse<String> instead of Object, all policies will have that type parameter as well. This way, they can act on the result (e.g. inspecting the http response and triggering a retry if status code is 500).
Jonathan Halterman
@jhalterman
@al3xandru Here's an example https://jodah.net/failsafe/strong-typing/
Alex Popescu
@al3xandru
Thanks @bannmann & @jhalterman . I was doing something wrong on my side (forgot the type on the constructor side and wondered what's complaining about).
aswinunni01
@aswinunni01
Hi, Im new to failsafe, and i was trying to retry a task in background without blocking the rest of my program.. does fail safe have any method thay does this? Thanks in advance
aswinunni01
@aswinunni01
Jfyi I was able to get the required functionality using runAsync() . Thanks
Divyanshi Mangal
@divyanshidm

Hi..

I want to throw an exception from a FailSafe method (Jodah)

return Failsafe.with(RetryPolicyProvider.HttpRetryPolicy.onRetry(e -> LOGGER.info("Retrying ", e.getAttemptCount(), tableName, messageId)))
.with(executor)
.getAsync(() -> this.httpClient.postRequestWithGZipHeader(url, batchRequestPayload))
.thenApply(response -> {
try {
// Do Something}
}
catch (Exception e) {
throw new Exception("Exc thrown!")}
})
..
But it seems that it is not possible to throw an exception like this from the Failsafe Jodah method. How to do so?

Jonathan Halterman
@jhalterman
@divyanshidm getAsync returns a CompletableFuture, which only throws an exception if you call get() on that future. If you call get, it would throw an ExecutionException that wraps your Exception.
Jonathan Halterman
@jhalterman
2.4.1 has been released, which improves support for using Timeouts with RetryPolicies: https://jodah.net/failsafe/timeout/#timeouts-with-retries
Jonathan Halterman
@jhalterman
2.4.2 has been released, with more improvements to Timeouts and a few other misc. improvements: https://github.com/failsafe-lib/failsafe/blob/master/CHANGELOG.md
Jonathan Halterman
@jhalterman
The Failsafe website has a spiffy new domain: https://failsafe.dev
Ian Agius
@ianagius

Hi, Im new to the channel and new to FailSafe too. Im trying to use FailSafe to retry for a number of times in case of failure. In the end, if it continues to fail it does actionA() otherwise if it fails for a particular reason (MyException) it does actionB(). Ideally, if this particular reason (MyException) is encountered, it doesnt even retry and just does actionB(). The retrying part works fine the problem is that I cant seem able to say: if MyException than do this.

val retryPolicy: RetryPolicy[Boolean] = new RetryPolicy[Boolean]
                  .withMaxAttempts(5)
                  .withBackoff(1, 10, ChronoUnit.SECONDS)
                  //.handle(classOf[MyException]) // I noticed that with/without this doesnt make a difference
                  .handleResultIf(result => {
                    if (!result) {
                      logger.info("Waiting for a bit and retrying");
                      true
                    } else false
                  })

                Failsafe.`with`(List(retryPolicy).asJava)
                  .onSuccess((e:Boolean) => logger.info("successful"))
                  .onFailure((e:Boolean) => actionA())
                  .get(() => functionThatThrowsAnExceptionButShouldReturnBoolean())

Is this the right approach for my problem? Do you have any suggestions/constructs which I should use to cater for the scenario mentioned above please?
Why does handle(MyException) do? Ive noticed that with/without it it still repeats

Ian Agius
@ianagius
Ive also tried creating different policies but it seems that you can combine 2 different types. The following is the last thing I tried but it did not compile:
val retryMyExceptionPolicy: RetryPolicy[MyException] = new RetryPolicy[MyException]
                  .withMaxAttempts(1)
                  .handle(classOf[MyException])
                  .handleResultIf(result => {
                    if (result.getMessage == "special check"){
                      false
                    }
                    else {
                      true
                    }
                  })

                val retryPolicy: RetryPolicy[Boolean] = new RetryPolicy[Boolean]
                  .withMaxAttempts(5)
                  .withBackoff(1, 10, ChronoUnit.SECONDS)
                  .handleResultIf(result => {
                    if (!result) {
                      logger.info("retrying");
                      true
                    } else false
                  })

                Failsafe.`with`(List(retryPolicy, retryMyExceptionPolicy).asJava)
                  .onSuccess((e:Boolean) => logger.info("successful"))
                  .onFailure((e:MyException) => throw e)
                  .get(() => functionThatShouldReturnBooleanButCanReturnException())
Jonathan Halterman
@jhalterman

@IanAgius

Why does handle(MyException) do

In the case of RetryPolicy it means retry if MyException is thrown. If you specify multiple "handle" statements, then they are logically OR'ed.

Jonathan Halterman
@jhalterman

@ianagius It sounds like you may want to use a Fallback in addition to the RetryPolicy, where the Fallback is composed around the RetryPolicy. A Fallback is meant to provide some alternative result. Ex:

RetryPolicy<Boolean> retryPolicy = new RetryPolicy<Boolean>()
  .withMaxAttempts(5)
  .withBackoff(1, 10, ChronoUnit.SECONDS)
  .handleResult(false);

Fallback<Boolean> fallback = Fallback.of(e -> {
  if (e.getLastFailure() instanceof MyException)
    actionB();
  else
    actionA();
});

Failsafe.with(fallback, retryPolicy).get(() -> functionThatShouldReturnBooleanButCanReturnException);

https://failsafe.dev/fallback/

Here the Fallback is composed around the RetryPolicy. If a failure occurs, the RetryPolicy is used first. If retries are exceeded, then the Fallback is used. https://failsafe.dev/policies/#policy-composition
Ian Agius
@ianagius
thanks a lot for your help @jhalterman
Succy
@Succy
hello everyone
is anyone here?
Jonathan Halterman
@jhalterman
@Succy Yes, though usually responses come asynchronously
Succy
@Succy
@jhalterman thanks for you reply! I had seen your commented from github
My English is not very good :(
Succy
@Succy
We have time difference. You should have been sleeping when I was working
Jonathan Halterman
@jhalterman
Yea I was sleeping, but happy to respond when I can.
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.
1 reply
Jonathan Halterman
@jhalterman
@iphan