make the assistant retry failed transfers

When a transfer fails, the progress info can be used to intelligently
retry it. If the transfer managed to make some progress, but did not
fully complete, then there's a good chance that a retry will finish it
(or at least make more progress).
This commit is contained in:
Joey Hess 2012-09-23 13:27:13 -04:00
parent d4055b3dd2
commit df07ccf404
6 changed files with 37 additions and 17 deletions

View file

@ -66,7 +66,7 @@ getKeyFile key file dest = dispatch =<< Remote.keyPossibilities key
either (const False) id <$> Remote.hasKey r key either (const False) id <$> Remote.hasKey r key
| otherwise = return True | otherwise = return True
docopy r continue = do docopy r continue = do
ok <- download (Remote.uuid r) key (Just file) $ do ok <- download (Remote.uuid r) key (Just file) noRetry $ do
showAction $ "from " ++ Remote.name r showAction $ "from " ++ Remote.name r
Remote.retrieveKeyFile r key (Just file) dest Remote.retrieveKeyFile r key (Just file) dest
if ok then return ok else continue if ok then return ok else continue

View file

@ -89,7 +89,7 @@ toPerform dest move key file = moveLock move key $ do
stop stop
Right False -> do Right False -> do
showAction $ "to " ++ Remote.name dest showAction $ "to " ++ Remote.name dest
ok <- upload (Remote.uuid dest) key (Just file) $ ok <- upload (Remote.uuid dest) key (Just file) noRetry $
Remote.storeKey dest key (Just file) Remote.storeKey dest key (Just file)
if ok if ok
then finish then finish
@ -138,7 +138,7 @@ fromPerform src move key file = moveLock move key $
, handle move =<< go , handle move =<< go
) )
where where
go = download (Remote.uuid src) key (Just file) $ do go = download (Remote.uuid src) key (Just file) noRetry $ do
showAction $ "from " ++ Remote.name src showAction $ "from " ++ Remote.name src
getViaTmp key $ Remote.retrieveKeyFile src key (Just file) getViaTmp key $ Remote.retrieveKeyFile src key (Just file)
handle _ False = stop -- failed handle _ False = stop -- failed

View file

@ -24,7 +24,7 @@ seek = [withKeys start]
start :: Key -> CommandStart start :: Key -> CommandStart
start key = ifM (inAnnex key) start key = ifM (inAnnex key)
( fieldTransfer Upload key $ \p -> do ( fieldTransfer Upload key $ \_p -> do
file <- inRepo $ gitAnnexLocation key file <- inRepo $ gitAnnexLocation key
liftIO $ rsyncServerSend file liftIO $ rsyncServerSend file
, do , do
@ -36,7 +36,7 @@ fieldTransfer :: Direction -> Key -> (MeterUpdate -> Annex Bool) -> CommandStart
fieldTransfer direction key a = do fieldTransfer direction key a = do
afile <- Fields.getField Fields.associatedFile afile <- Fields.getField Fields.associatedFile
ok <- maybe (a $ const noop) ok <- maybe (a $ const noop)
(\u -> runTransfer (Transfer direction (toUUID u) key) afile a) (\u -> runTransfer (Transfer direction (toUUID u) key) afile noRetry a)
=<< Fields.getField Fields.remoteUUID =<< Fields.getField Fields.remoteUUID
if ok if ok
then liftIO exitSuccess then liftIO exitSuccess

View file

@ -43,7 +43,7 @@ start to from file key =
toPerform :: Remote -> Key -> AssociatedFile -> CommandPerform toPerform :: Remote -> Key -> AssociatedFile -> CommandPerform
toPerform remote key file = next $ toPerform remote key file = next $
upload (uuid remote) key file $ \p -> do upload (uuid remote) key file forwardRetry $ \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
@ -51,5 +51,5 @@ toPerform remote key file = next $
fromPerform :: Remote -> Key -> AssociatedFile -> CommandPerform fromPerform :: Remote -> Key -> AssociatedFile -> CommandPerform
fromPerform remote key file = next $ fromPerform remote key file = next $
download (uuid remote) key file $ download (uuid remote) key file forwardRetry $
getViaTmp key $ Remote.retrieveKeyFile remote key file getViaTmp key $ Remote.retrieveKeyFile remote key file

View file

@ -74,11 +74,21 @@ percentComplete :: Transfer -> TransferInfo -> Maybe Percentage
percentComplete (Transfer { transferKey = key }) info = percentComplete (Transfer { transferKey = key }) info =
percentage <$> keySize key <*> Just (fromMaybe 0 $ bytesComplete info) percentage <$> keySize key <*> Just (fromMaybe 0 $ bytesComplete info)
upload :: UUID -> Key -> AssociatedFile -> (MeterUpdate -> Annex Bool) -> Annex Bool type RetryDecider = TransferInfo -> TransferInfo -> Bool
upload u key file a = runTransfer (Transfer Upload u key) file a
download :: UUID -> Key -> AssociatedFile -> Annex Bool -> Annex Bool noRetry :: RetryDecider
download u key file a = runTransfer (Transfer Download u key) file (const a) noRetry _ _ = False
{- Retries a transfer when it fails, as long as the failed transfer managed
- to send some data. -}
forwardRetry :: RetryDecider
forwardRetry old new = bytesComplete old < bytesComplete new
upload :: UUID -> Key -> AssociatedFile -> RetryDecider -> (MeterUpdate -> Annex Bool) -> Annex Bool
upload u key = runTransfer (Transfer Upload u key)
download :: UUID -> Key -> AssociatedFile -> RetryDecider -> Annex Bool -> Annex Bool
download u key file shouldretry a = runTransfer (Transfer Download u key) file shouldretry (const a)
{- Runs a transfer action. Creates and locks the lock file while the {- Runs a transfer action. Creates and locks the lock file while the
- action is running, and stores info in the transfer information - action is running, and stores info in the transfer information
@ -87,12 +97,12 @@ download u key file a = runTransfer (Transfer Download u key) file (const a)
- If the transfer action returns False, the transfer info is - If the transfer action returns False, the transfer info is
- left in the failedTransferDir. - left in the failedTransferDir.
-} -}
runTransfer :: Transfer -> Maybe FilePath -> (MeterUpdate -> Annex Bool) -> Annex Bool runTransfer :: Transfer -> Maybe FilePath -> RetryDecider -> (MeterUpdate -> Annex Bool) -> Annex Bool
runTransfer t file a = do runTransfer t file shouldretry a = do
info <- liftIO $ startTransferInfo file info <- liftIO $ startTransferInfo file
(meter, tfile) <- mkProgressUpdater t info (meter, tfile) <- mkProgressUpdater t info
mode <- annexFileMode mode <- annexFileMode
ok <- bracketIO (prep tfile mode info) (cleanup tfile) (a meter) ok <- retry tfile info $ bracketIO (prep tfile mode info) (cleanup tfile) (a meter)
unless ok $ failed info unless ok $ failed info
return ok return ok
where where
@ -113,7 +123,17 @@ runTransfer t file a = do
failedtfile <- fromRepo $ failedTransferFile t failedtfile <- fromRepo $ failedTransferFile t
createAnnexDirectory $ takeDirectory failedtfile createAnnexDirectory $ takeDirectory failedtfile
liftIO $ writeTransferInfoFile info failedtfile liftIO $ writeTransferInfoFile info failedtfile
retry tfile oldinfo run = do
ok <- run
if ok
then return ok
else do
v <- liftIO $ readTransferInfoFile Nothing tfile
case v of
Nothing -> return ok
Just newinfo -> if shouldretry oldinfo newinfo
then retry tfile newinfo run
else return ok
{- Generates a callback that can be called as transfer progresses to update {- Generates a callback that can be called as transfer progresses to update
- the transfer info file. Also returns the file it'll be updating. -} - the transfer info file. Also returns the file it'll be updating. -}

View file

@ -247,7 +247,7 @@ copyFromRemote r key file dest
liftIO $ onLocal r $ do liftIO $ onLocal r $ do
ensureInitialized ensureInitialized
loc <- inRepo $ gitAnnexLocation key loc <- inRepo $ gitAnnexLocation key
upload u key file $ upload u key file noRetry $
rsyncOrCopyFile params loc dest rsyncOrCopyFile params loc dest
| Git.repoIsSsh r = feedprogressback $ \feeder -> | Git.repoIsSsh r = feedprogressback $ \feeder ->
rsyncHelper (Just feeder) rsyncHelper (Just feeder)
@ -317,7 +317,7 @@ copyToRemote r key file p
( return False ( return False
, do , do
ensureInitialized ensureInitialized
download u key file $ download u key file noRetry $
Annex.Content.saveState True `after` Annex.Content.saveState True `after`
Annex.Content.getViaTmp key Annex.Content.getViaTmp key
(\d -> rsyncOrCopyFile params keysrc d p) (\d -> rsyncOrCopyFile params keysrc d p)