summaryrefslogtreecommitdiff
path: root/Database
diff options
context:
space:
mode:
authorGravatar Joey Hess <joeyh@joeyh.name>2015-02-17 13:04:22 -0400
committerGravatar Joey Hess <joeyh@joeyh.name>2015-02-17 13:30:24 -0400
commitc18aca6a6a1673d17467e25eb5c900f824c8231c (patch)
tree0ef32f822433fc6a6306db0b31640588224e19e8 /Database
parentd14b4c2a607409fbec81fedd2cc89ee77ba6a62e (diff)
avoid crash when starting fsck --incremental when one is already running
Turns out sqlite does not like having its database deleted out from underneath it. It might suffice to empty the table, but I would rather start each fsck over with a new database, so I added a lock file, and running incremental fscks use a shared lock. This leaves one concurrency bug left; running two concurrent fsck --more will lead to: "SQLite3 returned ErrorBusy while attempting to perform step." and one or both will fail. This is a concurrent writers problem.
Diffstat (limited to 'Database')
-rw-r--r--Database/Fsck.hs24
1 files changed, 20 insertions, 4 deletions
diff --git a/Database/Fsck.hs b/Database/Fsck.hs
index a137cba8e..a42988205 100644
--- a/Database/Fsck.hs
+++ b/Database/Fsck.hs
@@ -11,8 +11,8 @@
module Database.Fsck (
newPass,
openDb,
+ closeDb,
H.commitDb,
- H.closeDb,
H.DbHandle,
addDb,
inDb,
@@ -26,6 +26,7 @@ import Utility.Directory
import Annex
import Types.Key
import Annex.Perms
+import Annex.LockFile
import Database.Persist.TH
import Database.Esqueleto hiding (Key)
@@ -33,6 +34,8 @@ import Control.Monad
import Control.Monad.IfElse
import Control.Monad.IO.Class (liftIO)
import System.Directory
+import Data.Maybe
+import Control.Applicative
{- Each key stored in the database has already been fscked as part
- of the latest incremental fsck pass. -}
@@ -42,9 +45,16 @@ Fscked
UniqueKey key
|]
-{- The database is removed when starting a new incremental fsck pass. -}
-newPass :: Annex ()
-newPass = liftIO. nukeFile =<< fromRepo gitAnnexFsckDb
+{- The database is removed when starting a new incremental fsck pass.
+ -
+ - This may fail, if other fsck processes are currently running using the
+ - database. Removing the database in that situation would lead to crashes
+ - or undefined behavior.
+ -}
+newPass :: Annex Bool
+newPass = isJust <$> tryExclusiveLock gitAnnexFsckDbLock go
+ where
+ go = liftIO. nukeFile =<< fromRepo gitAnnexFsckDb
{- Opens the database, creating it atomically if it doesn't exist yet. -}
openDb :: Annex H.DbHandle
@@ -58,8 +68,14 @@ openDb = do
liftIO $ H.closeDb h
setAnnexFilePerm newdb
liftIO $ renameFile newdb db
+ lockFileShared =<< fromRepo gitAnnexFsckDbLock
liftIO $ H.openDb db
+closeDb :: H.DbHandle -> Annex ()
+closeDb h = do
+ liftIO $ H.closeDb h
+ unlockFile =<< fromRepo gitAnnexFsckDbLock
+
addDb :: H.DbHandle -> Key -> IO ()
addDb h = void . H.runDb' h commitPolicy . insert . Fscked . toSKey