lsm-tree-1.0.0.0: test/Database/LSMTree/Class.hs
{-# LANGUAGE TypeFamilies #-}
-- | An abstraction of the LSM API, instantiated by both the real
-- implementation and a model (see "Database.LSMTree.Model.IO").
module Database.LSMTree.Class (
IsTable (..)
, withTableNew
, withTableFromSnapshot
, withTableDuplicate
, withTableUnion
, withTableUnions
, withCursor
, module Common
, module Types
) where
import Control.Monad (void)
import Control.Monad.Class.MonadThrow (MonadThrow (..))
import Data.Kind (Constraint, Type)
import Data.List.NonEmpty (NonEmpty)
import Data.Typeable (Proxy (..))
import qualified Data.Vector as V
import Database.LSMTree as Types (Entry (..), LookupResult (..),
ResolveAsFirst (..), ResolveValue (..), Update (..))
import qualified Database.LSMTree as R
import Database.LSMTree.Class.Common as Common
import qualified Database.LSMTree.Internal.Paths as RIP
import qualified Database.LSMTree.Internal.Types as RT (Table (..))
import qualified Database.LSMTree.Internal.Unsafe as RU (SessionEnv (..),
Table (..), withKeepSessionOpen)
import Test.Util.FS (flipRandomBitInRandomFileHardlinkSafe)
import Test.Util.QC (Choice)
-- | Class abstracting over table operations.
--
type IsTable :: ((Type -> Type) -> Type -> Type -> Type -> Type) -> Constraint
class (IsSession (Session h)) => IsTable h where
type Session h :: (Type -> Type) -> Type
type TableConfig h :: Type
type BlobRef h :: (Type -> Type) -> Type -> Type
type Cursor h :: (Type -> Type) -> Type -> Type -> Type -> Type
newTableWith ::
( IOLike m
, C k v b
)
=> TableConfig h
-> Session h m
-> m (h m k v b)
closeTable ::
( IOLike m
, C k v b
)
=> h m k v b
-> m ()
lookups ::
( IOLike m
, C k v b
)
=> h m k v b
-> V.Vector k
-> m (V.Vector (LookupResult v (BlobRef h m b)))
rangeLookup ::
( IOLike m
, C k v b
)
=> h m k v b
-> Range k
-> m (V.Vector (Entry k v (BlobRef h m b)))
newCursor ::
( IOLike m
, C k v b
)
=> Maybe k
-> h m k v b
-> m (Cursor h m k v b)
closeCursor ::
( IOLike m
, C k v b
)
=> proxy h
-> Cursor h m k v b
-> m ()
readCursor ::
( IOLike m
, C k v b
)
=> proxy h
-> Int
-> Cursor h m k v b
-> m (V.Vector (Entry k v (BlobRef h m b)))
retrieveBlobs ::
( IOLike m
, CB b
)
=> proxy h
-> Session h m
-> V.Vector (BlobRef h m b)
-> m (V.Vector b)
updates ::
( IOLike m
, C k v b
)
=> h m k v b
-> V.Vector (k, Update v b)
-> m ()
inserts ::
( IOLike m
, C k v b
)
=> h m k v b
-> V.Vector (k, v, Maybe b)
-> m ()
deletes ::
( IOLike m
, C k v b
)
=> h m k v b
-> V.Vector k
-> m ()
upserts ::
( IOLike m
, C k v b
)
=> h m k v b
-> V.Vector (k, v)
-> m ()
saveSnapshot ::
( IOLike m
, C k v b
)
=> SnapshotName
-> SnapshotLabel
-> h m k v b
-> m ()
corruptSnapshot ::
( IOLike m
)
=> Choice
-> SnapshotName
-> h m k v b
-> m ()
openTableFromSnapshot ::
( IOLike m
, C k v b
)
=> Session h m
-> SnapshotName
-> SnapshotLabel
-> m (h m k v b)
duplicate ::
( IOLike m
, C k v b
)
=> h m k v b
-> m (h m k v b)
union ::
( IOLike m
, C k v b
)
=> h m k v b
-> h m k v b
-> m (h m k v b)
unions ::
( IOLike m
, C k v b
)
=> NonEmpty (h m k v b)
-> m (h m k v b)
remainingUnionDebt ::
( IOLike m
, C k v b
)
=> h m k v b
-> m UnionDebt
supplyUnionCredits ::
( IOLike m
, C k v b
)
=> h m k v b
-> UnionCredits
-> m UnionCredits
withTableNew :: forall h m k v b a.
(IOLike m, IsTable h, C k v b)
=> Session h m
-> TableConfig h
-> (h m k v b -> m a)
-> m a
withTableNew sesh conf = bracket (newTableWith conf sesh) closeTable
withTableFromSnapshot :: forall h m k v b a.
(IOLike m, IsTable h, C k v b)
=> Session h m
-> SnapshotLabel
-> SnapshotName
-> (h m k v b -> m a)
-> m a
withTableFromSnapshot sesh label snap = bracket (openTableFromSnapshot sesh snap label) closeTable
withTableDuplicate :: forall h m k v b a.
(IOLike m, IsTable h, C k v b)
=> h m k v b
-> (h m k v b -> m a)
-> m a
withTableDuplicate table = bracket (duplicate table) closeTable
withTableUnion :: forall h m k v b a.
(IOLike m, IsTable h, C k v b)
=> h m k v b
-> h m k v b
-> (h m k v b -> m a)
-> m a
withTableUnion table1 table2 = bracket (table1 `union` table2) closeTable
withTableUnions :: forall h m k v b a.
(IOLike m, IsTable h, C k v b)
=> NonEmpty (h m k v b)
-> (h m k v b -> m a)
-> m a
withTableUnions tables = bracket (unions tables) closeTable
withCursor :: forall h m k v b a.
(IOLike m, IsTable h, C k v b)
=> Maybe k
-> h m k v b
-> (Cursor h m k v b -> m a)
-> m a
withCursor offset tbl = bracket (newCursor offset tbl) (closeCursor (Proxy @h))
{-------------------------------------------------------------------------------
Real instance
-------------------------------------------------------------------------------}
-- | Snapshot corruption for the real instance.
-- Implemented here, instead of as part of the public API.
rCorruptSnapshot ::
IOLike m
=> Choice
-> SnapshotName
-> R.Table m k v b
-> m ()
rCorruptSnapshot choice name (RT.Table t) =
RU.withKeepSessionOpen (RU.tableSession t) $ \seshEnv ->
let hfs = RU.sessionHasFS seshEnv
root = RU.sessionRoot seshEnv
namedSnapDir = RIP.getNamedSnapshotDir (RIP.namedSnapshotDir root name)
in void $ flipRandomBitInRandomFileHardlinkSafe hfs choice namedSnapDir
instance IsTable R.Table where
type Session R.Table = R.Session
type TableConfig R.Table = R.TableConfig
type BlobRef R.Table = R.BlobRef
type Cursor R.Table = R.Cursor
newTableWith = R.newTableWith
closeTable = R.closeTable
lookups = R.lookups
updates = R.updates
inserts = R.inserts
deletes = R.deletes
upserts = R.upserts
rangeLookup = R.rangeLookup
retrieveBlobs _ = R.retrieveBlobs
newCursor = maybe R.newCursor (flip R.newCursorAtOffset)
closeCursor _ = R.closeCursor
readCursor _ = R.take
saveSnapshot = R.saveSnapshot
corruptSnapshot = rCorruptSnapshot
openTableFromSnapshot = R.openTableFromSnapshot
duplicate = R.duplicate
union = R.incrementalUnion
unions = R.incrementalUnions
remainingUnionDebt = R.remainingUnionDebt
supplyUnionCredits = R.supplyUnionCredits