retry- Retry combinators for monadic actions that may fail

MaintainerOzgun Ataman
Safe HaskellSafe-Inferred




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.


High Level Operation

data RetrySettings Source

Settings for retry behavior. Simply using def for default values should work in most cases.




numRetries :: RetryLimit

Number of retries. Defaults to 5.

backoff :: Bool

Whether to implement exponential backoff in retries. Defaults to True.

baseDelay :: Int

The base delay in miliseconds. Defaults to 50. Without backoff, this is the delay. With backoff, this base delay will grow by a factor of 2 on each subsequent retry.

limitedRetries :: Int -> RetryLimitSource

Set a limited number of retries. Default in def is 5.

unlimitedRetries :: RetryLimitSource

Set an unlimited number of retries. Note that with this option turned on, the combinator will keep retrying the action indefinitely and might essentially hang in some cases.



:: MonadIO m 
=> RetrySettings 
-> (b -> Bool)

A function to check whether the result should be retried. If True, we delay and retry the operation.

-> m b

Action to run

-> m b 

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 def isNothing f
Running action
Running action
Running action
Running action
Running action
Running action

Note how the latest failing result is returned after all retries have been exhausted.



:: forall m a . MonadCatchIO m 
=> RetrySettings

Just use def faor default settings

-> [Handler m Bool]

Should a given exception be retried? Action will be retried if this returns True.

-> m a

Action to perform

-> m a 

Run an action and recover from a raised exception by potentially retrying the action a number of times.

recoverAll :: MonadCatchIO m => RetrySettings -> m a -> m aSource

Retry ALL exceptions that may be raised. To be used with caution; this matches the exception on SomeException.

See how the action below is run once and retried 5 more times before finally failing for good:

>>> let f = putStrLn "Running action" >> error "this is an error"
>>> recoverAll def f
Running action
Running action
Running action
Running action
Running action
Running action
*** Exception: this is an error


delay :: RetrySettings -> IntSource

Delay in micro seconds

performDelay :: MonadIO m => RetrySettings -> Int -> m ()Source

Perform threadDelay for the nth retry for the given settings.

flatDelay :: MonadIO m => RetrySettings -> t -> m ()Source

Delay thread using flat delay

backoffDelay :: (Integral b, MonadIO m) => RetrySettings -> b -> m ()Source

Delay thread using backoff delay for the nth retry.