10

I am new to ReactiveX and reactive programming in general. I need to implement a retry mechanism for Couchbase CAS operations, but the example on the Couchbase website shows a retryWhen which seems to retry indefinitely. I need to have a retry limit and retry count somewhere in there.

The simple retry() would work, since it accepts a retryLimit, but I don't want it to retry on every exception, only on CASMismatchException.

Any ideas? I'm using the RxJava library.

Reezy
  • 984
  • 1
  • 9
  • 12

3 Answers3

9

In addition to what Simon Basle said, here is a quick version with linear backoff:

.retryWhen(notification ->
    notification
    .zipWith(Observable.range(1, 5), Tuple::create)
    .flatMap(att ->
            att.value2() == 3 ? Observable.error(att.value1()) : Observable.timer(att.value2(), TimeUnit.SECONDS)
    )
)

Note that "att" here is a tuple which consists of both the throwable and the number of retries, so you can very specifically implement a return logic based on those two params.

If you want to learn even more, you can peek at the resilient doc I'm currently writing: https://gist.github.com/daschl/db9fcc9d2b932115b679#retry-with-delay

daschl
  • 1,124
  • 6
  • 11
6

retryWhen is clearly a little bit more complicated than simple retry, but here's the gist of it:

  • you pass a notificationHandler function to retryWhen which takes an Observable<Throwable> and outputs an Observable<?>
  • the emission of this returned Observable determine when retry should occur or stop
  • so, for each occurring Exception in the original stream, if the handler's one emits 1 item, there'll be 1 retry. If it emits 2 items, there'll be 2...
  • as soon as the handler's stream emits an error, retry is aborted.

Using this, you can both:

  • work only on CasMismatchExceptions: just have your function return an Observable.error(t) in other cases
  • retry only for a specific number of times: for each exception, flatMap from an Observable.range representing the max number of retries, have it return an Observable.timer using the retry # if you need increasing delays.

Your use case is pretty close to the one in RxJava doc here

Simon Baslé
  • 27,105
  • 5
  • 69
  • 70
3

reviving this thread since in the Couchbase Java SDK 2.1.2 there's a new simpler way to do that: use the RetryBuilder:

Observable<Something> retryingObservable =
sourceObservable.retryWhen(
  RetryBuilder
    //will limit to the relevant exception
    .anyOf(CASMismatchException.class)
    //will retry only 5 times
    .max(5)
    //delay doubling each time, from 100ms to 2s
    .delay(Delay.linear(TimeUnit.MILLISECONDS, 2000, 100, 2.0))
  .build()
);
Simon Baslé
  • 27,105
  • 5
  • 69
  • 70
  • Can you append several different exceptions with different delays? And also can you set a default delay to all the rest? – Roee Gavirel Feb 05 '17 at 13:56
  • No you'd have to simply chain several retryWhen, each with its builder, like you'd chain several catch blocks in imperative code – Simon Baslé Feb 05 '17 at 15:01