Skip to content

Commit f081fb1

Browse files
committed
add prop_supplyCredits for MergingTree
1 parent efc7c40 commit f081fb1

File tree

7 files changed

+81
-19
lines changed

7 files changed

+81
-19
lines changed

src-extras/Database/LSMTree/Extras/RunData.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ unsafeCreateRunAt ::
149149
-> SerialisedRunData
150150
-> IO (Ref (Run IO h))
151151
unsafeCreateRunAt fs hbio runParams fsPaths (RunData m) = do
152+
-- the WBB file path doesn't have to be at a specific place relative to
153+
-- the run we want to create, but fsPaths should already point to a unique
154+
-- location, so we just append something to not conflict with that.
152155
let blobpath = FS.addExtension (runBlobPath fsPaths) ".wb"
153156
bracket (WBB.new fs blobpath) releaseRef $ \wbblobs -> do
154157
wb <- WB.fromMap <$> traverse (traverse (WBB.addBlob fs wbblobs)) m

src/Database/LSMTree/Internal/Merge.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ new ::
174174
new hfs hbio runParams mergeType mergeMappend targetPaths runs = do
175175
-- no offset, no write buffer
176176
mreaders <- Readers.new Readers.NoOffsetKey Nothing runs
177+
-- TODO: Exception safety! If Readers.new fails after already creating some
178+
-- run readers, or Builder.new fails, the run readers will stay open,
179+
-- holding handles of the input runs' files.
177180
for mreaders $ \mergeReaders -> do
178181
-- calculate upper bounds based on input runs
179182
let numEntries = V.foldMap' Run.size runs

src/Database/LSMTree/Internal/MergingRun.hs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ might not finish in time, which will mess up the shape of the levels tree.
388388
-}
389389

390390
newtype MergeCredits = MergeCredits Int
391-
deriving stock (Eq, Ord)
391+
deriving stock (Eq, Ord, Show)
392392
deriving newtype (Num, Real, Enum, Integral, NFData)
393393

394394
newtype MergeDebt = MergeDebt MergeCredits
@@ -414,13 +414,14 @@ numEntriesToMergeDebt (NumEntries n) = MergeDebt (MergeCredits n)
414414
-- co-prime so that merge work at different levels is not synchronised.
415415
--
416416
newtype CreditThreshold = CreditThreshold UnspentCredits
417+
deriving stock Show
417418

418419
-- | The spent credits are supplied credits that have been spent on performing
419420
-- merging steps plus the supplied credits that are in the process of being
420421
-- spent (by some thread calling 'supplyCredits').
421422
--
422423
newtype SpentCredits = SpentCredits MergeCredits
423-
deriving newtype (Eq, Ord)
424+
deriving newtype (Eq, Ord, Show)
424425

425426
-- | 40 bit unsigned number
426427
instance Bounded SpentCredits where
@@ -436,7 +437,7 @@ instance Bounded SpentCredits where
436437
-- current unspent credits being negative for a time.
437438
--
438439
newtype UnspentCredits = UnspentCredits MergeCredits
439-
deriving newtype (Eq, Ord)
440+
deriving newtype (Eq, Ord, Show)
440441

441442
-- | 24 bit signed number
442443
instance Bounded UnspentCredits where

test/Test/Database/LSMTree/Generators.hs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ import qualified Database.LSMTree.Internal.Index as Index
1919
import qualified Database.LSMTree.Internal.MergingRun as MR
2020
import Database.LSMTree.Internal.PageAcc (entryWouldFitInPage,
2121
sizeofEntry)
22+
import Database.LSMTree.Internal.Paths (RunFsPaths (..))
2223
import Database.LSMTree.Internal.RawBytes (RawBytes (..))
2324
import qualified Database.LSMTree.Internal.RawBytes as RB
2425
import qualified Database.LSMTree.Internal.RunAcc as RunAcc
2526
import qualified Database.LSMTree.Internal.RunBuilder as RunBuilder
27+
import Database.LSMTree.Internal.RunNumber (RunNumber (..))
2628
import Database.LSMTree.Internal.Serialise
2729
import Database.LSMTree.Internal.UniqCounter
2830
import qualified System.FS.API as FS
@@ -155,7 +157,10 @@ prop_withRunDoesntLeak ::
155157
-> IO Property
156158
prop_withRunDoesntLeak hfs hbio rd = do
157159
let indexType = Index.Ordinary
158-
withRunAt hfs hbio (runParams indexType) (simplePath 0) rd $ \_run -> do
160+
let path = FS.mkFsPath ["something-1"]
161+
let fsPaths = RunFsPaths path (RunNumber 0)
162+
FS.createDirectory hfs path
163+
withRunAt hfs hbio (runParams indexType) fsPaths rd $ \_run -> do
159164
return (QC.property True)
160165

161166
prop_withMergingRunDoesntLeak ::
@@ -165,7 +170,8 @@ prop_withMergingRunDoesntLeak ::
165170
-> IO Property
166171
prop_withMergingRunDoesntLeak hfs hbio mrd = do
167172
let indexType = Index.Ordinary
168-
let path = FS.mkFsPath []
173+
let path = FS.mkFsPath ["something-2"]
174+
FS.createDirectory hfs path
169175
counter <- newUniqCounter 0
170176
withMergingRun hfs hbio resolveVal (runParams indexType) path counter mrd $
171177
\_mr -> do
@@ -180,7 +186,8 @@ prop_withMergingTreeDoesntLeak ::
180186
-> IO Property
181187
prop_withMergingTreeDoesntLeak hfs hbio mrd = do
182188
let indexType = Index.Ordinary
183-
let path = FS.mkFsPath []
189+
let path = FS.mkFsPath ["something-3"]
190+
FS.createDirectory hfs path
184191
counter <- newUniqCounter 0
185192
withMergingTree hfs hbio resolveVal (runParams indexType) path counter mrd $
186193
\_tree -> do

test/Test/Database/LSMTree/Internal/MergingRun.hs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,6 @@ prop_CreditsPair spentCredits unspentCredits =
3737
deriving newtype instance Enum SpentCredits
3838
deriving newtype instance Enum UnspentCredits
3939

40-
deriving stock instance Show MergeCredits
41-
deriving stock instance Show SpentCredits
42-
deriving stock instance Show UnspentCredits
43-
4440
instance Arbitrary SpentCredits where
4541
arbitrary =
4642
frequency [ (1, pure minBound)

test/Test/Database/LSMTree/Internal/MergingTree.hs

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1+
{-# OPTIONS_GHC -Wno-orphans #-}
2+
13
module Test.Database.LSMTree.Internal.MergingTree (tests) where
24

35
import Control.ActionRegistry
46
import Control.Exception (bracket)
57
import Control.Monad.Class.MonadAsync as Async
68
import Control.RefCount
79
import Data.Arena (newArenaManager)
10+
import Data.Coerce (coerce)
811
import Data.Foldable (toList)
12+
import Data.List.NonEmpty (NonEmpty)
913
import Data.Map (Map)
1014
import qualified Data.Map as Map
15+
import Data.Traversable (for)
1116
import qualified Data.Vector as V
1217
import Database.LSMTree.Extras.MergingRunData
1318
import Database.LSMTree.Extras.MergingTreeData
@@ -17,9 +22,10 @@ import Database.LSMTree.Internal.Entry (Entry)
1722
import qualified Database.LSMTree.Internal.Entry as Entry
1823
import qualified Database.LSMTree.Internal.Index as Index
1924
import qualified Database.LSMTree.Internal.Lookup as Lookup
20-
import Database.LSMTree.Internal.MergingRun
25+
import qualified Database.LSMTree.Internal.MergingRun as MR
2126
import Database.LSMTree.Internal.MergingTree
2227
import Database.LSMTree.Internal.MergingTree.Lookup
28+
import qualified Database.LSMTree.Internal.Paths as Paths
2329
import qualified Database.LSMTree.Internal.Run as Run
2430
import qualified Database.LSMTree.Internal.RunAcc as RunAcc
2531
import 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

4555
runParams :: 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
195205
modelMerge 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

200210
resolveVal :: SerialisedValue -> SerialisedValue -> SerialisedValue
201211
resolveVal (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".

test/Test/Database/LSMTree/Internal/Snapshot/Codec.hs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,6 @@ deriving stock instance Show r => Show (SnapPendingMerge r)
384384
deriving stock instance Show r => Show (SnapPreExistingRun r)
385385

386386
deriving stock instance Show MergeDebt
387-
deriving stock instance Show MergeCredits
388387
deriving stock instance Show NominalDebt
389388
deriving stock instance Show NominalCredits
390389

0 commit comments

Comments
 (0)