retry package:retry

Retry combinators for monadic actions that may fail This package exposes combinators that can wrap arbitrary monadic actions. They run the action and potentially retry running it with some configurable delay for a configurable number of times. The purpose is to make it easier to work with IO and especially network IO actions that often experience temporary failure and warrant retrying of the original action. For example, a database query may time out for a while, in which case we should hang back for a bit and retry the query instead of simply raising an exception.
This module exposes combinators that can wrap arbitrary monadic actions. They run the action and potentially retry running it with some configurable delay for a configurable number of times. The express purpose of this library is to make it easier to work with IO and especially network IO actions that often experience temporary failure that warrant retrying of the original action. For example, a database query may time out for a while, in which case we should delay a bit and retry the query.
Unlifted Control.Retry.
Helper for making simplified policies that don't use the monadic context.
Default retry policy
Retry combinator for actions that don't raise exceptions, but signal in their type the outcome has failed. Examples are the Maybe, Either and EitherT monads. Let's write a function that always fails and watch this combinator retry it 5 additional times following the initial run:
>>> import Data.Maybe

>>> let f _ = putStrLn "Running action" >> return Nothing

>>> retrying retryPolicyDefault (const $ return . isNothing) f
Running action
Running action
Running action
Running action
Running action
Running action
Nothing
Note how the latest failing result is returned after all retries have been exhausted.
Same as retrying, but with the ability to override the delay of the retry policy based on information obtained after initiation. For example, if the action to run is a HTTP request that turns out to fail with a status code 429 ("too many requests"), the response may contain a "Retry-After" HTTP header which specifies the number of seconds the client should wait until performing the next request. This function allows overriding the delay calculated by the given retry policy with the delay extracted from this header value. In other words, given an arbitrary RetryPolicyM rp, the following invocation will always delay by 1000 microseconds:
retryingDynamic rp (\_ _ -> return $ ConsultPolicyOverrideDelay 1000) f
Note that a RetryPolicys decision to not perform a retry cannot be overridden. Ie. when to stop retrying is always decided by the retry policy, regardless of the returned RetryAction value.
How to handle a failed action.
Simplified RetryPolicyM without any use of the monadic context in determining policy. Mostly maintains backwards compatitibility with type signatures pre-0.7.
A RetryPolicyM is a function that takes an RetryStatus and possibly returns a delay in microseconds. Iteration numbers start at zero and increase by one on each retry. A *Nothing* return value from the function implies we have reached the retry limit. Please note that RetryPolicyM is a Monoid. You can collapse multiple strategies into one using mappend or <>. The semantics of this combination are as follows:
  1. If either policy returns Nothing, the combined policy returns Nothing. This can be used to inhibit after a number of retries, for example.
  2. If both policies return a delay, the larger delay will be used. This is quite natural when combining multiple policies to achieve a certain effect.
Example: One can easily define an exponential backoff policy with a limited number of retries:
> limitedBackoff = exponentialBackoff 50000 <> limitRetries 5
Naturally, mempty will retry immediately (delay 0) for an unlimited number of retries, forming the identity for the Monoid. The default retry policy retryPolicyDefault implements a constant 50ms delay, up to 5 times:
> retryPolicyDefault = constantDelay 50000 <> limitRetries 5
For anything more complex, just define your own RetryPolicyM:
> myPolicy = retryPolicy $ \ rs -> if rsIterNumber rs > 10 then Just 1000 else Just 10000
Since 0.7.
Datatype with stats about retries made thus far.
Don't retry (regardless of what the RetryPolicy says).
Initial, default retry status. Use fields or lenses to update.
Applies a natural transformation to a policy to run a RetryPolicy meant for the monad m in the monad n provided a transformation from m to n is available. A common case is if you have a pure policy, RetryPolicyM Identity and want to use it to govern an IO computation you could write:
purePolicyInIO :: RetryPolicyM Identity -> RetryPolicyM IO
purePolicyInIO = natTransformRetryPolicy (pure . runIdentity)
A variant of retrying that allows specifying the initial RetryStatus so that the retrying operation may pick up where it left off in regards to its retry policy.
A variant of retryingDynamic that allows specifying the initial RetryStatus so that a retrying operation may pick up where it left off in regards to its retry policy.
Convert a boolean answer to the question "Should we retry?" into a RetryAction.