implement annex.retry et al

Added annex.retry, annex.retry-delay, and per-remote versions to configure
transfer retries.

This commit was supported by the NSF-funded DataLad project.
This commit is contained in:
Joey Hess 2018-03-29 13:04:07 -04:00
parent 8a03f38931
commit 46d4316954
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
10 changed files with 67 additions and 21 deletions

View file

@ -1,6 +1,6 @@
{- git-annex transfers {- git-annex transfers
- -
- Copyright 2012-2017 Joey Hess <id@joeyh.name> - Copyright 2012-2018 Joey Hess <id@joeyh.name>
- -
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
@ -14,7 +14,7 @@ module Annex.Transfer (
runTransfer, runTransfer,
alwaysRunTransfer, alwaysRunTransfer,
noRetry, noRetry,
forwardRetry, stdRetry,
pickRemote, pickRemote,
) where ) where
@ -25,6 +25,7 @@ import Types.Transfer as X
import Annex.Notification as X import Annex.Notification as X
import Annex.Perms import Annex.Perms
import Utility.Metered import Utility.Metered
import Utility.ThreadScheduler
import Annex.LockPool import Annex.LockPool
import Types.Key import Types.Key
import qualified Types.Remote as Remote import qualified Types.Remote as Remote
@ -71,7 +72,8 @@ alwaysRunTransfer :: Observable v => Transfer -> AssociatedFile -> RetryDecider
alwaysRunTransfer = runTransfer' True alwaysRunTransfer = runTransfer' True
runTransfer' :: Observable v => Bool -> Transfer -> AssociatedFile -> RetryDecider -> (MeterUpdate -> Annex v) -> Annex v runTransfer' :: Observable v => Bool -> Transfer -> AssociatedFile -> RetryDecider -> (MeterUpdate -> Annex v) -> Annex v
runTransfer' ignorelock t afile shouldretry transferaction = checkSecureHashes t $ do runTransfer' ignorelock t afile retrydecider transferaction = checkSecureHashes t $ do
shouldretry <- retrydecider
info <- liftIO $ startTransferInfo afile info <- liftIO $ startTransferInfo afile
(meter, tfile, createtfile, metervar) <- mkProgressUpdater t info (meter, tfile, createtfile, metervar) <- mkProgressUpdater t info
mode <- annexFileMode mode <- annexFileMode
@ -81,7 +83,7 @@ runTransfer' ignorelock t afile shouldretry transferaction = checkSecureHashes t
showNote "transfer already in progress, or unable to take transfer lock" showNote "transfer already in progress, or unable to take transfer lock"
return observeFailure return observeFailure
else do else do
v <- retry info metervar $ transferaction meter v <- retry shouldretry info metervar $ transferaction meter
liftIO $ cleanup tfile lck liftIO $ cleanup tfile lck
if observeBool v if observeBool v
then removeFailedTransfer t then removeFailedTransfer t
@ -132,15 +134,16 @@ runTransfer' ignorelock t afile shouldretry transferaction = checkSecureHashes t
dropLock lockhandle dropLock lockhandle
void $ tryIO $ removeFile lck void $ tryIO $ removeFile lck
#endif #endif
retry oldinfo metervar run = tryNonAsync run >>= \case retry shouldretry oldinfo metervar run = tryNonAsync run >>= \case
Right b -> return b Right b -> return b
Left e -> do Left e -> do
warning (show e) warning (show e)
b <- getbytescomplete metervar b <- getbytescomplete metervar
let newinfo = oldinfo { bytesComplete = Just b } let newinfo = oldinfo { bytesComplete = Just b }
if shouldretry oldinfo newinfo ifM (shouldretry oldinfo newinfo)
then retry newinfo metervar run ( retry shouldretry newinfo metervar run
else return observeFailure , return observeFailure
)
getbytescomplete metervar getbytescomplete metervar
| transferDirection t == Upload = | transferDirection t == Upload =
liftIO $ readMVar metervar liftIO $ readMVar metervar
@ -172,15 +175,53 @@ checkSecureHashes t a
where where
variety = keyVariety (transferKey t) variety = keyVariety (transferKey t)
type RetryDecider = TransferInfo -> TransferInfo -> Bool type RetryDecider = Annex (TransferInfo -> TransferInfo -> Annex Bool)
{- The first RetryDecider will be checked first; only if it says not to
- retry will the second one be checked. -}
combineRetryDeciders :: RetryDecider -> RetryDecider -> RetryDecider
combineRetryDeciders a b = do
ar <- a
br <- b
return $ \old new -> ar old new <||> br old new
noRetry :: RetryDecider noRetry :: RetryDecider
noRetry _ _ = False noRetry = pure $ \_ _ -> pure False
stdRetry :: RetryDecider
stdRetry = combineRetryDeciders forwardRetry configuredRetry
{- Retries a transfer when it fails, as long as the failed transfer managed {- Retries a transfer when it fails, as long as the failed transfer managed
- to send some data. -} - to send some data. -}
forwardRetry :: RetryDecider forwardRetry :: RetryDecider
forwardRetry old new = bytesComplete old < bytesComplete new forwardRetry = pure $ \old new -> pure $ bytesComplete old < bytesComplete new
{- Retries a number of times with growing delays in between when enabled
- by git configuration. -}
configuredRetry :: RetryDecider
configuredRetry = do
retrycounter <- liftIO $ newMVar 0
return $ \_old new -> do
(maxretries, Seconds initretrydelay) <- getcfg $
Remote.gitconfig <$> transferRemote new
retries <- liftIO $ modifyMVar retrycounter $
\n -> return (n + 1, n + 1)
if retries < maxretries
then do
let retrydelay = Seconds (initretrydelay * 2^(retries-1))
showSideAction $ "Delaying " ++ show (fromSeconds retrydelay) ++ " before retrying."
liftIO $ threadDelaySeconds retrydelay
return True
else return False
where
globalretrycfg = fromMaybe 0 . annexRetry
<$> Annex.getGitConfig
globalretrydelaycfg = fromMaybe (Seconds 1) . annexRetryDelay
<$> Annex.getGitConfig
getcfg Nothing = (,) <$> globalretrycfg <*> globalretrydelaycfg
getcfg (Just gc) = (,)
<$> maybe globalretrycfg return (remoteAnnexRetry gc)
<*> maybe globalretrydelaycfg return (remoteAnnexRetryDelay gc)
{- Picks a remote from the list and tries a transfer to it. If the transfer {- Picks a remote from the list and tries a transfer to it. If the transfer
- does not succeed, goes on to try other remotes from the list. - does not succeed, goes on to try other remotes from the list.

View file

@ -4,6 +4,8 @@ git-annex (6.20180317) UNRELEASED; urgency=medium
* Fix calculation of estimated completion for progress meter. * Fix calculation of estimated completion for progress meter.
* OSX app: Work around libz/libPng/ImageIO.framework version skew * OSX app: Work around libz/libPng/ImageIO.framework version skew
by not bundling libz, assuming OSX includes a suitable libz.1.dylib. by not bundling libz, assuming OSX includes a suitable libz.1.dylib.
* Added annex.retry, annex.retry-delay, and per-remote versions
to configure transfer retries.
-- Joey Hess <id@joeyh.name> Mon, 19 Mar 2018 23:13:59 -0400 -- Joey Hess <id@joeyh.name> Mon, 19 Mar 2018 23:13:59 -0400

View file

@ -339,7 +339,7 @@ downloadWith' downloader dummykey u url afile =
checkDiskSpaceToGet dummykey Nothing $ do checkDiskSpaceToGet dummykey Nothing $ do
tmp <- fromRepo $ gitAnnexTmpObjectLocation dummykey tmp <- fromRepo $ gitAnnexTmpObjectLocation dummykey
ok <- Transfer.notifyTransfer Transfer.Download url $ ok <- Transfer.notifyTransfer Transfer.Download url $
Transfer.download u dummykey afile Transfer.forwardRetry $ \p -> do Transfer.download u dummykey afile Transfer.stdRetry $ \p -> do
liftIO $ createDirectoryIfMissing True (parentDir tmp) liftIO $ createDirectoryIfMissing True (parentDir tmp)
downloader tmp p downloader tmp p
if ok if ok

View file

@ -216,6 +216,8 @@ performExport r ea db ek af contentsha loc = do
sent <- case ek of sent <- case ek of
AnnexKey k -> ifM (inAnnex k) AnnexKey k -> ifM (inAnnex k)
( notifyTransfer Upload af $ ( notifyTransfer Upload af $
-- Using noRetry here because interrupted
-- exports cannot be resumed.
upload (uuid r) k af noRetry $ \pm -> do upload (uuid r) k af noRetry $ \pm -> do
let rollback = void $ let rollback = void $
performUnexport r ea db [ek] loc performUnexport r ea db [ek] loc

View file

@ -110,7 +110,7 @@ getKey' key afile = dispatch
either (const False) id <$> Remote.hasKey r key either (const False) id <$> Remote.hasKey r key
| otherwise = return True | otherwise = return True
docopy r witness = getViaTmp (RemoteVerify r) key $ \dest -> docopy r witness = getViaTmp (RemoteVerify r) key $ \dest ->
download (Remote.uuid r) key afile forwardRetry download (Remote.uuid r) key afile stdRetry
(\p -> do (\p -> do
showAction $ "from " ++ Remote.name r showAction $ "from " ++ Remote.name r
Remote.retrieveKeyFile r key afile dest p Remote.retrieveKeyFile r key afile dest p

View file

@ -134,7 +134,7 @@ toPerform dest move key afile fastcheck isthere =
Right False -> do Right False -> do
showAction $ "to " ++ Remote.name dest showAction $ "to " ++ Remote.name dest
ok <- notifyTransfer Upload afile $ ok <- notifyTransfer Upload afile $
upload (Remote.uuid dest) key afile forwardRetry $ upload (Remote.uuid dest) key afile stdRetry $
Remote.storeKey dest key afile Remote.storeKey dest key afile
if ok if ok
then finish $ then finish $
@ -199,7 +199,7 @@ fromPerform src move key afile = do
) )
where where
go = notifyTransfer Download afile $ go = notifyTransfer Download afile $
download (Remote.uuid src) key afile forwardRetry $ \p -> download (Remote.uuid src) key afile stdRetry $ \p ->
getViaTmp (RemoteVerify src) key $ \t -> getViaTmp (RemoteVerify src) key $ \t ->
Remote.retrieveKeyFile src key afile t p Remote.retrieveKeyFile src key afile t p
dispatch _ False = stop -- failed dispatch _ False = stop -- failed

View file

@ -51,7 +51,7 @@ start o key = case fromToOptions o of
toPerform :: Key -> AssociatedFile -> Remote -> CommandPerform toPerform :: Key -> AssociatedFile -> Remote -> CommandPerform
toPerform key file remote = go Upload file $ toPerform key file remote = go Upload file $
upload (uuid remote) key file forwardRetry $ \p -> do upload (uuid remote) key file stdRetry $ \p -> do
ok <- Remote.storeKey remote key file p ok <- Remote.storeKey remote key file p
when ok $ when ok $
Remote.logStatus remote key InfoPresent Remote.logStatus remote key InfoPresent
@ -59,7 +59,7 @@ toPerform key file remote = go Upload file $
fromPerform :: Key -> AssociatedFile -> Remote -> CommandPerform fromPerform :: Key -> AssociatedFile -> Remote -> CommandPerform
fromPerform key file remote = go Upload file $ fromPerform key file remote = go Upload file $
download (uuid remote) key file forwardRetry $ \p -> download (uuid remote) key file stdRetry $ \p ->
getViaTmp (RemoteVerify remote) key $ getViaTmp (RemoteVerify remote) key $
\t -> Remote.retrieveKeyFile remote key file t p \t -> Remote.retrieveKeyFile remote key file t p

View file

@ -35,13 +35,13 @@ start = do
where where
runner (TransferRequest direction remote key file) runner (TransferRequest direction remote key file)
| direction == Upload = notifyTransfer direction file $ | direction == Upload = notifyTransfer direction file $
upload (Remote.uuid remote) key file forwardRetry $ \p -> do upload (Remote.uuid remote) key file stdRetry $ \p -> do
ok <- Remote.storeKey remote key file p ok <- Remote.storeKey remote key file p
when ok $ when ok $
Remote.logStatus remote key InfoPresent Remote.logStatus remote key InfoPresent
return ok return ok
| otherwise = notifyTransfer direction file $ | otherwise = notifyTransfer direction file $
download (Remote.uuid remote) key file forwardRetry $ \p -> download (Remote.uuid remote) key file stdRetry $ \p ->
getViaTmp (RemoteVerify remote) key $ \t -> do getViaTmp (RemoteVerify remote) key $ \t -> do
r <- Remote.retrieveKeyFile remote key file t p r <- Remote.retrieveKeyFile remote key file t p
-- Make sure we get the current -- Make sure we get the current

View file

@ -120,6 +120,7 @@ runLocal runst runner a = case a of
where where
transfer mk k af ta = case runst of transfer mk k af ta = case runst of
-- Update transfer logs when serving. -- Update transfer logs when serving.
-- Using noRetry because we're the sender.
Serving theiruuid _ _ -> Serving theiruuid _ _ ->
mk theiruuid k af noRetry ta noNotification mk theiruuid k af noRetry ta noNotification
-- Transfer logs are updated higher in the stack when -- Transfer logs are updated higher in the stack when

View file

@ -467,7 +467,7 @@ copyFromRemote' forcersync r (State connpool _) key file dest meterupdate
Just (object, checksuccess) -> do Just (object, checksuccess) -> do
copier <- mkCopier hardlink params copier <- mkCopier hardlink params
runTransfer (Transfer Download u key) runTransfer (Transfer Download u key)
file forwardRetry file stdRetry
(\p -> copier object dest (combineMeterUpdate p meterupdate) checksuccess) (\p -> copier object dest (combineMeterUpdate p meterupdate) checksuccess)
| Git.repoIsSsh (repo r) = if forcersync | Git.repoIsSsh (repo r) = if forcersync
then fallback meterupdate then fallback meterupdate
@ -595,7 +595,7 @@ copyToRemote r (State connpool duc) key file meterupdate
ensureInitialized ensureInitialized
copier <- mkCopier hardlink params copier <- mkCopier hardlink params
let verify = Annex.Content.RemoteVerify r let verify = Annex.Content.RemoteVerify r
runTransfer (Transfer Download u key) file forwardRetry $ \p -> runTransfer (Transfer Download u key) file stdRetry $ \p ->
let p' = combineMeterUpdate meterupdate p let p' = combineMeterUpdate meterupdate p
in Annex.Content.saveState True `after` in Annex.Content.saveState True `after`
Annex.Content.getViaTmp verify key Annex.Content.getViaTmp verify key