polling-cache-0.0.1.0: src/Data/Cache/Internal.hs
-- | Caching implementation internals.
--
-- This module is not part of the Data.Cache public API and should not be directly relied upon by users.
-- Anything meant for external use defined here will be re-exported by a public module.
module Data.Cache.Internal
( MonadCache (..),
DelayMode (..),
FailureMode (..),
CacheOptions (..),
)
where
import Control.Concurrent
import Control.Monad (forever)
import Control.Monad.Catch
import Control.Monad.IO.Class
import Data.Time
import System.Random.Stateful
-- | The top-level 'Monad' in which caching operations are performed.
--
-- This exists primarily for testing purposes. Production uses should drop in 'IO' and forget that this exists.
class (MonadCatch m, MonadIO m) => MonadCache m where
-- | The current system time.
currentTime :: m UTCTime
-- | Generate a random number between two bounds (inclusive).
randomize :: (Int, Int) -> m Int
-- | Delay execution of the current thread for a number of microseconds.
delay :: Int -> m ()
-- | Spawn a new thread of execution to run an action.
newThread :: m () -> m ThreadId
-- | Run an action forever.
repeatedly :: m () -> m ()
-- | Stop a thread of execution. The thread can be assumed to have been started using 'newThread'.
killCache :: ThreadId -> m ()
instance MonadCache IO where
currentTime = getCurrentTime
randomize bounds = do
gen <- getStdGen >>= newIOGenM
uniformRM bounds gen
delay = threadDelay
newThread = forkIO
repeatedly = forever
killCache = killThread
-- | The supported delay modes for a 'Data.Cache.PollingCache' instance.
--
-- The delay associated with a cache instance define the amount of time that will pass between
-- cache refreshes.
data DelayMode a
= -- | Delay for static number of microseconds between each refresh.
DelayForMicroseconds Int
| -- | Delay for a dynamic number of microseconds between each refresh.
--
-- This is useful if different delays should be used for successes or failures, or
-- if the result being retrieved contains information that could affect the delay period.
DelayDynamically (Either SomeException a -> Int)
| -- | Delay for a dynamic number of microseconds between each refresh within a set of bounds.
--
-- This is similarly useful to 'DelayDynamically', but when a known lower and upper bound should
-- be applied to the delay period. Regardless of the dynamic delay generated by the user-supplied
-- function, the delay period will never be below the lower bound or above the upper bound.
DelayDynamicallyWithBounds (Int, Int) (Either SomeException a -> Int)
-- | The supported failure handling modes for a 'Data.Cache.PollingCache' instance.
--
-- In the context of the cache action, "failure" means an Exception thrown from
-- the user-supplied action that generates values to populate the cache.
--
-- Because these operations are performed in a background thread, the user must decide how failures are to be handled
-- upon cache creation.
data FailureMode
= -- | Failures should be ignored entirely; the most relaxed failure handling strategy.
--
-- This means that 'Data.Cache.LoadFailed' will never be populated as a cache result.
Ignore
| -- | If a failure occurs, any previously cached value is immediately evicted from the cache; the strictest failure handling strategy.
EvictImmediately
| -- | Failures will be ignored unless they persist beyond the supplied time span.
--
-- This is a middle-ground failure handling strategy that probably makes sense to use in most scenarios.
-- The nature of asynchronous polling implies that somewhat stale values are not an issue to the consumer;
-- therefore, allowing some level of transient failure can often improve reliability without sacrificing correctness.
EvictAfterTime NominalDiffTime
deriving (Eq, Show)
-- | Options that dictate the behavior of a 'Data.Cache.PollingCache' instance.
data CacheOptions a = CacheOptions
{ -- | The 'DelayMode' to use.
delayMode :: DelayMode a,
-- | The 'FailureMode' to use.
failureMode :: FailureMode,
-- | An optional fuzzing factor.
--
-- If provided, this factor defines the maximum number of microseconds that can be randomly added to each
-- delay period. This means that when fuzzing is enabled, the delay between any two refreshes will always be
-- greater than the delay period generated by the cache's 'DelayMode'. 'Nothing' disables fuzzing completely.
delayFuzzing :: Maybe Int
}