1+ {-# OPTIONS_GHC -Wno-orphans #-}
2+
13module Test.Database.LSMTree.Internal.MergingTree (tests ) where
24
35import Control.ActionRegistry
46import Control.Exception (bracket )
57import Control.Monad.Class.MonadAsync as Async
68import Control.RefCount
79import Data.Arena (newArenaManager )
10+ import Data.Coerce (coerce )
811import Data.Foldable (toList )
12+ import Data.List.NonEmpty (NonEmpty )
913import Data.Map (Map )
1014import qualified Data.Map as Map
15+ import Data.Traversable (for )
1116import qualified Data.Vector as V
1217import Database.LSMTree.Extras.MergingRunData
1318import Database.LSMTree.Extras.MergingTreeData
@@ -17,9 +22,10 @@ import Database.LSMTree.Internal.Entry (Entry)
1722import qualified Database.LSMTree.Internal.Entry as Entry
1823import qualified Database.LSMTree.Internal.Index as Index
1924import qualified Database.LSMTree.Internal.Lookup as Lookup
20- import Database.LSMTree.Internal.MergingRun
25+ import qualified Database.LSMTree.Internal.MergingRun as MR
2126import Database.LSMTree.Internal.MergingTree
2227import Database.LSMTree.Internal.MergingTree.Lookup
28+ import qualified Database.LSMTree.Internal.Paths as Paths
2329import qualified Database.LSMTree.Internal.Run as Run
2430import qualified Database.LSMTree.Internal.RunAcc as RunAcc
2531import qualified Database.LSMTree.Internal.RunBuilder as RunBuilder
@@ -40,6 +46,10 @@ tests = testGroup "Test.Database.LSMTree.Internal.MergingTree"
4046 ioProperty $
4147 withSimHasBlockIO propNoOpenHandles MockFS. empty $ \ hfs hbio _ ->
4248 prop_lookupTree hfs hbio keys mtd
49+ , testProperty " prop_supplyCredits" $ \ threshold credits mtd ->
50+ ioProperty $
51+ withSimHasBlockIO propNoOpenHandles MockFS. empty $ \ hfs hbio _ ->
52+ prop_supplyCredits hfs hbio threshold credits mtd
4353 ]
4454
4555runParams :: RunBuilder. RunParams
@@ -112,7 +122,7 @@ prop_lookupTree ::
112122 -> V. Vector SerialisedKey
113123 -> MergingTreeData SerialisedKey SerialisedValue SerialisedBlob
114124 -> IO Property
115- prop_lookupTree hfs hbio keys (serialiseMergingTreeData -> mtd) = do
125+ prop_lookupTree hfs hbio keys mtd = do
116126 let path = FS. mkFsPath []
117127 counter <- newUniqCounter 0
118128 withMergingTree hfs hbio resolveVal runParams path counter mtd $ \ tree -> do
@@ -178,24 +188,67 @@ modelFoldMergingTree = goMergingTree
178188 OngoingTreeMergeData mr ->
179189 goMergingRun mr
180190 PendingLevelMergeData prs t ->
181- modelMerge MergeLevel (map goPreExistingRun prs <> map goMergingTree (toList t))
191+ modelMerge MR. MergeLevel (map goPreExistingRun prs <> map goMergingTree (toList t))
182192 PendingUnionMergeData ts ->
183- modelMerge MergeUnion (map goMergingTree ts)
193+ modelMerge MR. MergeUnion (map goMergingTree ts)
184194
185195 goPreExistingRun = \ case
186196 PreExistingRunData r -> unRunData r
187197 PreExistingMergingRunData mr -> goMergingRun mr
188198
189- goMergingRun :: IsMergeType t => SerialisedMergingRunData t -> Map SerialisedKey SerialisedEntry
199+ goMergingRun :: MR. IsMergeType t => SerialisedMergingRunData t -> Map SerialisedKey SerialisedEntry
190200 goMergingRun = \ case
191201 CompletedMergeData _ r -> unRunData r
192202 OngoingMergeData mt rs -> modelMerge mt (map (unRunData . toRunData) rs)
193203
194- modelMerge :: (Ord k , IsMergeType t ) => t -> [Map k SerialisedEntry ] -> Map k SerialisedEntry
204+ modelMerge :: (Ord k , MR. IsMergeType t ) => t -> [Map k SerialisedEntry ] -> Map k SerialisedEntry
195205modelMerge mt = handleDeletes . Map. unionsWith (combine resolveVal)
196206 where
197- handleDeletes = if isLastLevel mt then Map. filter (/= Entry. Delete ) else id
198- combine = if isUnion mt then Entry. combineUnion else Entry. combine
207+ handleDeletes = if MR. isLastLevel mt then Map. filter (/= Entry. Delete ) else id
208+ combine = if MR. isUnion mt then Entry. combineUnion else Entry. combine
199209
200210resolveVal :: SerialisedValue -> SerialisedValue -> SerialisedValue
201211resolveVal (SerialisedValue x) (SerialisedValue y) = SerialisedValue (x <> y)
212+
213+ {- ------------------------------------------------------------------------------
214+ Supplying Credits
215+ -------------------------------------------------------------------------------}
216+
217+ prop_supplyCredits ::
218+ forall h .
219+ FS. HasFS IO h
220+ -> FS. HasBlockIO IO h
221+ -> MR. CreditThreshold
222+ -> NonEmpty MR. MergeCredits
223+ -> MergingTreeData SerialisedKey SerialisedValue SerialisedBlob
224+ -> IO Property
225+ prop_supplyCredits hfs hbio threshold credits mtd = do
226+ FS. createDirectory hfs setupPath
227+ FS. createDirectory hfs (FS. mkFsPath [" active" ])
228+ counter <- newUniqCounter 0
229+ withMergingTree hfs hbio resolveVal runParams setupPath counter mtd $ \ tree -> do
230+ props <- for credits $ \ c -> do
231+ (MR. MergeDebt debt, _) <- remainingMergeDebt tree
232+ leftovers <-
233+ supplyCredits hfs hbio resolveVal runParams threshold root counter tree c
234+ (MR. MergeDebt debt', _) <- remainingMergeDebt tree
235+ return $
236+ counterexample (show (debt, leftovers, debt')) $ conjoin [
237+ counterexample " negative values" $
238+ debt >= 0 && leftovers >= 0 && debt' >= 0
239+ , counterexample " did not reduce debt sufficiently" $
240+ debt' <= debt - (c - leftovers)
241+ ]
242+ return (conjoin (toList props))
243+ where
244+ root = Paths. SessionRoot (FS. mkFsPath [] )
245+ setupPath = FS. mkFsPath [" setup" ] -- separate dir, so it doesn't clash
246+
247+ instance Arbitrary MR. MergeCredits where
248+ arbitrary = MR. MergeCredits . getPositive <$> arbitrary
249+ shrink (MR. MergeCredits c) = [MR. MergeCredits c' | c' <- shrink c, c' > 0 ]
250+
251+ instance Arbitrary MR. CreditThreshold where
252+ arbitrary = coerce (arbitrary @ MR. MergeCredits )
253+ shrink = coerce (shrink @ MR. MergeCredits )
254+ -- TODO: does this make sense? in a way a larger threshold is "simpler".
0 commit comments