Skip to content

Commit 714f8b6

Browse files
committed
Bugfix: synchronise the snapshot directory and its contents
Not all the snapshot directory contents were being synchronised, and arguably not in the correct order. The synchronisation now only happens at the very end of taking a snapshot. Because of this, if other parts of the `createSnapshot` function fail, then we won't have flushed file and directory contents to disk unnecessarily.
1 parent 87549dd commit 714f8b6

File tree

4 files changed

+66
-46
lines changed

4 files changed

+66
-46
lines changed

blockio-api/src/System/FS/BlockIO/API.hs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ module System.FS.BlockIO.API (
3030
, LockFileHandle (..)
3131
-- ** Storage synchronisation
3232
, synchroniseFile
33+
, synchroniseDirectoryRecursive
3334
-- * Defaults for the real file system
3435
, tryLockFileIO
3536
, createHardLinkIO
@@ -39,6 +40,7 @@ module System.FS.BlockIO.API (
3940
) where
4041

4142
import Control.DeepSeq
43+
import Control.Monad (forM_)
4244
import Control.Monad.Class.MonadThrow (MonadCatch (bracketOnError),
4345
MonadThrow (..), bracketOnError, try)
4446
import Control.Monad.Primitive (PrimMonad (PrimState))
@@ -60,6 +62,7 @@ import qualified System.IO as GHC
6062
import System.IO.Error (doesNotExistErrorType, ioeSetErrorString,
6163
mkIOError)
6264
import System.Posix.Types (ByteCount, FileOffset)
65+
import Text.Printf
6366

6467
-- | Abstract interface for submitting large batches of I\/O operations.
6568
data HasBlockIO m h = HasBlockIO {
@@ -258,6 +261,7 @@ hDropCacheAll hbio h = hAdviseAll hbio h AdviceDontNeed
258261
Storage synchronisation
259262
-------------------------------------------------------------------------------}
260263

264+
{-# SPECIALISE synchroniseFile :: HasFS IO h -> HasBlockIO IO h -> FsPath -> IO () #-}
261265
-- TODO: currently, we perform an explicit check to see if the file exists and
262266
-- throw an error when it does not exist. We would prefer to be able to rely on
263267
-- withFile to throw an error for us that we could rethrow with an upated
@@ -284,6 +288,37 @@ synchroniseFile hfs hbio path = do
284288
(mkIOError doesNotExistErrorType "synchroniseFile" Nothing Nothing)
285289
("synchroniseFile: file does not exist")
286290

291+
{-# SPECIALISE synchroniseDirectoryRecursive ::
292+
HasFS IO h
293+
-> HasBlockIO IO h
294+
-> FsPath
295+
-> IO ()
296+
#-}
297+
-- | Synchronise a directory and recursively its contents with the storage
298+
-- device.
299+
synchroniseDirectoryRecursive ::
300+
MonadThrow m
301+
=> HasFS m h
302+
-> HasBlockIO m h
303+
-> FsPath
304+
-> m ()
305+
synchroniseDirectoryRecursive hfs hbio path = do
306+
entries <- FS.listDirectory hfs path
307+
forM_ entries $ \entry -> do
308+
let path' = path FS.</> FS.mkFsPath [entry]
309+
isFile <- FS.doesFileExist hfs path'
310+
if isFile then
311+
synchroniseFile hfs hbio path'
312+
else do
313+
isDirectory <- FS.doesDirectoryExist hfs path'
314+
if isDirectory then do
315+
synchroniseDirectoryRecursive hfs hbio path'
316+
synchroniseDirectory hbio path'
317+
else
318+
error $ printf
319+
"listDirectoryRecursive: %s is not a file or directory"
320+
(show path')
321+
287322
{-------------------------------------------------------------------------------
288323
File locks
289324
-------------------------------------------------------------------------------}

src/Database/LSMTree/Internal.hs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,15 +1173,18 @@ createSnapshot snap label tableType t = do
11731173
snapLevels <- toSnapLevels (tableLevels content)
11741174

11751175
-- Hard link runs into the named snapshot directory
1176-
snapLevels' <- snapshotRuns reg hbio snapUc snapDir snapLevels
1176+
snapLevels' <- snapshotRuns reg snapUc snapDir snapLevels
1177+
1178+
-- Release the table content
1179+
releaseTableContent reg content
11771180

11781181
let snapMetaData = SnapshotMetaData label tableType (tableConfig t) snapWriteBufferNumber snapLevels'
11791182
SnapshotMetaDataFile contentPath = Paths.snapshotMetaDataFile snapDir
11801183
SnapshotMetaDataChecksumFile checksumPath = Paths.snapshotMetaDataChecksumFile snapDir
11811184
writeFileSnapshotMetaData hfs contentPath checksumPath snapMetaData
11821185

1183-
-- Release the table content
1184-
releaseTableContent reg content
1186+
-- Make the directory and its contents durable.
1187+
FS.synchroniseDirectoryRecursive hfs hbio (Paths.getNamedSnapshotDir snapDir)
11851188

11861189
{-# SPECIALISE openSnapshot ::
11871190
Session IO h

src/Database/LSMTree/Internal/Snapshot.hs

Lines changed: 24 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,14 @@ module Database.LSMTree.Internal.Snapshot (
2222
-- * Opening from levels snapshot format
2323
, fromSnapLevels
2424
-- * Hard links
25-
, HardLinkDurable (..)
2625
, hardLinkRunFiles
2726
) where
2827

2928
import Control.ActionRegistry
3029
import Control.Concurrent.Class.MonadMVar.Strict
3130
import Control.Concurrent.Class.MonadSTM (MonadSTM)
3231
import Control.DeepSeq (NFData (..))
33-
import Control.Monad (void, when)
32+
import Control.Monad (void)
3433
import Control.Monad.Class.MonadST (MonadST)
3534
import Control.Monad.Class.MonadThrow (MonadMask)
3635
import Control.Monad.Primitive (PrimMonad)
@@ -271,13 +270,13 @@ snapshotWriteBuffer reg hfs hbio activeUc snapUc activeDir snapDir wb wbb = do
271270
-- Hard link the write buffer and write buffer blobs to the snapshot directory.
272271
snapWriteBufferNumber <- uniqueToRunNumber <$> incrUniqCounter snapUc
273272
let snapWriteBufferPaths = WriteBufferFsPaths (getNamedSnapshotDir snapDir) snapWriteBufferNumber
274-
hardLink reg hfs hbio HardLinkDurable
273+
hardLink reg hfs hbio
275274
(writeBufferKOpsPath activeWriteBufferPaths)
276275
(writeBufferKOpsPath snapWriteBufferPaths)
277-
hardLink reg hfs hbio HardLinkDurable
276+
hardLink reg hfs hbio
278277
(writeBufferBlobPath activeWriteBufferPaths)
279278
(writeBufferBlobPath snapWriteBufferPaths)
280-
hardLink reg hfs hbio HardLinkDurable
279+
hardLink reg hfs hbio
281280
(writeBufferChecksumsPath activeWriteBufferPaths)
282281
(writeBufferChecksumsPath snapWriteBufferPaths)
283282
pure snapWriteBufferPaths
@@ -334,36 +333,30 @@ openWriteBuffer reg resolve hfs hbio uc activeDir snapWriteBufferPaths = do
334333

335334
{-# SPECIALISE snapshotRuns ::
336335
ActionRegistry IO
337-
-> HasBlockIO IO h
338336
-> UniqCounter IO
339337
-> NamedSnapshotDir
340338
-> SnapLevels (Ref (Run IO h))
341339
-> IO (SnapLevels RunNumber) #-}
342340
-- | @'snapshotRuns' _ _ snapUc targetDir levels@ creates hard links for all run
343341
-- files associated with the runs in @levels@, and puts the new directory
344342
-- entries in the @targetDir@ directory. The entries are renamed using @snapUc@.
345-
-- The hard links and the @targetDir@ are made durable on disk.
346343
snapshotRuns ::
347344
(MonadMask m, PrimMonad m)
348345
=> ActionRegistry m
349-
-> HasBlockIO m h
350346
-> UniqCounter m
351347
-> NamedSnapshotDir
352348
-> SnapLevels (Ref (Run m h))
353349
-> m (SnapLevels RunNumber)
354-
snapshotRuns reg hbio0 snapUc (NamedSnapshotDir targetDir) levels = do
355-
levels' <-
356-
for levels $ \run@(DeRef Run.Run {
357-
Run.runHasFS = hfs,
358-
Run.runHasBlockIO = hbio
359-
}) -> do
360-
rn <- uniqueToRunNumber <$> incrUniqCounter snapUc
361-
let sourcePaths = Run.runFsPaths run
362-
let targetPaths = sourcePaths { runDir = targetDir , runNumber = rn}
363-
hardLinkRunFiles reg hfs hbio HardLinkDurable sourcePaths targetPaths
364-
pure (runNumber targetPaths)
365-
FS.synchroniseDirectory hbio0 targetDir
366-
pure levels'
350+
snapshotRuns reg snapUc (NamedSnapshotDir targetDir) levels = do
351+
for levels $ \run@(DeRef Run.Run {
352+
Run.runHasFS = hfs,
353+
Run.runHasBlockIO = hbio
354+
}) -> do
355+
rn <- uniqueToRunNumber <$> incrUniqCounter snapUc
356+
let sourcePaths = Run.runFsPaths run
357+
let targetPaths = sourcePaths { runDir = targetDir , runNumber = rn}
358+
hardLinkRunFiles reg hfs hbio sourcePaths targetPaths
359+
pure (runNumber targetPaths)
367360

368361
{-# SPECIALISE openRuns ::
369362
ActionRegistry IO
@@ -404,7 +397,7 @@ openRuns
404397
let sourcePaths = RunFsPaths sourceDir runNum
405398
runNum' <- uniqueToRunNumber <$> incrUniqCounter uc
406399
let targetPaths = RunFsPaths targetDir runNum'
407-
hardLinkRunFiles reg hfs hbio NoHardLinkDurable sourcePaths targetPaths
400+
hardLinkRunFiles reg hfs hbio sourcePaths targetPaths
408401

409402
withRollback reg
410403
(Run.openFromDisk hfs hbio caching targetPaths)
@@ -490,64 +483,53 @@ fromSnapLevels reg hfs hbio conf@TableConfig{..} uc resolve dir (SnapLevels leve
490483
Hard links
491484
-------------------------------------------------------------------------------}
492485

493-
data HardLinkDurable = HardLinkDurable | NoHardLinkDurable
494-
deriving stock Eq
495-
496486
{-# SPECIALISE hardLinkRunFiles ::
497487
ActionRegistry IO
498488
-> HasFS IO h
499489
-> HasBlockIO IO h
500-
-> HardLinkDurable
501490
-> RunFsPaths
502491
-> RunFsPaths
503492
-> IO () #-}
504-
-- | @'hardLinkRunFiles' _ _ _ dur sourcePaths targetPaths@ creates a hard link
505-
-- for each @sourcePaths@ path using the corresponding @targetPaths@ path as the
506-
-- name for the new directory entry. If @dur == HardLinkDurabl@, the links will
507-
-- also be made durable on disk.
493+
-- | @'hardLinkRunFiles' _ _ _ sourcePaths targetPaths@ creates a hard link for
494+
-- each @sourcePaths@ path using the corresponding @targetPaths@ path as the
495+
-- name for the new directory entry.
508496
hardLinkRunFiles ::
509497
(MonadMask m, PrimMonad m)
510498
=> ActionRegistry m
511499
-> HasFS m h
512500
-> HasBlockIO m h
513-
-> HardLinkDurable
514501
-> RunFsPaths
515502
-> RunFsPaths
516503
-> m ()
517-
hardLinkRunFiles reg hfs hbio dur sourceRunFsPaths targetRunFsPaths = do
504+
hardLinkRunFiles reg hfs hbio sourceRunFsPaths targetRunFsPaths = do
518505
let sourcePaths = pathsForRunFiles sourceRunFsPaths
519506
targetPaths = pathsForRunFiles targetRunFsPaths
520-
sequenceA_ (hardLink reg hfs hbio dur <$> sourcePaths <*> targetPaths)
521-
hardLink reg hfs hbio dur (runChecksumsPath sourceRunFsPaths) (runChecksumsPath targetRunFsPaths)
507+
sequenceA_ (hardLink reg hfs hbio <$> sourcePaths <*> targetPaths)
508+
hardLink reg hfs hbio (runChecksumsPath sourceRunFsPaths) (runChecksumsPath targetRunFsPaths)
522509

523510
{-# SPECIALISE
524511
hardLink ::
525512
ActionRegistry IO
526513
-> HasFS IO h
527514
-> HasBlockIO IO h
528-
-> HardLinkDurable
529515
-> FS.FsPath
530516
-> FS.FsPath
531517
-> IO ()
532518
#-}
533-
-- | @'hardLink' reg hfs hbio dur sourcePath targetPath@ creates a hard link
534-
-- from @sourcePath@ to @targetPath@.
519+
-- | @'hardLink' reg hfs hbio sourcePath targetPath@ creates a hard link from
520+
-- @sourcePath@ to @targetPath@.
535521
hardLink ::
536522
(MonadMask m, PrimMonad m)
537523
=> ActionRegistry m
538524
-> HasFS m h
539525
-> HasBlockIO m h
540-
-> HardLinkDurable
541526
-> FS.FsPath
542527
-> FS.FsPath
543528
-> m ()
544-
hardLink reg hfs hbio dur sourcePath targetPath = do
529+
hardLink reg hfs hbio sourcePath targetPath = do
545530
withRollback_ reg
546531
(FS.createHardLink hbio sourcePath targetPath)
547532
(FS.removeFile hfs targetPath)
548-
when (dur == HardLinkDurable) $
549-
FS.synchroniseFile hfs hbio targetPath
550-
551533

552534
{-------------------------------------------------------------------------------
553535
Copy file

test/Test/Database/LSMTree/Internal/Run.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ prop_WriteAndOpen fs hbio wb =
200200
withActionRegistry $ \reg -> do
201201
let paths = Run.runFsPaths written
202202
paths' = paths { runNumber = RunNumber 17}
203-
hardLinkRunFiles reg fs hbio NoHardLinkDurable paths paths'
203+
hardLinkRunFiles reg fs hbio paths paths'
204204
loaded <- openFromDisk fs hbio CacheRunData (simplePath 17)
205205

206206
Run.size written @=? Run.size loaded

0 commit comments

Comments
 (0)