{-| Module : Data.Blob.GC Stability : Experimental Portability : non-portable (requires POSIX) This module contains methods related to the garbage collection of the deleted blobs. -} module Data.Blob.GC where import Control.Monad (void, when) import Data.Blob.FileOperations import Data.Blob.Types import System.Directory import System.FilePath.Posix ((</>)) import System.IO.Error (tryIOError) -- | Initialize garbage collection for blobs in a given BlobStore. -- -- startGC throws an error if another GC is already running on -- the same BlobStore. startGC :: BlobStore -> IO () startGC (BlobStore dir) = do createDirectory gcDir forAllInDirectory currDir (createFileInDir gcDir) where gcDir = dir </> gcDirName currDir = dir </> currDirName -- | Mark a blob as accessible during a GC. -- -- You have to ensure that all the blobs which are accessible -- are marked by 'markAsAccessible' before calling 'endGC'. -- -- Blobs which are created after 'startGC' had finished need not -- be marked. markAsAccessible :: BlobId -> IO () markAsAccessible = void . tryIOError . deleteFile . getGCPath -- | Stops the garbage collection. -- -- This will __delete all the blobs which have not been marked__ -- by 'markAsAccessible' and created before 'startGC' had finished. endGC :: BlobStore -> IO () endGC (BlobStore dir) = do checkgcDir <- doesDirectoryExist gcDir when checkgcDir $ do forAllInDirectory gcDir (deleteFileInDir currDir) removeDirectoryRecursive gcDir where gcDir = dir </> gcDirName currDir = dir </> currDirName -- | 'markAccessibleBlobs' takes a list of 'BlobId' which are still -- accessible. 'markAccessibleBlobs' deletes the remaining blobs. -- -- It is safer to use this method instead of using 'startGC' and 'endGC', -- since if you forget to mark all the accessible blobs using -- 'markAsAccessible', 'endGC' might end up deleting accessible blobs. markAccessibleBlobs :: BlobStore -> [BlobId] -> IO () markAccessibleBlobs blobstore blobids = do startGC blobstore mapM_ markAsAccessible blobids endGC blobstore