packages feed

polysemy-conc-0.3.0.0: lib/Polysemy/Conc/Retry.hs

-- |Description: Action Retrying
module Polysemy.Conc.Retry where

import qualified Polysemy.Time as Time
import Polysemy.Time (Time, TimeUnit)

import Polysemy.Conc.Effect.Race (Race)
import qualified Polysemy.Conc.Effect.Sync as Sync
import Polysemy.Conc.Interpreter.Sync (interpretSync)
import qualified Polysemy.Conc.Race as Race

-- |Run an action repeatedly until it returns 'Right' or the timout has been exceeded.
retrying ::
  ∀ e w u t d r a .
  TimeUnit w =>
  TimeUnit u =>
  Members [Race, Time t d] r =>
  -- |The timeout after which the attempt is abandoned.
  w ->
  -- |The waiting interval between two tries.
  u ->
  Sem r (Either e a) ->
  Sem r (Maybe a)
retrying timeout interval action =
  Race.timeoutMaybe timeout spin
  where
    spin =
      action >>= \case
        Right a ->
          pure a
        Left _ -> do
          Time.sleep @t @d interval
          spin

-- |Run an action repeatedly until it returns 'Right' or the timout has been exceeded.
--
-- If the action failed at least once, the last error will be returned in case of timeout.
retryingWithError ::
  ∀ e w u t d r a .
  TimeUnit w =>
  TimeUnit u =>
  Members [Race, Time t d, Embed IO] r =>
  -- |The timeout after which the attempt is abandoned.
  w ->
  -- |The waiting interval between two tries.
  u ->
  Sem r (Either e a) ->
  Sem r (Maybe (Either e a))
retryingWithError timeout interval action =
  interpretSync @e do
    Race.timeoutMaybe timeout spin >>= \case
      Just a -> pure (Just (Right a))
      Nothing -> fmap Left <$> Sync.takeTry
  where
    spin =
      raise action >>= \case
        Right a ->
          pure a
        Left e -> do
          void (Sync.takeTry @e)
          Sync.putTry e
          Time.sleep @t @d interval
          spin