-- 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:
--
--
-- - Memory references are not tracked. At this point it is assumed
-- that the current persisted state is the only root for persistent GC
-- purposes. So no reference must be kept to previous persisted states.
-- This is not enforced by the API so care is needed. Otherwise
-- dereferencing a dangling reference might fail (but will not return bad
-- data thanks to the digest).
-- - Care is needed by the application developers to ensure they do not
-- change type persisters. The persisters themselves are not persisted so
-- the library cannot verify that it is using the same persister as with
-- previous executions. There are two problems with persisting
-- persisters: they are not acyclic, and they refer to user specified
-- bijections. We encourage you to treat types and persisters as
-- immutable code. Put each persisted type in a distinct file, such as
-- User0.hs. When a revision is needed create User1.hs.
-- - The current allocation strategy is trivial. We must add a new node
-- type in the free space representation that will allow us to search
-- efficiently for a block of sufficient size. We currently scan all free
-- intervals from the start. This relatively simple fix should be done
-- shortly, but until it is done the library will not be appropriate for
-- databases beyond ~100MB.
-- - Reclamation of free space simply occurs after some fixed number of
-- state writes.
-- - Replication is local only at the moment. We write to all replicas,
-- and read from the first correct one. We need to support remote storage
-- which can become temporarily unavailable, and user controlled policies
-- regarding those occurrences. Note that quorum voting is not needed for
-- replica control of most (non-root) allocations because digests are
-- contained in the references and we can detect when we are reading
-- outdated data.
-- - Only raw disks configured for write-through caching will guarantee
-- atomicity. Any other setup introduces more layers, such as file
-- systems, which could lie about when data has actually been written out
-- to disk. Perhaps our view is too pessimistic, and with proper action
-- by this library other storage types could be supported. Currently file
-- based storage is only supported for testing purposes and should not be
-- used for live databases.
-- - A directory of bad sectors with alternative locations will need to
-- be added. It will only need to be looked-up and updated for reads with
-- incorrect digests.
-- - Sharing is not realized unless it happens in separate steps.
-- During the writing of some state, a reference creates an allocation.
-- In subsequent states we can refer to that same reference in multiple
-- locations and sharing will occur. If a reference is shared in memory
-- before being first written out, it will be written out multiple times
-- without sharing of the referent. In the future we may rely on
-- System.Mem.StableName to improve on that.
-- - Currently, references are local. We would like to be able to refer
-- to the allocations of another thread or process. This will be a more
-- involved addition so it will require distributed garbage collection
-- such as weighted reference counting.
-- - Documentation and examples are not sufficient and should be our
-- first focus. For a minimal example, look at
-- Database.Perdure.TestState.testStatesF in exe-src.
--
--
-- 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