narrow the race where a file gets modified before update-index

Check just before running update-index if the worktree file's content is
still the same, don't update it when it's been modified. This narrows
the race window a lot, from possibly minutes or hours, to seconds or
less.

(Use replaceFile so that the worktree update happens atomically,
allowing the InodeCache of the new worktree file to itself be gathered
w/o any other race.)

This doesn't eliminate the race; it can still occur in the window before
update-index runs. When annex.queue is large, a lot of files will be
statted by the checks, and so the window may still be large enough to be a
problem.

When only a few files are being processed, the window is as small as it
is in the race where a modification gets overwritten by git-annex when
it updates the worktree. Or maybe as small as whatever race git
checkout/pull/merge may have when the worktree gets modified during it.
Still, I've kept a todo about this race.

This commit was supported by the NSF-funded DataLad project.
This commit is contained in:
Joey Hess 2018-08-16 15:15:20 -04:00
parent 6a445dc086
commit 82a239675f
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
5 changed files with 52 additions and 29 deletions

View file

@ -9,6 +9,7 @@
module Annex.Queue (
addCommand,
addCommandCond,
addUpdateIndex,
flush,
flushWhenFull,
@ -29,6 +30,12 @@ addCommand command params files = do
store <=< flushWhenFull <=< inRepo $
Git.Queue.addCommand command params files q
addCommandCond :: String -> [CommandParam] -> [(FilePath, IO Bool)] -> Annex ()
addCommandCond command params files = do
q <- get
store <=< flushWhenFull <=< inRepo $
Git.Queue.addCommandCond command params files q
{- Adds an update-index stream to the queue. -}
addUpdateIndex :: Git.UpdateIndex.Streamer -> Annex ()
addUpdateIndex streamer = do