@@ -353,7 +353,7 @@ invariant conf@LSMConfig{..} (LSMContent _ levels ul) = do
353
353
-- levelling run becomes too large and is promoted, in that case
354
354
-- initially there's no merge, but it is still represented as an
355
355
-- 'IncomingRun', using 'Single'. Thus there are no other resident runs.
356
- MergePolicyLevelling -> assertST $ null rs
356
+ MergePolicyLevelling -> assertST $ null rs && null ls
357
357
-- Runs in tiering levels usually fit that size, but they can be one
358
358
-- larger, if a run has been held back (creating a (T+1)-way merge).
359
359
MergePolicyTiering -> assertST $ all (\ r -> runToLevelNumber MergePolicyTiering conf r `elem` [ln, ln+ 1 ]) rs
@@ -383,18 +383,19 @@ invariant conf@LSMConfig{..} (LSMContent _ levels ul) = do
383
383
(_, CompletedMerge r) ->
384
384
assertST $ runToLevelNumber MergePolicyLevelling conf r <= ln+ 1
385
385
386
- -- An ongoing merge for levelling should have T incoming runs of
387
- -- the right size for the level below (or slightly larger due to
388
- -- holding back underfull runs), and 1 run from this level,
389
- -- but the run from this level can be of almost any size for the
390
- -- same reasons as above. Although if this is the first merge for
391
- -- a new level, it'll have only T runs.
386
+ -- An ongoing merge for levelling should have T incoming runs of the
387
+ -- right size for the level below (or slightly larger due to holding
388
+ -- back underfull runs), and at most 1 run from this level. The run
389
+ -- from this level can be of almost any size for the same reasons as
390
+ -- above. Although if this is the first merge for a new level, it'll
391
+ -- have only T runs.
392
392
(_, OngoingMerge _ rs _) -> do
393
393
assertST $ length rs `elem` [configSizeRatio, configSizeRatio + 1 ]
394
394
assertST $ all (\ r -> runSize r > 0 ) rs -- don't merge empty runs
395
395
let incoming = take configSizeRatio rs
396
396
let resident = drop configSizeRatio rs
397
397
assertST $ all (\ r -> runToLevelNumber MergePolicyTiering conf r `elem` [ln- 1 , ln]) incoming
398
+ assertST $ length resident `elem` [0 , 1 ]
398
399
assertST $ all (\ r -> runToLevelNumber MergePolicyLevelling conf r <= ln+ 1 ) resident
399
400
400
401
MergePolicyTiering ->
@@ -421,13 +422,19 @@ invariant conf@LSMConfig{..} (LSMContent _ levels ul) = do
421
422
(_, CompletedMerge r, MergeMidLevel ) ->
422
423
assertST $ runToLevelNumber MergePolicyTiering conf r `elem` [ln- 1 , ln, ln+ 1 ]
423
424
424
- -- An ongoing merge for tiering should have T incoming runs of
425
- -- the right size for the level below, and at most 1 run held back
426
- -- due to being too small (which would thus also be of the size of
427
- -- the level below).
425
+ -- An ongoing merge for tiering should have T incoming runs of the
426
+ -- right size for the level below (or slightly larger due to holding
427
+ -- back underfull runs), and at most 1 run held back due to being
428
+ -- too small (which would thus also be of the size of the level
429
+ -- below).
428
430
(_, OngoingMerge _ rs _, _) -> do
429
- assertST $ length rs == configSizeRatio || length rs == configSizeRatio + 1
430
- assertST $ all (\ r -> runToLevelNumber MergePolicyTiering conf r == ln- 1 ) rs
431
+ assertST $ length rs `elem` [configSizeRatio, configSizeRatio + 1 ]
432
+ assertST $ all (\ r -> runSize r > 0 ) rs -- don't merge empty runs
433
+ let incoming = take configSizeRatio rs
434
+ let heldBack = drop configSizeRatio rs
435
+ assertST $ all (\ r -> runToLevelNumber MergePolicyTiering conf r `elem` [ln- 1 , ln]) incoming
436
+ assertST $ length heldBack `elem` [0 , 1 ]
437
+ assertST $ all (\ r -> runToLevelNumber MergePolicyTiering conf r == ln- 1 ) heldBack
431
438
432
439
-- We don't make many assumptions apart from what the types already enforce.
433
440
-- In particular, there are no invariants on the progress of the merges,
@@ -526,6 +533,21 @@ assert p x = Exc.assert p (const x callStack)
526
533
assertST :: HasCallStack => Bool -> ST s ()
527
534
assertST p = assert p $ pure ()
528
535
536
+ assertWithMsg :: HasCallStack => Maybe String -> a -> a
537
+ assertWithMsg = assert . p
538
+ where
539
+ p Nothing = True
540
+ p (Just msg) = error $ " Assertion failed: " <> msg
541
+
542
+ assertWithMsgM :: (HasCallStack , Monad m ) => Maybe String -> m ()
543
+ assertWithMsgM mmsg = assertWithMsg mmsg $ pure ()
544
+
545
+ leq :: (Show a , Ord a ) => a -> a -> Maybe String
546
+ leq x y = if x <= y then Nothing else Just $
547
+ printf " Expected x <= y, but got %s > %s"
548
+ (show x)
549
+ (show y)
550
+
529
551
-------------------------------------------------------------------------------
530
552
-- Run sizes
531
553
--
@@ -1461,7 +1483,7 @@ newLevelMerge _ _ _ _ _ [r] = pure (Single r)
1461
1483
newLevelMerge tr conf@ LSMConfig {.. } level mergePolicy mergeType rs = do
1462
1484
assertST (length rs `elem` [configSizeRatio, configSizeRatio + 1 ])
1463
1485
mergingRun@ (MergingRun _ physicalDebt _) <- newMergingRun mergeType rs
1464
- assertST (totalDebt physicalDebt <= maxPhysicalDebt)
1486
+ assertWithMsgM $ leq (totalDebt physicalDebt) maxPhysicalDebt
1465
1487
traceWith tr MergeStartedEvent {
1466
1488
mergePolicy,
1467
1489
mergeType,
@@ -1484,15 +1506,24 @@ newLevelMerge tr conf@LSMConfig{..} level mergePolicy mergeType rs = do
1484
1506
-- such that we pay off the physical and nominal debts at the same time.
1485
1507
--
1486
1508
-- We can bound the worst case physical debt: this is the maximum amount of
1487
- -- steps a merge at this level could need. Note that for levelling this is
1488
- -- includes the single run in the current level.
1509
+ -- steps a merge at this level could need. See the
1510
+ -- 'expectedMergingRunLengths' where-clause of the 'invariant' function for
1511
+ -- the full reasoning.
1489
1512
maxPhysicalDebt =
1490
1513
case mergePolicy of
1491
1514
MergePolicyLevelling ->
1492
- configSizeRatio * levelNumberToMaxRunSize MergePolicyTiering conf (level- 1 )
1515
+ -- Incoming runs, which may be slightly overfull with respect to the
1516
+ -- previous level
1517
+ configSizeRatio * levelNumberToMaxRunSize MergePolicyTiering conf level
1518
+ -- The single run that was already on this level
1493
1519
+ levelNumberToMaxRunSize MergePolicyLevelling conf level
1494
1520
MergePolicyTiering ->
1495
- length rs * levelNumberToMaxRunSize MergePolicyTiering conf (level- 1 )
1521
+ -- Incoming runs, which may be slightly overfull with respect to the
1522
+ -- previous level
1523
+ configSizeRatio * levelNumberToMaxRunSize MergePolicyTiering conf level
1524
+ -- Held back run that is underfull with respect to the current
1525
+ -- level
1526
+ + levelNumberToMaxRunSize MergePolicyTiering conf (level - 1 )
1496
1527
1497
1528
-------------------------------------------------------------------------------
1498
1529
-- MergingTree abstraction
0 commit comments