-
Notifications
You must be signed in to change notification settings - Fork 7.6k
RxJava retryWhen bizarre behavior #4207
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
Comments
No idea, what does the log contain? This looks suspicious to me, maybe it leads to IndexOutOfBoundsException a line below. Otherwise, try single-stepping on the callable.
You count down the latch before the value is returned and thus the main thread may not see the call to |
@akarnokd
If I set the retry count to 1 (default is 0), the code works fine. It's as if the code insists on retrying instead of falling back to it. The official doc only shows a trivial example that doesn't help at all. |
Yes, due to the inner wireing, if the inner retry sequence completes at any time, that may complete the outer sequence no matter where it is at the moment. |
But don't you think that I shouldn't have to retry if the first attempt succeeds? That's the most basic use case. |
The operator requires that your It can get quite convoluted (especially if time is involved) to have those terminal events of the inner source.retryWhen(o -> {
int[] count = { 0 };
return o.concatMap(e -> {
if (++count[0] <= numRetries) {
return Observable.just(1);
}
return Observable.error(e);
});
}); Note that the body of the function can be stateful as it is invoked for each downstream subscriber to |
@akarnokd David, your answer is quite involved so allow me to ask some follow up questions:
What operator? Are you referring to the
Do you mean logic in
Why not simply
But I want Lastly, if what you said above is right, the official document is just wrong. It uses a |
The operator
You need it to be mutable inside the
Then return that.
Unfortunately, we have a few operators contributed and forgotten; often with problems. One of them is |
I'm assuming you didn't try to compile this because it doesn't. |
I helped the compiler out by creating a
All my test cases work now. Thanks for your help @akarnokd David. If you choose to improve the docs by adding non-trivial use cases, you have my permission to reference to my GitHub code. |
If that doesn't work in your IDE you can provide hints to the compiler for the error case to coerce the type system into agreement. return Observable.<Integer>error(e); The purpose of the operator is to give users a reactive feedback loop by which they could transform exceptions into a signal that the source observable should be resubscribed to. You should not drop/filter any signals. Your initial example is very long. When you post a question could you try to reduce the example down to the Minimal, Complete, Verifiable example? I have implementation of a retryWhen notification handler that look very similar to yours. There shouldn't be a problem there. I suspect that in reducing the problem to the MCV example you'll find what was causing your problem. Also you could take a look at this blog post RxJava's repeatWhen and retryWhen, explained.
Cute @akarnokd. @abersnaze and I wrote the I have made successful (read non-buggy) implementations which rely heavily on |
I don't agree. My example is not long, it has just as much code as required for the use cases I'm interested in. The overzealousness for "Minimal, Complete, Verifiable" produces the kind of cute but nonsensical hello world type example that's in the official doc or in the blog you referred to, where an error is thrown every time the Also, if you read my original question again, you'll find that the blog post you referred to is already mentioned, and as I said, doesn't cover the various use cases I stated. At the end of the day, the discussion here with David lead me to find a solution. All the power to you if you implemented complex solution at Netflix using |
The someOffBoxHystrixObservable
.timeout(TIMEOUT_THRESHOLD_MS, TimeUnit.MILLISECONDS)
.retryWhen(o -> {
Observable<Integer> range = Observable.range(1, RETRY_MAX_ATTEMPTS);
Observable<Observable<Long>> zipWith = o.zipWith(range, (e, i) ->
i < RETRY_MAX_ATTEMPTS ?
Observable.timer(RETRY_DELAY_MS, TimeUnit.MILLISECONDS) :
Observable.error(e));
return Observable.merge(zipWith);
}) The goal of RxJava is to provide a composable framework for async development. Since each operator should be composable and its external behavior can be verified the MCV process should lend very well to these kinds of bugs. You can use the |
Oh, one more thing to mention that might be helpful. I use the return someOffBoxCall()
.timeout(threshold, MILLISECONDS)
.retryWhen(this::backoffHandler)
.cacheWithInitialCapacity(1); will return an observable that's resilient to resubscriptions and returns a copy of the same data (for any call that is idempotent in the lifetime of that observable's usage). |
@stealthcode
|
I'm playing with the RxJava retryWhen operator. Very little is found about it on the internet, the only one worthy of any mention being this. That too falls short of exploring the various use cases that I'd like to understand. I also threw in asynchronous execution and retry with back-off to make it more realistic.
My setup is simple: I've a class
ChuckNorrisJokesRepository
that returns random number of Chuck Norris jokes from a JSON file. My class under test isChuckNorrisJokesService
which is shown below. The use cases I'm interested in are as follows:Note: The project is available on my GitHub.
ChuckNorrisJokesService.java:
For brevity, I'll only show the Spock test case for the 1st use case. See my GitHub for the other test cases.
This fails with:
What I'm expecting is that
fromCallable
is called once, it succeeds,onNext
is called once, followed byonCompleted
. What am I missing?P.S.: Full disclosure - I've also posted this question on StackOverflow but my hopes are not high of getting an answer there.
The text was updated successfully, but these errors were encountered: