-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Robust persistence for acyclic immutable data -- -- Perdure(TM), a Cognimeta product, aims to provide a simple and robust -- persistence mechanism for acyclic immutable data with an easily -- comprehensible cost-model. It persists to raw block devices. -- -- For some classes of applications, it can replace the use of -- traditional DMBS and keep the data modelled in a manner that is -- natural for purely functional languages. It is not quite orthogonal -- persistence for Haskell, which would automatically turn any -- non-persistent program into a persistent one, but it aims to minimize -- the scope of changes required to make an application persistent. -- -- The persistence process is strict, it does not persist thunks or -- closures, but only the fully evaluated data, which must be acyclic. It -- also requires some changes to the data types and algorithms. -- References must be inserted at some strategic places within data -- structures. These cut the structure into separately loadable parts. -- These parts should hold at least a few hundred bytes to reduce the -- frequency of disk reads. The application has control over the -- representation so it can optimize it in terms of the anticipated -- access patterns. A SizeRef reference type is provided which separates -- automatically if its size threshold is reached, and inlines the data -- otherwise. As a convenience, a Map type is also provided which uses -- SizeRef on internal tree nodes. It can grow beyond the memory size and -- still remain efficient for lookups and traversals. Other persistent -- data structures with similar properties can be built on top of -- Perdure. -- -- For the user application, dereferencing is a pure computation. This is -- similar to lazy loading, but the reference data types do not hold -- ordinary references to their referent, only their location. This -- allows the referent to be unloaded transparently. Also since the -- reference holds the location and size of the data on the storage -- medium, dereferencing is very simple and efficient. No index need to -- be accessed, we only need to check a cache in case the data is already -- loaded. -- -- Persisters for data types are created using safe combinators. -- Serialization is done at the bit level, allowing for very compact -- representations. -- -- Undetected corruption is virtually impossible even in the presence of -- drive failures. Each separately loadable section has a 128-bit digest, -- and that digest is stored in the reference(s). This approach -- alleviates the need for low-level storage replication such as RAID, -- and takes care of replication at the persistence layer level. This -- seems appropriate given the increasing need for geographically -- distributed replication. -- -- Persistence occurs in discrete transactions. These run in the IO -- monad. Transactions may be requested by multiple threads but they may -- block until they get their turn. Within a transaction, multiple -- threads or sparks can be used to examine the current state and compute -- the next state to be persisted. -- -- Reference counting is used to automatically reclaim unused storage. -- -- Since persisted data is immutable, it is trivial for applications to -- keep some or all historical states by using a list-like type as their -- root persisted type. Those past states can be used for analysis or -- possibly to recover data lost because of an application-level error. -- The library includes a History data type which automatically preserves -- a greater number of recent states and fewer older states. -- -- We support 32bit, 64bit, little-endian and big-endian architectures. -- We allow platforms to write data and generate digests in their native -- format for maximum speed, but they should be able to read the data -- written by other platforms with the necessary conversions. Each -- reference stores the endianness and word size of the referent -- representation so databases can be moved between platforms without any -- conversion, and they could be read concurrently by different -- platforms. -- -- A general mechanism Database.Perdure.Rev is provided to upgrade -- the persisted types. Its goal is similar to that of the safecopy -- package. Here however, the Rev module simply exports a type that is -- similar to Either but whose persister leaves room in the -- representation to accommodate future versions. We use it to create -- growing lists of alternatives such as User1.User :> User0.User -- :> NoRev. This type should always be your root persisted type. You -- should also use it on the nested parts of your data whose type is more -- subject to change: this will avoid having to propagate all type -- changes up to the root. -- -- This library is young and there remains limitations that will need to -- be addressed in future releases: -- -- -- -- Given those limitations, Perdure is not applicable for very large -- scale projects at the moment. But it can be ideal for smaller projects -- where there is no point in burdening the developer with a distinct -- data model. It can also be used as a temporary solution before -- integrating to external databases. -- -- Cognimeta's Iota Charts web application https://iotacharts.com, -- is based on Perdure and has been has been live for close to a year. -- Its database is relatively small at ~80MB currently, but we have been -- very pleased with the results. -- -- Perdure has been developed by Cognimeta Inc. over the past two years. -- We are releasing this as open source under the permissive Apache -- license 2.0. The persistence mechanism is relatively simple and -- concise and its open source nature can provide the inquisitive user -- with added confidence about the security of its data. Also Cognimeta -- has been using Haskell exclusively, and has benefited from many -- excellent open source libraries. We are happy to contribute back, and -- are hoping for constructive and critical feedback from this very -- bright community. @package perdure @version 0.2.0 module Database.Perdure.Persistent data Persister a PartialWordPersister :: !Len Bool Word -> Persister Word PairPersister :: !Persister a -> !Persister b -> Persister (a, b) EitherPersister :: !Persister a -> !Persister b -> Persister (Either a b) ViewPersister :: !InjectionA' a b -> Persister b -> Persister a SummationPersister :: !Persister i -> !forall z. (forall b. Persister b -> (b -> a) -> z) -> i -> z -> !forall z. (forall b. i -> Persister b -> (b -> a) -> b -> z) -> a -> z -> Persister a DRefPersister' :: Persister (DRef a) CRefPersister' :: !RefPersister r -> !Persister (r a) -> Persister (CRef r a) class Persistent a persister :: Persistent a => Persister a class Persistent1 r persister1 :: (Persistent1 r, Typeable a, Persistent a) => Persister (r a) class Persistent1_ (r :: * -> *) persister1_ :: Persistent1_ r => Persister (r a) class LgPersistent1_ (r :: * -> *) lgPersister1_ :: (LgPersistent1_ r, LgMultiple Word64 w) => Persister (r w) data RefPersister r Ref0Persister :: RefPersister Ref0 RefView :: (forall a. rb a -> ra a) -> RefPersister rb -> RefPersister ra SizeRefPersister :: Len Bool Word -> RefPersister (Sum Ref0 DRef) CRefPersister :: RefPersister r -> RefPersister (CRef r) DRefPersister :: RefPersister DRef IRefPersister :: RefPersister r -> RefPersister (IRef r) class RefPersistent r refPersister :: RefPersistent r => RefPersister r (&.) :: Persister a -> Persister b -> Persister (a, b) (|.) :: Persister a -> Persister b -> Persister (Either a b) lenPersister :: Persister a -> Persister (Len u a) summationPersister :: (Persister i) -> (forall z. (forall b. Persister b -> (b -> a) -> z) -> i -> z) -> (forall z. (forall b. i -> Persister b -> (b -> a) -> b -> z) -> a -> z) -> Persister a ratioPersister :: Integral a => Persister a -> Persister (Ratio a) -- | Persister for 'Maybe a' built from a specified a persister. -- Uses a single bit to represent Nothing. maybePersister :: Persister a -> Persister (Maybe a) -- | Takes persisters for 2 types, and an injection from the smaller type -- a to the larger type b, and gives a persister for -- the larger type which uses the smaller type representation when -- possible, plus one bit to identify which representation is used. shortcutPersister :: InjectionM i => i a b -> Persister b -> Persister a -> Persister b -- | Specialization of shortcutPersister with the super injection. (>.) :: Super a b => Persister b -> Persister a -> Persister b -- | Persister for lists built from a specified element persister. listPersister :: List a => Persister (Listed a) -> Persister a -- | A list of LocalStoreFile to be used as replicates. We write to -- all replicates and read from the first one that reports no error. data ReplicatedFile data CRef r a Refed :: !r a -> CRef r a ToRef :: !a -> CRef r a onCRef :: (r a -> b) -> (a -> b) -> CRef r a -> b data DeserializerContext DeserializerContext :: f -> MVar Cache -> DeserializerContext dcFile :: DeserializerContext -> f dcCache :: DeserializerContext -> MVar Cache data DRef a DRef :: !Persister a -> !DeserializerContext -> !WArrayRef BasicRef -> DRef a data WordArrayRef r32 r64 (r :: * -> *) Word32ArrayRef :: !r32 r -> WordArrayRef r32 r64 Word64ArrayRef :: !r64 r -> WordArrayRef r32 r64 data WordNArrayRef v (r :: * -> *) WordNArrayRef :: !v -> !r (ValidatedElem v) -> !Endianness -> WordNArrayRef v type WArrayRef = WordArrayRef (WordNArrayRef W32Validator) (WordNArrayRef W64Validator) newtype IRef r t IRef :: r t -> IRef r t getIRef :: IRef r t -> r t newtype Ref0 a Ref0 :: a -> Ref0 a type CDRef = CRef DRef type Cache = LRU (Len Word64 Word64) Dynamic instance Structured (WordArrayRef r320 r640 r0) instance Structured (WordNArrayRef v0 r0) instance Structured UTCTime instance Structured Day instance Structured (IRef r0 t0) instance Structured (Ref0 a0) instance Typeable1 DRef instance Functor r => Functor (IRef r) instance Applicative r => Applicative (IRef r) instance Functor Ref0 instance Eq a => Eq (Ref0 a) instance (Persistent (r32 r), Persistent (r64 r)) => Persistent (WordArrayRef r32 r64 r) instance (Persistent v, LgPersistent1_ r, LgMultiple Word64 (ValidatedElem v)) => Persistent (WordNArrayRef v r) instance LgPersistent1_ BasicRef instance Persistent (BasicRef w) instance (Typeable a, Persistent a) => Persistent (DRef a) instance (Persistent (a -> c), Persistent (b -> c)) => Persistent (Either a b -> c) instance Persistent c => Persistent (() -> c) instance Persistent DiffTime instance Persistent UTCTime instance Persistent Day instance (Ord k, Persistent k) => Persistent (Set k) instance (Ord k, Persistent k, Persistent v) => Persistent (Map k v) instance Persistent a => Persistent (Len u a) instance (RefPersistent r, Persistent1 r, Typeable a, Persistent a) => Persistent (CRef r a) instance (Persistent1 ra, Persistent1 rb, Typeable a, Persistent a) => Persistent (Sum ra rb a) instance (Persistent1 r, Typeable a, Persistent a) => Persistent (IRef r a) instance Persistent h => Persistent (Skein512Digest h) instance Persistent MD5Digest instance Persistent Word128 instance Persistent W64Validator instance Persistent W32Validator instance Persistent a => Persistent (Ref0 a) instance Persistent a => Persistent [a] instance Persistent a => Persistent (Maybe a) instance RWordC Word64 n => Persistent (RWord Word64 n) instance RWordC Word32 n => Persistent (RWord Word32 n) instance RWordC Word16 n => Persistent (RWord Word16 n) instance RWordC Word8 n => Persistent (RWord Word8 n) instance (Integral a, Persistent a) => Persistent (Ratio a) instance Persistent Integer instance Persistent Double instance Persistent Float instance Persistent Int64 instance Persistent Int32 instance Persistent Int16 instance Persistent Int8 instance Persistent Word64 instance Persistent Word32 instance Persistent Word16 instance Persistent Word8 instance Persistent Ordering instance (Persistent a1, Persistent a2, Persistent a3, Persistent a4, Persistent a5, Persistent a6) => Persistent (a1, a2, a3, a4, a5, a6) instance (Persistent a1, Persistent a2, Persistent a3, Persistent a4, Persistent a5) => Persistent (a1, a2, a3, a4, a5) instance (Persistent a1, Persistent a2, Persistent a3, Persistent a4) => Persistent (a1, a2, a3, a4) instance (Persistent a1, Persistent a2, Persistent a3) => Persistent (a1, a2, a3) instance (Persistent a1, Persistent a2) => Persistent (a1, a2) instance (Persistent a, Persistent b) => Persistent (Either a b) instance Persistent Char instance Persistent Bool instance Persistent () instance (Persistent1 ra, Persistent1 rb) => Persistent1 (Sum ra rb) instance (RefPersistent r, Persistent1 r) => Persistent1 (CRef r) instance Persistent1 r => Persistent1 (IRef r) instance Persistent1 DRef instance Persistent1 Ref0 instance RefPersistent r => RefPersistent (IRef r) instance RefPersistent DRef instance RefPersistent r => RefPersistent (CRef r) instance RefPersistent Ref0 instance InjectionACofunctor Persister module Database.Perdure.Rev -- | A data type which is equivalent to Either, but which is persisted in -- an open manner which allows us to chain new variants on the left. As -- more variants are added (going from NoRev to V1 :> NoRev and then -- to V2 :> V1 :> NoRev), the persisted representation gets -- expanded without breaking the representation of previous variants. We -- do not use Either because of the risk of persisting it in the standard -- manner and therefore losing upgradability. data (:>) a b Current :: a -> :> a b Previous :: b -> :> a b onRev :: (a -> z) -> (b -> z) -> (a :> b) -> z -- | An uninhabited type used as the last (rightmost) type in chains of -- '(:>)' data NoRev onNoRev :: NoRev -> z -- | Converts a chain of revisions to the Current type, given a way -- to convert the Previous type to the Current type. toCurrent :: (b -> a) -> (a :> b) -> a toOnlyRev :: (a :> NoRev) -> a -- | The persister for '(:>)' first writes out the numeric index, from -- the right, in the chain of revisions. This way the chain of -- alternative revisions can lengthen without changing the indices of -- past revisions. revPersister :: PersistentRev a => Persister a -- | This is not a legal lens since it violates the law which says that -- setting back what you got must have no effect. Here it is almost true -- since the only effect it has is to upgrade to the current -- representation, an idempotent change for a semantically equivalent -- value. latestLens :: (b -> a) -> Lens (a :> b) a instance Typeable2 :> instance Typeable NoRev instance PersistentRev (b :> r) => Persistent (b :> r) instance Persistent NoRev instance (Persistent b, PersistentRev r) => PersistentRev (b :> r) instance PersistentRev NoRev instance Rev b => Rev (a :> b) instance Rev NoRev module Database.Perdure.Deref class Deref r derefIO :: Deref r => r a -> IO a deref :: Deref r => r a -> a derefEq :: (Deref r, Eq a) => r a -> r a -> Bool instance (Deref r, Eq a) => Eq (CRef r a) instance (Deref r, Show a) => Show (CRef r a) instance Deref r => Deref (CRef r) instance Show a => Show (Ref0 a) instance Deref Ref0 instance (Deref r, Show t) => Show (IRef r t) instance (Deref r, Eq t) => Eq (IRef r t) instance Deref r => Deref (IRef r) instance (Deref ra, Deref rb) => Deref (Sum ra rb) instance Eq a => Eq (DRef a) instance Show a => Show (DRef a) instance Deref DRef module Database.Perdure.Ref class Deref r => Ref r refIO :: Ref r => a -> IO (r a) ref :: Ref r => a -> r a refLens :: Ref r => Lens (r a) a instance Deref r => Functor (CRef r) instance Deref r => Ref (CRef r) instance Ref Ref0 instance Ref r => Ref (IRef r) module Database.Perdure.SizeRef -- | A reference type which automatically puts its referent is a separately -- loadable allocation when that allocation's size is greater than 2^n -- bytes. data SizeRef n a instance Structured (SizeRef n0 a0) instance Typeable2 SizeRef instance Show a => Show (SizeRef n a) instance Deref (SizeRef n) instance Nat n => RefPersistent (SizeRef n) instance Persistent1 (SizeRef n) module Database.Perdure.Data.Map -- | Unlike Data.Map, this Map does not support constant time size -- computation data Map k a -- | General update/lookup at a single key. The State monad used supports -- non-modification. Nothing denotes the absence of a value. updateM :: Ord k => k -> State (Maybe a) b -> State (Map k a) b empty :: Map k a null :: Map k a -> Bool lookup :: Ord k => k -> Map k a -> Maybe a insert :: Ord k => k -> a -> Map k a -> Map k a insertWith :: Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a delete :: Ord k => k -> Map k a -> Map k a foldlWithKey :: (z -> k -> a -> z) -> z -> Map k a -> z foldrWithKey :: (k -> a -> z -> z) -> z -> Map k a -> z fromList :: Ord k => [(k, a)] -> Map k a toList :: Map k a -> [(k, a)] assocs :: Map k a -> [(k, a)] elems :: Map k a -> [a] maxKey :: Map k a -> Maybe k scan :: (k -> a -> z) -> (k -> z -> z -> z) -> Map k a -> Maybe z mapLens :: Ord k => k -> Lens (Map k a) (Maybe a) instance Structured (Upper t0 k0 a0) instance Structured (Reference t0 k0 a0) instance Structured (Node t0 k0 a0) instance Structured (Leaf k0 a0) instance Structured (Tree t0 k0 a0) instance Structured (Map k0 a0) instance Typeable2 Leaf instance Typeable2 Map instance Functor (t k) => Functor (Reference t k) instance Functor (Leaf k) instance Functor (t k) => Functor (Upper t k) instance Functor (t k) => Functor (Node t k) instance Functor (t k) => Functor (Tree t k) instance Functor (Map k) instance Functor Modify instance (Persistent k, Persistent (t k a)) => Persistent (Upper t k a) instance (Persistent (t k a), Typeable (t k a)) => Persistent (Reference t k a) instance (Persistent k, Persistent (t k a)) => Persistent (Node t k a) instance Persistent a => Persistent (Leaf k a) instance (Persistent k, Persistent (t k a), Typeable2 t, Typeable k, Typeable a) => Persistent (Tree t k a) instance (Persistent a, Persistent k, Typeable k, Typeable a) => Persistent (Map k a) instance (Eq k, Eq a) => Eq (Map k a) instance Tr t => Tr (Node t) instance Tr t => Tr (Reference t) instance Tr Leaf instance Typeable2 t => Typeable2 (Node t) instance Typeable2 t => Typeable2 (Reference t) instance (Show k, Show a) => Show (Map k a) module Database.Perdure.History -- | The History type is used as the state type so as to keep some -- snapshots of the past, in case data is lost due to a programming error -- by the application developer. It is an homogenous collection so the -- argument type has to take care of versionning. To avoid needless -- reserialization of the past states, the argument type should be a Ref -- type. -- -- We keep the last n samples inserted, then n samples keeping one out of -- every two, then n samples keeping one out of every four... Currently n -- is hard coded to 2. data History a initial :: a -> History a -- | The history is never empty, so it is safe to get the current sample. current :: History a -> a -- | Add a newer state to the history insert :: a -> History a -> History a -- | Changes a transformation on a into a transformation on -- 'History a'. Adds a new state into the History. updateHistory :: Monad m => StateT a m b -> StateT (History a) m b -- | Changes a transformation on a into a transformation on -- 'History a'. Adds a new state into the History, unless the -- state has not changed. Uses State. updateHistoryM :: Monad m => StateT a m b -> StateT (History a) m b instance Structured (Queue a0) instance Structured (History a0) instance Typeable1 Queue instance Typeable1 History instance Show a => Show (Queue a) instance Show a => Show (History a) instance (Typeable a, Persistent a) => Persistent (Queue a) instance (Typeable a, Persistent a) => Persistent (History a) module Database.Perdure.Internal type SerializerContext l c = (MVar Cache, l, CountDest c) cSer :: (Multiset c, Allocator l) => Persister a -> SerializerContext l c -> (a -> Dest -> IO z) -> a -> Dest -> IO z -- | The passed Persister must hace no references serializeToArray :: AllocCopy w => Persister a -> a -> PrimArray Pinned w type Address = Len Word64 Word64 cDeser :: Persister a -> DeserializerContext -> Deserializer Free a deserializeFromArray :: (Allocation f, Allocation df, Deserializable w) => Deserializer df a -> ArrayRange (PrimArray f w) -> DeserOut a deserializeFromFullArray :: (Allocation f, Allocation df, Deserializable w, LgMultiple w Bool, Prim w) => Deserializer df a -> ArrayRange (PrimArray f w) -> a -- | The passed persister must have no references unsafeSeqDeserializer :: Persister a -> Deserializer Free a newtype Deserializer f a Deserializer :: (Len Bool Word -> PrimArray f Word -> DeserOut a) -> Deserializer f a deserialize :: Deserializer f a -> Len Bool Word -> PrimArray f Word -> DeserOut a class Deserializable a deserInput :: (Deserializable a, Allocation f, Allocation f') => ArrayRange (PrimArray f a) -> ArrayRange (PrimArray f' Word) data DeserOut a DeserOut :: !a -> {-# UNPACK #-} !Len Bool Word -> DeserOut a deserValue :: DeserOut a -> !a deserPos :: DeserOut a -> {-# UNPACK #-} !Len Bool Word data CRef r a Refed :: !r a -> CRef r a ToRef :: !a -> CRef r a onCRef :: (r a -> b) -> (a -> b) -> CRef r a -> b prnf :: Persister a -> a -> () class SyncableStoreFile f => StoreFile f where type family StoreRef f :: * -> * storeFileWrite :: (StoreFile f, Endian w) => f -> Len Word64 Word64 -> Endianness -> [PrimArray Pinned w] -> IO (StoreRef f w) storeFileRead :: (StoreFile f, Validator v, ValidatedElem v ~ w, Endian w, LgMultiple w Word8) => f -> StoreRef f w -> Endianness -> v -> Async (Maybe (ArrayRange (PrimArray Pinned w))) () class SyncableStoreFile f storeFileSync :: SyncableStoreFile f => f -> IO () -> IO () storeFileFullBarrier :: SyncableStoreFile f => f -> IO () await0 :: (IO () -> IO a) -> IO a await1 :: Async a () -> IO a refSpan :: LgMultiple Word64 w => BasicRef w -> Span -- | 64-bit unsigned integer type data Word64 :: * data BasicRef w BasicRef :: {-# UNPACK #-} !Len Word64 Word64 -> {-# UNPACK #-} !Len w Word32 -> BasicRef w refStart :: BasicRef w -> {-# UNPACK #-} !Len Word64 Word64 refSize :: BasicRef w -> {-# UNPACK #-} !Len w Word32 -- | The PState represents the whole state of the database. It is needed to -- perform updates. data PState a -- | Root persisted data. The a type parameter is the user -- persisted data type. data Root a Root :: StateId -> Maybe (RootValues a) -> RootValues a -> Root a rootId :: Root a -> StateId rootDecr :: Root a -> Maybe (RootValues a) rootScan :: Root a -> RootValues a data RootValues a RootValues :: CDRef SpaceBook -> CDRef a -> RootValues a rootCS :: RootValues a -> CDRef SpaceBook rootValue :: RootValues a -> CDRef a -- | We reserve the option of growing roots to 1MB, so use this as a -- minimum distance between the various RootAddress in RootLocation rootAllocSize :: Len Word64 Word32 -- | Writes an initial state (creates a new database). Most often the -- passed a will be a fresh unpersisted value. This is always -- safe. However it is legal for parts of a to be already -- persisted, but they must only use allocations within the passed -- SpaceTree. To read the state of an existing database use readState. initState :: (Persistent a, Typeable a) => RootLocation -> SpaceTree -> a -> IO (PState a) -- | Reads the state of an existing database. It only reads the root, and -- the rest is lazy loaded. The RootLocation must match the one use when -- writing. On failure it returns Nothing. readState :: (Persistent a, Typeable a) => RootLocation -> IO (Maybe (PState a)) -- | Takes the current state and the new value to be written, and writes -- and returns a new state. Writing is strict so make sure you do not -- have cycles in the value to be written. After writing, you should no -- longer use the value you passed in, but instead use the equivalent -- value present in the in the returned state. That new equivalent value -- knows where it is stored and will be lazily loadable. The value just -- written will be partially or totally in the cache. IMPORTANT: This -- call overwrites the value that was in the state passed as input, so -- you should not use it after this call returns. However it is safe for -- this call to use it implicitly, because often the new value will be a -- function of the old one, and the strict write process will force parts -- of the old value to be read. If by accident you do use a value which -- was overwritten, its digests will be incorrect (with very high -- probability) and deref will return error. This calls collectState -- implicity once every 1000 calls. We will make this optional in future -- revisions. writeState :: (Persistent a, Typeable a) => a -> PState a -> IO (PState a) -- | Writes a new state if the passed state change requires it. The StateT -- monad used here is like the usual StateT monad but it has an -- additional unchanged case which allow us to avoid needless -- writes. updateState :: (Persistent a, Typeable a, MonadIO m) => StateT a m b -> StateT (PState a) m b -- | Like updateState but the updater has access to the input PState -- throught an additional ReaderT updateStateRead :: (Persistent a, Typeable a, MonadIO m) => StateT a (ReaderT (PState a) m) b -> StateT (PState a) m b -- | Collects the garbage accumulated by the calls to writeState. Uses -- reference counting: first does an increment pass on the current value, -- and then does a decrement pass on the value that was present at the -- last collection. Only new allocations are scanned in the increment -- pass, not what was already allocated at the last collection. The -- decrement pass only traverses the allocations that need to be -- deallocated. collectState :: (Persistent a, Typeable a) => PState a -> IO (PState a) collectStateM :: (Persistent a, Typeable a) => StateT (PState a) IO () -- | Create an empty cache of the specified size (number of dereferenced -- DRefs). Note that eventually we would like a cache with a size -- measured in bytes, for a better prediction of memory consumption. emptyCache :: Integer -> Cache class Space a emptySpace :: Space a => a removeSpan :: Space a => Span -> a -> a addSpan :: Space a => Span -> a -> a findSpan :: Space a => Word64 -> a -> [Span] isFreeSpace :: Space a => Word64 -> a -> Bool type Span = SortedPair Word64 data CachedFile CachedFile :: ReplicatedFile -> (MVar Cache) -> CachedFile -- | The RootLocation specifies where roots are written, and provides a -- cache. data RootLocation RootLocation :: CachedFile -> [RootAddress] -> RootLocation newtype RootAddress RootAddress :: Address -> RootAddress getRootAddress :: RootAddress -> Address stateValue :: PState a -> a incr :: Persister a -> a -> SpaceBook -> SpaceBook -- | Has effects through unsafePerformIO on the caches stored in the DRefs -- (removes any cache entries for the deallocated allocations). decr :: Persister a -> a -> SpaceBook -> SpaceBook data MapMultiset a data SpaceBook SpaceBook :: !MapMultiset Address -> !SpaceTree -> SpaceBook bookCount :: SpaceBook -> !MapMultiset Address bookSpace :: SpaceBook -> !SpaceTree module Database.Perdure -- | A file or raw device where we can persist bytes. data LocalStoreFile -- | Like nesting multiple calls to withRawDeviceStoreFile. withRawDeviceStoreFiles :: [FilePath] -> ([LocalStoreFile] -> IO a) -> ErrorT String IO a -- | Opens the specified raw device as a LocalStoreFile, runs the provided -- function and closes the device. Do not make concurrent calls on the -- same device, place concurrency in the passed function. withRawDeviceStoreFile :: FilePath -> (LocalStoreFile -> IO a) -> ErrorT String IO a -- | Opens the specified file as a LocalStoreFile, runs the provided -- function and closes the file. Do not make concurrent calls on the same -- file, place concurrency in the passed function. withFileStoreFile :: FilePath -> (LocalStoreFile -> IO a) -> ErrorT String IO a -- | A list of LocalStoreFile to be used as replicates. We write to -- all replicates and read from the first one that reports no error. newtype ReplicatedFile ReplicatedFile :: [LocalStoreFile] -> ReplicatedFile -- | Wraps a ReplicatedFile with cache of a given size (number of -- dereferenced DRefs) newCachedFile :: Integer -> ReplicatedFile -> IO CachedFile -- | The RootLocation specifies where roots are written, and provides a -- cache. data RootLocation -- | At the moment this is the only way to create a rootLocation. The root -- of the database will be located in one of two reserved locations at -- the start of the specified files. defaultRootLocation :: CachedFile -> RootLocation -- | Represents a persisted database. Contains a (ram-only) lock to -- sequence multithreaded operations, so only one PVar must be -- created per RootLocation. data PVar s -- | Attempts to open a PVar by reading at the given RootLocation. -- Do not open the same location multiple times, share the PVar instead. openPVar :: (Typeable s, Persistent s) => RootLocation -> IO (Maybe (PVar s)) -- | Creates a PVar with the specified initial state. Writes at the -- specified location, using the given maximum usable space (in bytes). createPVar :: (Typeable s, Persistent s) => s -> Word64 -> RootLocation -> IO (PVar s) -- | Persist a state change updatePVar :: (Typeable s, Persistent s) => PVar s -> StateT s IO a -> IO a -- | This function allows read access to the bookkeeping structures of the -- database. The PState type is subject to change. updateInspectPVar :: (Typeable s, Persistent s) => PVar s -> StateT s (ReaderT (PState s) IO) a -> IO a