Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Lwt_retrySourceUtilities for retrying Lwt computations
These utilities are useful for dealing with failure-prone computations that are expected to succeed after some number of repeated attempts. E.g.,
let flaky_computation () = match try_to_get_resource () with
| Flaky_error msg -> Error (`Retry msg)
| Fatal_error err -> Error (`Fatal err)
| Success result -> Ok result
let error_tolerant_computation () =
Lwt_retry.(flaky_computation
|> on_error (* Retry when [`Retry]able results are produced. *)
|> with_sleep (* Add a delay between attempts, with an exponential backoff. *)
|> n_times 10 (* Try up to 10 times, so long as errors are retryable. *)
)This library provides a few combinators, but retry attempts are produced on demand in an Lwt_stream.t, and they can be consumed and traversed using the Lwt_stream functions directly.
The type of errors that a retryable computation can produce.
`Retry r when r represents an error that can be retried.`Fatal f when f represents an error that cannot be retried.A ('ok, 'retry, 'fatal) attempt is the result of a retryable computation, with its the erroneous results enumerated.
Ok v is a successfully computed value vError (err, n) is the error err produced on the nth attemptThe enumeration of attempts is 1-based, because making 0 attempts means making no attempts all, making 1 attempt means trying once, and (when i>0) making n attempts means trying once and then retrying up to n-1 times.
val pp_error :
?retry:(Format.formatter -> 'retry -> unit) ->
?fatal:(Format.formatter -> 'fatal -> unit) ->
Format.formatter ->
('retry, 'fatal) error ->
unitpp_error ~retry ~fatal is a pretty printer for errors that formats fatal and retryable errors according to the provided printers.
If a printers is not provided, values of the type are represented as "<opaque>".
val on_error :
(unit -> ('ok, ('retry, 'fatal) error) result Lwt.t) ->
('ok, 'retry, 'fatal) attempt Lwt_stream.tLwt_retry.on_error f is a stream of attempts to compute f, with attempts made on demand. Attempts will be added to the stream when results are requested until the computation either succeeds or produces a fatal error.
Examples
# let success () = Lwt.return_ok ();;
val success : unit -> (unit, 'a) result Lwt.t = <fun>
# Lwt_retry.(success |> on_error) |> Lwt_stream.to_list;;
- : (unit, 'a, 'b) Lwt_retry.attempt list = [Ok ()]
# let fatal_failure () = Lwt.return_error (`Fatal ());;
val fatal_failure : unit -> ('a, [> `Fatal of unit ]) result Lwt.t = <fun>
# Lwt_retry.(fatal_failure |> on_error) |> Lwt_stream.to_list;;
- : ('a, 'b, unit) Lwt_retry.attempt list = [Error (`Fatal (), 1)]
# let retryable_error () = Lwt.return_error (`Retry ());;
val retryable_error : unit -> ('a, [> `Retry of unit ]) result Lwt.t = <fun>
# Lwt_retry.(retryable_error |> on_error) |> Lwt_stream.nget 3;;
- : ('a, unit, 'b) Lwt_retry.attempt list =
[Error (`Retry (), 1); Error (`Retry (), 2); Error (`Retry (), 3)]val with_sleep :
?duration:(int -> float) ->
('ok, 'retry, 'fatal) attempt Lwt_stream.t ->
('ok, 'retry, 'fatal) attempt Lwt_stream.twith_sleep ~duration attempts is the stream of attempts with a sleep of duration n seconds added before computing each nth retryable attempt.
Examples
# let f () = Lwt.return_error (`Retry ());;
# let attempts_with_sleeps = Lwt_retry.(f |> on_error |> with_sleep);;
# Lwt_stream.get attempts_with_sleeps;;
(* computed immediately *)
Some (Error (`Retry (), 1))
# Lwt_stream.get attempts_with_sleeps;;
(* computed after 3 seconds *)
Some (Error (`Retry (), 2))
# Lwt_stream.get attempts_with_sleeps;;
(* computed after 9 seconds *)
Some (Error (`Retry (), 3))
(* a stream with a constant 1s sleep between attempts *)
# let attempts_with_constant_sleeps =
Lwt_retry.(f |> on_error |> with_sleep ~duration:(fun _ -> 1.0));;default_sleep_duration n is an exponential backoff computed as n * 2 * (2 ^ n), which gives the sequence [0.; 4.; 16.; 48.; 128.; 320.; 768.; 1792.; ...] .
val n_times :
int ->
('ok, 'retry, 'fatal) attempt Lwt_stream.t ->
('ok, 'retry, 'fatal) attempt Lwt.tn_times n attempts is Ok v if one of the attempts succeeds within n retries (or n+1 attempts), Error (`Fatal f, n+1) if any of the attempts results in the fatal error, or Error (`Retry r, n+1) if all n retries are exhausted and the n+1th attempt results in a retry error.
In particular n_times 0 attempts will *try* 1 attempt but *re-try* 0, so it is guaranteed to produce some result.
n_times forces up to n elements of the on-demand stream of attempts.
Examples
# let f () =
let i = ref 0 in
fun () -> Lwt.return_error (if !i < 3 then (incr i; `Retry ()) else `Fatal "error!");;
# Lwt_retry.(f () |> on_error |> n_times 0);;
Error (`Retry (), 1)
# Lwt_retry.(f () |> on_error |> n_times 4);;
Error (`Fatal "error!", 3)