fsck: Commit incremental fsck database after every 1000 files fscked, or every 5 minutes, whichever comes first.

Previously, commits were made every 1000 files fscked.

Also, improve docs
This commit is contained in:
Joey Hess 2015-07-31 16:42:15 -04:00
parent 9dfe03dbcd
commit bc4129cc77
5 changed files with 59 additions and 20 deletions

View file

@ -34,6 +34,7 @@ import Annex.LockFile
import Database.Persist.TH import Database.Persist.TH
import Database.Esqueleto hiding (Key) import Database.Esqueleto hiding (Key)
import Data.Time.Clock
data FsckHandle = FsckHandle H.DbHandle UUID data FsckHandle = FsckHandle H.DbHandle UUID
@ -84,11 +85,18 @@ closeDb (FsckHandle h u) = do
unlockFile =<< fromRepo (gitAnnexFsckDbLock u) unlockFile =<< fromRepo (gitAnnexFsckDbLock u)
addDb :: FsckHandle -> Key -> IO () addDb :: FsckHandle -> Key -> IO ()
addDb (FsckHandle h _) k = H.queueDb h 1000 $ addDb (FsckHandle h _) k = H.queueDb h checkcommit $
void $ insertUnique $ Fscked sk void $ insertUnique $ Fscked sk
where where
sk = toSKey k sk = toSKey k
-- commit queue after 1000 files or 5 minutes, whichever comes first
checkcommit sz lastcommittime
| sz > 1000 = return True
| otherwise = do
now <- getCurrentTime
return $ diffUTCTime lastcommittime now > 300
inDb :: FsckHandle -> Key -> IO Bool inDb :: FsckHandle -> Key -> IO Bool
inDb (FsckHandle h _) = H.queryDb h . inDb' . toSKey inDb (FsckHandle h _) = H.queryDb h . inDb' . toSKey

View file

@ -20,6 +20,7 @@ module Database.Handle (
) where ) where
import Utility.Exception import Utility.Exception
import Utility.Monad
import Messages import Messages
import Database.Persist.Sqlite import Database.Persist.Sqlite
@ -33,6 +34,7 @@ import qualified Data.Text as T
import Control.Monad.Trans.Resource (runResourceT) import Control.Monad.Trans.Resource (runResourceT)
import Control.Monad.Logger (runNoLoggingT) import Control.Monad.Logger (runNoLoggingT)
import Data.List import Data.List
import Data.Time.Clock
{- A DbHandle is a reference to a worker thread that communicates with {- A DbHandle is a reference to a worker thread that communicates with
- the database. It has a MVar which Jobs are submitted to. -} - the database. It has a MVar which Jobs are submitted to. -}
@ -64,7 +66,7 @@ openDb :: FilePath -> TableName -> IO DbHandle
openDb db tablename = do openDb db tablename = do
jobs <- newEmptyMVar jobs <- newEmptyMVar
worker <- async (workerThread (T.pack db) tablename jobs) worker <- async (workerThread (T.pack db) tablename jobs)
q <- newMVar emptyDbQueue q <- newMVar =<< emptyDbQueue
return $ DbHandle worker jobs q return $ DbHandle worker jobs q
data Job data Job
@ -145,16 +147,19 @@ closeDb h@(DbHandle worker jobs _) = do
type Size = Int type Size = Int
{- A queue of actions to perform, with a count of the number of actions type LastCommitTime = UTCTime
- queued. -}
data DbQueue = DbQueue Size (SqlPersistM ())
emptyDbQueue :: DbQueue {- A queue of actions to perform, with a count of the number of actions
emptyDbQueue = DbQueue 0 (return ()) - queued, and a last commit time. -}
data DbQueue = DbQueue Size LastCommitTime (SqlPersistM ())
emptyDbQueue :: IO DbQueue
emptyDbQueue = do
now <- getCurrentTime
return $ DbQueue 0 now (return ())
{- Queues a change to be made to the database. It will be buffered {- Queues a change to be made to the database. It will be buffered
- to be committed later, unless the queue gets larger than the specified - to be committed later, unless the commitchecker action returns true.
- size.
- -
- (Be sure to call closeDb or flushQueueDb to ensure the change - (Be sure to call closeDb or flushQueueDb to ensure the change
- gets committed.) - gets committed.)
@ -164,25 +169,32 @@ emptyDbQueue = DbQueue 0 (return ())
- process, the transaction is put back in the queue. This solves - process, the transaction is put back in the queue. This solves
- the sqlite multiple writer problem. - the sqlite multiple writer problem.
-} -}
queueDb :: DbHandle -> Size -> SqlPersistM () -> IO () queueDb
queueDb h@(DbHandle _ _ qvar) maxsz a = do :: DbHandle
DbQueue sz qa <- takeMVar qvar -> (Size -> LastCommitTime -> IO Bool)
-> SqlPersistM ()
-> IO ()
queueDb h@(DbHandle _ _ qvar) commitchecker a = do
DbQueue sz lastcommittime qa <- takeMVar qvar
let !sz' = sz + 1 let !sz' = sz + 1
let qa' = qa >> a let qa' = qa >> a
let enqueue newsz = putMVar qvar (DbQueue newsz qa') let enqueue = putMVar qvar
if sz' > maxsz ifM (commitchecker sz' lastcommittime)
then do ( do
r <- commitDb h qa' r <- commitDb h qa'
case r of case r of
Left _ -> enqueue 0 Left _ -> enqueue $ DbQueue sz' lastcommittime qa'
Right _ -> putMVar qvar emptyDbQueue Right _ -> do
else enqueue sz' now <- getCurrentTime
enqueue $ DbQueue 0 now (return ())
, enqueue $ DbQueue sz' lastcommittime qa'
)
{- If flushing the queue fails, this could be because there is another {- If flushing the queue fails, this could be because there is another
- writer to the database. Retry repeatedly for up to 10 seconds. -} - writer to the database. Retry repeatedly for up to 10 seconds. -}
flushQueueDb :: DbHandle -> IO () flushQueueDb :: DbHandle -> IO ()
flushQueueDb h@(DbHandle _ _ qvar) = do flushQueueDb h@(DbHandle _ _ qvar) = do
DbQueue sz qa <- takeMVar qvar DbQueue sz _ qa <- takeMVar qvar
when (sz > 0) $ when (sz > 0) $
robustly Nothing 100 (commitDb h qa) robustly Nothing 100 (commitDb h qa)
where where

3
debian/changelog vendored
View file

@ -6,6 +6,9 @@ git-annex (5.20150732) UNRELEASED; urgency=medium
* fsck: Commit incremental fsck database when --time-limit is reached. * fsck: Commit incremental fsck database when --time-limit is reached.
Previously, some of the last files fscked did not make it into the Previously, some of the last files fscked did not make it into the
database when using --time-limit. database when using --time-limit.
* fsck: Commit incremental fsck database after every 1000 files
fscked, or every 5 minutes, whichever comes first. Previously,
commits were made every 1000 files fscked.
-- Joey Hess <id@joeyh.name> Fri, 31 Jul 2015 12:31:39 -0400 -- Joey Hess <id@joeyh.name> Fri, 31 Jul 2015 12:31:39 -0400

View file

@ -21,3 +21,14 @@ Also tried with `docker/ubuntu:latest` using a clean install from https://downlo
git-annex: 5.20150522-gb199d65 git-annex: 5.20150522-gb199d65
Linux: 3.16.0-43-generic #58-Ubuntu SMP Fri Jun 19 11:04:02 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux Linux: 3.16.0-43-generic #58-Ubuntu SMP Fri Jun 19 11:04:02 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
> I've adjusted the timing of the fsck checkpoints used in resumes some.
> Now it makes one every 5 minutes, or every 1000 files, whichever comes
> first. I could make this tunable, but I don't think it's worth adding the
> complexity; this heuristic should work pretty well.
>
> Another approach would be to catch sigint and commit the fsck database
> then, as is now done when --time-limit interrupts a fsck run.
> But, I am leery of complicating git-annex with signal handling,
> so I've not done that currently.
>
> Also, documented this in fsck's man page. [[done]] --[[Joey]]

View file

@ -37,7 +37,12 @@ With parameters, only the specified files are checked.
* `--more` * `--more`
Continue the last incremental fsck pass, where it left off. Resume the last incremental fsck pass, where it left off.
Resuming may redundantly check some files that were checked
before. Any files that fsck found problems with before will be re-checked
on resume. Also, checkpoints are made every 1000 files or every 5 minutes
during a fsck, and it resumes from the last checkpoint.
* `--incremental-schedule=time` * `--incremental-schedule=time`