retry 0.7.0.1 → 0.7.1
raw patch · 3 files changed
+122/−36 lines, 3 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Control.Retry: [getRetryPolicyM] :: RetryPolicyM m -> RetryStatus -> m (Maybe Int)
- Control.Retry: instance GHC.Base.Monad m => Data.Default.Class.Default (Control.Retry.RetryPolicyM m)
- Control.Retry: instance GHC.Base.Monad m => GHC.Base.Monoid (Control.Retry.RetryPolicyM m)
- Control.Retry: instance GHC.Classes.Eq Control.Retry.RetryStatus
- Control.Retry: instance GHC.Generics.Constructor Control.Retry.C1_0RetryStatus
- Control.Retry: instance GHC.Generics.Datatype Control.Retry.D1RetryStatus
- Control.Retry: instance GHC.Generics.Generic Control.Retry.RetryStatus
- Control.Retry: instance GHC.Generics.Selector Control.Retry.S1_0_0RetryStatus
- Control.Retry: instance GHC.Generics.Selector Control.Retry.S1_0_1RetryStatus
- Control.Retry: instance GHC.Generics.Selector Control.Retry.S1_0_2RetryStatus
- Control.Retry: instance GHC.Show.Show Control.Retry.RetryStatus
+ Control.Retry: RetryStatus :: !Int -> !Int -> !(Maybe Int) -> RetryStatus
+ Control.Retry: applyAndDelay :: MonadIO m => RetryPolicyM m -> RetryStatus -> m (Maybe RetryStatus)
+ Control.Retry: applyPolicy :: Monad m => RetryPolicyM m -> RetryStatus -> m (Maybe RetryStatus)
+ Control.Retry: defaultLogMsg :: (Show e, Exception e) => Bool -> e -> RetryStatus -> String
+ Control.Retry: getRetryPolicyM :: RetryPolicyM m -> RetryStatus -> m (Maybe Int)
+ Control.Retry: instance Constructor C1_0RetryStatus
+ Control.Retry: instance Datatype D1RetryStatus
+ Control.Retry: instance Eq RetryStatus
+ Control.Retry: instance Generic RetryStatus
+ Control.Retry: instance Monad m => Default (RetryPolicyM m)
+ Control.Retry: instance Monad m => Monoid (RetryPolicyM m)
+ Control.Retry: instance Read RetryStatus
+ Control.Retry: instance Selector S1_0_0RetryStatus
+ Control.Retry: instance Selector S1_0_1RetryStatus
+ Control.Retry: instance Selector S1_0_2RetryStatus
+ Control.Retry: instance Show RetryStatus
+ Control.Retry: stepping :: (MonadIO m, MonadMask m) => RetryPolicyM m -> [(RetryStatus -> Handler m Bool)] -> (RetryStatus -> m ()) -> (RetryStatus -> m a) -> RetryStatus -> m (Maybe a)
- Control.Retry: logRetries :: (Monad m, Show e, Exception e) => (e -> m Bool) -> (Bool -> String -> m ()) -> RetryStatus -> Handler m Bool
+ Control.Retry: logRetries :: (Monad m, Show e, Exception e) => (e -> m Bool) -> (Bool -> e -> RetryStatus -> m ()) -> RetryStatus -> Handler m Bool
- Control.Retry: rsCumulativeDelay :: RetryStatus -> Int
+ Control.Retry: rsCumulativeDelay :: RetryStatus -> !Int
- Control.Retry: rsIterNumber :: RetryStatus -> Int
+ Control.Retry: rsIterNumber :: RetryStatus -> !Int
- Control.Retry: rsPreviousDelay :: RetryStatus -> (Maybe Int)
+ Control.Retry: rsPreviousDelay :: RetryStatus -> !(Maybe Int)
Files
- changelog.md +7/−0
- retry.cabal +1/−1
- src/Control/Retry.hs +114/−35
changelog.md view
@@ -1,3 +1,10 @@+0.7.1+* Various documentation updates.+* Add stepping combinator for manual retries.+* Add applyPolicy and applyAndDelay+* Add Read instance for RetryStatus+* Fix logic bug in rsPreviousDelay in first retry+ 0.7.0.1 * Officially drop support for GHC < 7.6 due to usage of Generics.
retry.cabal view
@@ -14,7 +14,7 @@ case we should hang back for a bit and retry the query instead of simply raising an exception. -version: 0.7.0.1+version: 0.7.1 synopsis: Retry combinators for monadic actions that may fail license: BSD3 license-file: LICENSE
src/Control/Retry.hs view
@@ -32,13 +32,12 @@ RetryPolicyM (..) , RetryPolicy , retryPolicy- , RetryStatus- -- ** Fields for 'RetryStatus'- , rsIterNumber- , rsCumulativeDelay- , rsPreviousDelay+ , RetryStatus (..) , defaultRetryStatus+ , applyPolicy+ , applyAndDelay + -- ** Lenses for 'RetryStatus' , rsIterNumberL , rsCumulativeDelayL@@ -47,6 +46,7 @@ -- * Applying Retry Policies , retrying , recovering+ , stepping , recoverAll , logRetries @@ -162,8 +162,8 @@ data RetryStatus = RetryStatus { rsIterNumber :: !Int -- ^ Iteration number, where 0 is the first try , rsCumulativeDelay :: !Int -- ^ Delay incurred so far from retries in microseconds- , rsPreviousDelay :: !(Maybe Int) -- ^ Previous attempt's delay. Will always be Nothing on first run.- } deriving (Show, Eq, Generic)+ , rsPreviousDelay :: !(Maybe Int) -- ^ Latest attempt's delay. Will always be Nothing on first run.+ } deriving (Read, Show, Eq, Generic) -------------------------------------------------------------------------------@@ -190,7 +190,46 @@ {-# INLINE rsPreviousDelayL #-} + -------------------------------------------------------------------------------+-- | Apply policy on status to see what the decision would be.+-- 'Nothing' implies no retry, 'Just' returns updated status.+applyPolicy + :: Monad m + => RetryPolicyM m + -> RetryStatus + -> m (Maybe RetryStatus)+applyPolicy (RetryPolicyM policy) s = do+ res <- policy s+ case res of+ Just delay -> return $! Just $! RetryStatus + { rsIterNumber = rsIterNumber s + 1+ , rsCumulativeDelay = rsCumulativeDelay s + delay+ , rsPreviousDelay = Just delay }+ Nothing -> return Nothing+++-------------------------------------------------------------------------------+-- | 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)+applyAndDelay policy s = do+ chk <- applyPolicy policy s+ case chk of+ Just rs -> do+ case (rsPreviousDelay rs) of+ Nothing -> return ()+ Just delay -> liftIO $ threadDelay delay+ return (Just rs)+ Nothing -> return Nothing++ ++------------------------------------------------------------------------------- -- | Helper for making simplified policies that don't use the monadic -- context. retryPolicy :: (RetryStatus -> Maybe Int) -> RetryPolicy@@ -314,21 +353,17 @@ -> (RetryStatus -> m b) -- ^ Action to run -> m b-retrying (RetryPolicyM policy) chk f = go defaultRetryStatus+retrying policy chk f = go defaultRetryStatus where go s = do res <- f s chk' <- chk s res case chk' of True -> do- chk <- policy s- case chk of- Just delay -> do- liftIO (threadDelay delay)- go $! RetryStatus { rsIterNumber = rsIterNumber s + 1- , rsCumulativeDelay = rsCumulativeDelay s + delay- , rsPreviousDelay = Just (maybe 0 (const delay) (rsPreviousDelay s))}+ rs <- applyAndDelay policy s+ case rs of Nothing -> return res+ Just rs' -> go $! rs' False -> return res @@ -386,21 +421,21 @@ -- 'recoverAll' recovering #if MIN_VERSION_exceptions(0, 6, 0)- :: (MonadIO m, MonadMask m)+ :: (MonadIO m, MonadMask m) #else- :: (MonadIO m, MonadCatch m)+ :: (MonadIO m, MonadCatch m) #endif- => RetryPolicyM m- -- ^ Just use 'def' for default settings- -> [(RetryStatus -> Handler m Bool)]- -- ^ Should a given exception be retried? Action will be- -- retried if this returns True *and* the policy allows it.- -- This action will be consulted first even if the policy- -- later blocks it.- -> (RetryStatus -> m a)- -- ^ Action to perform- -> m a-recovering p@(RetryPolicyM policy) hs f = mask $ \restore -> go restore defaultRetryStatus+ => RetryPolicyM m+ -- ^ Just use 'def' for default settings+ -> [(RetryStatus -> Handler m Bool)]+ -- ^ Should a given exception be retried? Action will be+ -- retried if this returns True *and* the policy allows it.+ -- This action will be consulted first even if the policy+ -- later blocks it.+ -> (RetryStatus -> m a)+ -- ^ Action to perform+ -> m a+recovering policy hs f = mask $ \restore -> go restore defaultRetryStatus where go restore = loop where@@ -416,16 +451,60 @@ chk <- h e' case chk of True -> do- res <- policy s- case res of- Just delay -> do- liftIO $ threadDelay delay- loop $! RetryStatus { rsIterNumber = rsIterNumber s + 1- , rsCumulativeDelay = rsCumulativeDelay s + delay- , rsPreviousDelay = Just (maybe 0 (const delay) (rsPreviousDelay s))}+ rs <- applyAndDelay policy s+ case rs of+ Just rs' -> loop $! rs' Nothing -> throwM e' False -> throwM e' | otherwise = recover e hs'++++-------------------------------------------------------------------------------+-- | A version of 'recovering' 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+#if MIN_VERSION_exceptions(0, 6, 0)+ :: (MonadIO m, MonadMask m)+#else+ :: (MonadIO m, MonadCatch m)+#endif+ => RetryPolicyM m+ -- ^ Just use 'def' for default settings+ -> [(RetryStatus -> Handler m Bool)]+ -- ^ Should a given exception be retried? Action will be+ -- retried if this returns True *and* the policy allows it.+ -- This action will be consulted first even if the policy+ -- later blocks it.+ -> (RetryStatus -> m ())+ -- ^ Action to run with updated status upon failure.+ -> (RetryStatus -> m a)+ -- ^ Main action to perform with current status.+ -> RetryStatus+ -- ^ Current status of this step+ -> m (Maybe a)+stepping policy hs schedule f s = do+ r <- try $ f s+ case r of+ Right x -> return $ Just x+ Left e -> recover (e :: SomeException) hs+ where+ recover e [] = throwM e+ recover e ((($ s) -> Handler h) : hs')+ | Just e' <- fromException e = do+ chk <- h e'+ case chk of+ True -> do + res <- applyPolicy policy s+ case res of+ Just rs -> do+ schedule $! rs+ return Nothing+ Nothing -> throwM e'+ False -> throwM e'+ | otherwise = recover e hs' -------------------------------------------------------------------------------