diff --git a/lsm-tree.cabal b/lsm-tree.cabal index bcb6d7e10..4861fc075 100644 --- a/lsm-tree.cabal +++ b/lsm-tree.cabal @@ -343,9 +343,12 @@ test-suite lsm-tree-test hs-source-dirs: test main-is: Main.hs other-modules: + Database.LSMTree.Class + Database.LSMTree.Class.Common Database.LSMTree.Class.Monoidal Database.LSMTree.Class.Normal Database.LSMTree.Model + Database.LSMTree.Model.IO Database.LSMTree.Model.IO.Monoidal Database.LSMTree.Model.IO.Normal Database.LSMTree.Model.Session @@ -380,10 +383,10 @@ test-suite lsm-tree-test Test.Database.LSMTree.Internal.Vector.Growing Test.Database.LSMTree.Model.Table Test.Database.LSMTree.Monoidal - Test.Database.LSMTree.Normal.StateMachine - Test.Database.LSMTree.Normal.StateMachine.DL - Test.Database.LSMTree.Normal.StateMachine.Op - Test.Database.LSMTree.Normal.UnitTests + Test.Database.LSMTree.StateMachine + Test.Database.LSMTree.StateMachine.DL + Test.Database.LSMTree.StateMachine.Op + Test.Database.LSMTree.UnitTests Test.System.Posix.Fcntl.NoCache Test.Util.FS Test.Util.Orphans diff --git a/src-extras/Database/LSMTree/Extras/Generators.hs b/src-extras/Database/LSMTree/Extras/Generators.hs index 2a7009181..463adc9cf 100644 --- a/src-extras/Database/LSMTree/Extras/Generators.hs +++ b/src-extras/Database/LSMTree/Extras/Generators.hs @@ -46,6 +46,7 @@ import Data.List (sort) import qualified Data.Primitive.ByteArray as BA import qualified Data.Vector.Primitive as VP import Data.Word +import qualified Database.LSMTree as Unified import Database.LSMTree.Common (Range (..)) import Database.LSMTree.Extras import Database.LSMTree.Extras.Index (Append (..)) @@ -75,6 +76,25 @@ import Test.QuickCheck.Instances () Common LSMTree types -------------------------------------------------------------------------------} +instance (Arbitrary v, Arbitrary blob) => Arbitrary (Unified.Update v blob) where + arbitrary = QC.arbitrary2 + shrink = QC.shrink2 + +instance Arbitrary2 Unified.Update where + liftArbitrary2 genVal genBlob = frequency + [ (10, Unified.Insert <$> genVal <*> liftArbitrary genBlob) + , (5, Unified.Mupsert <$> genVal) + , (1, pure Unified.Delete) + ] + + liftShrink2 shrinkVal shrinkBlob = \case + Unified.Insert v blob -> + Unified.Delete + : map (uncurry Unified.Insert) + (liftShrink2 shrinkVal (liftShrink shrinkBlob) (v, blob)) + Unified.Mupsert v -> Unified.Insert v Nothing : map Unified.Mupsert (shrinkVal v) + Unified.Delete -> [] + instance (Arbitrary v, Arbitrary blob) => Arbitrary (Normal.Update v blob) where arbitrary = QC.arbitrary2 shrink = QC.shrink2 diff --git a/test/Database/LSMTree/Class.hs b/test/Database/LSMTree/Class.hs new file mode 100644 index 000000000..20cca462f --- /dev/null +++ b/test/Database/LSMTree/Class.hs @@ -0,0 +1,274 @@ +{-# LANGUAGE TypeFamilies #-} + +-- | An abstraction of the normal LSM API, instantiated by both the real +-- implementation and a model (see "Database.LSMTree.Model.IO"). +module Database.LSMTree.Class ( + IsTable (..) + , withTableNew + , withTableFromSnapshot + , withTableDuplicate + , withCursor + , module Common + , module Types + ) where + +import Control.Monad.Class.MonadThrow (MonadThrow (..)) +import Data.Kind (Constraint, Type) +import Data.Typeable (Proxy (..)) +import qualified Data.Vector as V +import Database.LSMTree as Types (LookupResult (..), QueryResult (..), + ResolveValue, Update (..)) +import qualified Database.LSMTree as R +import Database.LSMTree.Class.Common as Common + +-- | 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 + + new :: + ( IOLike m + , C k v b + ) + => Session h m + -> TableConfig h + -> m (h m k v b) + + close :: + ( IOLike m + , C k v b + ) + => h m k v b + -> m () + + lookups :: + ( IOLike m + , SerialiseKey k + , SerialiseValue v + , ResolveValue v + , C k v b + ) + => h m k v b + -> V.Vector k + -> m (V.Vector (LookupResult v (BlobRef h m b))) + + rangeLookup :: + ( IOLike m + , SerialiseKey k + , SerialiseValue v + , ResolveValue v + , C k v b + ) + => h m k v b + -> Range k + -> m (V.Vector (QueryResult k v (BlobRef h m b))) + + newCursor :: + ( IOLike m + , SerialiseKey k + , 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 + , SerialiseKey k + , SerialiseValue v + , ResolveValue v + , C k v b + ) + => proxy h + -> Int + -> Cursor h m k v b + -> m (V.Vector (QueryResult k v (BlobRef h m b))) + + retrieveBlobs :: + ( IOLike m + , SerialiseValue b + , C_ b + ) + => proxy h + -> Session h m + -> V.Vector (BlobRef h m b) + -> m (V.Vector b) + + updates :: + ( IOLike m + , SerialiseKey k + , SerialiseValue v + , SerialiseValue b + , ResolveValue v + , C k v b + ) + => h m k v b + -> V.Vector (k, Update v b) + -> m () + + inserts :: + ( IOLike m + , SerialiseKey k + , SerialiseValue v + , SerialiseValue b + , ResolveValue v + , C k v b + ) + => h m k v b + -> V.Vector (k, v, Maybe b) + -> m () + + deletes :: + ( IOLike m + , SerialiseKey k + , SerialiseValue v + , SerialiseValue b + , ResolveValue v + , C k v b + ) + => h m k v b + -> V.Vector k + -> m () + + mupserts :: + ( IOLike m + , SerialiseKey k + , SerialiseValue v + , SerialiseValue b + , ResolveValue v + , C k v b + ) + => h m k v b + -> V.Vector (k, v) + -> m () + + createSnapshot :: + ( IOLike m + , SerialiseKey k + , SerialiseValue v + , SerialiseValue b + , ResolveValue v + , C k v b + ) + => SnapshotLabel + -> SnapshotName + -> h m k v b + -> m () + + openSnapshot :: + ( IOLike m + , SerialiseKey k + , SerialiseValue v + , ResolveValue v + , SerialiseValue b + , C k v b + ) + => Session h m + -> SnapshotLabel + -> SnapshotName + -> 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 + , ResolveValue v + , SerialiseValue v + , C k v b + ) + => h m k v b + -> h m k v b + -> m (h m k v b) + +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 (new sesh conf) close + +withTableFromSnapshot :: forall h m k v b a. + ( IOLike m, IsTable h + , SerialiseKey k, SerialiseValue v, SerialiseValue b, ResolveValue v + , C k v b + ) + => Session h m + -> SnapshotLabel + -> SnapshotName + -> (h m k v b -> m a) + -> m a +withTableFromSnapshot sesh label snap = bracket (openSnapshot sesh label snap) close + +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) close + +withCursor :: forall h m k v b a. + ( IOLike m + , IsTable h + , SerialiseKey k + , C k v b + ) + => Maybe k + -> h m k v b + -> (Cursor h m k v b -> m a) + -> m a +withCursor offset hdl = bracket (newCursor offset hdl) (closeCursor (Proxy @h)) + +{------------------------------------------------------------------------------- + Real instance +-------------------------------------------------------------------------------} + +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 + + new = R.new + close = R.close + lookups = R.lookups + updates = R.updates + inserts = R.inserts + deletes = R.deletes + mupserts = R.mupserts + + rangeLookup = R.rangeLookup + retrieveBlobs _ = R.retrieveBlobs + + newCursor = maybe R.newCursor R.newCursorAtOffset + closeCursor _ = R.closeCursor + readCursor _ = R.readCursor + + createSnapshot = R.createSnapshot + openSnapshot sesh snap = R.openSnapshot sesh R.configNoOverride snap + + duplicate = R.duplicate + union = R.union diff --git a/test/Database/LSMTree/Class/Common.hs b/test/Database/LSMTree/Class/Common.hs new file mode 100644 index 000000000..ce98f0e08 --- /dev/null +++ b/test/Database/LSMTree/Class/Common.hs @@ -0,0 +1,72 @@ +{-# LANGUAGE TypeFamilies #-} + +module Database.LSMTree.Class.Common ( + C + , C_ + , IsSession (..) + , SessionArgs (..) + , withSession + , module Types + ) where + +import Control.Monad.Class.MonadThrow (MonadThrow (..)) +import Control.Tracer (nullTracer) +import Data.Kind (Constraint, Type) +import Data.Typeable (Typeable) +import Database.LSMTree.Common as Types (IOLike, Range (..), + SerialiseKey, SerialiseValue, SnapshotLabel (..), + SnapshotName) +import qualified Database.LSMTree.Common as R +import System.FS.API (FsPath, HasFS) +import System.FS.BlockIO.API (HasBlockIO) + +-- | Model-specific constraints +type C k v blob = (C_ k, C_ v, C_ blob) +type C_ a = (Show a, Eq a, Typeable a) + +-- | Class abstracting over session operations. +-- +type IsSession :: ((Type -> Type) -> Type) -> Constraint +class IsSession s where + data SessionArgs s :: (Type -> Type) -> Type + + openSession :: + IOLike m + => SessionArgs s m + -> m (s m) + + closeSession :: + IOLike m + => s m + -> m () + + deleteSnapshot :: + IOLike m + => s m + -> SnapshotName + -> m () + + listSnapshots :: + IOLike m + => s m + -> m [SnapshotName] + +withSession :: (IOLike m, IsSession s) => SessionArgs s m -> (s m -> m a) -> m a +withSession seshArgs = bracket (openSession seshArgs) closeSession + +{------------------------------------------------------------------------------- + Real instance +-------------------------------------------------------------------------------} + +instance IsSession R.Session where + data SessionArgs R.Session m where + SessionArgs :: + forall m h. Typeable h + => HasFS m h -> HasBlockIO m h -> FsPath + -> SessionArgs R.Session m + + openSession (SessionArgs hfs hbio dir) = do + R.openSession nullTracer hfs hbio dir + closeSession = R.closeSession + deleteSnapshot = R.deleteSnapshot + listSnapshots = R.listSnapshots diff --git a/test/Database/LSMTree/Class/Monoidal.hs b/test/Database/LSMTree/Class/Monoidal.hs index 94f08a218..4fc48d394 100644 --- a/test/Database/LSMTree/Class/Monoidal.hs +++ b/test/Database/LSMTree/Class/Monoidal.hs @@ -3,38 +3,26 @@ -- | An abstraction of the monoidal LSM API, instantiated by both the real -- implementation and a model (see "Database.LSMTree.Model.IO.Monoidal"). module Database.LSMTree.Class.Monoidal ( - C - , C_ - , IsSession (..) - , SessionArgs (..) - , withSession - , IsTable (..) + IsTable (..) , withTableNew , withTableFromSnapshot , withTableDuplicate , withTableUnion , withCursor + , module Common , module Types ) where import Control.Monad.Class.MonadThrow (MonadThrow (..)) import Data.Kind (Constraint, Type) -import Data.Typeable (Proxy (Proxy), Typeable) +import Data.Typeable (Proxy (..)) import qualified Data.Vector as V import Data.Void (Void) -import Database.LSMTree.Class.Normal (IsSession (..), - SessionArgs (..), withSession) -import Database.LSMTree.Common as Types (IOLike, Range (..), - SerialiseKey, SerialiseValue, SnapshotLabel (..), - SnapshotName) +import Database.LSMTree.Class.Common as Common import Database.LSMTree.Monoidal as Types (LookupResult (..), QueryResult (..), ResolveValue, Update (..)) import qualified Database.LSMTree.Monoidal as R --- | Model-specific constraints -type C k v blob = (C_ k, C_ v, C_ blob) -type C_ a = (Show a, Eq a, Typeable a) - -- | Class abstracting over table operations. -- type IsTable :: ((Type -> Type) -> Type -> Type -> Type) -> Constraint diff --git a/test/Database/LSMTree/Class/Normal.hs b/test/Database/LSMTree/Class/Normal.hs index dc21954f0..38dabe973 100644 --- a/test/Database/LSMTree/Class/Normal.hs +++ b/test/Database/LSMTree/Class/Normal.hs @@ -3,66 +3,23 @@ -- | An abstraction of the normal LSM API, instantiated by both the real -- implementation and a model (see "Database.LSMTree.Model.IO.Normal"). module Database.LSMTree.Class.Normal ( - C - , C_ - , IsSession (..) - , SessionArgs (..) - , withSession - , IsTable (..) + IsTable (..) , withTableNew , withTableFromSnapshot , withTableDuplicate , withCursor + , module Common , module Types ) where -import Control.Monad.Class.MonadThrow (bracket) -import Control.Tracer (nullTracer) +import Control.Monad.Class.MonadThrow (MonadThrow (..)) import Data.Kind (Constraint, Type) -import Data.Typeable (Proxy (Proxy), Typeable) +import Data.Typeable (Proxy (..)) import qualified Data.Vector as V -import Database.LSMTree.Common as Types (IOLike, Range (..), - SerialiseKey, SerialiseValue, SnapshotLabel (..), - SnapshotName) +import Database.LSMTree.Class.Common as Common import Database.LSMTree.Normal as Types (LookupResult (..), QueryResult (..), Update (..)) import qualified Database.LSMTree.Normal as R -import System.FS.API (FsPath, HasFS) -import System.FS.BlockIO.API (HasBlockIO) - --- | Model-specific constraints -type C k v blob = (C_ k, C_ v, C_ blob) -type C_ a = (Show a, Eq a, Typeable a) - --- | Class abstracting over session operations. --- -type IsSession :: ((Type -> Type) -> Type) -> Constraint -class IsSession s where - data SessionArgs s :: (Type -> Type) -> Type - - openSession :: - IOLike m - => SessionArgs s m - -> m (s m) - - closeSession :: - IOLike m - => s m - -> m () - - deleteSnapshot :: - IOLike m - => s m - -> SnapshotName - -> m () - - listSnapshots :: - IOLike m - => s m - -> m [SnapshotName] - -withSession :: (IOLike m, IsSession s) => SessionArgs s m -> (s m -> m a) -> m a -withSession seshArgs = bracket (openSession seshArgs) closeSession -- | Class abstracting over table operations. -- @@ -268,19 +225,6 @@ withCursor offset hdl = bracket (newCursor offset hdl) (closeCursor (Proxy @h)) Real instance -------------------------------------------------------------------------------} -instance IsSession R.Session where - data SessionArgs R.Session m where - SessionArgs :: - forall m h. Typeable h - => HasFS m h -> HasBlockIO m h -> FsPath - -> SessionArgs R.Session m - - openSession (SessionArgs hfs hbio dir) = do - R.openSession nullTracer hfs hbio dir - closeSession = R.closeSession - deleteSnapshot = R.deleteSnapshot - listSnapshots = R.listSnapshots - instance IsTable R.Table where type Session R.Table = R.Session type TableConfig R.Table = R.TableConfig diff --git a/test/Database/LSMTree/Model/IO.hs b/test/Database/LSMTree/Model/IO.hs new file mode 100644 index 000000000..aec583812 --- /dev/null +++ b/test/Database/LSMTree/Model/IO.hs @@ -0,0 +1,112 @@ +{-# LANGUAGE TypeFamilies #-} + +-- | An instance of `Class.IsTable`, modelling potentially closed sessions in +-- @IO@ by lifting the pure session model from "Database.LSMTree.Model.Session". +module Database.LSMTree.Model.IO ( + Err (..) + , Session (..) + , Class.SessionArgs (NoSessionArgs) + , Table (..) + , TableConfig (..) + , BlobRef (..) + , Cursor (..) + -- * helpers + , runInOpenSession + , convLookupResult + , convQueryResult + , convUpdate + ) where + +import Control.Concurrent.Class.MonadSTM.Strict +import Control.Exception (Exception) +import Control.Monad.Class.MonadThrow (MonadThrow (..)) +import qualified Database.LSMTree.Class as Class +import Database.LSMTree.Model.Session (TableConfig (..)) +import qualified Database.LSMTree.Model.Session as Model + +newtype Session m = Session (StrictTVar m (Maybe Model.Model)) + +data Table m k v b = Table { + _thSession :: !(Session m) + , _thTable :: !(Model.Table k v b) + } + +data BlobRef m b = BlobRef { + _brSession :: !(Session m) + , _brBlobRef :: !(Model.BlobRef b) + } + +data Cursor m k v b = Cursor { + _cSession :: !(Session m) + , _cCursor :: !(Model.Cursor k v b) + } + +newtype Err = Err (Model.Err) + deriving stock Show + deriving anyclass Exception + +runInOpenSession :: (MonadSTM m, MonadThrow (STM m)) => Session m -> Model.ModelM a -> m a +runInOpenSession (Session var) action = atomically $ do + readTVar var >>= \case + Nothing -> error "session closed" + Just m -> do + let (r, m') = Model.runModelM action m + case r of + Left e -> throwSTM (Err e) + Right x -> writeTVar var (Just m') >> pure x + +instance Class.IsSession Session where + data SessionArgs Session m = NoSessionArgs + openSession NoSessionArgs = Session <$> newTVarIO (Just $! Model.initModel) + closeSession (Session var) = atomically $ writeTVar var Nothing + deleteSnapshot s x = runInOpenSession s $ Model.deleteSnapshot x + listSnapshots s = runInOpenSession s $ Model.listSnapshots + +instance Class.IsTable Table where + type Session Table = Session + type TableConfig Table = Model.TableConfig + type BlobRef Table = BlobRef + type Cursor Table = Cursor + + new s x = Table s <$> runInOpenSession s (Model.new x) + close (Table s t) = runInOpenSession s (Model.close t) + lookups (Table s t) x1 = fmap convLookupResult . fmap (fmap (BlobRef s)) <$> + runInOpenSession s (Model.lookups x1 t) + updates (Table s t) x1 = runInOpenSession s (Model.updates Model.getResolve (fmap (fmap convUpdate) x1) t) + inserts (Table s t) x1 = runInOpenSession s (Model.inserts Model.getResolve x1 t) + deletes (Table s t) x1 = runInOpenSession s (Model.deletes Model.getResolve x1 t) + mupserts (Table s t) x1 = runInOpenSession s (Model.mupserts Model.getResolve x1 t) + + rangeLookup (Table s t) x1 = fmap convQueryResult . fmap (fmap (BlobRef s)) <$> + runInOpenSession s (Model.rangeLookup x1 t) + retrieveBlobs _ s x1 = runInOpenSession s (Model.retrieveBlobs (fmap _brBlobRef x1)) + + newCursor k (Table s t) = Cursor s <$> runInOpenSession s (Model.newCursor k t) + closeCursor _ (Cursor s c) = runInOpenSession s (Model.closeCursor c) + readCursor _ x1 (Cursor s c) = fmap convQueryResult . fmap (fmap (BlobRef s)) <$> + runInOpenSession s (Model.readCursor x1 c) + + createSnapshot x1 x2 (Table s t) = runInOpenSession s (Model.createSnapshot x1 x2 t) + openSnapshot s x1 x2 = Table s <$> runInOpenSession s (Model.openSnapshot x1 x2) + + duplicate (Table s t) = Table s <$> runInOpenSession s (Model.duplicate t) + + union (Table s1 t1) (Table _s2 t2) = + Table s1 <$> runInOpenSession s1 (Model.union Model.getResolve t1 t2) + +convLookupResult :: Model.LookupResult v b -> Class.LookupResult v b +convLookupResult = \case + Model.NotFound -> Class.NotFound + Model.Found v -> Class.Found v + Model.FoundWithBlob v b -> Class.FoundWithBlob v b + +convQueryResult :: Model.QueryResult k v b -> Class.QueryResult k v b +convQueryResult = \case + Model.FoundInQuery k v -> Class.FoundInQuery k v + Model.FoundInQueryWithBlob k v b -> Class.FoundInQueryWithBlob k v b + +convUpdate :: Class.Update v b -> Model.Update v b +convUpdate = \case + Class.Insert v b -> Model.Insert v b + Class.Delete -> Model.Delete + Class.Mupsert v -> Model.Mupsert v diff --git a/test/Main.hs b/test/Main.hs index d5412aa68..f0ae0a3f4 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -34,9 +34,9 @@ import qualified Test.Database.LSMTree.Internal.Vector import qualified Test.Database.LSMTree.Internal.Vector.Growing import qualified Test.Database.LSMTree.Model.Table import qualified Test.Database.LSMTree.Monoidal -import qualified Test.Database.LSMTree.Normal.StateMachine -import qualified Test.Database.LSMTree.Normal.StateMachine.DL -import qualified Test.Database.LSMTree.Normal.UnitTests +import qualified Test.Database.LSMTree.StateMachine +import qualified Test.Database.LSMTree.StateMachine.DL +import qualified Test.Database.LSMTree.UnitTests import qualified Test.System.Posix.Fcntl.NoCache import Test.Tasty @@ -72,9 +72,9 @@ main = do , Test.Database.LSMTree.Internal.Vector.Growing.tests , Test.Database.LSMTree.Model.Table.tests , Test.Database.LSMTree.Monoidal.tests - , Test.Database.LSMTree.Normal.UnitTests.tests - , Test.Database.LSMTree.Normal.StateMachine.tests - , Test.Database.LSMTree.Normal.StateMachine.DL.tests + , Test.Database.LSMTree.UnitTests.tests + , Test.Database.LSMTree.StateMachine.tests + , Test.Database.LSMTree.StateMachine.DL.tests , Test.System.Posix.Fcntl.NoCache.tests , Test.Data.Arena.tests ] diff --git a/test/Test/Database/LSMTree/Normal/StateMachine.hs b/test/Test/Database/LSMTree/StateMachine.hs similarity index 93% rename from test/Test/Database/LSMTree/Normal/StateMachine.hs rename to test/Test/Database/LSMTree/StateMachine.hs index 33eee64d0..54c5e4f36 100644 --- a/test/Test/Database/LSMTree/Normal/StateMachine.hs +++ b/test/Test/Database/LSMTree/StateMachine.hs @@ -29,6 +29,7 @@ {- HLINT ignore "Evaluate" -} {- HLINT ignore "Use camelCase" -} {- HLINT ignore "Redundant fmap" -} +{- HLINT ignore "Short-circuited list comprehension" -} -- TODO: remove once table union is implemented {- TODO: improve generation and shrinking of dependencies. See @@ -46,7 +47,7 @@ TODO: it is currently not correctly modelled what happens if blob references are retrieved from an incorrect table. -} -module Test.Database.LSMTree.Normal.StateMachine ( +module Test.Database.LSMTree.StateMachine ( tests , labelledExamples -- * Properties @@ -84,9 +85,9 @@ import qualified Data.Set as Set import Data.Typeable (Proxy (..), Typeable, cast, eqT, type (:~:) (Refl)) import qualified Data.Vector as V -import Database.LSMTree.Class.Normal (LookupResult (..), - QueryResult (..)) -import qualified Database.LSMTree.Class.Normal as Class +import qualified Database.LSMTree as R +import Database.LSMTree.Class (LookupResult (..), QueryResult (..)) +import qualified Database.LSMTree.Class as Class import Database.LSMTree.Extras (showPowersOf) import Database.LSMTree.Extras.Generators (KeyForIndexCompact) import Database.LSMTree.Extras.NoThunks (assertNoThunks) @@ -94,9 +95,8 @@ import Database.LSMTree.Internal (LSMTreeError (..)) import qualified Database.LSMTree.Internal as R.Internal import Database.LSMTree.Internal.Serialise (SerialisedBlob, SerialisedValue) -import qualified Database.LSMTree.Model.IO.Normal as ModelIO +import qualified Database.LSMTree.Model.IO as ModelIO import qualified Database.LSMTree.Model.Session as Model -import qualified Database.LSMTree.Normal as R import NoThunks.Class import Prelude hiding (init) import System.Directory (removeDirectoryRecursive) @@ -109,8 +109,8 @@ import qualified System.FS.Sim.MockFS as MockFS import System.FS.Sim.MockFS (MockFS) import System.IO.Temp (createTempDirectory, getCanonicalTemporaryDirectory) -import Test.Database.LSMTree.Normal.StateMachine.Op - (HasBlobRef (getBlobRef), Op (..)) +import Test.Database.LSMTree.StateMachine.Op (HasBlobRef (getBlobRef), + Op (..)) import qualified Test.QuickCheck as QC import Test.QuickCheck (Arbitrary, Gen, Property) import qualified Test.QuickCheck.Extras as QD @@ -133,7 +133,7 @@ import Test.Util.TypeFamilyWrappers (WrapBlob (..), WrapBlobRef (..), -------------------------------------------------------------------------------} tests :: TestTree -tests = testGroup "Normal.StateMachine" [ +tests = testGroup "Test.Database.LSMTree.StateMachine" [ testProperty "propLockstep_ModelIOImpl" propLockstep_ModelIOImpl @@ -323,7 +323,7 @@ getAllSessionTables :: getAllSessionTables (R.Internal.Session' s) = do R.Internal.withOpenSession s $ \seshEnv -> do ts <- readMVar (R.Internal.sessionOpenTables seshEnv) - pure ((\x -> SomeTable (R.Internal.NormalTable x)) <$> Map.elems ts) + pure ((\x -> SomeTable (R.Internal.Table' x)) <$> Map.elems ts) getAllSessionCursors :: (MonadSTM m, MonadThrow m, MonadMVar m) @@ -332,7 +332,7 @@ getAllSessionCursors :: getAllSessionCursors (R.Internal.Session' s) = R.Internal.withOpenSession s $ \seshEnv -> do cs <- readMVar (R.Internal.sessionOpenCursors seshEnv) - pure ((\x -> SomeCursor (R.Internal.NormalCursor x)) <$> Map.elems cs) + pure ((\x -> SomeCursor (R.Internal.Cursor' x)) <$> Map.elems cs) realHandler :: Monad m => Handler m (Maybe Model.Err) realHandler = Handler $ pure . handler' @@ -373,6 +373,9 @@ newtype Blob = Blob SerialisedBlob keyValueBlobLabel :: R.SnapshotLabel keyValueBlobLabel = R.SnapshotLabel "Key Value Blob" +instance R.ResolveValue Value where + resolveValue _ = (<>) + {------------------------------------------------------------------------------- Model state -------------------------------------------------------------------------------} @@ -402,11 +405,18 @@ type K a = ( type V a = ( Class.C_ a , R.SerialiseValue a + , R.ResolveValue a + , Arbitrary a + ) + +type B a = ( + Class.C_ a + , R.SerialiseValue a , Arbitrary a ) -- | Common constraints for keys, values and blobs -type C k v blob = (K k, V v, V blob) +type C k v blob = (K k, V v, B blob) {------------------------------------------------------------------------------- StateModel @@ -455,8 +465,11 @@ instance ( Show (Class.TableConfig h) Deletes :: C k v blob => V.Vector k -> Var h (WrapTable h IO k v blob) -> Act h () + Mupserts :: C k v blob + => V.Vector (k, v) -> Var h (WrapTable h IO k v blob) + -> Act h () -- Blobs - RetrieveBlobs :: V blob + RetrieveBlobs :: B blob => Var h (V.Vector (WrapBlobRef h IO blob)) -> Act h (V.Vector (WrapBlob blob)) -- Snapshots @@ -468,10 +481,15 @@ instance ( Show (Class.TableConfig h) -> Act h (WrapTable h IO k v blob) DeleteSnapshot :: R.SnapshotName -> Act h () ListSnapshots :: Act h [R.SnapshotName] - -- Multiple writable tables + -- Duplicate tables Duplicate :: C k v blob => Var h (WrapTable h IO k v blob) -> Act h (WrapTable h IO k v blob) + -- Table union + Union :: C k v blob + => Var h (WrapTable h IO k v blob) + -> Var h (WrapTable h IO k v blob) + -> Act h (WrapTable h IO k v blob) initialState = Lockstep.Defaults.initialState initModelState nextState = Lockstep.Defaults.nextState @@ -481,7 +499,7 @@ instance ( Show (Class.TableConfig h) -- TODO: show instance does not show key-value-blob types. Example: -- --- Normal.StateMachine +-- StateMachine -- prop_lockstepIO_ModelIOImpl: FAIL -- *** Failed! Exception: 'open: inappropriate type (table type mismatch)' (after 25 tests and 2 shrinks): -- do action $ New TableConfig @@ -518,6 +536,8 @@ instance ( Eq (Class.TableConfig h) Just inss1 == cast inss2 && Just var1 == cast var2 go (Deletes ks1 var1) (Deletes ks2 var2) = Just ks1 == cast ks2 && Just var1 == cast var2 + go (Mupserts mups1 var1) (Mupserts mups2 var2) = + Just mups1 == cast mups2 && Just var1 == cast var2 go (RetrieveBlobs vars1) (RetrieveBlobs vars2) = Just vars1 == cast vars2 go (CreateSnapshot label1 name1 var1) (CreateSnapshot label2 name2 var2) = @@ -530,6 +550,8 @@ instance ( Eq (Class.TableConfig h) True go (Duplicate var1) (Duplicate var2) = Just var1 == cast var2 + go (Union var1_1 var1_2) (Union var2_1 var2_2) = + Just var1_1 == cast var2_1 && Just var1_2 == cast var2_2 go _ _ = False _coveredAllCases :: LockstepAction (ModelState h) a -> () @@ -544,12 +566,14 @@ instance ( Eq (Class.TableConfig h) Updates{} -> () Inserts{} -> () Deletes{} -> () + Mupserts{} -> () RetrieveBlobs{} -> () CreateSnapshot{} -> () OpenSnapshot{} -> () DeleteSnapshot{} -> () ListSnapshots{} -> () Duplicate{} -> () + Union{} -> () {------------------------------------------------------------------------------- InLockstep @@ -649,12 +673,14 @@ instance ( Eq (Class.TableConfig h) Updates _ tableVar -> [SomeGVar tableVar] Inserts _ tableVar -> [SomeGVar tableVar] Deletes _ tableVar -> [SomeGVar tableVar] + Mupserts _ tableVar -> [SomeGVar tableVar] RetrieveBlobs blobsVar -> [SomeGVar blobsVar] CreateSnapshot _ _ tableVar -> [SomeGVar tableVar] OpenSnapshot _ _ -> [] DeleteSnapshot _ -> [] ListSnapshots -> [] Duplicate tableVar -> [SomeGVar tableVar] + Union table1Var table2Var -> [SomeGVar table1Var, SomeGVar table2Var] arbitraryWithVars :: ModelVarContext (ModelState h) @@ -760,12 +786,14 @@ instance ( Eq (Class.TableConfig h) Updates{} -> OEither $ bimap OId OId result Inserts{} -> OEither $ bimap OId OId result Deletes{} -> OEither $ bimap OId OId result + Mupserts{} -> OEither $ bimap OId OId result RetrieveBlobs{} -> OEither $ bimap OId (OVector . fmap OBlob) result CreateSnapshot{} -> OEither $ bimap OId OId result OpenSnapshot{} -> OEither $ bimap OId (const OTable) result DeleteSnapshot{} -> OEither $ bimap OId OId result ListSnapshots{} -> OEither $ bimap OId (OList . fmap OId) result Duplicate{} -> OEither $ bimap OId (const OTable) result + Union{} -> OEither $ bimap OId (const OTable) result showRealResponse :: Proxy (RealMonad h IO) @@ -782,12 +810,14 @@ instance ( Eq (Class.TableConfig h) Updates{} -> Just Dict Inserts{} -> Just Dict Deletes{} -> Just Dict + Mupserts{} -> Just Dict RetrieveBlobs{} -> Just Dict CreateSnapshot{} -> Just Dict OpenSnapshot{} -> Nothing DeleteSnapshot{} -> Just Dict ListSnapshots -> Just Dict Duplicate{} -> Nothing + Union{} -> Nothing instance ( Eq (Class.TableConfig h) , Class.IsTable h @@ -814,12 +844,14 @@ instance ( Eq (Class.TableConfig h) Updates{} -> OEither $ bimap OId OId result Inserts{} -> OEither $ bimap OId OId result Deletes{} -> OEither $ bimap OId OId result + Mupserts{} -> OEither $ bimap OId OId result RetrieveBlobs{} -> OEither $ bimap OId (OVector . fmap OBlob) result CreateSnapshot{} -> OEither $ bimap OId OId result OpenSnapshot{} -> OEither $ bimap OId (const OTable) result DeleteSnapshot{} -> OEither $ bimap OId OId result ListSnapshots{} -> OEither $ bimap OId (OList . fmap OId) result Duplicate{} -> OEither $ bimap OId (const OTable) result + Union{} -> OEither $ bimap OId (const OTable) result showRealResponse :: Proxy (RealMonad h (IOSim s)) @@ -836,12 +868,14 @@ instance ( Eq (Class.TableConfig h) Updates{} -> Just Dict Inserts{} -> Just Dict Deletes{} -> Just Dict + Mupserts{} -> Just Dict RetrieveBlobs{} -> Just Dict CreateSnapshot{} -> Just Dict OpenSnapshot{} -> Nothing DeleteSnapshot{} -> Just Dict ListSnapshots -> Just Dict Duplicate{} -> Nothing + Union{} -> Nothing {------------------------------------------------------------------------------- RunModel @@ -900,13 +934,16 @@ runModel lookUp = \case . Model.runModelM (Model.readCursor n (getCursor $ lookUp cursorVar)) Updates kups tableVar -> wrap MUnit - . Model.runModelM (Model.updates Model.noResolve (fmap ModelIO.convUpdate <$> kups) (getTable $ lookUp tableVar)) + . Model.runModelM (Model.updates Model.getResolve (fmap ModelIO.convUpdate <$> kups) (getTable $ lookUp tableVar)) Inserts kins tableVar -> wrap MUnit - . Model.runModelM (Model.inserts Model.noResolve kins (getTable $ lookUp tableVar)) + . Model.runModelM (Model.inserts Model.getResolve kins (getTable $ lookUp tableVar)) Deletes kdels tableVar -> wrap MUnit - . Model.runModelM (Model.deletes Model.noResolve kdels (getTable $ lookUp tableVar)) + . Model.runModelM (Model.deletes Model.getResolve kdels (getTable $ lookUp tableVar)) + Mupserts kmups tableVar -> + wrap MUnit + . Model.runModelM (Model.mupserts Model.getResolve kmups (getTable $ lookUp tableVar)) RetrieveBlobs blobsVar -> wrap (MVector . fmap (MBlob . WrapBlob)) . Model.runModelM (Model.retrieveBlobs (getBlobRefs . lookUp $ blobsVar)) @@ -925,6 +962,9 @@ runModel lookUp = \case Duplicate tableVar -> wrap MTable . Model.runModelM (Model.duplicate (getTable $ lookUp tableVar)) + Union table1Var table2Var -> + wrap MTable + . Model.runModelM (Model.union Model.getResolve (getTable $ lookUp table1Var) (getTable $ lookUp table2Var)) where getTable :: ModelValue (ModelState h) (WrapTable h IO k v blob) @@ -987,6 +1027,8 @@ runIO action lookUp = ReaderT $ \(session, handler) -> do Class.inserts (unwrapTable $ lookUp' tableVar) kins Deletes kdels tableVar -> catchErr handler $ Class.deletes (unwrapTable $ lookUp' tableVar) kdels + Mupserts kmups tableVar -> catchErr handler $ + Class.mupserts (unwrapTable $ lookUp' tableVar) kmups RetrieveBlobs blobRefsVar -> catchErr handler $ fmap WrapBlob <$> Class.retrieveBlobs (Proxy @h) session (unwrapBlobRef <$> lookUp' blobRefsVar) CreateSnapshot label name tableVar -> catchErr handler $ @@ -999,6 +1041,8 @@ runIO action lookUp = ReaderT $ \(session, handler) -> do Class.listSnapshots session Duplicate tableVar -> catchErr handler $ WrapTable <$> Class.duplicate (unwrapTable $ lookUp' tableVar) + Union table1Var table2Var -> catchErr handler $ + WrapTable <$> Class.union (unwrapTable $ lookUp' table1Var) (unwrapTable $ lookUp' table2Var) lookUp' :: Var h x -> Realized IO x lookUp' = lookUpGVar (Proxy @(RealMonad h IO)) lookUp @@ -1037,6 +1081,8 @@ runIOSim action lookUp = ReaderT $ \(session, handler) -> Class.inserts (unwrapTable $ lookUp' tableVar) kins Deletes kdels tableVar -> catchErr handler $ Class.deletes (unwrapTable $ lookUp' tableVar) kdels + Mupserts kmups tableVar -> catchErr handler $ + Class.mupserts (unwrapTable $ lookUp' tableVar) kmups RetrieveBlobs blobRefsVar -> catchErr handler $ fmap WrapBlob <$> Class.retrieveBlobs (Proxy @h) session (unwrapBlobRef <$> lookUp' blobRefsVar) CreateSnapshot label name tableVar -> catchErr handler $ @@ -1049,6 +1095,8 @@ runIOSim action lookUp = ReaderT $ \(session, handler) -> Class.listSnapshots session Duplicate tableVar -> catchErr handler $ WrapTable <$> Class.duplicate (unwrapTable $ lookUp' tableVar) + Union table1Var table2Var -> catchErr handler $ + WrapTable <$> Class.union (unwrapTable $ lookUp' table1Var) (unwrapTable $ lookUp' table2Var) lookUp' :: Var h x -> Realized (IOSim s) x lookUp' = lookUpGVar (Proxy @(RealMonad h (IOSim s))) lookUp @@ -1099,12 +1147,14 @@ arbitraryActionWithVars _ label ctx (ModelState st _stats) = Updates{} -> () Inserts{} -> () Deletes{} -> () + Mupserts{} -> () RetrieveBlobs{} -> () CreateSnapshot{} -> () DeleteSnapshot{} -> () ListSnapshots{} -> () OpenSnapshot{} -> () Duplicate{} -> () + Union{} -> () genTableVar = QC.elements tableVars @@ -1179,6 +1229,7 @@ arbitraryActionWithVars _ label ctx (ModelState st _stats) = , (10, fmap Some $ Updates <$> genUpdates <*> genTableVar) , (10, fmap Some $ Inserts <$> genInserts <*> genTableVar) , (10, fmap Some $ Deletes <$> genDeletes <*> genTableVar) + , (10, fmap Some $ Mupserts <$> genMupserts <*> genTableVar) ] ++ [ (3, fmap Some $ NewCursor <$> QC.arbitrary <*> genTableVar) | length cursorVars <= 5 -- no more than 5 cursors at once @@ -1189,6 +1240,10 @@ arbitraryActionWithVars _ label ctx (ModelState st _stats) = ++ [ (5, fmap Some $ Duplicate <$> genTableVar) | length tableVars <= 5 -- no more than 5 tables at once ] + ++ [ (2, fmap Some $ Union <$> genTableVar <*> genTableVar) + | length tableVars <= 5 -- no more than 5 tables at once + , False -- TODO: enable once table union is implemented + ] genActionsCursor :: [(Int, Gen (Any (LockstepAction (ModelState h))))] genActionsCursor @@ -1219,12 +1274,14 @@ arbitraryActionWithVars _ label ctx (ModelState st _stats) = genUpdates :: Gen (V.Vector (k, R.Update v blob)) genUpdates = QC.liftArbitrary ((,) <$> QC.arbitrary <*> QC.oneof [ R.Insert <$> QC.arbitrary <*> genBlob + , R.Mupsert <$> QC.arbitrary , pure R.Delete ]) where _coveredAllCases :: R.Update v blob -> () _coveredAllCases = \case R.Insert{} -> () + R.Mupsert{} -> () R.Delete{} -> () genInserts :: Gen (V.Vector (k, v, Maybe blob)) @@ -1233,6 +1290,9 @@ arbitraryActionWithVars _ label ctx (ModelState st _stats) = genDeletes :: Gen (V.Vector k) genDeletes = QC.arbitrary + genMupserts :: Gen (V.Vector (k, v)) + genMupserts = QC.liftArbitrary ((,) <$> QC.arbitrary <*> QC.arbitrary) + genBlob :: Gen (Maybe blob) genBlob = QC.arbitrary @@ -1307,8 +1367,8 @@ data Stats = Stats { , numLookupsResults :: {-# UNPACK #-} !(Int, Int, Int) -- (NotFound, Found, FoundWithBlob) -- | Number of succesful updates - , numUpdates :: {-# UNPACK #-} !(Int, Int, Int) - -- (Insert, InsertWithBlob, Delete) + , numUpdates :: {-# UNPACK #-} !(Int, Int, Int, Int) + -- (Insert, InsertWithBlob, Delete, Mupsert) -- | Actions that succeeded , successActions :: [String] -- | Actions that failed with an error @@ -1338,7 +1398,7 @@ initStats = Stats { snapshotted = Set.empty -- === Final tags , numLookupsResults = (0, 0, 0) - , numUpdates = (0, 0, 0) + , numUpdates = (0, 0, 0, 0) , successActions = [] , failActions = [] , numActionsPerTable = Map.empty @@ -1412,15 +1472,16 @@ updateStats action lookUp modelBefore _modelAfter result = } _ -> stats where - countAll :: forall k v blob. V.Vector (k, R.Update v blob) -> (Int, Int, Int) + countAll :: forall k v blob. V.Vector (k, R.Update v blob) -> (Int, Int, Int, Int) countAll upds = - let count :: (Int, Int, Int) + let count :: (Int, Int, Int, Int) -> (k, R.Update v blob) - -> (Int, Int, Int) - count (i, iwb, d) (_, upd) = case upd of - R.Insert _ Nothing -> (i+1, iwb , d ) - R.Insert _ Just{} -> (i , iwb+1, d ) - R.Delete{} -> (i , iwb , d+1) + -> (Int, Int, Int, Int) + count (i, iwb, d, m) (_, upd) = case upd of + R.Insert _ Nothing -> (i+1, iwb , d , m ) + R.Insert _ Just{} -> (i , iwb+1, d , m ) + R.Delete{} -> (i , iwb , d+1, m ) + R.Mupsert{} -> (i , iwb , d , m+1) in V.foldl' count (numUpdates stats) upds updSuccessActions stats = case result of @@ -1446,6 +1507,9 @@ updateStats action lookUp modelBefore _modelAfter result = Duplicate{} | MEither (Right (MTable table)) <- result -> initCount table | otherwise -> stats + Union{} + | MEither (Right (MTable table)) <- result -> initCount table + | otherwise -> stats -- Note that for the other actions we don't count success vs failure. -- We don't need that level of detail. We just want to see the @@ -1456,6 +1520,7 @@ updateStats action lookUp modelBefore _modelAfter result = Updates _ tableVar -> updateCount tableVar Inserts _ tableVar -> updateCount tableVar Deletes _ tableVar -> updateCount tableVar + Mupserts _ tableVar -> updateCount tableVar -- Note that we don't remove tracking map entries for tables that get -- closed. We want to know actions per table of all tables used, not -- just those that were still open at the end of the sequence of @@ -1662,6 +1727,10 @@ data FinalTag = -- (this includes submissions through both 'Class.updates' and -- 'Class.deletes') | NumDeletes String + -- | Number of 'Class.Mupsert's succesfully submitted to a table + -- (this includes submissions through both 'Class.updates' and + -- 'Class.mupserts') + | NumMupserts String -- | Total number of actions (failing, succeeding, either) | NumActions String -- | Which actions succeded @@ -1705,8 +1774,9 @@ tagFinalState' (getModel -> ModelState finalState finalStats) = concat [ ("Inserts" , [NumInserts $ showPowersOf 10 i]) , ("Inserts with blobs" , [NumInsertsWithBlobs $ showPowersOf 10 iwb]) , ("Deletes" , [NumDeletes $ showPowersOf 10 d]) + , ("Mupserts" , [NumMupserts $ showPowersOf 10 m]) ] - where (i, iwb, d) = numUpdates finalStats + where (i, iwb, d, m) = numUpdates finalStats tagNumActions = [ let n = length (successActions finalStats) in diff --git a/test/Test/Database/LSMTree/Normal/StateMachine/DL.hs b/test/Test/Database/LSMTree/StateMachine/DL.hs similarity index 91% rename from test/Test/Database/LSMTree/Normal/StateMachine/DL.hs rename to test/Test/Database/LSMTree/StateMachine/DL.hs index 4c5321a2b..e62d47102 100644 --- a/test/Test/Database/LSMTree/Normal/StateMachine/DL.hs +++ b/test/Test/Database/LSMTree/StateMachine/DL.hs @@ -1,19 +1,19 @@ {-# OPTIONS_GHC -Wno-unused-do-bind #-} {-# OPTIONS_GHC -Wno-orphans #-} -module Test.Database.LSMTree.Normal.StateMachine.DL ( +module Test.Database.LSMTree.StateMachine.DL ( tests ) where import Control.Tracer import qualified Data.Map.Strict as Map import qualified Data.Vector as V +import Database.LSMTree as R import qualified Database.LSMTree.Model.Session as Model (fromSomeTable, tables) import qualified Database.LSMTree.Model.Table as Model (values) -import Database.LSMTree.Normal as R import Prelude -import Test.Database.LSMTree.Normal.StateMachine hiding (tests) -import Test.Database.LSMTree.Normal.StateMachine.Op +import Test.Database.LSMTree.StateMachine hiding (tests) +import Test.Database.LSMTree.StateMachine.Op import Test.QuickCheck as QC import Test.QuickCheck.DynamicLogic import qualified Test.QuickCheck.Gen as QC @@ -24,7 +24,7 @@ import qualified Test.Tasty.QuickCheck as QC import Test.Util.PrettyProxy tests :: TestTree -tests = testGroup "Test.Database.LSMTree.Normal.StateMachine.DL" [ +tests = testGroup "Test.Database.LSMTree.StateMachine.DL" [ QC.testProperty "prop_example" prop_example ] diff --git a/test/Test/Database/LSMTree/Normal/StateMachine/Op.hs b/test/Test/Database/LSMTree/StateMachine/Op.hs similarity index 91% rename from test/Test/Database/LSMTree/Normal/StateMachine/Op.hs rename to test/Test/Database/LSMTree/StateMachine/Op.hs index 451810743..866f9e14f 100644 --- a/test/Test/Database/LSMTree/Normal/StateMachine/Op.hs +++ b/test/Test/Database/LSMTree/StateMachine/Op.hs @@ -6,7 +6,7 @@ {-# LANGUAGE MultiParamTypeClasses #-} -- | SumProd Op extended with BlobRef extraction -module Test.Database.LSMTree.Normal.StateMachine.Op ( +module Test.Database.LSMTree.StateMachine.Op ( -- * 'Op' Op (..) , intOpId @@ -19,7 +19,7 @@ import Control.Monad.IOSim (IOSim) import Control.Monad.Reader (ReaderT) import Control.Monad.State (StateT) import qualified Data.Vector as V -import qualified Database.LSMTree.Class.Normal as Class +import qualified Database.LSMTree.Class as Class import qualified Database.LSMTree.Model.Table as Model import GHC.Show (appPrec) import Test.QuickCheck.StateModel.Lockstep (InterpretOp, Operation) @@ -139,16 +139,16 @@ instance Show (Op a b) where _ -> go op where go :: Op x y -> String -> String - go OpId = showString "id" - go OpFst = showString "fst" - go OpSnd = showString "snd" - go OpLeft = showString "Left" - go OpRight = showString "Right" - go OpFromLeft = showString "FromLeft" - go OpFromRight = showString "FromRight" - go (OpComp g f) = go g . showString " . " . go f - go OpLookupResults = showString "mapMaybe getBlobRef" - go OpQueryResults = showString "mapMaybe getBlobRef" + go OpId = showString "OpId" + go OpFst = showString "OpFst" + go OpSnd = showString "OpSnd" + go OpLeft = showString "OpLeft" + go OpRight = showString "OpRight" + go OpFromLeft = showString "OpFromLeft" + go OpFromRight = showString "OpFromRight" + go (OpComp g f) = go g . showString " `OpComp` " . go f + go OpLookupResults = showString "OpLookupResults" + go OpQueryResults = showString "OpQueryResults" {------------------------------------------------------------------------------- 'HasBlobRef' class diff --git a/test/Test/Database/LSMTree/Normal/UnitTests.hs b/test/Test/Database/LSMTree/UnitTests.hs similarity index 92% rename from test/Test/Database/LSMTree/Normal/UnitTests.hs rename to test/Test/Database/LSMTree/UnitTests.hs index 7afc1ba3b..bcb20cb2e 100644 --- a/test/Test/Database/LSMTree/Normal/UnitTests.hs +++ b/test/Test/Database/LSMTree/UnitTests.hs @@ -3,7 +3,7 @@ {- HLINT ignore "Use void" -} -module Test.Database.LSMTree.Normal.UnitTests (tests) where +module Test.Database.LSMTree.UnitTests (tests) where import Control.Tracer (nullTracer) import Data.ByteString (ByteString) @@ -13,7 +13,7 @@ import qualified Data.Vector as V import Data.Word import qualified System.FS.API as FS -import Database.LSMTree.Normal as R +import Database.LSMTree as R import Control.Exception (Exception, try) import Database.LSMTree.Extras.Generators (KeyForIndexCompact) @@ -25,7 +25,7 @@ import Test.Util.FS (withTempIOHasBlockIO) tests :: TestTree tests = - testGroup "Normal.UnitTests" + testGroup "Test.Database.LSMTree.UnitTests" [ testCaseSteps "unit_blobs" unit_blobs , testCase "unit_closed_table" unit_closed_table , testCase "unit_closed_cursor" unit_closed_cursor @@ -37,15 +37,15 @@ unit_blobs :: (String -> IO ()) -> Assertion unit_blobs info = withTempIOHasBlockIO "test" $ \hfs hbio -> withSession nullTracer hfs hbio (FS.mkFsPath []) $ \sess -> do - table <- new @_ @ByteString @ByteString @ByteString sess defaultTableConfig - inserts table [("key1", "value1", Just "blob1")] + table <- new @_ @ByteString @(ResolveAsFirst ByteString) @ByteString sess defaultTableConfig + inserts table [("key1", ResolveAsFirst "value1", Just "blob1")] res <- lookups table ["key1"] info (show res) case res of [FoundWithBlob val bref] -> do - val @?= "value1" + val @?= ResolveAsFirst "value1" blob <- retrieveBlobs sess [bref] info (show blob) blob @?= ["blob1"] @@ -166,6 +166,8 @@ newtype Value1 = Value1 Word64 deriving stock (Show, Eq, Ord) deriving newtype (SerialiseValue) +deriving via ResolveAsFirst Word64 instance ResolveValue Value1 + newtype Blob1 = Blob1 Word64 deriving stock (Show, Eq, Ord) deriving newtype (SerialiseValue) @@ -181,6 +183,8 @@ newtype Value2 = Value2 BS.ByteString deriving stock (Show, Eq, Ord) deriving newtype (QC.Arbitrary, SerialiseValue) +deriving via ResolveAsFirst BS.ByteString instance ResolveValue Value2 + newtype Blob2 = Blob2 BS.ByteString deriving stock (Show, Eq, Ord) deriving newtype (SerialiseValue) diff --git a/test/Test/Util/Orphans.hs b/test/Test/Util/Orphans.hs index 8f895f117..6670a29d2 100644 --- a/test/Test/Util/Orphans.hs +++ b/test/Test/Util/Orphans.hs @@ -19,10 +19,9 @@ import qualified Control.Concurrent.STM as Real import Control.Monad ((<=<)) import Control.Monad.IOSim (IOSim) import Data.Kind (Type) +import Database.LSMTree (Cursor, LookupResult, QueryResult, Table) import Database.LSMTree.Common (BlobRef, IOLike, SerialiseValue) import Database.LSMTree.Internal.Serialise (SerialiseKey) -import Database.LSMTree.Normal (Cursor, LookupResult, QueryResult, - Table) import Test.QuickCheck.Modifiers (Small (..)) import Test.QuickCheck.StateModel (Realized) import Test.QuickCheck.StateModel.Lockstep (InterpretOp) diff --git a/test/Test/Util/TypeFamilyWrappers.hs b/test/Test/Util/TypeFamilyWrappers.hs index 3ad8b4c36..0a4e53a10 100644 --- a/test/Test/Util/TypeFamilyWrappers.hs +++ b/test/Test/Util/TypeFamilyWrappers.hs @@ -21,7 +21,7 @@ module Test.Util.TypeFamilyWrappers ( ) where import Data.Kind (Type) -import qualified Database.LSMTree.Class.Normal as SUT.Class +import qualified Database.LSMTree.Class as SUT.Class type WrapSession :: ((Type -> Type) -> Type -> Type -> Type -> Type)