1- {-# LANGUAGE LambdaCase #-}
2-
31module Test.Database.LSMTree.Internal.Merge (tests ) where
42
53import Control.Exception (evaluate )
@@ -9,7 +7,7 @@ import qualified Data.BloomFilter as Bloom
97import Data.Foldable (traverse_ )
108import Data.Map.Strict (Map )
119import qualified Data.Map.Strict as Map
12- import Data.Maybe (isJust )
10+ import Data.Maybe (isJust , mapMaybe )
1311import qualified Data.Vector as V
1412import Database.LSMTree.Extras
1513import Database.LSMTree.Extras.Generators (KeyForIndexCompact )
@@ -38,12 +36,15 @@ import Test.Tasty.QuickCheck
3836
3937tests :: TestTree
4038tests = testGroup " Test.Database.LSMTree.Internal.Merge"
41- [ testProperty " prop_MergeDistributes" $ \ level stepSize wbs ->
39+ [ testProperty " prop_MergeDistributes" $ \ mergeType stepSize rds ->
40+ ioPropertyWithMockFS $ \ fs hbio ->
41+ prop_MergeDistributes fs hbio mergeType stepSize rds
42+ , testProperty " prop_MergeUnion" $ \ stepSize rds ->
4243 ioPropertyWithMockFS $ \ fs hbio ->
43- prop_MergeDistributes fs hbio level stepSize wbs
44- , testProperty " prop_AbortMerge" $ \ level stepSize wbs ->
44+ prop_MergeUnion fs hbio stepSize rds
45+ , testProperty " prop_AbortMerge" $ \ level stepSize rds ->
4546 ioPropertyWithMockFS $ \ fs hbio ->
46- prop_AbortMerge fs hbio level stepSize wbs
47+ prop_AbortMerge fs hbio level stepSize rds
4748 ]
4849 where
4950 ioPropertyWithMockFS ::
@@ -70,12 +71,12 @@ prop_MergeDistributes ::
7071 StepSize ->
7172 SmallList (RunData KeyForIndexCompact SerialisedValue SerialisedBlob ) ->
7273 IO Property
73- prop_MergeDistributes fs hbio level stepSize (SmallList rds) =
74+ prop_MergeDistributes fs hbio mergeType stepSize (SmallList rds) =
7475 withRuns fs hbio (V. fromList (zip (simplePaths [10 .. ]) rds')) $ \ runs -> do
7576 let stepsNeeded = sum (map (Map. size . unRunData) rds)
76- (stepsDone, lhs) <- mergeRuns fs hbio level (RunNumber 0 ) runs stepSize
77- withRun fs hbio (simplePath 1 )
78- ( RunData $ mergeWriteBuffers level $ fmap unRunData rds') $ \ rhs -> do
77+ (stepsDone, lhs) <- mergeRuns fs hbio mergeType (RunNumber 0 ) runs stepSize
78+ let runData = RunData $ mergeWriteBuffers mergeType $ fmap unRunData rds'
79+ withRun fs hbio (simplePath 1 ) runData $ \ rhs -> do
7980
8081 (lhsSize, lhsFilter, lhsIndex, lhsKOps,
8182 lhsKOpsFileContent, lhsBlobFileContent) <- getRunContent lhs
@@ -133,6 +134,45 @@ prop_MergeDistributes fs hbio level stepSize (SmallList rds) =
133134 , blobFileContent
134135 )
135136
137+ -- | Union-merging multiple runs behaves like 'Map.unionsWith' on their values
138+ -- and blobs.
139+ prop_MergeUnion ::
140+ FS. HasFS IO h ->
141+ FS. HasBlockIO IO h ->
142+ StepSize ->
143+ SmallList (RunData KeyForIndexCompact SerialisedValue SerialisedBlob ) ->
144+ IO Property
145+ prop_MergeUnion fs hbio stepSize (SmallList rds) =
146+ withRuns fs hbio (V. fromList (zip (simplePaths [10 .. ]) rds')) $ \ runs -> do
147+ (_, run) <- mergeRuns fs hbio MergeUnion (RunNumber 0 ) runs stepSize
148+
149+ lhsKOps <- readKOps Nothing run
150+ let lhs = Map. fromList (mapMaybe (traverse getValueAndBlob) lhsKOps)
151+
152+ -- cleanup
153+ releaseRef run
154+
155+ return $
156+ lhs === rhs
157+ .&&. counterexample (" Deletes in " <> show lhs)
158+ (all ((/= Entry. Delete ) . snd ) lhsKOps)
159+ where
160+ rds' = fmap serialiseRunData rds
161+
162+ rhs :: Map SerialisedKey (SerialisedValue , Maybe SerialisedBlob )
163+ rhs = Map. unionsWith resolveValueAndBlob
164+ (map (Map. mapMaybe getValueAndBlob . unRunData) rds')
165+
166+ getValueAndBlob :: Entry. Entry v b -> Maybe (v , Maybe b )
167+ getValueAndBlob = \ case
168+ Entry. Insert v -> Just (v, Nothing )
169+ Entry. InsertWithBlob v b -> Just (v, Just b)
170+ Entry. Mupdate v -> Just (v, Nothing )
171+ Entry. Delete -> Nothing
172+
173+ resolveValueAndBlob (v', Nothing ) (v, b) = (mappendValues v' v, b)
174+ resolveValueAndBlob (v', Just b) (v, _) = (mappendValues v' v, Just b)
175+
136176-- | After merging for a few steps, we can prematurely abort the merge, which
137177-- should clean up properly.
138178prop_AbortMerge ::
@@ -196,10 +236,12 @@ type SerialisedEntry = Entry.Entry SerialisedValue SerialisedBlob
196236mergeWriteBuffers :: MergeType
197237 -> [Map SerialisedKey SerialisedEntry ]
198238 -> Map SerialisedKey SerialisedEntry
199- mergeWriteBuffers mergeType =
200- -- TODO: review and update this to support MergeUnion
201- (if mergeType == MergeMidLevel then id else Map. filter (not . isDelete))
202- . Map. unionsWith (Entry. combine mappendValues)
239+ mergeWriteBuffers = \ case
240+ MergeMidLevel -> Map. unionsWith (Entry. combine mappendValues)
241+ MergeLastLevel -> Map. filter (not . isDelete)
242+ . Map. unionsWith (Entry. combine mappendValues)
243+ MergeUnion -> Map. filter (not . isDelete)
244+ . Map. unionsWith (Entry. combineUnion mappendValues)
203245 where
204246 isDelete Entry. Delete = True
205247 isDelete _ = False
0 commit comments