1- {-# LANGUAGE FlexibleInstances #-}
2- {-# LANGUAGE GADTs #-}
3- {-# LANGUAGE MultiParamTypeClasses #-}
4- {-# LANGUAGE NamedFieldPuns #-}
5- {-# LANGUAGE RankNTypes #-}
6- {-# LANGUAGE ScopedTypeVariables #-}
7- {-# LANGUAGE StandaloneDeriving #-}
8- {-# LANGUAGE TypeFamilies #-}
1+ {-# LANGUAGE TypeFamilies #-}
92
103module ScheduledMergesTestQLS (tests ) where
114
@@ -15,10 +8,12 @@ import Data.Map.Strict (Map)
158import qualified Data.Map.Strict as Map
169
1710import Data.Constraint (Dict (.. ))
11+ import Data.Foldable (traverse_ )
1812import Data.Proxy
1913import Data.STRef
2014
2115import Control.Exception
16+ import Control.Monad (replicateM_ )
2217import Control.Monad.ST
2318import Control.Tracer (Tracer (Tracer ), nullTracer )
2419import qualified Control.Tracer as Tracer
@@ -43,6 +38,8 @@ tests :: TestTree
4338tests = testGroup " ScheduledMerges" [
4439 testProperty " ScheduledMerges vs model" prop_LSM
4540 , testCase " regression_empty_run" test_regression_empty_run
41+ , testCase " merge_again_with_incoming" test_merge_again_with_incoming
42+ , testCase " merge_again_with_incoming'" test_merge_again_with_incoming'
4643 ]
4744
4845prop_LSM :: Actions (Lockstep Model ) -> Property
@@ -84,6 +81,48 @@ test_regression_empty_run =
8481 -- finish merge
8582 LSM. supply lsm 16
8683
84+ -- | Covers the case where a run ends up too small for a level, so it gets
85+ -- merged again with the next incoming runs.
86+ -- That merge gets completed by supplying credits.
87+ test_merge_again_with_incoming :: IO ()
88+ test_merge_again_with_incoming =
89+ runWithTracer $ \ tracer -> do
90+ stToIO $ do
91+ lsm <- LSM. new
92+ let ins k = LSM. insert tracer lsm k 0
93+ -- get something to 3rd level (so 2nd level is not levelling)
94+ -- (needs 5 runs to go to level 2 so the resulting run becomes too big)
95+ traverse_ ins [101 .. 100 + (5 * 16 )]
96+ -- get a very small run (4 elements) to 2nd level
97+ replicateM_ 4 $
98+ traverse_ ins [201 .. 200 + 4 ]
99+ -- get another run to 2nd level, which the small run can be merged with
100+ traverse_ ins [301 .. 300 + 16 ]
101+ -- complete the merge
102+ LSM. supply lsm 32
103+
104+ -- | Covers the case where a run ends up too small for a level, so it gets
105+ -- merged again with the next incoming runs.
106+ -- That merge gets completed and becomes part of another merge.
107+ test_merge_again_with_incoming' :: IO ()
108+ test_merge_again_with_incoming' =
109+ runWithTracer $ \ tracer -> do
110+ stToIO $ do
111+ lsm <- LSM. new
112+ let ins k = LSM. insert tracer lsm k 0
113+ -- get something to 3rd level (so 2nd level is not levelling)
114+ -- (needs 5 runs to go to level 2 so the resulting run becomes too big)
115+ traverse_ ins [101 .. 100 + (5 * 16 )]
116+ -- get a very small run (4 elements) to 2nd level
117+ replicateM_ 4 $
118+ traverse_ ins [201 .. 200 + 4 ]
119+ -- get another run to 2nd level, which the small run can be merged with
120+ traverse_ ins [301 .. 300 + 16 ]
121+ -- get 3 more to 2nd level, so the merge above is expected to complete
122+ -- (actually more, as runs only move once a fifth run arrives...)
123+ traverse_ ins [401 .. 400 + (6 * 16 )]
124+
125+
87126-- | Provides a tracer and will add the log of traced events to the reported
88127-- failure.
89128runWithTracer :: (Tracer (ST RealWorld ) Event -> IO a ) -> IO a
0 commit comments