TankerTom
TankerTom

Reputation: 11

in spring webflux how do I alter the context for a retry?

I have a reactor chain configured which I would like to retry on failure - I can do this however I am not able to change the context so that the chain knows when it is in a retry.

In the below example I would like for the FLAG value to be "altered" (in deferContextual) during a retry.

const val FLAG = "Flag"

fun main() {

    val tr = TestRetry()
    tr.main()
}

class TestRetry {

    fun main() {
        Mono.deferContextual { ctx ->
            val flag = ctx.get<Any>(FLAG)
            println("flag ${flag}")
            Mono.just("hello")
        }
        .subscribeOn(Schedulers.boundedElastic())
        .map {
            println("map: $it")
            if (true) {
                throw IllegalArgumentException()
            }
            "there"
        }
        .retryWhen(retrySpec())
        .contextWrite {
            println("CONTEXT WRITE initialising FLAG")
            it.put(FLAG, "original")
        }
        .subscribe()

        Thread.sleep(15000);
    }

    fun retrySpec(): RetrySpec =
        Retry.max(2L)
            .doBeforeRetry {
                val ctx = it.retryContextView()
                val flag = ctx.getOrDefault(FLAG, "oops") // NOTE: here FLAG is altered
                println ("DO BEFORE RETRY Flag ${flag}")
            }
            .filter { throwable ->
                throwable is IllegalArgumentException
            }
            .onRetryExhaustedThrow { _, retrySignal ->
                println("retries exhausted")
                throw RuntimeException("done")
            }.withRetryContext(
                Context.of(FLAG, "altered")
            )
}

When running the above example I am trying to change the value of the FLAG variable in the withRetryContext, and I can see that in doBeforeRetry the value has changed, however when we get back to Mono.deferContextual the value is back to "original". I don't think I correctly understand how the reactor code works.

Upvotes: 1

Views: 437

Answers (1)

rethab
rethab

Reputation: 8443

There is some sample code of how the context can be modified in the javadoc of retryWhen:

Retry customStrategy = Retry.from(companion -> companion.handle((retrySignal, sink) -> {
  Context ctx = sink.currentContext();
  int rl = ctx.getOrDefault("retriesLeft", 0);
  
  if (rl > 0) {
    sink.next(Context.of(
      "retriesLeft", rl - 1,
      "lastError", retrySignal.failure()
    ));
  } else {
    sink.error(Exceptions.retryExhausted("retries exhausted", retrySignal.failure()));
  }
}));

Mono<T> retried = originalMono.retryWhen(customStrategy);

Upvotes: 0

Related Questions