@@ -16,6 +16,8 @@ module Database.LSMTree.Internal.MergeSchedule (
1616 , Level (.. )
1717 , IncomingRun (.. )
1818 , MergePolicyForLevel (.. )
19+ -- * Union level
20+ , UnionLevel (.. )
1921 -- * Flushes and scheduled merges
2022 , updatesWithInterleavedFlushes
2123 , flushWriteBuffer
@@ -50,6 +52,7 @@ import Database.LSMTree.Internal.Lookup (ResolveSerialisedValue)
5052import Database.LSMTree.Internal.Merge (MergeType (.. ))
5153import Database.LSMTree.Internal.MergingRun (MergingRun , NumRuns (.. ))
5254import qualified Database.LSMTree.Internal.MergingRun as MR
55+ import Database.LSMTree.Internal.MergingTree (MergingTree )
5356import Database.LSMTree.Internal.Paths (RunFsPaths (.. ),
5457 SessionRoot (.. ))
5558import qualified Database.LSMTree.Internal.Paths as Paths
@@ -108,15 +111,21 @@ data MergeTrace =
108111 Table content
109112-------------------------------------------------------------------------------}
110113
114+ -- | The levels of the table, from most to least recently inserted.
111115data TableContent m h = TableContent {
112- -- TODO: probably less allocation to make this a MutVar
116+ -- | The in-memory level 0 of the table
117+ --
118+ -- TODO: probably less allocation to make this a MutVar
113119 tableWriteBuffer :: ! WriteBuffer
114120 -- | The blob storage for entries in the write buffer
115121 , tableWriteBufferBlobs :: ! (Ref (WriteBufferBlobs m h ))
116- -- | A hierarchy of levels. The vector indexes double as level numbers.
122+ -- | A hierarchy of \"regular\" on-disk levels numbered 1 and up. Note that
123+ -- vector index @n@ refers to level @n+1@.
117124 , tableLevels :: ! (Levels m h )
118- -- | Cache of flattened 'levels'.
125+ -- | Cache of flattened regular 'levels'.
119126 , tableCache :: ! (LevelsCache m h )
127+ -- | An optional final union level, not included in the table cache.
128+ , tableUnionLevel :: ! (UnionLevel m h )
120129 }
121130
122131{-# SPECIALISE duplicateTableContent :: ActionRegistry IO -> TableContent IO h -> IO (TableContent IO h) #-}
@@ -125,22 +134,24 @@ duplicateTableContent ::
125134 => ActionRegistry m
126135 -> TableContent m h
127136 -> m (TableContent m h )
128- duplicateTableContent reg (TableContent wb wbb levels cache) = do
137+ duplicateTableContent reg (TableContent wb wbb levels cache ul ) = do
129138 wbb' <- withRollback reg (dupRef wbb) releaseRef
130139 levels' <- duplicateLevels reg levels
131140 cache' <- duplicateLevelsCache reg cache
132- return $! TableContent wb wbb' levels' cache'
141+ ul' <- duplicateUnionLevel reg ul
142+ return $! TableContent wb wbb' levels' cache' ul'
133143
134144{-# SPECIALISE releaseTableContent :: ActionRegistry IO -> TableContent IO h -> IO () #-}
135145releaseTableContent ::
136146 (PrimMonad m , MonadMask m )
137147 => ActionRegistry m
138148 -> TableContent m h
139149 -> m ()
140- releaseTableContent reg (TableContent _wb wbb levels cache) = do
150+ releaseTableContent reg (TableContent _wb wbb levels cache ul ) = do
141151 delayedCommit reg (releaseRef wbb)
142152 releaseLevels reg levels
143153 releaseLevelsCache reg cache
154+ releaseUnionLevel reg ul
144155
145156{- ------------------------------------------------------------------------------
146157 Levels cache
@@ -276,7 +287,9 @@ releaseLevelsCache reg cache =
276287
277288type Levels m h = V. Vector (Level m h )
278289
279- -- | Runs in order from newer to older
290+ -- | A level is a sequence of resident runs at this level, prefixed by an
291+ -- incoming run, which is usually multiple runs that are being merged. Once
292+ -- completed, the resulting run will become a resident run at this level.
280293data Level m h = Level {
281294 incomingRun :: ! (IncomingRun m h )
282295 , residentRuns :: ! (V. Vector (Ref (Run m h )))
@@ -294,13 +307,23 @@ instance NFData MergePolicyForLevel where
294307 rnf LevelTiering = ()
295308 rnf LevelLevelling = ()
296309
297- mergePolicyForLevel :: MergePolicy -> LevelNo -> Levels m h -> MergePolicyForLevel
298- mergePolicyForLevel MergePolicyLazyLevelling (LevelNo n) nextLevels
310+ -- | We use levelling on the last level, unless that is also the first level.
311+ mergePolicyForLevel ::
312+ MergePolicy
313+ -> LevelNo
314+ -> Levels m h
315+ -> UnionLevel m h
316+ -> MergePolicyForLevel
317+ mergePolicyForLevel MergePolicyLazyLevelling (LevelNo n) nextLevels unionLevel
299318 | n == 1
300- , V. null nextLevels
301319 = LevelTiering -- always use tiering on first level
302- | V. null nextLevels = LevelLevelling -- levelling on last level
303- | otherwise = LevelTiering
320+
321+ | V. null nextLevels
322+ , NoUnion <- unionLevel
323+ = LevelLevelling -- levelling on last level
324+
325+ | otherwise
326+ = LevelTiering
304327
305328{-# SPECIALISE duplicateLevels :: ActionRegistry IO -> Levels IO h -> IO (Levels IO h) #-}
306329duplicateLevels ::
@@ -353,6 +376,50 @@ releaseIncomingRun reg (Merging _ mr) = delayedCommit reg (releaseRef mr)
353376iforLevelM_ :: Monad m => Levels m h -> (LevelNo -> Level m h -> m () ) -> m ()
354377iforLevelM_ lvls k = V. iforM_ lvls $ \ i lvl -> k (LevelNo (i + 1 )) lvl
355378
379+ {- ------------------------------------------------------------------------------
380+ Union level
381+ -------------------------------------------------------------------------------}
382+
383+ -- | An additional optional last level, created as a result of
384+ -- 'Database.LSMTree.Monoidal.union'. It can not only contain an ongoing merge
385+ -- of multiple runs, but a nested tree of merges.
386+ --
387+ -- TODO: So far, this is
388+ -- * never created
389+ -- * not stored in snapshots
390+ -- * not loaded from snapshots
391+ -- * ignored in lookups
392+ -- * never merged into the regular levels
393+ data UnionLevel m h =
394+ NoUnion
395+ | Union ! (Ref (MergingTree m h ))
396+
397+ {-# SPECIALISE duplicateUnionLevel ::
398+ ActionRegistry IO
399+ -> UnionLevel IO h
400+ -> IO (UnionLevel IO h) #-}
401+ duplicateUnionLevel ::
402+ (PrimMonad m , MonadMask m )
403+ => ActionRegistry m
404+ -> UnionLevel m h
405+ -> m (UnionLevel m h )
406+ duplicateUnionLevel reg ul =
407+ case ul of
408+ NoUnion -> return ul
409+ Union tree -> Union <$> withRollback reg (dupRef tree) releaseRef
410+
411+ {-# SPECIALISE releaseUnionLevel ::
412+ ActionRegistry IO
413+ -> UnionLevel IO h
414+ -> IO () #-}
415+ releaseUnionLevel ::
416+ (PrimMonad m , MonadMask m )
417+ => ActionRegistry m
418+ -> UnionLevel m h
419+ -> m ()
420+ releaseUnionLevel _ NoUnion = return ()
421+ releaseUnionLevel reg (Union tree) = delayedCommit reg (releaseRef tree)
422+
356423{- ------------------------------------------------------------------------------
357424 Flushes and scheduled merges
358425-------------------------------------------------------------------------------}
@@ -524,13 +591,17 @@ flushWriteBuffer tr conf@TableConfig{confDiskCachePolicy}
524591 delayedCommit reg (releaseRef (tableWriteBufferBlobs tc))
525592 wbblobs' <- withRollback reg (WBB. new hfs (Paths. tableBlobPath root n))
526593 releaseRef
527- levels' <- addRunToLevels tr conf resolve hfs hbio root uc r reg (tableLevels tc)
594+ levels' <- addRunToLevels tr conf resolve hfs hbio root uc r reg
595+ (tableLevels tc)
596+ (tableUnionLevel tc)
528597 tableCache' <- rebuildCache reg (tableCache tc) levels'
529598 pure $! TableContent {
530599 tableWriteBuffer = WB. empty
531600 , tableWriteBufferBlobs = wbblobs'
532601 , tableLevels = levels'
533602 , tableCache = tableCache'
603+ -- TODO: move into regular levels if merge completed and size fits
604+ , tableUnionLevel = tableUnionLevel tc
534605 }
535606
536607{-# SPECIALISE addRunToLevels ::
@@ -544,6 +615,7 @@ flushWriteBuffer tr conf@TableConfig{confDiskCachePolicy}
544615 -> Ref (Run IO h)
545616 -> ActionRegistry IO
546617 -> Levels IO h
618+ -> UnionLevel IO h
547619 -> IO (Levels IO h) #-}
548620-- | Add a run to the levels, and propagate merges.
549621--
@@ -562,8 +634,9 @@ addRunToLevels ::
562634 -> Ref (Run m h )
563635 -> ActionRegistry m
564636 -> Levels m h
637+ -> UnionLevel m h
565638 -> m (Levels m h )
566- addRunToLevels tr conf@ TableConfig {.. } resolve hfs hbio root uc r0 reg levels = do
639+ addRunToLevels tr conf@ TableConfig {.. } resolve hfs hbio root uc r0 reg levels ul = do
567640 go (LevelNo 1 ) (V. singleton r0) levels
568641 where
569642 -- NOTE: @go@ is based on the @increment@ function from the
@@ -578,16 +651,16 @@ addRunToLevels tr conf@TableConfig{..} resolve hfs hbio root uc r0 reg levels =
578651 go ! ln rs (V. uncons -> Nothing ) = do
579652 traceWith tr $ AtLevel ln TraceAddLevel
580653 -- Make a new level
581- let policyForLevel = mergePolicyForLevel confMergePolicy ln V. empty
654+ let policyForLevel = mergePolicyForLevel confMergePolicy ln V. empty ul
582655 ir <- newMerge policyForLevel MergeLastLevel ln rs
583656 return $! V. singleton $ Level ir V. empty
584657 go ! ln rs' (V. uncons -> Just (Level ir rs, ls)) = do
585658 r <- expectCompletedMerge ln ir
586- case mergePolicyForLevel confMergePolicy ln ls of
659+ case mergePolicyForLevel confMergePolicy ln ls ul of
587660 -- If r is still too small for this level then keep it and merge again
588661 -- with the incoming runs.
589662 LevelTiering | Run. size r <= maxRunSize' conf LevelTiering (pred ln) -> do
590- let mergelast = mergeLastForLevel ls
663+ let mergelast = mergeLastForLevel ls ul
591664 ir' <- newMerge LevelTiering mergelast ln (rs' `V.snoc` r)
592665 pure $! Level ir' rs `V.cons` ls
593666 -- This tiering level is now full. We take the completed merged run
@@ -601,7 +674,7 @@ addRunToLevels tr conf@TableConfig{..} resolve hfs hbio root uc r0 reg levels =
601674 -- This tiering level is not yet full. We move the completed merged run
602675 -- into the level proper, and start the new merge for the incoming runs.
603676 LevelTiering -> do
604- let mergelast = mergeLastForLevel ls
677+ let mergelast = mergeLastForLevel ls ul
605678 ir' <- newMerge LevelTiering mergelast ln rs'
606679 traceWith tr $ AtLevel ln
607680 $ TraceAddRun
@@ -722,10 +795,12 @@ maxRunSize' :: TableConfig -> MergePolicyForLevel -> LevelNo -> NumEntries
722795maxRunSize' config policy ln =
723796 maxRunSize (confSizeRatio config) (confWriteBufferAlloc config) policy ln
724797
725- mergeLastForLevel :: Levels m h -> MergeType
726- mergeLastForLevel levels
727- | V. null levels = MergeLastLevel
728- | otherwise = MergeMidLevel
798+ -- | If there are no further levels provided, this level is the last one.
799+ -- However, if a 'Union' is present, it acts as another (last) level.
800+ mergeLastForLevel :: Levels m h -> UnionLevel m h -> MergeType
801+ mergeLastForLevel levels unionLevel
802+ | V. null levels, NoUnion <- unionLevel = MergeLastLevel
803+ | otherwise = MergeMidLevel
729804
730805levelIsFull :: SizeRatio -> V. Vector run -> Bool
731806levelIsFull sr rs = V. length rs + 1 >= (sizeRatioInt sr)
0 commit comments