packages feed

TCache 0.13.3 → 1.0.0

raw patch · 7 files changed

+146/−46 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.TCache.IndexQuery: instance (Data.TCache.IndexQuery.Queriable reg a, Data.TCache.IndexQuery.Queriable reg' a) => Data.TCache.IndexQuery.RelationOps (reg -> a) (reg' -> a) (Data.TCache.IndexQuery.JoinData reg reg')
- Data.TCache.IndexQuery: instance (Data.Typeable.Internal.Typeable reg, Data.Typeable.Internal.Typeable a) => Data.TCache.Defs.Indexable (Data.TCache.IndexQuery.Index reg a)
- Data.TCache.IndexQuery: instance Data.TCache.IndexQuery.Queriable reg a => Data.TCache.IResource.IResource (Data.TCache.IndexQuery.Index reg a)
- Data.TCache.IndexQuery: instance Data.TCache.IndexQuery.Queriable reg a => Data.TCache.IndexQuery.RelationOps (reg -> a) a [Data.TCache.Defs.DBRef reg]
+ Data.TCache: existDataForKey :: (IResource a, Typeable a) => a -> String -> IO Bool
+ Data.TCache: referencedKeys :: IResource a => a -> [String]
+ Data.TCache: syncDBRef :: (IResource a, Typeable a) => DBRef a -> IO ()
+ Data.TCache.IResource: referencedKeys :: IResource a => a -> [String]
+ Data.TCache.IndexQuery: instance (Data.TCache.Defs.Indexable reg, Data.TCache.IndexQuery.Queriable reg a) => Data.TCache.IResource.IResource (Data.TCache.IndexQuery.Index reg a)
+ Data.TCache.IndexQuery: instance (Data.TCache.Defs.Indexable reg, Data.TCache.IndexQuery.Queriable reg a) => Data.TCache.IndexQuery.RelationOps (reg -> a) a [Data.TCache.Defs.DBRef reg]
+ Data.TCache.IndexQuery: instance (Data.TCache.Defs.Indexable reg, Data.TCache.IndexQuery.Queriable reg a, Data.TCache.Defs.Indexable reg', Data.TCache.IndexQuery.Queriable reg' a) => Data.TCache.IndexQuery.RelationOps (reg -> a) (reg' -> a) (Data.TCache.IndexQuery.JoinData reg reg')
+ Data.TCache.IndexQuery: instance (Data.TCache.Defs.Indexable reg, Data.Typeable.Internal.Typeable reg, Data.Typeable.Internal.Typeable a) => Data.TCache.Defs.Indexable (Data.TCache.IndexQuery.Index reg a)
- Data.Persistent.Collection: type RefQueue a = DBRef (Queue a)
+ Data.Persistent.Collection: type RefQueue a = DBRef Queue a
- Data.TCache.Defs: type TPVar a = TVar (Status (Elem a))
+ Data.TCache.Defs: type TPVar a = TVar Status Elem a
- Data.TCache.IndexQuery: index :: Queriable reg a => (reg -> a) -> IO ()
+ Data.TCache.IndexQuery: index :: (Indexable reg, Queriable reg a) => (reg -> a) -> IO ()
- Data.TCache.IndexQuery: indexOf :: Queriable reg a => (reg -> a) -> STM [(a, [DBRef reg])]
+ Data.TCache.IndexQuery: indexOf :: (Indexable reg, Queriable reg a) => (reg -> a) -> STM [(a, [DBRef reg])]
- Data.TCache.Memoization: class Executable m
+ Data.TCache.Memoization: class Executable (m :: Type -> Type)

Files

ChangeLog.md view
@@ -1,3 +1,15 @@+* 1.0.0 : Breaking changes (hence the major version):+  - The name of the files of the indexes of `Data.TCache.IndexQuery` has changed: from `index-<type><field>` to `index/<type>-<field>`.+    The index files created by previous versions are not found by this one.+  - `Show` of `DBRef` writes `DB# "key"` instead of `DBRef "key"`. `Read` accepts both formats, so the data written by previous+    versions can be read, but the data written by this version can not be read by previous ones.+  - The functions of `Data.TCache.IndexQuery` (`index`, `getIndex`, `join`, the relational operators...) and the instance+    `IResource (Index reg a)` now require `Indexable reg`.+  New:+  - `referencedKeys`, a new method of `IResource` (default: no references): the keys of the DBRefs that a record contains.+  - `syncDBRef`: saves a DBRef and, recursively, the DBRefs that it references (cycles are handled).+  - `existDataForKey`: tells if there are data for a key, in the cache or in the permanent storage, without adding it to the cache.+  Internal: `safeIOToSTM` uses `forkFinally`; `takeDBRef` reads the resource by key. * 0.13.x : (WIP) Major refaktoring and cleanups may break older code * 0.12.1.0 : Dropped Data.Hashtable (deprecated). Now it uses the package hashtables * 0.12.0.0 : space in index data in indexQuery.hs and IndexText.hs triggered errors in the AWS backend.  The space has been changed by \'-\'. So rename the "index *" files in the TCache folder in order to be recognized.  
Data/Persistent/Collection.hs view
@@ -31,7 +31,7 @@ unreadSTM,isEmpty,isEmptySTM ) where import Data.Typeable-import Control.Concurrent.STM(STM,atomically, retry)+import Control.Concurrent.STM import Control.Monad import Data.TCache.DefaultPersistence 
Data/TCache.hs view
@@ -134,6 +134,7 @@ ,writeDBRef ,delDBRef + -- * @IResource@ class {- | Cached objects must be instances of `IResource`. Such instances can be implicitly derived trough auxiliary classes for file persistence.@@ -287,17 +288,21 @@ ,SyncMode(..) ,clearSyncCacheProc ,defaultCheck+,existDataForKey+,syncDBRef -- * Other ,onNothing ) where  +import qualified Data.Set as Set+ import GHC.Conc import GHC.MVar(MVar) import Control.Monad(when, void) import qualified Data.HashTable.IO as H(BasicHashTable, new, insert, lookup, toList)-import Data.IORef(IORef, newIORef, readIORef, writeIORef)+import Data.IORef(IORef, newIORef, readIORef, writeIORef, modifyIORef) import System.IO.Unsafe(unsafePerformIO) import System.IO(hPutStr, stderr) import Data.Maybe(catMaybes)@@ -312,8 +317,9 @@ import System.Mem.Weak(Weak, deRefWeak, mkWeakPtr, finalize)  import Control.Concurrent.MVar(newMVar, newEmptyMVar, takeMVar, putMVar)-import Control.Exception(catch, handle, throw, evaluate, bracket, SomeException)-+import Control.Concurrent(forkFinally)+import Control.Exception(catch, handle, throw, evaluate, bracket, SomeException(..),throwIO)+import Data.Maybe --import Debug.Trace --(!>) = flip trace @@ -468,9 +474,12 @@     readsPrec _ str1= readit str        where        str = dropWhile isSpace str1-       readit ('D':'B':'R':'e':'f':' ':'\"':str2)=-         let   (key1,nstr) =  break (== '\"') str2-         in  [( getDBRef key1 :: DBRef a, tail  nstr)]+       readit ('D':'B':'#':' ':'\"':str2)=+         let (key1,nstr) = break (== '\"') str2+         in  [(getDBRef key1 :: DBRef a, tail nstr)]+       readit ('D':'B':'R':'e':'f':' ':'\"':str2)=  -- legacy format+         let (key1,nstr) = break (== '\"') str2+         in  [(getDBRef key1 :: DBRef a, tail nstr)]        readit  _ = []  -- | Return the key of the object referenced by the DBRef@@ -511,6 +520,26 @@      putMVar getRefFlag ()      return  dbref +-- | ligtweight way to read an object without adding the reference to it to the cache if it does not exist.++-- | Check whether data for a key exists in cache or in persistent storage.+-- Does not add anything to the cache. Pass a @Proxy a@ to fix the type.+existDataForKey :: (IResource a, Typeable a) => a -> String -> IO Bool+existDataForKey x k = do+  (cache, _) <- readIORef refcache+  mc <- H.lookup cache k+  case mc of+    Just (CacheElem _ w) -> do+      mdbref <- deRefWeak w+      case mdbref of+        Just _  -> return True+        Nothing -> checkDisk+    Nothing -> checkDisk+  where+  checkDisk = isJust <$> (readResourceByKey k `asTypeOf` type1 x )+  type1 :: a -> IO(Maybe a)+  type1= undefined+ getRefFlag :: MVar () {-# NOINLINE getRefFlag #-} getRefFlag= unsafePerformIO $ newMVar ()@@ -593,21 +622,48 @@ --        H.insert cache key ( CacheElem Nothing w) --      return dbref --- | Delete the content of the DBRef form the cache and from permanent storage+-- | Delete the content of the DBRef from the cache and from permanent storage delDBRef :: (IResource a, Typeable a) => DBRef a -> STM() delDBRef dbref@(DBRef _ tv)= do   mr <- readDBRef dbref+   case mr of    Just x -> do      applyTriggers [dbref] [Nothing]      writeTVar tv DoNotExist-      safeIOToSTM . criticalSection saving $ delResource x     Nothing -> return ()   +-- | Save a single DBRef and recursively all DBRefs referenced by its record,+-- as reported by 'referencedKeys'. Visited keys are tracked to handle cycles.+syncDBRef :: (IResource a, Typeable a) => DBRef a -> IO ()+syncDBRef root = do+    visited <- newIORef Set.empty+    criticalSection saving $ go visited (keyObjDBRef root)+  where+    go visited key1 = do+        seen <- readIORef visited+        if Set.member key1 seen then return () else do+            modifyIORef visited (Set.insert key1)+            (cache, _) <- readIORef refcache+            mc <- H.lookup cache key1+            case mc of+                Nothing -> return ()+                Just (CacheElem _ w) -> do+                    mr <- deRefWeak w+                    case mr of+                        Nothing -> finalize w+                        Just (DBRef _ tv) -> do+                            r <- readTVarIO tv+                            case r of+                                Exist (Elem x _ _) -> do+                                    writeResource x+                                    mapM_ (go visited) (referencedKeys x)+                                _ -> return ()+ -- | Handles Nothing cases in a simpler way than runMaybeT. -- it is used in infix notation. for example: --@@ -641,7 +697,7 @@             Nothing -> unsafeIOToSTM (finalize w)  >> flushKey key1        Nothing   -> return () --- | label the object as not existent in database+-- | label the object as non existent in database invalidateKey :: String -> STM () invalidateKey key1=  do    (cache, _) <- unsafeIOToSTM $ readIORef refcache@@ -764,20 +820,24 @@ takeDBRef :: (IResource a, Typeable a) =>  Ht  -> CheckTPVarFlags -> a -> STM(Maybe (DBRef a)) takeDBRef cache flags x =do    let  keyr= keyResource x+   takeDBRefByKey cache flags keyr++takeDBRefByKey :: (IResource a, Typeable a) =>  Ht  -> CheckTPVarFlags -> String -> STM(Maybe (DBRef a))+takeDBRefByKey cache flags keyr = do    c <- unsafeIOToSTM $ H.lookup cache keyr    case c of        Just  (CacheElem _ w) -> do           mr <- unsafeIOToSTM $ deRefWeak w           case mr of             Just dbref -> return . Just $! castErr dbref-            Nothing -> unsafeIOToSTM (finalize w)  >> takeDBRef cache flags x+            Nothing -> unsafeIOToSTM (finalize w)  >> takeDBRefByKey cache flags keyr        Nothing   ->            safeIOToSTM $ readToCache flags cache  keyr               -- unsafeIOToSTM $ readResourceByKey keyr     where    readToCache flags1 cache1 key1= do-       mr <- readResource x+       mr <- readResourceByKey key1        case mr of             Nothing -> return Nothing             Just r2 -> do@@ -1049,13 +1109,25 @@ -- so the IO computation must be idempotent. -- Exceptions are bubbled up to the STM transaction safeIOToSTM :: IO a -> STM a-safeIOToSTM req= unsafeIOToSTM  $ do-  tv   <- newEmptyMVar-  _ <- forkIO $ (req  >>= putMVar  tv . Right)-          `Control.Exception.catch`-          (\(e :: SomeException) -> putMVar tv $ Left e )+safeIOToSTM req = unsafeIOToSTM $ do+  tv <- newEmptyMVar+  void $ forkFinally req $ \result -> putMVar tv result     r <- takeMVar tv   case r of-   Right x -> return x-   Left e -> throw e+    Right x -> return x+    Left e  -> throwIO (SomeException e) +-- | when a STM transaction is retried, if the safeIOToSTM action changes as a result of the retry, +-- it is necessary to undo the effect before retry. the additional parameter is the compensation to be executed+-- when  the IO action is interrupted by the STM transaction to perform a retry or when the IO action produces an error.+safeIOToSTMComp :: IO a -> IO () -> STM a+safeIOToSTMComp req reqCompensation = unsafeIOToSTM $ do+  tv <- newEmptyMVar+  forkFinally req $ \result ->+    case result of+      Right x  -> putMVar tv (Right x)  +      Left  e  -> reqCompensation >> putMVar tv (Left e)  +  mr <- takeMVar tv+  case mr of+    Right r  -> return r  +    Left e   -> throwIO e
Data/TCache/Defs.hs view
@@ -33,8 +33,13 @@  data DBRef a = DBRef !String  !(TPVar a)  deriving Typeable +-- WARNING: syncDBRef relies on referencedKeys, which for Serializable types is+-- typically implemented by parsing the serialized form for "DB# \"key\"" patterns.+-- Both "DB# \"key\"" (current) and "DBRef \"key\"" (legacy) must be matched.+-- String fields containing either literal will cause spurious cache lookups but+-- are otherwise harmless. instance  Show (DBRef a) where-  show (DBRef key1 _)= "DBRef \""++ key1 ++ "\""+  show (DBRef key1 _)= "DB# \""++ key1 ++ "\""  instance Eq (DBRef a) where   DBRef k _ == DBRef k' _ =  k == k'
Data/TCache/IResource.hs view
@@ -66,6 +66,12 @@          delResources :: [a] -> IO()         delResources= mapM_ delResource++        -- | Keys of DBRefs embedded in this resource. Used by 'syncDBRef' for recursive saves.+        -- Override this to enable recursive sync; default returns no references.+        referencedKeys :: a -> [String]+        referencedKeys _ = []+         -- | Resources data definition used by 'withSTMResources' data Resources a b        = Retry             -- ^ forces a retry
Data/TCache/IndexQuery.hs view
@@ -106,7 +106,7 @@       , Typeable a,Ord a,PersistIndex reg)       => Queriable reg a -instance  Queriable reg a => IResource (Index reg a) where+instance  (Indexable reg,Queriable reg a) => IResource (Index reg a) where   keyResource = key   writeResource =defWriteResource   readResourceByKey = defReadResourceByKey@@ -133,23 +133,25 @@   keyIndex :: (Show a1, Show a2) => a1 -> a2 -> String-keyIndex treg tv= "index-" ++ show treg ++ show tv+keyIndex treg tv= "index/" ++ show treg <> "-" <> show tv -instance (Typeable reg, Typeable a) => Indexable (Index reg a) where+instance (Indexable reg, Typeable reg, Typeable a) => Indexable (Index reg a) where+   defPath index= defPath (getType index)+      where+      getType :: Index reg a -> reg+      getType= undefined+    key map1= keyIndex typeofreg typeofa-       where-       [typeofreg, typeofa]= typeRepArgs $! typeOf map1---   defPath index= defPath $ ofRegister index---       where---       ofRegister :: Index reg a -> reg---       ofRegister = undefined -- type level+      where+      [typeofreg, typeofa]= typeRepArgs $! typeOf map1+ -- instance (Queriable reg a, Typeable reg, Typeable a) => IResource (Index reg a) where --  keyResource = key --  writeResource =defWriteResource --  readResourceByKey = defReadResourceByKey --  delResource = defDelResource -getIndex :: (Queriable reg a)+getIndex :: (Indexable reg,Queriable reg a)    => ( reg -> a) -> a -> STM(DBRef (Index reg a), Index reg a,[DBRef reg]) getIndex selector val= do    let [one, two]= typeRepArgs $! typeOf selector@@ -157,7 +159,7 @@    getIndexr rindex val  -getIndexr :: (Queriable reg a)+getIndexr :: (Indexable reg,Queriable reg a)    => DBRef(Index reg a) -> a -> STM(DBRef (Index reg a), Index reg a,[DBRef reg]) getIndexr rindex val= do    mindex <- readDBRef rindex@@ -165,19 +167,22 @@    let index = case mindex of          Just (Index index) ->  index         _ -> do-             let fields= show $ typeOf  rindex-             error $ "the index for "++ fields ++" do not exist. At main, use \"Data.TCache.IdexQuery.index\" to start indexing this field"+             let fields= show $ typeOf $ typ rindex+             error $ "the index for "++ fields ++" do not exist. At main, use \"Data.TCache.IdexQuery.index " <> fields <> "\" to start indexing this field"     let dbrefs= case M.lookup  val index of         Just  dbrefs -> dbrefs         Nothing      -> []     return (rindex, Index index, dbrefs)+   where+   typ :: DBRef (Index a b) -> b+   typ = undefined  selectorIndex-  :: (Queriable reg a, IResource reg-      ) =>-     (reg -> a) -> DBRef (Index reg a) -> DBRef reg -> Maybe reg -> STM ()+  :: (Queriable reg a, Indexable reg) =>+     (reg -> a) -> DBRef (Index reg a) -> +     DBRef reg -> Maybe reg -> STM ()  selectorIndex selector rindex1 pobject mobj1 = do    moldobj <- readDBRef pobject@@ -195,7 +200,7 @@       (Just oldobj, Nothing) -> do  -- delete the old selector value from the index           let val= selector oldobj-          (rindex,Index index2, dbrefs) <-  getIndexr rindex1 val+          (rindex,Index index2, dbrefs) <- getIndexr rindex1 val           let dbrefs'=   Data.List.delete pobject  dbrefs           writeDBRef rindex $ Index (M.insert  val dbrefs' index2) @@ -209,7 +214,7 @@  the indexed field can be used to perform relational-like searches -} -index :: (Queriable reg a) => (reg -> a) -> IO ()+index :: (Indexable reg, Queriable reg a) => (reg -> a) -> IO () index sel= do    let [one, two]= typeRepArgs $! typeOf sel        rindex= getDBRef $! keyIndex one two@@ -236,7 +241,7 @@  -- Instance of relations betweeen fields and values -- field .op. value-instance (Queriable reg a) => RelationOps (reg -> a) a  [DBRef reg] where+instance (Indexable reg, Queriable reg a) => RelationOps (reg -> a) a  [DBRef reg] where     (.==.) field value= do        (_ ,_ ,dbrefs) <- getIndex field value        return dbrefs@@ -247,7 +252,7 @@      (.>=.) field value= retrieve field value (>=) -join:: (Queriable rec v, Queriable rec' v)+join:: (Indexable rec,Queriable rec v, Indexable rec', Queriable rec' v)        =>(v->v-> Bool) -> (rec -> v) -> (rec' -> v) -> STM[([DBRef rec], [DBRef rec'])] join op field1 field2 =do   idxs   <- indexOf field1@@ -263,7 +268,7 @@  -- Instance of relations betweeen fields -- field1 .op. field2-instance (Queriable reg a ,Queriable reg' a ) =>RelationOps (reg -> a) (reg' -> a)  (JoinData reg reg') where+instance (Indexable reg,Queriable reg a ,Indexable reg', Queriable reg' a ) =>RelationOps (reg -> a) (reg' -> a)  (JoinData reg reg') where      (.==.)= join (==)     (.>.) = join (>)@@ -318,7 +323,7 @@   -- |  return all  the (indexed)  values which this field has and a DBRef pointer to the register-indexOf :: (Queriable reg a) => (reg -> a) -> STM [(a,[DBRef reg])]+indexOf :: (Indexable reg, Queriable reg a) => (reg -> a) -> STM [(a,[DBRef reg])] indexOf selector= do    let [one, two]= typeRepArgs $! typeOf selector    let rindex= getDBRef $! keyIndex one two@@ -329,11 +334,11 @@         let fields= show $ typeOf  selector         error $ "the index for "++ fields ++" do not exist. At main, use \"Data.TCache.IndexQuery.index\" to start indexing this field" -retrieve :: Queriable reg a => (reg -> a) -> a -> (a -> a -> Bool) -> STM[DBRef reg]+retrieve :: (Indexable reg, Queriable reg a) => (reg -> a) -> a -> (a -> a -> Bool) -> STM[DBRef reg] retrieve field value op= do    index1 <- indexOf field-   let higuer = map (\(v, vals) -> if op v value then  vals else [])  index1-   return $ concat higuer+   let higher = map (\(v, vals) -> if op v value then  vals else [])  index1+   return $ concat higher  -- from a Query result, return the records, rather than the references recordsWith
TCache.cabal view
@@ -1,5 +1,5 @@ name:           TCache-version:        0.13.3+version:        1.0.0 cabal-version:  >= 1.10 synopsis:       A Transactional cache with user-defined persistence description:    Please see the README on GitHub at <https://github.com/agocorona/TCache#readme>