fix export db locking deadlock
This commit is contained in:
parent
7e35c81ada
commit
e3a704224f
5 changed files with 85 additions and 27 deletions
|
@ -128,15 +128,15 @@ buildImportCommit remote importtreeconfig importcommitconfig importable =
|
||||||
importedtree
|
importedtree
|
||||||
return (Just commit)
|
return (Just commit)
|
||||||
|
|
||||||
updateexportdb importedtree =
|
updateexportdb importedtree = do
|
||||||
withExclusiveLock (gitAnnexExportLock (Remote.uuid remote)) $ do
|
db <- Export.openDb (Remote.uuid remote)
|
||||||
db <- Export.openDb (Remote.uuid remote)
|
Export.writeLockDbWhile db $ do
|
||||||
prevtree <- liftIO $ fromMaybe emptyTree
|
prevtree <- liftIO $ fromMaybe emptyTree
|
||||||
<$> Export.getExportTreeCurrent db
|
<$> Export.getExportTreeCurrent db
|
||||||
when (importedtree /= prevtree) $ do
|
when (importedtree /= prevtree) $ do
|
||||||
Export.updateExportDb db prevtree importedtree
|
Export.updateExportDb db prevtree importedtree
|
||||||
liftIO $ Export.recordExportTreeCurrent db importedtree
|
liftIO $ Export.recordExportTreeCurrent db importedtree
|
||||||
Export.closeDb db
|
Export.closeDb db
|
||||||
|
|
||||||
updateexportlog importedtree = do
|
updateexportlog importedtree = do
|
||||||
oldexport <- getExport (Remote.uuid remote)
|
oldexport <- getExport (Remote.uuid remote)
|
||||||
|
|
|
@ -48,6 +48,7 @@ module Annex.Locations (
|
||||||
gitAnnexSmudgeLock,
|
gitAnnexSmudgeLock,
|
||||||
gitAnnexExportDbDir,
|
gitAnnexExportDbDir,
|
||||||
gitAnnexExportLock,
|
gitAnnexExportLock,
|
||||||
|
gitAnnexExportUpdateLock,
|
||||||
gitAnnexContentIdentifierDbDir,
|
gitAnnexContentIdentifierDbDir,
|
||||||
gitAnnexContentIdentifierLock,
|
gitAnnexContentIdentifierLock,
|
||||||
gitAnnexScheduleState,
|
gitAnnexScheduleState,
|
||||||
|
@ -350,6 +351,10 @@ gitAnnexExportDbDir u r = gitAnnexExportDir u r </> "db"
|
||||||
gitAnnexExportLock :: UUID -> Git.Repo -> FilePath
|
gitAnnexExportLock :: UUID -> Git.Repo -> FilePath
|
||||||
gitAnnexExportLock u r = gitAnnexExportDbDir u r ++ ".lck"
|
gitAnnexExportLock u r = gitAnnexExportDbDir u r ++ ".lck"
|
||||||
|
|
||||||
|
{- Lock file for updating the export state for a special remote. -}
|
||||||
|
gitAnnexExportUpdateLock :: UUID -> Git.Repo -> FilePath
|
||||||
|
gitAnnexExportUpdateLock u r = gitAnnexExportDbDir u r ++ ".upl"
|
||||||
|
|
||||||
{- Directory containing database used to record remote content ids. -}
|
{- Directory containing database used to record remote content ids. -}
|
||||||
gitAnnexContentIdentifierDbDir :: Git.Repo -> FilePath
|
gitAnnexContentIdentifierDbDir :: Git.Repo -> FilePath
|
||||||
gitAnnexContentIdentifierDbDir r = gitAnnexDir r </> "cid"
|
gitAnnexContentIdentifierDbDir r = gitAnnexDir r </> "cid"
|
||||||
|
|
|
@ -24,7 +24,6 @@ import Annex.Export
|
||||||
import Annex.Content
|
import Annex.Content
|
||||||
import Annex.Transfer
|
import Annex.Transfer
|
||||||
import Annex.CatFile
|
import Annex.CatFile
|
||||||
import Annex.LockFile
|
|
||||||
import Annex.RemoteTrackingBranch
|
import Annex.RemoteTrackingBranch
|
||||||
import Logs.Location
|
import Logs.Location
|
||||||
import Logs.Export
|
import Logs.Export
|
||||||
|
@ -84,12 +83,12 @@ seek o = do
|
||||||
tree <- fromMaybe (giveup "unknown tree") <$>
|
tree <- fromMaybe (giveup "unknown tree") <$>
|
||||||
inRepo (Git.Ref.tree (fromMaybe (exportTreeish o) (fmap snd mtbcommitsha)))
|
inRepo (Git.Ref.tree (fromMaybe (exportTreeish o) (fmap snd mtbcommitsha)))
|
||||||
|
|
||||||
withExclusiveLock (gitAnnexExportLock (uuid r)) $ do
|
db <- openDb (uuid r)
|
||||||
db <- openDb (uuid r)
|
writeLockDbWhile db $ do
|
||||||
changeExport r db tree
|
changeExport r db tree
|
||||||
unlessM (Annex.getState Annex.fast) $ do
|
unlessM (Annex.getState Annex.fast) $ do
|
||||||
void $ fillExport r db tree mtbcommitsha
|
void $ fillExport r db tree mtbcommitsha
|
||||||
closeDb db
|
closeDb db
|
||||||
|
|
||||||
-- | When the treeish is a branch like master or refs/heads/master
|
-- | When the treeish is a branch like master or refs/heads/master
|
||||||
-- (but not refs/remotes/...), find the commit it points to
|
-- (but not refs/remotes/...), find the commit it points to
|
||||||
|
|
|
@ -59,7 +59,6 @@ import Annex.Ssh
|
||||||
import Annex.BloomFilter
|
import Annex.BloomFilter
|
||||||
import Annex.UpdateInstead
|
import Annex.UpdateInstead
|
||||||
import Annex.Export
|
import Annex.Export
|
||||||
import Annex.LockFile
|
|
||||||
import Annex.TaggedPush
|
import Annex.TaggedPush
|
||||||
import Annex.CurrentBranch
|
import Annex.CurrentBranch
|
||||||
import qualified Database.Export as Export
|
import qualified Database.Export as Export
|
||||||
|
@ -691,8 +690,11 @@ syncFile ebloom rs af k = onlyActionOn' k $ do
|
||||||
seekExportContent :: [Remote] -> CurrBranch -> Annex Bool
|
seekExportContent :: [Remote] -> CurrBranch -> Annex Bool
|
||||||
seekExportContent rs (currbranch, _) = or <$> forM rs go
|
seekExportContent rs (currbranch, _) = or <$> forM rs go
|
||||||
where
|
where
|
||||||
go r = withExclusiveLock (gitAnnexExportLock (Remote.uuid r)) $ do
|
go r = bracket
|
||||||
db <- Export.openDb (Remote.uuid r)
|
(Export.openDb (Remote.uuid r))
|
||||||
|
Export.closeDb
|
||||||
|
(\db -> Export.writeLockDbWhile db (go' r db))
|
||||||
|
go' r db = do
|
||||||
(exported, mtbcommitsha) <- case remoteAnnexTrackingBranch (Remote.gitconfig r) of
|
(exported, mtbcommitsha) <- case remoteAnnexTrackingBranch (Remote.gitconfig r) of
|
||||||
Nothing -> nontracking r
|
Nothing -> nontracking r
|
||||||
Just b -> do
|
Just b -> do
|
||||||
|
@ -706,7 +708,7 @@ seekExportContent rs (currbranch, _) = or <$> forM rs go
|
||||||
Just cur -> do
|
Just cur -> do
|
||||||
Command.Export.changeExport r db cur
|
Command.Export.changeExport r db cur
|
||||||
return ([mkExported cur []], mtbcommitsha)
|
return ([mkExported cur []], mtbcommitsha)
|
||||||
Export.closeDb db `after` fillexport r db (exportedTreeishes exported) mtbcommitsha
|
fillexport r db (exportedTreeishes exported) mtbcommitsha
|
||||||
|
|
||||||
nontracking r = do
|
nontracking r = do
|
||||||
exported <- getExport (Remote.uuid r)
|
exported <- getExport (Remote.uuid r)
|
||||||
|
|
|
@ -15,6 +15,7 @@ module Database.Export (
|
||||||
ExportHandle,
|
ExportHandle,
|
||||||
openDb,
|
openDb,
|
||||||
closeDb,
|
closeDb,
|
||||||
|
writeLockDbWhile,
|
||||||
flushDbQueue,
|
flushDbQueue,
|
||||||
addExportedLocation,
|
addExportedLocation,
|
||||||
removeExportedLocation,
|
removeExportedLocation,
|
||||||
|
@ -48,6 +49,7 @@ import Types.Export
|
||||||
import Annex.Export
|
import Annex.Export
|
||||||
import qualified Logs.Export as Log
|
import qualified Logs.Export as Log
|
||||||
import Annex.LockFile
|
import Annex.LockFile
|
||||||
|
import Annex.LockPool
|
||||||
import Git.Types
|
import Git.Types
|
||||||
import Git.Sha
|
import Git.Sha
|
||||||
import Git.FilePath
|
import Git.FilePath
|
||||||
|
@ -258,22 +260,72 @@ updateExportDb = runExportDiffUpdater $ mkExportDiffUpdater removeold addnew
|
||||||
addExportTree h k loc
|
addExportTree h k loc
|
||||||
addExportedLocation h k loc
|
addExportedLocation h k loc
|
||||||
|
|
||||||
|
{- Runs an action with the database locked for write. Waits for any other
|
||||||
|
- writers to finish first. The queue is flushed at the end.
|
||||||
|
-
|
||||||
|
- This first updates the ExportTree table with any new information
|
||||||
|
- from the git-annex branch export log.
|
||||||
|
-}
|
||||||
|
writeLockDbWhile :: ExportHandle -> Annex a -> Annex a
|
||||||
|
writeLockDbWhile db@(ExportHandle _ u) a = do
|
||||||
|
updatelck <- takeExclusiveLock (gitAnnexExportUpdateLock u)
|
||||||
|
withExclusiveLock (gitAnnexExportLock u) $ do
|
||||||
|
bracket_ (setup updatelck) cleanup a
|
||||||
|
where
|
||||||
|
setup updatelck = do
|
||||||
|
void $ updateExportTreeFromLog' db
|
||||||
|
-- flush the update so it's available immediately to
|
||||||
|
-- anything waiting on the updatelck
|
||||||
|
liftIO $ flushDbQueue db
|
||||||
|
liftIO $ dropLock updatelck
|
||||||
|
cleanup = liftIO $ flushDbQueue db
|
||||||
|
|
||||||
data ExportUpdateResult = ExportUpdateSuccess | ExportUpdateConflict
|
data ExportUpdateResult = ExportUpdateSuccess | ExportUpdateConflict
|
||||||
deriving (Eq)
|
deriving (Eq)
|
||||||
|
|
||||||
|
{- Updates the ExportTree table with information from the
|
||||||
|
- git-annex branch export log.
|
||||||
|
-
|
||||||
|
- This can safely be called whether the database is locked for write or
|
||||||
|
- not. Either way, it will block until the update is complete.
|
||||||
|
-}
|
||||||
updateExportTreeFromLog :: ExportHandle -> Annex ExportUpdateResult
|
updateExportTreeFromLog :: ExportHandle -> Annex ExportUpdateResult
|
||||||
updateExportTreeFromLog db@(ExportHandle _ u) =
|
updateExportTreeFromLog db@(ExportHandle _ u) =
|
||||||
withExclusiveLock (gitAnnexExportLock u) $ do
|
-- If another process or thread is performing the update,
|
||||||
old <- liftIO $ fromMaybe emptyTree
|
-- this will block until it's done.
|
||||||
<$> getExportTreeCurrent db
|
withExclusiveLock (gitAnnexExportUpdateLock u) $ do
|
||||||
l <- Log.getExport u
|
-- If the database is locked by something else,
|
||||||
case Log.exportedTreeishes l of
|
-- this will not run the update. But, in that case,
|
||||||
[] -> return ExportUpdateSuccess
|
-- writeLockDbWhile is running, and has already
|
||||||
(new:[])
|
-- completed the update, so we don't need to do anything.
|
||||||
| new /= old -> do
|
mr <- tryExclusiveLock (gitAnnexExportLock u) $
|
||||||
updateExportTree db old new
|
updateExportTreeFromLog' db
|
||||||
liftIO $ recordExportTreeCurrent db new
|
case mr of
|
||||||
liftIO $ flushDbQueue db
|
Just r -> return r
|
||||||
return ExportUpdateSuccess
|
Nothing -> do
|
||||||
| new == old -> return ExportUpdateSuccess
|
old <- liftIO $ fromMaybe emptyTree
|
||||||
_ts -> return ExportUpdateConflict
|
<$> getExportTreeCurrent db
|
||||||
|
l <- Log.getExport u
|
||||||
|
return $ case Log.exportedTreeishes l of
|
||||||
|
[] -> ExportUpdateSuccess
|
||||||
|
(new:[])
|
||||||
|
| new /= old -> ExportUpdateSuccess
|
||||||
|
| new == old -> ExportUpdateSuccess
|
||||||
|
_ts -> ExportUpdateConflict
|
||||||
|
|
||||||
|
{- The database should be locked when calling this. -}
|
||||||
|
updateExportTreeFromLog' :: ExportHandle -> Annex ExportUpdateResult
|
||||||
|
updateExportTreeFromLog' db@(ExportHandle _ u) = do
|
||||||
|
old <- liftIO $ fromMaybe emptyTree
|
||||||
|
<$> getExportTreeCurrent db
|
||||||
|
l <- Log.getExport u
|
||||||
|
case Log.exportedTreeishes l of
|
||||||
|
[] -> return ExportUpdateSuccess
|
||||||
|
(new:[])
|
||||||
|
| new /= old -> do
|
||||||
|
updateExportTree db old new
|
||||||
|
liftIO $ recordExportTreeCurrent db new
|
||||||
|
liftIO $ flushDbQueue db
|
||||||
|
return ExportUpdateSuccess
|
||||||
|
| new == old -> return ExportUpdateSuccess
|
||||||
|
_ts -> return ExportUpdateConflict
|
||||||
|
|
Loading…
Reference in a new issue