Skip to content

Commit 72e0b7f

Browse files
committed
WIP: store the session salt in a session metadata file
1 parent 6fbee36 commit 72e0b7f

File tree

17 files changed

+784
-258
lines changed

17 files changed

+784
-258
lines changed

bench-unions/Bench/Unions.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ doSetup gopts = do
292292
createDirectoryIfMissing True $ rootDir gopts
293293

294294
-- Populate the specified number of tables
295-
LSM.withSession (rootDir gopts) $ \session -> do
295+
LSM.withOpenSession (rootDir gopts) $ \session -> do
296296
-- Create a "baseline" table
297297
--
298298
-- We create a single table that *already has* all the same key value pairs
@@ -337,7 +337,7 @@ tableRange gopts =
337337
-- | Count duplicate keys in all tables that will be unioned together
338338
doCollisionAnalysis :: GlobalOpts -> IO ()
339339
doCollisionAnalysis gopts = do
340-
LSM.withSession (rootDir gopts) $ \session -> do
340+
LSM.withOpenSession (rootDir gopts) $ \session -> do
341341
seenRef <- newIORef Set.empty
342342
dupRef <- newIORef Set.empty
343343

@@ -381,7 +381,7 @@ doRun gopts opts = do
381381
withFile dataPath WriteMode $ \h -> do
382382
hPutStrLn h "# iteration \t baseline (ops/sec) \t union (ops/sec) \t union debt"
383383

384-
LSM.withSession (rootDir gopts) $ \session -> do
384+
LSM.withOpenSession (rootDir gopts) $ \session -> do
385385
-- Load the baseline table
386386
LSM.withTableFromSnapshot session baselineTableName label
387387
$ \baselineTable -> do

bench/macro/lsm-tree-bench-wp8.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ doSetup' gopts opts = do
416416

417417
let name = LSM.toSnapshotName "bench"
418418

419-
LSM.withSession (mkTracer gopts) hasFS hasBlockIO benchSalt (FS.mkFsPath []) $ \session -> do
419+
LSM.withOpenSession (mkTracer gopts) hasFS hasBlockIO benchSalt (FS.mkFsPath []) $ \session -> do
420420
tbl <- LSM.newTableWith @IO @K @V @B (mkTableConfigSetup gopts opts benchTableConfig) session
421421

422422
forM_ (groupsOfN 256 [ 0 .. initialSize gopts ]) $ \batch -> do
@@ -578,7 +578,7 @@ doRun gopts opts = do
578578

579579
let name = LSM.toSnapshotName "bench"
580580

581-
LSM.withSession (mkTracer gopts) hasFS hasBlockIO benchSalt (FS.mkFsPath []) $ \session ->
581+
LSM.withOpenSession (mkTracer gopts) hasFS hasBlockIO benchSalt (FS.mkFsPath []) $ \session ->
582582
withLatencyHandle $ \h -> do
583583
-- open snapshot
584584
-- In checking mode we start with an empty table, since our pure

lsm-tree.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ library
594594
, lsm-tree:kmerge
595595
, primitive ^>=0.9
596596
, random ^>=1.0 || ^>=1.1 || ^>=1.2 || ^>=1.3
597+
, serialise ^>=0.2
597598
, text ^>=2.1.1
598599
, utf8-string ^>=1.0
599600
, vector ^>=0.13

src/Database/LSMTree.hs

Lines changed: 206 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@ module Database.LSMTree (
1919

2020
-- * Sessions
2121
Session,
22-
withSession,
23-
withSessionIO,
22+
withOpenSession,
23+
withOpenSessionIO,
24+
withNewSession,
25+
withRestoreSession,
2426
openSession,
2527
openSessionIO,
28+
newSession,
29+
restoreSession,
2630
closeSession,
2731

2832
-- * Tables
@@ -200,8 +204,8 @@ import Control.DeepSeq (NFData (..))
200204
import Control.Exception.Base (assert)
201205
import Control.Monad.Class.MonadAsync (MonadAsync)
202206
import Control.Monad.Class.MonadST (MonadST)
203-
import Control.Monad.Class.MonadThrow (MonadCatch (..), MonadMask,
204-
MonadThrow (..))
207+
import Control.Monad.Class.MonadThrow (MonadCatch (..), MonadEvaluate,
208+
MonadMask, MonadThrow (..))
205209
import Control.Monad.Primitive (PrimMonad)
206210
import Control.Tracer (Tracer)
207211
import Data.Bifunctor (Bifunctor (..))
@@ -290,6 +294,7 @@ type IOLike m =
290294
, MonadMask m
291295
, PrimMonad m
292296
, MonadST m
297+
, MonadEvaluate m
293298
)
294299

295300
--------------------------------------------------------------------------------
@@ -384,7 +389,7 @@ runExample action = do
384389
let createSessionDir = Dir.createDirectoryIfMissing True sessionDir
385390
let removeSessionDir = Dir.removeDirectoryRecursive sessionDir
386391
bracket_ createSessionDir removeSessionDir $ do
387-
LSMT.withSessionIO mempty sessionDir $ \session -> do
392+
LSMT.withOpenSessionIO mempty sessionDir $ \session -> do
388393
LSMT.withTable session $ \table ->
389394
action session table
390395
:}
@@ -399,8 +404,8 @@ runExample action = do
399404
{- |
400405
Run an action with access to a session opened from a session directory.
401406
402-
If the session directory is empty, a new session is created.
403-
Otherwise, the session directory is opened as an existing session.
407+
If the session directory is empty, a new session is created using the given salt.
408+
Otherwise, the session directory is restored as an existing session ignoring the given salt.
404409
405410
If there are no open tables or cursors when the session terminates, then the disk I\/O complexity of this operation is \(O(1)\).
406411
Otherwise, 'closeTable' is called for each open table and 'closeCursor' is called for each open cursor.
@@ -426,7 +431,7 @@ Throws the following exceptions:
426431
If the session directory is malformed.
427432
-}
428433
{-# SPECIALISE
429-
withSession ::
434+
withOpenSession ::
430435
Tracer IO LSMTreeTrace ->
431436
HasFS IO HandleIO ->
432437
HasBlockIO IO HandleIO ->
@@ -435,7 +440,7 @@ Throws the following exceptions:
435440
(Session IO -> IO a) ->
436441
IO a
437442
#-}
438-
withSession ::
443+
withOpenSession ::
439444
forall m h a.
440445
(IOLike m, Typeable h) =>
441446
Tracer m LSMTreeTrace ->
@@ -447,28 +452,132 @@ withSession ::
447452
FsPath ->
448453
(Session m -> m a) ->
449454
m a
450-
withSession tracer hasFS hasBlockIO sessionSalt sessionDir action = do
451-
Internal.withSession tracer hasFS hasBlockIO sessionSalt sessionDir (action . Session)
455+
withOpenSession tracer hasFS hasBlockIO sessionSalt sessionDir action = do
456+
Internal.withSessionOpen tracer hasFS hasBlockIO sessionSalt sessionDir (action . Session)
452457

453-
-- | Variant of 'withSession' that is specialised to 'IO' using the real filesystem.
454-
withSessionIO ::
458+
-- | Variant of 'withOpenSession' that is specialised to 'IO' using the real filesystem.
459+
withOpenSessionIO ::
455460
Tracer IO LSMTreeTrace ->
456461
FilePath ->
457462
(Session IO -> IO a) ->
458463
IO a
459-
withSessionIO tracer sessionDir action = do
464+
withOpenSessionIO tracer sessionDir action = do
460465
let mountPoint = MountPoint sessionDir
461466
let sessionDirFsPath = mkFsPath []
462467
let hasFS = ioHasFS mountPoint
463468
sessionSalt <- randomIO
464469
withIOHasBlockIO hasFS defaultIOCtxParams $ \hasBlockIO ->
465-
withSession tracer hasFS hasBlockIO sessionSalt sessionDirFsPath action
470+
withOpenSession tracer hasFS hasBlockIO sessionSalt sessionDirFsPath action
471+
472+
{- |
473+
Run an action with access to a new session.
474+
475+
The session directory must be empty.
476+
477+
If there are no open tables or cursors when the session terminates, then the disk I\/O complexity of this operation is \(O(1)\).
478+
Otherwise, 'closeTable' is called for each open table and 'closeCursor' is called for each open cursor.
479+
Consequently, the worst-case disk I\/O complexity of this operation depends on the merge policy of the open tables in the session.
480+
The following assumes all tables in the session have the same merge policy:
481+
482+
['LazyLevelling']:
483+
\(O(o \: T \log_T \frac{n}{B})\).
484+
485+
The variable \(o\) refers to the number of open tables and cursors in the session.
486+
487+
This function is exception-safe for both synchronous and asynchronous exceptions.
488+
489+
It is recommended to use this function instead of 'newSession' and 'closeSession'.
490+
491+
Throws the following exceptions:
492+
493+
['SessionDirDoesNotExistError']:
494+
If the session directory does not exist.
495+
['SessionDirLockedError']:
496+
If the session directory is locked by another process.
497+
['SessionDirCorruptedError']:
498+
If the session directory is malformed.
499+
-}
500+
{-# SPECIALISE
501+
withNewSession ::
502+
Tracer IO LSMTreeTrace ->
503+
HasFS IO HandleIO ->
504+
HasBlockIO IO HandleIO ->
505+
Salt ->
506+
FsPath ->
507+
(Session IO -> IO a) ->
508+
IO a
509+
#-}
510+
withNewSession ::
511+
forall m h a.
512+
(IOLike m, Typeable h) =>
513+
Tracer m LSMTreeTrace ->
514+
HasFS m h ->
515+
HasBlockIO m h ->
516+
-- | The session salt.
517+
Salt ->
518+
-- | The session directory.
519+
FsPath ->
520+
(Session m -> m a) ->
521+
m a
522+
withNewSession tracer hasFS hasBlockIO sessionSalt sessionDir action = do
523+
Internal.withNewSession tracer hasFS hasBlockIO sessionSalt sessionDir (action . Session)
524+
525+
{- |
526+
Run an action with access to a restored session.
527+
528+
The session directory must be non-empty: a session must have previously been
529+
opened and closed in this directory.
530+
531+
If there are no open tables or cursors when the session terminates, then the disk I\/O complexity of this operation is \(O(1)\).
532+
Otherwise, 'closeTable' is called for each open table and 'closeCursor' is called for each open cursor.
533+
Consequently, the worst-case disk I\/O complexity of this operation depends on the merge policy of the open tables in the session.
534+
The following assumes all tables in the session have the same merge policy:
535+
536+
['LazyLevelling']:
537+
\(O(o \: T \log_T \frac{n}{B})\).
538+
539+
The variable \(o\) refers to the number of open tables and cursors in the session.
540+
541+
This function is exception-safe for both synchronous and asynchronous exceptions.
542+
543+
It is recommended to use this function instead of 'restoreSession' and 'closeSession'.
544+
545+
Throws the following exceptions:
546+
547+
['SessionDirDoesNotExistError']:
548+
If the session directory does not exist.
549+
['SessionDirLockedError']:
550+
If the session directory is locked by another process.
551+
['SessionDirCorruptedError']:
552+
If the session directory is malformed.
553+
-}
554+
{-# SPECIALISE
555+
withRestoreSession ::
556+
Tracer IO LSMTreeTrace ->
557+
HasFS IO HandleIO ->
558+
HasBlockIO IO HandleIO ->
559+
FsPath ->
560+
(Session IO -> IO a) ->
561+
IO a
562+
#-}
563+
withRestoreSession ::
564+
forall m h a.
565+
(IOLike m, Typeable h) =>
566+
Tracer m LSMTreeTrace ->
567+
HasFS m h ->
568+
HasBlockIO m h ->
569+
-- | The session directory.
570+
FsPath ->
571+
(Session m -> m a) ->
572+
m a
573+
withRestoreSession tracer hasFS hasBlockIO sessionDir action = do
574+
Internal.withRestoreSession tracer hasFS hasBlockIO sessionDir (action . Session)
466575

467576
{- |
468577
Open a session from a session directory.
469578
470-
If the session directory is empty, a new session is created.
471-
Otherwise, the session directory is opened as an existing session.
579+
If the session directory is empty, a new session is created using the given salt.
580+
Otherwise, the session directory is restored as an existing session ignoring the given salt.
472581
473582
The worst-case disk I\/O complexity of this operation is \(O(1)\).
474583
@@ -522,6 +631,86 @@ openSessionIO tracer sessionDir = do
522631
bracketOnError acquireHasBlockIO releaseHasBlockIO $ \hasBlockIO ->
523632
openSession tracer hasFS hasBlockIO sessionSalt sessionDirFsPath
524633

634+
{- |
635+
Create a new session.
636+
637+
The session directory must be empty.
638+
639+
The worst-case disk I\/O complexity of this operation is \(O(1)\).
640+
641+
__Warning:__ Sessions hold open resources and must be closed using 'closeSession'.
642+
643+
Throws the following exceptions:
644+
645+
['SessionDirDoesNotExistError']:
646+
If the session directory does not exist.
647+
['SessionDirLockedError']:
648+
If the session directory is locked by another process.
649+
['SessionDirCorruptedError']:
650+
If the session directory is malformed.
651+
-}
652+
{-# SPECIALISE
653+
newSession ::
654+
Tracer IO LSMTreeTrace ->
655+
HasFS IO HandleIO ->
656+
HasBlockIO IO HandleIO ->
657+
Salt ->
658+
FsPath ->
659+
IO (Session IO)
660+
#-}
661+
newSession ::
662+
forall m h.
663+
(IOLike m, Typeable h) =>
664+
Tracer m LSMTreeTrace ->
665+
HasFS m h ->
666+
HasBlockIO m h ->
667+
-- | The session salt.
668+
Salt ->
669+
-- | The session directory.
670+
FsPath ->
671+
m (Session m)
672+
newSession tracer hasFS hasBlockIO sessionSalt sessionDir =
673+
Session <$> Internal.newSession tracer hasFS hasBlockIO sessionSalt sessionDir
674+
675+
{- |
676+
Restore a session from a session directory.
677+
678+
The session directory must be non-empty: a session must have previously been
679+
opened (and closed) in this directory.
680+
681+
The worst-case disk I\/O complexity of this operation is \(O(1)\).
682+
683+
__Warning:__ Sessions hold open resources and must be closed using 'closeSession'.
684+
685+
Throws the following exceptions:
686+
687+
['SessionDirDoesNotExistError']:
688+
If the session directory does not exist.
689+
['SessionDirLockedError']:
690+
If the session directory is locked by another process.
691+
['SessionDirCorruptedError']:
692+
If the session directory is malformed.
693+
-}
694+
{-# SPECIALISE
695+
restoreSession ::
696+
Tracer IO LSMTreeTrace ->
697+
HasFS IO HandleIO ->
698+
HasBlockIO IO HandleIO ->
699+
FsPath ->
700+
IO (Session IO)
701+
#-}
702+
restoreSession ::
703+
forall m h.
704+
(IOLike m, Typeable h) =>
705+
Tracer m LSMTreeTrace ->
706+
HasFS m h ->
707+
HasBlockIO m h ->
708+
-- | The session directory.
709+
FsPath ->
710+
m (Session m)
711+
restoreSession tracer hasFS hasBlockIO sessionDir =
712+
Session <$> Internal.restoreSession tracer hasFS hasBlockIO sessionDir
713+
525714
{- |
526715
Close a session.
527716

0 commit comments

Comments
 (0)