-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | 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. @package retry @version 0.7 -- | 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. module Control.Retry -- | 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: -- --
-- > limitedBackoff = exponentialBackoff 50 <> limitRetries 5 ---- -- Naturally, mempty will retry immediately (delay 0) for an -- unlimited number of retries, forming the identity for the -- Monoid. -- -- The default under def implements a constant 50ms delay, up to 5 -- times: -- --
-- > def = constantDelay 50000 <> limitRetries 5 ---- -- For anything more complex, just define your own RetryPolicyM: -- --
-- > myPolicy = retryPolicy $ \ rs -> if rsIterNumber n > 10 then Just 1000 else Just 10000 ---- -- Since 0.7. newtype RetryPolicyM m RetryPolicyM :: (RetryStatus -> m (Maybe Int)) -> RetryPolicyM m [getRetryPolicyM] :: RetryPolicyM m -> RetryStatus -> m (Maybe Int) -- | Simplified RetryPolicyM without any use of the monadic context -- in determining policy. Mostly maintains backwards compatitibility with -- type signatures pre-0.7. type RetryPolicy = forall m. Monad m => RetryPolicyM m -- | Helper for making simplified policies that don't use the monadic -- context. retryPolicy :: (RetryStatus -> Maybe Int) -> RetryPolicy -- | Datatype with stats about retries made thus far. The constructor is -- deliberately not exported to make additional fields easier to add in a -- backward-compatible manner. To read or modify fields in RetryStatus, -- use the accessors or lenses below. Note that if you don't want to use -- lenses, the exported field names can be used for updates: -- --
-- > retryStatus { rsIterNumber = newIterNumber }
-- > retryStatus & rsIterNumberL .~ newIterNumber
--
data RetryStatus
-- | Iteration number, where 0 is the first try
rsIterNumber :: RetryStatus -> Int
-- | Delay incurred so far from retries in microseconds
rsCumulativeDelay :: RetryStatus -> Int
-- | Previous attempt's delay. Will always be Nothing on first run.
rsPreviousDelay :: RetryStatus -> (Maybe Int)
-- | Initial, default retry status. Exported mostly to allow user code to
-- test their handlers and retry policies. Use fields or lenses to
-- update.
defaultRetryStatus :: RetryStatus
rsIterNumberL :: Lens' RetryStatus Int
rsCumulativeDelayL :: Lens' RetryStatus Int
rsPreviousDelayL :: Lens' RetryStatus (Maybe Int)
-- | 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 (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. retrying :: MonadIO m => RetryPolicyM m -> (RetryStatus -> b -> m Bool) -> (RetryStatus -> m b) -> m b -- | Run an action and recover from a raised exception by potentially -- retrying the action a number of times. Note that if you're going to -- use a handler for SomeException, you should add explicit cases -- *earlier* in the list of handlers to reject AsyncException and -- SomeAsyncException, as catching these can cause thread and -- program hangs. recoverAll already does this for you so if you -- just plan on catching SomeException, you may as well ues -- recoverAll recovering :: (MonadIO m, MonadMask m) => RetryPolicyM m -> [(RetryStatus -> Handler m Bool)] -> (RetryStatus -> m a) -> m a -- | Retry ALL exceptions that may be raised. To be used with caution; this -- matches the exception on SomeException. Note that this handler -- explicitly does not handle AsyncException nor -- SomeAsyncException (for versions of base >= 4.7). It is not -- a good idea to catch async exceptions as it can result in hanging -- threads and programs. Note that if you just throw an exception to this -- thread that does not descend from SomeException, recoverAll will catch -- it. -- -- 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 --recoverAll :: (MonadIO m, MonadMask m) => RetryPolicyM m -> (RetryStatus -> m a) -> m a -- | Helper function for constructing handler functions of the form -- required by recovering. logRetries :: (Monad m, Show e, Exception e) => (e -> m Bool) -> (Bool -> String -> m ()) -> RetryStatus -> Handler m Bool -- | Implement a constant delay with unlimited retries. constantDelay :: Int -> RetryPolicy -- | Grow delay exponentially each iteration. Each delay will increase by a -- factor of two. exponentialBackoff :: Int -> RetryPolicy -- | FullJitter exponential backoff as explained in AWS Architecture Blog -- article. -- --
-- http://www.awsarchitectureblog.com/2015/03/backoff.html ---- -- temp = min(cap, base * 2 ** attempt) -- -- sleep = temp 2 + random_between(0, temp 2) fullJitterBackoff :: MonadIO m => Int -> RetryPolicyM m -- | Implement Fibonacci backoff. fibonacciBackoff :: Int -> RetryPolicy -- | Retry immediately, but only up to n times. limitRetries :: Int -> RetryPolicy -- | Add an upperbound to a policy such that once the given time-delay -- amount has been reached or exceeded, the policy will stop retrying and -- fail. limitRetriesByDelay :: Int -> RetryPolicy -> RetryPolicy -- | Set a time-upperbound for any delays that may be directed by the given -- policy. This function does not terminate the retrying. The policy -- `capDelay maxDelay (exponentialBackoff n)` will never stop retrying. -- It will reach a state where it retries forever with a delay of -- maxDelay between each one. To get termination you need to use -- one of the limitRetries function variants. capDelay :: Monad m => Int -> RetryPolicyM m -> RetryPolicyM m -- | Run given policy up to N iterations and gather results. In the pair, -- the Int is the iteration number and the Maybe Int is -- the delay in microseconds. simulatePolicy :: Monad m => Int -> RetryPolicyM m -> m [(Int, Maybe Int)] -- | Run given policy up to N iterations and pretty print results on the -- console. simulatePolicyPP :: Int -> RetryPolicyM IO -> IO () instance GHC.Generics.Selector Control.Retry.S1_0_2RetryStatus instance GHC.Generics.Selector Control.Retry.S1_0_1RetryStatus instance GHC.Generics.Selector Control.Retry.S1_0_0RetryStatus instance GHC.Generics.Constructor Control.Retry.C1_0RetryStatus instance GHC.Generics.Datatype Control.Retry.D1RetryStatus instance GHC.Generics.Generic Control.Retry.RetryStatus instance GHC.Classes.Eq Control.Retry.RetryStatus instance GHC.Show.Show Control.Retry.RetryStatus instance GHC.Base.Monad m => Data.Default.Class.Default (Control.Retry.RetryPolicyM m) instance GHC.Base.Monad m => GHC.Base.Monoid (Control.Retry.RetryPolicyM m)