content locking during drop working for local git remotes

Only ssh remotes lack locking now
This commit is contained in:
Joey Hess 2015-10-09 13:07:03 -04:00
parent ceb5819538
commit 4c6095b6f5
Failed to extract signature
4 changed files with 52 additions and 8 deletions

View file

@ -5,6 +5,8 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable #-}
module Annex.NumCopies ( module Annex.NumCopies (
module Types.NumCopies, module Types.NumCopies,
module Logs.NumCopies, module Logs.NumCopies,
@ -30,6 +32,10 @@ import qualified Types.Remote as Remote
import Annex.UUID import Annex.UUID
import Annex.Content import Annex.Content
import Control.Exception
import qualified Control.Monad.Catch as M
import Data.Typeable
defaultNumCopies :: NumCopies defaultNumCopies :: NumCopies
defaultNumCopies = NumCopies 1 defaultNumCopies = NumCopies 1
@ -124,10 +130,31 @@ verifyEnoughCopiesToDrop nolocmsg key need skip preverified tocheck dropaction n
Right proof -> dropaction proof Right proof -> dropaction proof
Left stillhave -> helper bad missing stillhave (r:rs) Left stillhave -> helper bad missing stillhave (r:rs)
| otherwise = case Remote.lockContent r of | otherwise = case Remote.lockContent r of
Just lockcontent -> do
-- The remote's lockContent will throw
-- an exception if it is unable to lock,
-- in which case the fallback should be
-- run.
--
-- On the other hand, the callback passed
-- to the lockContent could itself throw an
-- exception (ie, the eventual drop
-- action fails), and in this case we don't
-- want to use the fallback since part
-- of the drop action may have already been
-- performed.
--
-- Differentiate between these two sorts
-- of exceptions by using DropException.
let a = lockcontent key $ \vc ->
helper bad missing (vc : have) rs
`catchNonAsync` (throw . DropException)
a `M.catches`
[ M.Handler (\ (e :: AsyncException) -> throwM e)
, M.Handler (\ (DropException e') -> throwM e')
, M.Handler (\ (_e :: SomeException) -> fallback)
]
Nothing -> fallback Nothing -> fallback
Just lockcontent -> lockcontent key $ \v -> case v of
Nothing -> fallback
Just vc -> helper bad missing (vc : have) rs
where where
fallback = do fallback = do
haskey <- Remote.hasKey r key haskey <- Remote.hasKey r key
@ -136,6 +163,11 @@ verifyEnoughCopiesToDrop nolocmsg key need skip preverified tocheck dropaction n
Left _ -> helper (r:bad) missing have rs Left _ -> helper (r:bad) missing have rs
Right False -> helper bad (Remote.uuid r:missing) have rs Right False -> helper bad (Remote.uuid r:missing) have rs
data DropException = DropException SomeException
deriving (Typeable, Show)
instance Exception DropException
notEnoughCopies :: Key -> NumCopies -> [VerifiedCopy] -> [UUID] -> [Remote] -> String -> Annex () notEnoughCopies :: Key -> NumCopies -> [VerifiedCopy] -> [UUID] -> [Remote] -> String -> Annex ()
notEnoughCopies key need have skip bad nolocmsg = do notEnoughCopies key need have skip bad nolocmsg = do
showNote "unsafe" showNote "unsafe"

View file

@ -53,6 +53,7 @@ import Annex.Path
import Creds import Creds
import Annex.CatFile import Annex.CatFile
import Messages.Progress import Messages.Progress
import Types.NumCopies
import Control.Concurrent import Control.Concurrent
import Control.Concurrent.MSampleVar import Control.Concurrent.MSampleVar
@ -142,7 +143,7 @@ gen r u c gc
, retrieveKeyFile = copyFromRemote new , retrieveKeyFile = copyFromRemote new
, retrieveKeyFileCheap = copyFromRemoteCheap new , retrieveKeyFileCheap = copyFromRemoteCheap new
, removeKey = dropKey new , removeKey = dropKey new
, lockContent = Nothing , lockContent = Just (lockKey new)
, checkPresent = inAnnex new , checkPresent = inAnnex new
, checkPresentCheap = repoCheap r , checkPresentCheap = repoCheap r
, whereisKey = Nothing , whereisKey = Nothing
@ -359,6 +360,16 @@ dropKey r key
| Git.repoIsHttp (repo r) = error "dropping from http remote not supported" | Git.repoIsHttp (repo r) = error "dropping from http remote not supported"
| otherwise = commitOnCleanup r $ Ssh.dropKey (repo r) key | otherwise = commitOnCleanup r $ Ssh.dropKey (repo r) key
lockKey :: Remote -> Key -> (VerifiedCopy -> Annex r) -> Annex r
lockKey r key a
| not $ Git.repoIsUrl (repo r) =
guardUsable (repo r) cantlock $
onLocal r $ Annex.Content.lockContentShared key a
| Git.repoIsHttp (repo r) = cantlock
| otherwise = error "TODO"
where
cantlock = error "can't lock content"
{- Tries to copy a key's content from a remote's annex to a file. -} {- Tries to copy a key's content from a remote's annex to a file. -}
copyFromRemote :: Remote -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> Annex (Bool, Verification) copyFromRemote :: Remote -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> Annex (Bool, Verification)
copyFromRemote r key file dest p = parallelMetered (Just p) key file $ copyFromRemote r key file dest p = parallelMetered (Just p) key file $

View file

@ -1,6 +1,6 @@
{- git-annex numcopies types {- git-annex numcopies types
- -
- Copyright 2014 Joey Hess <id@joeyh.name> - Copyright 2014-2015 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}

View file

@ -78,10 +78,11 @@ data RemoteA a = Remote {
-- Removes a key's contents (succeeds if the contents are not present) -- Removes a key's contents (succeeds if the contents are not present)
removeKey :: Key -> a Bool, removeKey :: Key -> a Bool,
-- Uses locking to prevent removal of a key's contents, -- Uses locking to prevent removal of a key's contents,
-- thus producing a VerifiedCopy. -- thus producing a VerifiedCopy, which is passed to the callback.
-- The action must be run whether or not the locking succeeds. -- If unable to lock, does not run the callback, and throws an
-- error.
-- This is optional; remotes do not have to support locking. -- This is optional; remotes do not have to support locking.
lockContent :: forall r. Maybe (Key -> (Maybe VerifiedCopy -> a r) -> a r), lockContent :: forall r. Maybe (Key -> (VerifiedCopy -> a r) -> a r),
-- Checks if a key is present in the remote. -- Checks if a key is present in the remote.
-- Throws an exception if the remote cannot be accessed. -- Throws an exception if the remote cannot be accessed.
checkPresent :: Key -> a Bool, checkPresent :: Key -> a Bool,