-- 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.4.1


-- | 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 <a>RetryPolicyM</a> is a function that takes an <a>RetryStatus</a>
--   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 <a>RetryPolicyM</a> is a <a>Monoid</a>. You can
--   collapse multiple strategies into one using <a>mappend</a> or
--   <a>&lt;&gt;</a>. The semantics of this combination are as follows:
--   
--   <ol>
--   <li>If either policy returns <a>Nothing</a>, the combined policy
--   returns <a>Nothing</a>. This can be used to <tt>inhibit</tt> after a
--   number of retries, for example.</li>
--   <li>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.</li>
--   </ol>
--   
--   Example:
--   
--   One can easily define an exponential backoff policy with a limited
--   number of retries:
--   
--   <pre>
--   &gt; limitedBackoff = exponentialBackoff 50 &lt;&gt; limitRetries 5
--   </pre>
--   
--   Naturally, <a>mempty</a> will retry immediately (delay 0) for an
--   unlimited number of retries, forming the identity for the
--   <a>Monoid</a>.
--   
--   The default under <a>def</a> implements a constant 50ms delay, up to 5
--   times:
--   
--   <pre>
--   &gt; def = constantDelay 50000 &lt;&gt; limitRetries 5
--   </pre>
--   
--   For anything more complex, just define your own <a>RetryPolicyM</a>:
--   
--   <pre>
--   &gt; myPolicy = retryPolicy $ \ rs -&gt; if rsIterNumber n &gt; 10 then Just 1000 else Just 10000
--   </pre>
--   
--   Since 0.7.
newtype RetryPolicyM m
RetryPolicyM :: (RetryStatus -> m (Maybe Int)) -> RetryPolicyM m
[getRetryPolicyM] :: RetryPolicyM m -> RetryStatus -> m (Maybe Int)

-- | Simplified <a>RetryPolicyM</a> 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:
--   
--   <pre>
--   &gt; retryStatus { rsIterNumber = newIterNumber }
--   &gt; retryStatus &amp; rsIterNumberL .~ newIterNumber
--   </pre>
data RetryStatus
RetryStatus :: !Int -> !Int -> !(Maybe Int) -> RetryStatus

-- | Iteration number, where 0 is the first try
[rsIterNumber] :: RetryStatus -> !Int

-- | Delay incurred so far from retries in microseconds
[rsCumulativeDelay] :: RetryStatus -> !Int

-- | Latest 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

-- | Apply policy on status to see what the decision would be.
--   <a>Nothing</a> implies no retry, <a>Just</a> returns updated status.
applyPolicy :: Monad m => RetryPolicyM m -> RetryStatus -> m (Maybe RetryStatus)

-- | Apply policy and delay by its amount if it results in a retry. Return
--   updated status.
applyAndDelay :: MonadIO m => RetryPolicyM m -> RetryStatus -> m (Maybe 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 <a>Maybe</a>,
--   <a>Either</a> and <tt>EitherT</tt> monads.
--   
--   Let's write a function that always fails and watch this combinator
--   retry it 5 additional times following the initial run:
--   
--   <pre>
--   &gt;&gt;&gt; import Data.Maybe
--   
--   &gt;&gt;&gt; let f _ = putStrLn "Running action" &gt;&gt; return Nothing
--   
--   &gt;&gt;&gt; retrying def (const $ return . isNothing) f
--   Running action
--   Running action
--   Running action
--   Running action
--   Running action
--   Running action
--   Nothing
--   </pre>
--   
--   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 <a>SomeException</a>, you should add explicit cases
--   *earlier* in the list of handlers to reject <a>AsyncException</a> and
--   <a>SomeAsyncException</a>, as catching these can cause thread and
--   program hangs. <a>recoverAll</a> already does this for you so if you
--   just plan on catching <a>SomeException</a>, you may as well ues
--   <a>recoverAll</a>
recovering :: (MonadIO m, MonadMask m) => RetryPolicyM m -> [(RetryStatus -> Handler m Bool)] -> (RetryStatus -> m a) -> m a

-- | A version of <a>recovering</a> that tries to run the action only a
--   single time. The control will return immediately upon both success and
--   failure. Useful for implementing retry logic in distributed queues and
--   similar external-interfacing systems.
stepping :: (MonadIO m, MonadMask m) => RetryPolicyM m -> [(RetryStatus -> Handler m Bool)] -> (RetryStatus -> m ()) -> (RetryStatus -> m a) -> RetryStatus -> m (Maybe a)

-- | Retry ALL exceptions that may be raised. To be used with caution; this
--   matches the exception on <a>SomeException</a>. Note that this handler
--   explicitly does not handle <a>AsyncException</a> nor
--   <a>SomeAsyncException</a> (for versions of base &gt;= 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:
--   
--   <pre>
--   &gt;&gt;&gt; let f _ = putStrLn "Running action" &gt;&gt; error "this is an error"
--   
--   &gt;&gt;&gt; recoverAll def f
--   Running action
--   Running action
--   Running action
--   Running action
--   Running action
--   Running action
--   *** Exception: this is an error
--   </pre>
recoverAll :: (MonadIO m, MonadMask m) => RetryPolicyM m -> (RetryStatus -> m a) -> m a

-- | Helper function for constructing handler functions of the form
--   required by <a>recovering</a>.
logRetries :: (Monad m, Show e, Exception e) => (e -> m Bool) -> (Bool -> e -> RetryStatus -> m ()) -> RetryStatus -> Handler m Bool

-- | For use with <a>logRetries</a>.
defaultLogMsg :: (Show e, Exception e) => Bool -> e -> RetryStatus -> String

-- | 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.
--   
--   <pre>
--   http://www.awsarchitectureblog.com/2015/03/backoff.html
--   </pre>
--   
--   temp = min(cap, base * 2 ** attempt)
--   
--   sleep = temp <i> 2 + random_between(0, temp </i> 2)
fullJitterBackoff :: MonadIO m => Int -> RetryPolicyM m

-- | Implement Fibonacci backoff.
fibonacciBackoff :: Int -> RetryPolicy

-- | Retry immediately, but only up to <tt>n</tt> 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
--   <tt>maxDelay</tt> between each one. To get termination you need to use
--   one of the <a>limitRetries</a> function variants.
capDelay :: Monad m => Int -> RetryPolicyM m -> RetryPolicyM m

-- | Run given policy up to N iterations and gather results. In the pair,
--   the <tt>Int</tt> is the iteration number and the <tt>Maybe Int</tt> 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.Generic Control.Retry.RetryStatus
instance GHC.Classes.Eq Control.Retry.RetryStatus
instance GHC.Show.Show Control.Retry.RetryStatus
instance GHC.Read.Read 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)
