@@ -65,6 +65,7 @@ module ScheduledMerges (
65
65
MergeDebt (.. ),
66
66
NominalCredit (.. ),
67
67
NominalDebt (.. ),
68
+ maxBufferSize ,
68
69
Run ,
69
70
runSize ,
70
71
UnionCredits (.. ),
@@ -85,6 +86,7 @@ import Prelude hiding (lookup)
85
86
86
87
import Data.Bits
87
88
import Data.Foldable (for_ , toList , traverse_ )
89
+ import Data.Functor ((<&>) )
88
90
import Data.Map.Strict (Map )
89
91
import qualified Data.Map.Strict as Map
90
92
import Data.Maybe (catMaybes )
@@ -344,19 +346,25 @@ invariant (LSMContent _ levels ul) = do
344
346
expectedRunLengths :: Int -> [Run ] -> [Level s ] -> ST s ()
345
347
expectedRunLengths ln rs ls =
346
348
case mergePolicyForLevel ln ls ul of
347
- -- Levels using levelling have only one (incoming) run, which almost
348
- -- always consists of an ongoing merge. The exception is when a
349
- -- levelling run becomes too large and is promoted, in that case
350
- -- initially there's no merge, but it is still represented as an
351
- -- 'IncomingRun', using 'Single'. Thus there are no other resident runs.
352
- MergePolicyLevelling -> assertST $ null rs
353
- -- Runs in tiering levels usually fit that size, but they can be one
354
- -- larger, if a run has been held back (creating a 5-way merge).
355
- MergePolicyTiering -> assertST $ all (\ r -> tieringRunSizeToLevel r `elem` [ln, ln+ 1 ]) rs
356
- -- (This is actually still not really true, but will hold in practice.
357
- -- In the pathological case, all runs passed to the next level can be
358
- -- factor (5/4) too large, and there the same holding back can lead to
359
- -- factor (6/4) etc., until at level 12 a run is two levels too large.
349
+ MergePolicyLevelling ->
350
+ -- Levels using levelling have only one (incoming) run, which almost
351
+ -- always consists of an ongoing merge. The exception is when a
352
+ -- levelling run becomes too large and is promoted, in that case
353
+ -- initially there's no merge, but it is still represented as an
354
+ -- 'IncomingRun', using 'Single'. Thus there are no other resident
355
+ -- runs.
356
+ assertST $ null rs
357
+ MergePolicyTiering -> do
358
+ -- Runs in tiering levels usually fit that size, but they can be one
359
+ -- larger, if a run has been held back (creating a 5-way merge).
360
+ --
361
+ -- TODO: This is actually still not really true, but will hold in
362
+ -- practice. In the pathological case, all runs passed to the next
363
+ -- level can be factor (5/4) too large, and there the same holding
364
+ -- back can lead to factor (6/4) etc., until at level 12 a run is two
365
+ -- levels too large.
366
+ assertST $ all (\ r -> runSize r > 0 ) rs
367
+ assertST $ all (\ r -> tieringRunSizeToLevel r `elem` [ln, ln+ 1 ]) rs
360
368
361
369
-- Incoming runs being merged also need to be of the right size, but the
362
370
-- conditions are more complicated.
@@ -367,11 +375,12 @@ invariant (LSMContent _ levels ul) = do
367
375
MergePolicyLevelling -> do
368
376
case (ir, mrs) of
369
377
-- A single incoming run (which thus didn't need merging) must be
370
- -- of the expected size range already
378
+ -- of the expected size range already, but it could also be smaller
379
+ -- if it comes from a union level.
371
380
(Single r, m) -> do
372
381
assertST $ case m of CompletedMerge {} -> True
373
382
OngoingMerge {} -> False
374
- assertST $ levellingRunSizeToLevel r = = ln
383
+ assertST $ levellingRunSizeToLevel r < = ln
375
384
376
385
-- A completed merge for levelling can be of almost any size at all!
377
386
-- It can be smaller, due to deletions in the last level. But it
@@ -496,6 +505,11 @@ isCompletedMergingTree (MergingTree ref) = do
496
505
OngoingTreeMerge mr -> isCompletedMergingRun mr
497
506
PendingTreeMerge _ -> failI $ " not completed: PendingTreeMerge"
498
507
508
+ getCompletedMergingTree :: MergingTree s -> ST s (Maybe Run )
509
+ getCompletedMergingTree t =
510
+ either (const Nothing ) Just
511
+ <$> evalInvariant (isCompletedMergingTree t)
512
+
499
513
type Invariant s = E. ExceptT String (ST s )
500
514
501
515
assertI :: String -> Bool -> Invariant s ()
@@ -781,8 +795,11 @@ update tr (LSMHandle scr lsmr) k op = do
781
795
let wb' = Map. insertWith combine k op wb
782
796
if bufferSize wb' >= maxBufferSize
783
797
then do
784
- ls' <- increment tr sc (bufferToRun wb') ls unionLevel
785
- let content' = LSMContent Map. empty ls' unionLevel
798
+ -- Before adding the run to the regular levels, we check if we can get
799
+ -- rid of the union level (by moving it into into the regular ones).
800
+ (ls', ul') <- migrateUnionLevel tr sc ls unionLevel
801
+ ls'' <- increment tr sc (bufferToRun wb') ls' ul'
802
+ let content' = LSMContent Map. empty ls'' ul'
786
803
invariant content'
787
804
writeSTRef lsmr content'
788
805
else
@@ -1158,9 +1175,44 @@ depositNominalCredit (NominalDebt nominalDebt)
1158
1175
-- Updates
1159
1176
--
1160
1177
1178
+ -- | At some point, we want to merge the union level with the regular levels.
1179
+ -- We achieve this by moving it into a new last regular level once it is both
1180
+ -- completed (merged down to a single run) and fits into such a new level.
1181
+ --
1182
+ -- Our representation doesn't allow for empty levels, so we can only put the
1183
+ -- run directly after the pre-existing regular levels. If it is too large for
1184
+ -- that, we don't want to move it yet to avoid violating run size invariants
1185
+ -- and doing inefficient merges of runs with very different sizes.
1186
+ migrateUnionLevel :: forall s . Tracer (ST s ) Event
1187
+ -> Counter -> Levels s -> UnionLevel s
1188
+ -> ST s (Levels s , UnionLevel s )
1189
+ migrateUnionLevel _ _ ls NoUnion = do
1190
+ -- nothing to do
1191
+ return (ls, NoUnion )
1192
+ migrateUnionLevel _tr _sc ls ul@ (Union t _) =
1193
+ -- TODO: tracing
1194
+ getCompletedMergingTree t <&> \ case
1195
+ Just r
1196
+ | null r ->
1197
+ -- If the union level is empty, we can just drop it.
1198
+ (ls, NoUnion )
1199
+ | levellingRunSizeToLevel r <= length ls + 1 ->
1200
+ -- If it fits into a hypothetical new last level, put it there.
1201
+ --
1202
+ -- TODO: In some cases it seems desirable to even add it to the
1203
+ -- existing last regular level (so it becomes part of a merge
1204
+ -- sooner), but that would lead to additional merging work that was
1205
+ -- not accounted for. We'd need to be careful to ensure the merge
1206
+ -- completes in time, without doing a lot of work in a short time.
1207
+ (ls ++ [Level (Single r) [] ], NoUnion )
1208
+ _ ->
1209
+ -- Otherwise, just leave it for now.
1210
+ (ls, ul)
1211
+
1161
1212
increment :: forall s . Tracer (ST s ) Event
1162
- -> Counter -> Run -> Levels s -> UnionLevel s -> ST s (Levels s )
1163
- increment tr sc run0 ls0 ul = do
1213
+ -> Counter -> Run -> Levels s -> UnionLevel s
1214
+ -> ST s (Levels s )
1215
+ increment tr sc run0 ls0 ul =
1164
1216
go 1 [run0] ls0
1165
1217
where
1166
1218
mergeTypeFor :: Levels s -> LevelMergeType
0 commit comments