packages feed

cached-io 1.3.2.0 → 1.4.0.0

raw patch · 4 files changed

+39/−16 lines, 4 filesdep +transformersdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: transformers

Dependency ranges changed: base

API changes (from Hackage documentation)

- Control.Concurrent.CachedIO: cachedIO :: (MonadIO m, MonadIO t, MonadCatch t) => NominalDiffTime -> t a -> m (Cached t a)
+ Control.Concurrent.CachedIO: cachedIO :: (MonadIO m, MonadIO t, MonadMask t) => NominalDiffTime -> t a -> m (Cached t a)
- Control.Concurrent.CachedIO: cachedIO' :: (MonadIO m, MonadIO t, MonadCatch t) => NominalDiffTime -> (Maybe (UTCTime, a) -> t a) -> m (Cached t a)
+ Control.Concurrent.CachedIO: cachedIO' :: (MonadIO m, MonadIO t, MonadMask t) => NominalDiffTime -> (Maybe (UTCTime, a) -> t a) -> m (Cached t a)
- Control.Concurrent.CachedIO: cachedIOFromConfig :: forall m (t :: Type -> Type) a. (MonadIO m, MonadIO t, MonadCatch t) => Config t a -> m (Cached t a)
+ Control.Concurrent.CachedIO: cachedIOFromConfig :: forall m (t :: Type -> Type) a. (MonadIO m, MonadIO t, MonadMask t) => Config t a -> m (Cached t a)
- Control.Concurrent.CachedIO: cachedIOWith :: (MonadIO m, MonadIO t, MonadCatch t) => (UTCTime -> UTCTime -> Bool) -> t a -> m (Cached t a)
+ Control.Concurrent.CachedIO: cachedIOWith :: (MonadIO m, MonadIO t, MonadMask t) => (UTCTime -> UTCTime -> Bool) -> t a -> m (Cached t a)
- Control.Concurrent.CachedIO: cachedIOWith' :: (MonadIO m, MonadIO t, MonadCatch t) => (UTCTime -> UTCTime -> Bool) -> (Maybe (UTCTime, a) -> t a) -> m (Cached t a)
+ Control.Concurrent.CachedIO: cachedIOWith' :: (MonadIO m, MonadIO t, MonadMask t) => (UTCTime -> UTCTime -> Bool) -> (Maybe (UTCTime, a) -> t a) -> m (Cached t a)
- Control.Concurrent.CachedIO: cachedSTM :: (MonadIO m, MonadCatch m) => NominalDiffTime -> m a -> STM (Cached m a)
+ Control.Concurrent.CachedIO: cachedSTM :: (MonadIO m, MonadMask m) => NominalDiffTime -> m a -> STM (Cached m a)
- Control.Concurrent.CachedIO: cachedSTM' :: (MonadIO m, MonadCatch m) => NominalDiffTime -> (Maybe (UTCTime, a) -> m a) -> STM (Cached m a)
+ Control.Concurrent.CachedIO: cachedSTM' :: (MonadIO m, MonadMask m) => NominalDiffTime -> (Maybe (UTCTime, a) -> m a) -> STM (Cached m a)
- Control.Concurrent.CachedIO: cachedSTMFromConfig :: forall (m :: Type -> Type) a. (MonadIO m, MonadCatch m) => Config m a -> STM (Cached m a)
+ Control.Concurrent.CachedIO: cachedSTMFromConfig :: forall (m :: Type -> Type) a. (MonadIO m, MonadMask m) => Config m a -> STM (Cached m a)
- Control.Concurrent.CachedIO: cachedSTMWith :: (MonadIO m, MonadCatch m) => (UTCTime -> UTCTime -> Bool) -> m a -> STM (Cached m a)
+ Control.Concurrent.CachedIO: cachedSTMWith :: (MonadIO m, MonadMask m) => (UTCTime -> UTCTime -> Bool) -> m a -> STM (Cached m a)
- Control.Concurrent.CachedIO: cachedSTMWith' :: (MonadIO m, MonadCatch m) => (UTCTime -> UTCTime -> Bool) -> (Maybe (UTCTime, a) -> m a) -> STM (Cached m a)
+ Control.Concurrent.CachedIO: cachedSTMWith' :: (MonadIO m, MonadMask m) => (UTCTime -> UTCTime -> Bool) -> (Maybe (UTCTime, a) -> m a) -> STM (Cached m a)

Files

CHANGELOG.md view
@@ -1,8 +1,21 @@ # Revision history for cached-io +## 1.4.0.0++- **Breaking** All `cached...` functions now require `MonadMask` for the monad+  running the refresh action. This was required to support the following bug+  fix.+- Fix a subtle bug: if you used a monad with explicit early-exit semantics like+  `MaybeT m` or `ExceptT e m`, and your cached action uses this to exit early, the+  cache would get stuck in `Initializing` state and subsequent requests for the+  cached data would hang forever waiting for initialisation to complete.+ ## 1.3.2.0  - Add `Functor` instance for `Cached`.+- Add `cachedIOFromConfig` and `cachedSTMFromConfig` (and a supporting `Config`+  type), which let the freshness check inspect the cached value in addition to+  its timestamp.  ## 1.3.1.0 
cached-io.cabal view
@@ -1,6 +1,6 @@ cabal-version:   2.2 name:            cached-io-version:         1.3.2.0+version:         1.4.0.0 synopsis:        A simple library to cache IO actions description:   Provides functions that convert an IO action into a cached one by storing the@@ -68,5 +68,6 @@     ghc-options: -Werror    build-depends:-    , tasty        ^>=1.5-    , tasty-hunit  ^>=0.10.0.3+    , tasty         ^>=1.5+    , tasty-hunit   ^>=0.10.0.3+    , transformers  >=0.5.6     && <0.7
src/Control/Concurrent/CachedIO.hs view
@@ -48,7 +48,7 @@     writeTVar,   ) import Control.Monad (join)-import Control.Monad.Catch (MonadCatch, onException)+import Control.Monad.Catch (MonadMask, onError) import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Time.Clock (NominalDiffTime, UTCTime, addUTCTime, getCurrentTime) @@ -79,7 +79,7 @@ -- either get the cached value or refresh, if the cache is older than 'interval' -- seconds. cachedIO ::-  (MonadIO m, MonadIO t, MonadCatch t) =>+  (MonadIO m, MonadIO t, MonadMask t) =>   -- | Number of seconds before refreshing cache   NominalDiffTime ->   -- | IO action to cache@@ -94,7 +94,7 @@ -- either get the cached value or refresh, if the cache is older than 'interval' -- seconds. cachedIO' ::-  (MonadIO m, MonadIO t, MonadCatch t) =>+  (MonadIO m, MonadIO t, MonadMask t) =>   -- | Number of seconds before refreshing cache   NominalDiffTime ->   -- | action to cache. The stale value and its refresh date@@ -108,7 +108,7 @@ -- The outer IO is responsible for setting up the cache. Use the inner one to -- either get the cached value or refresh cachedIOWith ::-  (MonadIO m, MonadIO t, MonadCatch t) =>+  (MonadIO m, MonadIO t, MonadMask t) =>   -- | Test function:   -- If @isCacheStillFresh lastUpdated now@ returns 'True',   -- the cache is considered still fresh and returns the cached IO action@@ -123,7 +123,7 @@ -- The outer IO is responsible for setting up the cache. Use the inner one to -- either get the cached value or refresh cachedIOWith' ::-  (MonadIO m, MonadIO t, MonadCatch t) =>+  (MonadIO m, MonadIO t, MonadMask t) =>   -- | Test function:   -- If @isCacheStillFresh lastUpdated now@ returns 'True'   -- the cache is considered still fresh and returns the cached IO action@@ -143,7 +143,7 @@ -- -- The outer IO is responsible for setting up the cache. Use the inner one to -- either get the cached value or refresh-cachedIOFromConfig :: (MonadIO m, MonadIO t, MonadCatch t) => Config t a -> m (Cached t a)+cachedIOFromConfig :: (MonadIO m, MonadIO t, MonadMask t) => Config t a -> m (Cached t a) cachedIOFromConfig = liftIO . atomically . cachedSTMFromConfig  -- $stm@@ -154,7 +154,7 @@ -- | Set up a cached IO action in a transaction; producing a version of this IO -- action that is cached for 'interval' seconds. The cache begins uninitialized. cachedSTM ::-  (MonadIO m, MonadCatch m) =>+  (MonadIO m, MonadMask m) =>   -- | Number of seconds before refreshing cache   NominalDiffTime ->   -- | IO action to cache@@ -165,7 +165,7 @@ -- | Set up a cached IO action in a transaction; producing a version of this IO -- action that is cached for 'interval' seconds. The cache begins uninitialized. cachedSTM' ::-  (MonadIO m, MonadCatch m) =>+  (MonadIO m, MonadMask m) =>   -- | Number of seconds before refreshing cache   NominalDiffTime ->   -- | action to cache. The stale value and its refresh date@@ -176,7 +176,7 @@  -- | Set up a cached IO action in a transaction; The cache begins uninitialized. cachedSTMWith ::-  (MonadIO m, MonadCatch m) =>+  (MonadIO m, MonadMask m) =>   -- | Test function:   -- If @isCacheStillFresh lastUpdated now@ returns 'True',   -- the cache is considered still fresh and returns the cached IO action@@ -188,7 +188,7 @@  -- | Set up a cached IO action in a transaction; the cache begins uninitialized. cachedSTMWith' ::-  (MonadIO m, MonadCatch m) =>+  (MonadIO m, MonadMask m) =>   -- | Test function:   -- If @isCacheStillFresh lastUpdated now@ returns 'True'   -- the cache is considered still fresh and returns the cached IO action@@ -206,7 +206,7 @@  -- | Set up a cached IO action in a transaction; the cache begins uninitialized. cachedSTMFromConfig ::-  (MonadIO m, MonadCatch m) =>+  (MonadIO m, MonadMask m) =>   Config m a ->   STM (Cached m a) cachedSTMFromConfig config = do@@ -242,7 +242,7 @@             _ -> Nothing       newValue <-         refreshAction config previous-          `onException` liftIO (atomically (writeTVar cachedT previousState))+          `onError` liftIO (atomically (writeTVar cachedT previousState))       liftIO $ do         now <- getCurrentTime         atomically (writeTVar cachedT (Fresh now newValue))
test/test-cachedIO.hs view
@@ -2,6 +2,7 @@  import Control.Concurrent (forkIO, threadDelay) import Control.Concurrent.CachedIO (Cached (..), cachedIO)+import Control.Monad.Trans.Except (ExceptT (..), runExceptT, throwE) import Data.Functor (void) import Data.IORef (IORef, atomicModifyIORef', newIORef, readIORef) import Test.Tasty (TestTree, defaultMain, testGroup)@@ -29,7 +30,15 @@         void . forkIO $ void action         void action         count <- readIORef ref-        count @?= 1+        count @?= 1,+      testCase "Cache resets when ExceptT action fails via throwError" $ do+        Cached action <- cachedIO (5 * 60) (throwE () :: ExceptT () IO ())+        result1 <- runExceptT action+        result1 @?= Left ()+        -- If we do not handle the throwE correctly this will hang because the+        -- cache is waiting to finish initialising.+        result2 <- runExceptT action+        result2 @?= Left ()     ]  increment :: IORef Int -> IO Int