packages feed

cas-store 1.0.1 → 1.1.0

raw patch · 5 files changed

+75/−152 lines, 5 filesdep +unliftiodep −monad-controlPVP ok

version bump matches the API change (PVP)

Dependencies added: unliftio

Dependencies removed: monad-control

API changes (from Hackage documentation)

- Data.CAS.ContentStore: constructOrAsync :: forall m remoteCache. (MonadIO m, MonadBaseControl IO m, MonadMask m, Cacher m remoteCache) => ContentStore -> remoteCache -> ContentHash -> m (Status (Path Abs Dir) (Async Update) Item)
- Data.CAS.ContentStore: constructOrWait :: (MonadIO m, MonadMask m, MonadBaseControl IO m, Cacher m remoteCache) => ContentStore -> remoteCache -> ContentHash -> m (Status (Path Abs Dir) Void Item)
+ Data.CAS.ContentStore: cacheComputation :: (MonadIO m, MonadUnliftIO m, MonadMask m, Cacher m remoteCache) => ContentStore -> remoteCache -> m () -> ContentHash -> (Path Abs Dir -> m a) -> m (Maybe a, Item)
- Data.CAS.ContentStore: cacheKleisliIO :: (MonadIO m, MonadBaseControl IO m, MonadMask m, Cacher m remoteCache) => Maybe Int -> CacherM m i o -> ContentStore -> remoteCache -> (i -> m o) -> i -> m o
+ Data.CAS.ContentStore: cacheKleisliIO :: (MonadIO m, MonadUnliftIO m, MonadMask m, Cacher m remoteCache) => Maybe Int -> CacherM m i o -> ContentStore -> remoteCache -> (i -> m o) -> i -> m o
- Data.CAS.ContentStore: constructIfMissing :: (MonadIO m, MonadBaseControl IO m, MonadMask m, Cacher m remoteCache) => ContentStore -> remoteCache -> ContentHash -> m (Status (Path Abs Dir) () Item)
+ Data.CAS.ContentStore: constructIfMissing :: (MonadIO m, MonadUnliftIO m, MonadMask m, Cacher m remoteCache) => ContentStore -> remoteCache -> ContentHash -> m (Status (Path Abs Dir) () Item)
- Data.CAS.ContentStore: putInStore :: (MonadIO m, MonadMask m, MonadBaseControl IO m, Cacher m remoteCacher, ContentHashable IO t) => ContentStore -> remoteCacher -> (ContentHash -> m ()) -> (Path Abs Dir -> t -> m ()) -> t -> m Item
+ Data.CAS.ContentStore: putInStore :: (MonadIO m, MonadMask m, MonadUnliftIO m, Cacher m remoteCacher, ContentHashable IO t) => ContentStore -> remoteCacher -> (ContentHash -> m ()) -> (Path Abs Dir -> t -> m ()) -> t -> m Item
- Data.CAS.ContentStore: withConstructIfMissing :: (MonadIO m, MonadBaseControl IO m, MonadMask m, Cacher m remoteCache) => ContentStore -> remoteCache -> ContentHash -> (Path Abs Dir -> m (Either e a)) -> m (Status e () (Maybe a, Item))
+ Data.CAS.ContentStore: withConstructIfMissing :: (MonadIO m, MonadUnliftIO m, MonadMask m, Cacher m remoteCache) => ContentStore -> remoteCache -> m () -> ContentHash -> (Path Abs Dir -> m (Either e a)) -> m (Status e () (Maybe a, Item))
- Data.CAS.Lock: withLock :: MonadBaseControl IO m => Lock -> m a -> m a
+ Data.CAS.Lock: withLock :: MonadUnliftIO m => Lock -> m a -> m a

Files

cas-store.cabal view
@@ -1,5 +1,5 @@ Name:                cas-store-Version:             1.0.1+Version:             1.1.0 Synopsis:            A content-addressed storage Description:             A content-addressed storage supporting a remote caching. The API mainly consists of the cacheKleisliIO function which takes a (a -> m b) function@@ -41,7 +41,6 @@                , hashable                , hostname                , lens-               , monad-control                , path                , path-io                , random@@ -51,6 +50,7 @@                , tar                , text                , unix+               , unliftio    if os(linux)      CPP-options: -DOS_Linux      Other-modules: Data.CAS.ContentStore.Notify.Linux
changelog.md view
@@ -0,0 +1,4 @@+# Version 1.1.0++- monad-control is no longer a dependency. cas-store is now using unliftio for+  the same effect
src/Data/CAS/ContentStore.hs view
@@ -61,7 +61,7 @@   , cacheKleisliIO   , putInStore   , contentPath-  +   -- * List Contents   , listAll   , listPending@@ -78,8 +78,7 @@   , waitUntilComplete    -- * Construct Items-  , constructOrAsync-  , constructOrWait+  , cacheComputation   , constructIfMissing   , withConstructIfMissing   , markPending@@ -138,7 +137,6 @@                                                       void, when, (<=<), (>=>),                                                       mzero) import           Control.Monad.IO.Class              (MonadIO, liftIO)-import           Control.Monad.Trans.Control         (MonadBaseControl) import           Crypto.Hash                         (hashUpdate) import           Data.Aeson                          (FromJSON, ToJSON) import           Data.Bits                           (complement)@@ -151,11 +149,9 @@ import qualified Data.Hashable import           Data.List                           (foldl', stripPrefix) import           Data.Maybe                          (fromMaybe, listToMaybe)-import           Data.Monoid                         ((<>)) import qualified Data.Store import           Data.String                         (IsString (..)) import qualified Data.Text                           as T-import           Data.Typeable                       (Typeable) import           Data.Void import qualified Database.SQLite.Simple              as SQL import qualified Database.SQLite.Simple.FromField    as SQL@@ -178,6 +174,7 @@                                                       toBytes) import           Data.CAS.Lock import qualified Data.CAS.RemoteCache                as Remote+import           UnliftIO                            (MonadUnliftIO)   -- | Status of an item in the store.@@ -320,7 +317,7 @@ All item ^</> path = item :</> path (item :</> dir) ^</> path = item :</> dir </> path infixl 4 ^</>-  + newtype Alias = Alias { unAlias :: T.Text }   deriving (ContentHashable IO, Eq, Ord, Show, SQL.FromField, SQL.ToField, Data.Store.Store) @@ -469,55 +466,8 @@     Failed -> return Nothing  -- | Atomically query the state under the given key and mark pending if missing.------ Returns @'Complete' item@ if the item is complete.--- Returns @'Pending' async@ if the item is pending, where @async@ is an--- 'Control.Concurrent.Async' to await updates on.--- Returns @'Missing' buildDir@ if the item was missing, and is now pending.--- It should be constructed in the given @buildDir@,--- and then marked as complete using 'markComplete'.-constructOrAsync-  :: forall m remoteCache.-     (MonadIO m, MonadBaseControl IO m, MonadMask m, Remote.Cacher m remoteCache)-  => ContentStore-  -> remoteCache-  -> ContentHash-  -> m (Status (Path Abs Dir) (Async Update) Item)-constructOrAsync store remoteCacher hash =-  constructIfMissing store remoteCacher hash >>= \case-    Complete item -> return $ Complete item-    Missing path -> return $ Missing path-    Pending _ -> Pending <$> liftIO (internalWatchPending store hash)---- | Atomically query the state under the given key and mark pending if missing.--- Wait for the item to be completed, if already pending.--- Throws a 'FailedToConstruct' error if construction fails.------ Returns @'Complete' item@ if the item is complete.--- Returns @'Missing' buildDir@ if the item was missing, and is now pending.--- It should be constructed in the given @buildDir@,--- and then marked as complete using 'markComplete'.-constructOrWait-  :: (MonadIO m, MonadMask m, MonadBaseControl IO m, Remote.Cacher m remoteCache)-  => ContentStore-  -> remoteCache-  -> ContentHash-  -> m (Status (Path Abs Dir) Void Item)-constructOrWait store remoteCacher hash = constructOrAsync store remoteCacher hash >>= \case-  Pending a -> liftIO (wait a) >>= \case-    Completed item -> return $ Complete item-    -- XXX: Consider extending 'Status' with a 'Failed' constructor.-    --   If the store contains metadata as well, it could keep track of the-    --   number of failed attempts and further details about the failure.-    --   If an external task is responsible for the failure, the client could-    --   choose to resubmit a certain number of times.-    Failed -> liftIO . throwIO $ FailedToConstruct hash-  Complete item -> return $ Complete item-  Missing dir -> return $ Missing dir---- | Atomically query the state under the given key and mark pending if missing. constructIfMissing-  :: (MonadIO m, MonadBaseControl IO m, MonadMask m, Remote.Cacher m remoteCache)+  :: (MonadIO m, MonadUnliftIO m, MonadMask m, Remote.Cacher m remoteCache)   => ContentStore   -> remoteCache   -> ContentHash@@ -531,7 +481,7 @@         Remote.PullOK () -> return $ Complete (Item hash)         Remote.NotInCache ->           Missing <$> liftIO (internalMarkPending store hash)-        Remote.PullError _ ->+        Remote.PullError _ ->  -- TODO: That error should not be silenced           Missing <$> liftIO (internalMarkPending store hash)     Pending _ -> return $ Pending () @@ -540,18 +490,21 @@ -- and remove on failure. Forcibly removes if an uncaught exception occurs -- during item construction. withConstructIfMissing-  :: (MonadIO m, MonadBaseControl IO m, MonadMask m, Remote.Cacher m remoteCache)+  :: (MonadIO m, MonadUnliftIO m, MonadMask m, Remote.Cacher m remoteCache)   => ContentStore   -> remoteCache+  -> m () -- ^ In case an exception occurs (to log something for instance)   -> ContentHash   -> (Path Abs Dir -> m (Either e a))   -> m (Status e () (Maybe a, Item))-withConstructIfMissing store remoteCacher hash f =+withConstructIfMissing store remoteCacher ifExc hash f =   bracketOnError     (constructIfMissing store remoteCacher hash)-    (\case-      Missing _ -> removeForcibly store hash-      _ -> return ())+    (\status -> do+        case status of+          Missing _ -> removeForcibly store hash+          _ -> return ()+        ifExc)     (\case       Pending () -> return (Pending ())       Complete item -> return (Complete (Nothing, item))@@ -785,8 +738,8 @@     Pending _ -> do       let path = mkMetadataFilePath store hash file       createDirIfMissing True (parent path)-      handle <- openFile (fromAbsFile path) WriteMode-      pure (path, handle)+      hndl <- openFile (fromAbsFile path) WriteMode+      pure (path, hndl)     _ -> throwIO $ NotPending hash  -- | Return the path to a metadata file if it exists.@@ -815,7 +768,7 @@  -- | Holds a lock on the global 'MVar' and on the global lock file -- for the duration of the given action.-withStoreLock :: MonadBaseControl IO m => ContentStore -> m a -> m a+withStoreLock :: MonadUnliftIO m => ContentStore -> m a -> m a withStoreLock store = withLock (storeLock store)  prefixHashPath :: C8.ByteString -> ContentHash -> Path Rel Dir@@ -1088,11 +1041,31 @@ defaultIOCacherWithIdent ident = c{cacherKey = \x i -> liftIO $ cacherKey c x i}   where c = defaultCacherWithIdent ident +-- | Runs a computation only if the ContentHash isn't already associated to an+-- entry in the store+cacheComputation+  :: (MonadIO m, MonadUnliftIO m, MonadMask m, Remote.Cacher m remoteCache)+  => ContentStore+  -> remoteCache+  -> m ()                  -- ^ In case an exception occurs+  -> ContentHash           -- ^ A ContentHash to identify the computation inputs+  -> (Path Abs Dir -> m a) -- ^ The computation to cache, receving the path of a+                           -- store folder to which it should write its results+  -> m (Maybe a, Item)     -- ^ The result if it was just computed, and the item+                           -- corresponding to the store folder+cacheComputation store remoteCacher ifExc inputCHash computation =+  withConstructIfMissing store remoteCacher ifExc inputCHash (fmap Right . computation) >>= \case+    Missing e -> absurd e+    Pending _ ->+      liftIO (waitUntilComplete store inputCHash) >>= \case+        Just item -> return (Nothing, item)+        Nothing -> throwM $ FailedToConstruct inputCHash+    Complete resultAndItem -> return resultAndItem  -- | Caches a Kleisli of some MonadIO action in the store given the required -- properties cacheKleisliIO-  :: (MonadIO m, MonadBaseControl IO m, MonadMask m, Remote.Cacher m remoteCache)+  :: (MonadIO m, MonadUnliftIO m, MonadMask m, Remote.Cacher m remoteCache)   => Maybe Int  -- ^ This can be used to disambiguate the same program run in                 -- multiple configurations. If Nothing, then it means this                 -- program has no identity, this implies that steps will be@@ -1106,31 +1079,23 @@ cacheKleisliIO confIdent c@Cache{} store remoteCacher f i   | Just confIdent' <- confIdent = do       chash <- cacherKey c confIdent' i-      let computeAndStore fp = do-            res <- f i  -- Do the actual computation-            liftIO $ BS.writeFile (toFilePath $ fp </> [relfile|out|])-                   . cacherStoreValue c $ res-            return $ Right res-          readItem item = do-            bs <- liftIO . BS.readFile $ simpleOutPath item-            return . cacherReadValue c $ bs-      withConstructIfMissing store remoteCacher chash computeAndStore >>= \case-        Missing e -> absurd e-        Pending _ ->-          liftIO (waitUntilComplete store chash) >>= \case-            Just item -> readItem item-            Nothing -> throwM $ FailedToConstruct chash-        Complete (Just a, _) -> return a-        Complete (_, item) -> readItem item-  where-    simpleOutPath item =-      toFilePath $ itemPath store item </> [relfile|out|]+      (res, item) <- cacheComputation store remoteCacher (return ()) chash computeAndStore+      case res of+        Just r -> return r+        Nothing -> do+          bs <- liftIO . BS.readFile $ toFilePath $ itemPath store item </> [relfile|out|]+          return $ cacherReadValue c $ bs+  where computeAndStore fp = do+          res <- f i  -- Do the actual computation+          liftIO $ BS.writeFile (toFilePath $ fp </> [relfile|out|])+                 $ cacherStoreValue c $ res+          return res cacheKleisliIO _ _ _ _ f i = f i  -- | Caches an action that writes content-addressed data to the store. Returns -- the Item of the written content. putInStore-  :: (MonadIO m, MonadMask m, MonadBaseControl IO m+  :: (MonadIO m, MonadMask m, MonadUnliftIO m      ,Remote.Cacher m remoteCacher      ,ContentHashable IO t)   => ContentStore@@ -1142,15 +1107,4 @@   -> m Item -- ^ The Item in the store to which @t@ has been written putInStore store remoteCacher ifExc f x = do   chash <- liftIO $ contentHash x-  constructOrWait store remoteCacher chash >>= \case-    Pending y -> absurd y-    Complete item -> return item-    Missing fp ->-      do-        f fp x-        finalItem <- markComplete store chash-        _ <- Remote.push remoteCacher (itemHash finalItem) (Just chash) (itemPath store finalItem)-        pure finalItem-      `onException`-        (do ifExc chash-            removeFailed store chash)+  snd <$> cacheComputation store remoteCacher (ifExc chash) chash (flip f x)
src/Data/CAS/Lock.hs view
@@ -16,10 +16,9 @@   , withLock   ) where -import           Control.Concurrent+import           Control.Concurrent          (threadDelay) import           Control.Exception.Safe import           Control.Monad               (unless)-import           Control.Monad.Trans.Control (MonadBaseControl, liftBaseOp_) import           Network.HostName            (getHostName) import           Path import           Path.IO@@ -27,6 +26,8 @@ import           System.Posix.IO import           System.Posix.Process import           System.Random+import           UnliftIO                    (MonadUnliftIO, withRunInIO)+import           UnliftIO.MVar  -- | Thread and process write lock. --@@ -60,11 +61,11 @@   takeMVar (lockMVar lock)  -- | Acquire the lock for the duration of the given action and release after.-withLock :: MonadBaseControl IO m => Lock -> m a -> m a-withLock lock = liftBaseOp_ $ \action ->+withLock :: MonadUnliftIO m => Lock -> m a -> m a+withLock lock action = withRunInIO $ \unliftIO ->   withMVar (lockMVar lock) $ \() ->     bracket_ (acquireDirLock $ lockDir lock) (releaseDirLock $ lockDir lock) $-      action+      unliftIO action  ---------------------------------------------------------------------- -- Internals
test/CAS/ContentStore.hs view
@@ -96,7 +96,7 @@     withEmptyStore $ \store -> do       hash <- contentHash ("test" :: String) -      ContentStore.constructOrAsync store myCache hash >>= \case+      ContentStore.constructIfMissing store myCache hash >>= \case         ContentStore.Pending _ ->           assertFailure "missing already under construction"         ContentStore.Complete _ ->@@ -104,15 +104,13 @@         ContentStore.Missing _ ->           return () -      a <- ContentStore.constructOrAsync store Remote.NoCache hash >>= \case+      ContentStore.constructIfMissing store Remote.NoCache hash >>= \case         ContentStore.Missing _ -> do           assertFailure "under construction still missing"-          undefined         ContentStore.Complete _ -> do           assertFailure "under construction already complete"-          undefined-        ContentStore.Pending a ->-          return a+        ContentStore.Pending _ ->+          return ()        b <- ContentStore.lookupOrWait store hash >>= \case         ContentStore.Missing _ -> do@@ -126,13 +124,10 @@        item <- ContentStore.markComplete store hash -      item' <- wait a-      item' @?= ContentStore.Completed item-       item'' <- wait b       item'' @?= ContentStore.Completed item -      ContentStore.constructOrAsync store Remote.NoCache hash >>= \case+      ContentStore.constructIfMissing store Remote.NoCache hash >>= \case         ContentStore.Missing _ -> do           assertFailure "complete still missing"         ContentStore.Pending _ -> do@@ -144,7 +139,7 @@     withEmptyStore $ \store -> do       hash <- contentHash ("test" :: String) -      ContentStore.constructOrAsync store Remote.NoCache hash >>= \case+      ContentStore.constructIfMissing store Remote.NoCache hash >>= \case         ContentStore.Pending _ ->           assertFailure "missing already under construction"         ContentStore.Complete _ ->@@ -152,15 +147,13 @@         ContentStore.Missing _ ->           return () -      a <- ContentStore.constructOrAsync store Remote.NoCache hash >>= \case+      ContentStore.constructIfMissing store Remote.NoCache hash >>= \case         ContentStore.Missing _ -> do           assertFailure "under construction still missing"-          undefined         ContentStore.Complete _ -> do           assertFailure "under construction already complete"-          undefined-        ContentStore.Pending a ->-          return a+        ContentStore.Pending _ ->+          return ()        b <- ContentStore.lookupOrWait store hash >>= \case         ContentStore.Missing _ -> do@@ -174,13 +167,10 @@        ContentStore.removeFailed store hash -      item' <- wait a-      item' @?= ContentStore.Failed-       item'' <- wait b       item'' @?= ContentStore.Failed -      ContentStore.constructOrAsync store Remote.NoCache hash >>= \case+      ContentStore.constructIfMissing store Remote.NoCache hash >>= \case         ContentStore.Pending _ -> do           assertFailure "failed still under construction"         ContentStore.Complete _ -> do@@ -386,32 +376,6 @@         r <- ContentStore.lookupAlias store aliasB         r @?= Nothing -    , testCase "Remote caching (constructOrAsync)" $ do-      cacher <- Remote.memoryCache-      hash <- contentHash ("test" :: String)-      let file = [relfile|file|]-          expectedContent = "Hello World"--      -- Populate the remote cache-      withEmptyStore $ \store -> do-        ContentStore.constructOrAsync store cacher hash >>= \case-          ContentStore.Pending _ ->-            assertFailure "missing already under construction"-          ContentStore.Complete _ ->-            assertFailure "missing already complete"-          ContentStore.Missing subtree -> do-            isWritable subtree @? "under construction not writable"-            writeFile (fromAbsFile $ subtree </> file) expectedContent-            Remote.push cacher hash Nothing subtree--      -- Expects having the item in cache-      withEmptyStore $ \store -> do-        ContentStore.constructOrAsync store cacher hash >>= \case-          ContentStore.Pending _ ->-            assertFailure "missing already under construction"-          ContentStore.Complete _ -> pure ()-          ContentStore.Missing _ -> assertFailure "Not found in the cache"-     , testCase "Remote caching (withConstructIfMissing)" $ do       cacher <- Remote.memoryCache       hash <- contentHash ("test" :: String)@@ -424,7 +388,7 @@              -- Populates the remote cache       withEmptyStore $ \store -> do-        ContentStore.withConstructIfMissing store cacher hash doWrite >>= \case+        ContentStore.withConstructIfMissing store cacher (return ()) hash doWrite >>= \case           ContentStore.Missing _ -> assertFailure "not found in the cache"           ContentStore.Pending _ ->             assertFailure "missing already under construction"@@ -435,7 +399,7 @@            -- Expects having the item in the remote cache       withEmptyStore $ \store -> do-        ContentStore.withConstructIfMissing store cacher hash+        ContentStore.withConstructIfMissing store cacher (return ()) hash           (const $ assertFailure "should not try to write the file a second time") >>= \case             ContentStore.Missing _ -> assertFailure "Not found in the cache"             ContentStore.Pending _ ->