2014-03-22 14:42:38 +00:00
|
|
|
{- git-annex transfers
|
|
|
|
-
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
- Copyright 2012-2024 Joey Hess <id@joeyh.name>
|
2014-03-22 14:42:38 +00:00
|
|
|
-
|
2019-03-13 19:48:14 +00:00
|
|
|
- Licensed under the GNU AGPL version 3 or higher.
|
2014-03-22 14:42:38 +00:00
|
|
|
-}
|
|
|
|
|
filter out control characters in warning messages
Converted warning and similar to use StringContainingQuotedPath. Most
warnings are static strings, some do refer to filepaths that need to be
quoted, and others don't need quoting.
Note that, since quote filters out control characters of even
UnquotedString, this makes all warnings safe, even when an attacker
sneaks in a control character in some other way.
When json is being output, no quoting is done, since json gets its own
quoting.
This does, as a side effect, make warning messages in json output not
be indented. The indentation is only needed to offset warning messages
underneath the display of the file they apply to, so that's ok.
Sponsored-by: Brett Eisenberg on Patreon
2023-04-10 18:47:32 +00:00
|
|
|
{-# LANGUAGE CPP, BangPatterns, OverloadedStrings #-}
|
2014-03-22 14:42:38 +00:00
|
|
|
|
|
|
|
module Annex.Transfer (
|
|
|
|
module X,
|
|
|
|
upload,
|
2020-12-07 18:44:21 +00:00
|
|
|
upload',
|
2018-11-06 17:00:25 +00:00
|
|
|
alwaysUpload,
|
2014-03-22 14:42:38 +00:00
|
|
|
download,
|
2020-12-07 18:44:21 +00:00
|
|
|
download',
|
2014-03-22 14:42:38 +00:00
|
|
|
runTransfer,
|
2014-08-15 18:17:05 +00:00
|
|
|
alwaysRunTransfer,
|
2014-03-22 14:42:38 +00:00
|
|
|
noRetry,
|
2018-03-29 17:04:07 +00:00
|
|
|
stdRetry,
|
2016-09-06 16:42:50 +00:00
|
|
|
pickRemote,
|
2014-03-22 14:42:38 +00:00
|
|
|
) where
|
|
|
|
|
2016-01-20 20:36:33 +00:00
|
|
|
import Annex.Common
|
2016-09-06 16:42:50 +00:00
|
|
|
import qualified Annex
|
2014-03-22 14:42:38 +00:00
|
|
|
import Logs.Transfer as X
|
2016-08-03 16:37:12 +00:00
|
|
|
import Types.Transfer as X
|
2014-03-22 19:01:48 +00:00
|
|
|
import Annex.Notification as X
|
2020-12-07 18:44:21 +00:00
|
|
|
import Annex.Content
|
2014-03-22 14:42:38 +00:00
|
|
|
import Annex.Perms
|
2020-12-07 18:44:21 +00:00
|
|
|
import Annex.Action
|
2014-03-22 14:42:38 +00:00
|
|
|
import Utility.Metered
|
2018-03-29 17:04:07 +00:00
|
|
|
import Utility.ThreadScheduler
|
2023-04-27 19:57:50 +00:00
|
|
|
import Utility.FileMode
|
2015-11-12 22:05:45 +00:00
|
|
|
import Annex.LockPool
|
2017-02-27 19:21:24 +00:00
|
|
|
import Types.Key
|
2016-09-06 16:42:50 +00:00
|
|
|
import qualified Types.Remote as Remote
|
2023-03-27 19:10:46 +00:00
|
|
|
import qualified Types.Backend
|
2016-09-09 16:57:42 +00:00
|
|
|
import Types.Concurrency
|
2021-02-03 19:35:32 +00:00
|
|
|
import Annex.Concurrent
|
use fine-grained WorkerStages when transferring and verifying
This means that Command.Move and Command.Get don't need to
manually set the stage, and is a lot cleaner conceptually.
Also, this makes Command.Sync.syncFile use the worker pool better.
In the scenario where it first downloads content and then uploads it to
some other remotes, it will start in TransferStage, then enter VerifyStage
and then go back to TransferStage for each transfer to the remotes.
Before, it entered CleanupStage after the download, and stayed in it for
the upload, so too many transfer jobs could run at the same time.
Note that, in Remote.Git, it uses runTransfer and also verifyKeyContent
inside onLocal. That has a Annex state for the remote, with no worker pool.
So the resulting calls to enteringStage won't block in there.
While Remote.Git.copyToRemote does do checksum verification, I
realized that should not use a verification slot in the WorkerPool
to do it. Because, it's reading back from eg, a removable disk to checksum.
That will contend with other writes to that disk. It's best to treat
that checksum verification as just part of the transer. So, removed the todo
item about that, as there's nothing needing to be done.
2019-06-19 17:09:26 +00:00
|
|
|
import Types.WorkerPool
|
2020-04-17 18:36:45 +00:00
|
|
|
import Annex.WorkerPool
|
2020-12-07 20:11:29 +00:00
|
|
|
import Annex.TransferrerPool
|
2021-02-03 19:35:32 +00:00
|
|
|
import Annex.StallDetection
|
2024-02-29 21:21:29 +00:00
|
|
|
import Backend (isCryptographicallySecureKey)
|
2020-12-08 19:22:18 +00:00
|
|
|
import Types.StallDetection
|
2020-10-29 18:20:57 +00:00
|
|
|
import qualified Utility.RawFilePath as R
|
2014-03-22 14:42:38 +00:00
|
|
|
|
|
|
|
import Control.Concurrent
|
2021-02-03 19:35:32 +00:00
|
|
|
import Control.Concurrent.Async
|
|
|
|
import Control.Concurrent.STM hiding (retry)
|
2017-03-08 18:49:30 +00:00
|
|
|
import qualified Data.Map.Strict as M
|
2020-10-29 18:20:57 +00:00
|
|
|
import qualified System.FilePath.ByteString as P
|
2017-03-08 18:49:30 +00:00
|
|
|
import Data.Ord
|
2014-03-22 14:42:38 +00:00
|
|
|
|
2021-02-03 19:35:32 +00:00
|
|
|
-- Upload, supporting canceling detected stalls.
|
2020-12-07 18:44:21 +00:00
|
|
|
upload :: Remote -> Key -> AssociatedFile -> RetryDecider -> NotifyWitness -> Annex Bool
|
2024-07-01 14:42:27 +00:00
|
|
|
upload r key af d witness =
|
2024-01-19 19:14:26 +00:00
|
|
|
case getStallDetection Upload r of
|
2021-09-22 14:46:10 +00:00
|
|
|
Nothing -> go (Just ProbeStallDetection)
|
|
|
|
Just StallDetectionDisabled -> go Nothing
|
2024-07-01 14:42:27 +00:00
|
|
|
Just sd -> runTransferrer sd r key af d Upload witness
|
2020-12-08 19:22:18 +00:00
|
|
|
where
|
2024-07-01 14:42:27 +00:00
|
|
|
go sd = upload' (Remote.uuid r) key af sd d (action . Remote.storeKey r key af Nothing) witness
|
2020-12-07 18:44:21 +00:00
|
|
|
|
2021-02-03 19:35:32 +00:00
|
|
|
-- Upload, not supporting canceling detected stalls
|
|
|
|
upload' :: Observable v => UUID -> Key -> AssociatedFile -> Maybe StallDetection -> RetryDecider -> (MeterUpdate -> Annex v) -> NotifyWitness -> Annex v
|
|
|
|
upload' u key f sd d a _witness = guardHaveUUID u $
|
2023-03-27 19:10:46 +00:00
|
|
|
runTransfer (Transfer Upload u (fromKey id key)) Nothing f sd d a
|
2015-05-12 19:50:03 +00:00
|
|
|
|
2021-02-03 19:35:32 +00:00
|
|
|
alwaysUpload :: Observable v => UUID -> Key -> AssociatedFile -> Maybe StallDetection -> RetryDecider -> (MeterUpdate -> Annex v) -> NotifyWitness -> Annex v
|
|
|
|
alwaysUpload u key f sd d a _witness = guardHaveUUID u $
|
2023-03-27 19:10:46 +00:00
|
|
|
alwaysRunTransfer (Transfer Upload u (fromKey id key)) Nothing f sd d a
|
2018-11-06 17:00:25 +00:00
|
|
|
|
2021-02-03 19:35:32 +00:00
|
|
|
-- Download, supporting canceling detected stalls.
|
2020-12-07 18:44:21 +00:00
|
|
|
download :: Remote -> Key -> AssociatedFile -> RetryDecider -> NotifyWitness -> Annex Bool
|
2023-01-23 17:45:26 +00:00
|
|
|
download r key f d witness =
|
2024-01-19 19:14:26 +00:00
|
|
|
case getStallDetection Download r of
|
2021-09-22 14:46:10 +00:00
|
|
|
Nothing -> go (Just ProbeStallDetection)
|
|
|
|
Just StallDetectionDisabled -> go Nothing
|
|
|
|
Just sd -> runTransferrer sd r key f d Download witness
|
2020-12-07 18:44:21 +00:00
|
|
|
where
|
disk free checking for unsized keys
Improve disk free space checking when transferring unsized keys to
local git remotes. Since the size of the object file is known, can
check that instead.
Getting unsized keys from local git remotes does not check the actual
object size. It would be harder to handle that direction because the size
check is run locally, before anything involving the remote is done. So it
doesn't know the size of the file on the remote.
Also, transferring unsized keys to other remotes, including ssh remotes and
p2p remotes don't do disk size checking for unsized keys. This would need a
change in protocol.
(It does seem like it would be possible to implement the same thing for
directory special remotes though.)
In some sense, it might be better to not ever do disk free checking for
unsized keys, than to do it only sometimes. A user might notice this
direction working and consider it a bug that the other direction does not.
On the other hand, disk reserve checking is not implemented for most
special remotes at all, and yet it is implemented for a few, which is also
inconsistent, but best effort. And so doing this best effort seems to make
some sense. Fundamentally, if the user wants the size to always be checked,
they should not use unsized keys.
Sponsored-by: Brock Spratlen on Patreon
2024-01-16 18:29:10 +00:00
|
|
|
go sd = getViaTmp (Remote.retrievalSecurityPolicy r) vc key f Nothing $ \dest ->
|
2021-02-03 19:35:32 +00:00
|
|
|
download' (Remote.uuid r) key f sd d (go' dest) witness
|
|
|
|
go' dest p = verifiedAction $
|
2021-08-17 16:41:36 +00:00
|
|
|
Remote.retrieveKeyFile r key f (fromRawFilePath dest) p vc
|
|
|
|
vc = Remote.RemoteVerify r
|
2020-12-07 18:44:21 +00:00
|
|
|
|
2021-02-03 19:35:32 +00:00
|
|
|
-- Download, not supporting canceling detected stalls.
|
|
|
|
download' :: Observable v => UUID -> Key -> AssociatedFile -> Maybe StallDetection -> RetryDecider -> (MeterUpdate -> Annex v) -> NotifyWitness -> Annex v
|
|
|
|
download' u key f sd d a _witness = guardHaveUUID u $
|
2023-03-27 19:10:46 +00:00
|
|
|
runTransfer (Transfer Download u (fromKey id key)) Nothing f sd d a
|
2016-06-02 17:50:05 +00:00
|
|
|
|
|
|
|
guardHaveUUID :: Observable v => UUID -> Annex v -> Annex v
|
|
|
|
guardHaveUUID u a
|
|
|
|
| u == NoUUID = return observeFailure
|
|
|
|
| otherwise = a
|
2014-03-22 14:42:38 +00:00
|
|
|
|
|
|
|
{- Runs a transfer action. Creates and locks the lock file while the
|
|
|
|
- action is running, and stores info in the transfer information
|
|
|
|
- file.
|
|
|
|
-
|
|
|
|
- If the transfer action returns False, the transfer info is
|
|
|
|
- left in the failedTransferDir.
|
|
|
|
-
|
|
|
|
- If the transfer is already in progress, returns False.
|
|
|
|
-
|
|
|
|
- An upload can be run from a read-only filesystem, and in this case
|
|
|
|
- no transfer information or lock file is used.
|
2021-02-03 19:35:32 +00:00
|
|
|
-
|
|
|
|
- Cannot cancel stalls, but when a likely stall is detected,
|
|
|
|
- suggests to the user that they enable stall detection handling.
|
2014-03-22 14:42:38 +00:00
|
|
|
-}
|
2023-03-27 19:10:46 +00:00
|
|
|
runTransfer :: Observable v => Transfer -> Maybe Backend -> AssociatedFile -> Maybe StallDetection -> RetryDecider -> (MeterUpdate -> Annex v) -> Annex v
|
2014-08-15 18:17:05 +00:00
|
|
|
runTransfer = runTransfer' False
|
|
|
|
|
|
|
|
{- Like runTransfer, but ignores any existing transfer lock file for the
|
|
|
|
- transfer, allowing re-running a transfer that is already in progress.
|
2020-09-29 21:53:48 +00:00
|
|
|
-}
|
2023-03-27 19:10:46 +00:00
|
|
|
alwaysRunTransfer :: Observable v => Transfer -> Maybe Backend -> AssociatedFile -> Maybe StallDetection -> RetryDecider -> (MeterUpdate -> Annex v) -> Annex v
|
2014-08-15 18:17:05 +00:00
|
|
|
alwaysRunTransfer = runTransfer' True
|
|
|
|
|
2023-03-27 19:10:46 +00:00
|
|
|
runTransfer' :: Observable v => Bool -> Transfer -> Maybe Backend -> AssociatedFile -> Maybe StallDetection -> RetryDecider -> (MeterUpdate -> Annex v) -> Annex v
|
|
|
|
runTransfer' ignorelock t eventualbackend afile stalldetection retrydecider transferaction =
|
2023-01-24 17:45:01 +00:00
|
|
|
enteringStage (TransferStage (transferDirection t)) $
|
2021-02-03 19:35:32 +00:00
|
|
|
debugLocks $
|
2023-03-27 19:10:46 +00:00
|
|
|
preCheckSecureHashes (transferKey t) eventualbackend go
|
2014-03-22 14:42:38 +00:00
|
|
|
where
|
2021-02-03 19:35:32 +00:00
|
|
|
go = do
|
|
|
|
info <- liftIO $ startTransferInfo afile
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
(tfile, lckfile, moldlckfile) <- fromRepo $ transferFileAndLockFile t
|
|
|
|
(meter, createtfile, metervar) <- mkProgressUpdater t info tfile
|
2021-02-03 19:35:32 +00:00
|
|
|
mode <- annexFileMode
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
(lck, inprogress) <- prep lckfile moldlckfile createtfile mode
|
2021-02-03 19:35:32 +00:00
|
|
|
if inprogress && not ignorelock
|
|
|
|
then do
|
2021-10-27 18:46:21 +00:00
|
|
|
warning "transfer already in progress, or unable to take transfer lock"
|
2021-02-03 19:35:32 +00:00
|
|
|
return observeFailure
|
|
|
|
else do
|
|
|
|
v <- retry 0 info metervar $
|
|
|
|
detectStallsAndSuggestConfig stalldetection metervar $
|
|
|
|
transferaction meter
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
liftIO $ cleanup tfile lckfile moldlckfile lck
|
2021-02-03 19:35:32 +00:00
|
|
|
if observeBool v
|
|
|
|
then removeFailedTransfer t
|
|
|
|
else recordFailedTransfer t info
|
|
|
|
return v
|
|
|
|
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
prep :: RawFilePath -> Maybe RawFilePath -> Annex () -> ModeSetter -> Annex (Maybe (LockHandle, Maybe LockHandle), Bool)
|
2014-03-22 14:42:38 +00:00
|
|
|
#ifndef mingw32_HOST_OS
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
prep lckfile moldlckfile createtfile mode = catchPermissionDenied (const prepfailed) $ do
|
|
|
|
createAnnexDirectory $ P.takeDirectory lckfile
|
|
|
|
tryLockExclusive (Just mode) lckfile >>= \case
|
2015-05-12 23:36:16 +00:00
|
|
|
Nothing -> return (Nothing, True)
|
2021-10-27 18:55:30 +00:00
|
|
|
-- Since the lock file is removed in cleanup,
|
|
|
|
-- there's a race where different processes
|
|
|
|
-- may have a deleted and a new version of the same
|
|
|
|
-- lock file open. checkSaneLock guards against
|
|
|
|
-- that.
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
Just lockhandle -> ifM (checkSaneLock lckfile lockhandle)
|
|
|
|
( case moldlckfile of
|
|
|
|
Nothing -> do
|
|
|
|
createtfile
|
|
|
|
return (Just (lockhandle, Nothing), False)
|
|
|
|
Just oldlckfile -> do
|
|
|
|
createAnnexDirectory $ P.takeDirectory oldlckfile
|
|
|
|
tryLockExclusive (Just mode) oldlckfile >>= \case
|
|
|
|
Nothing -> do
|
|
|
|
liftIO $ dropLock lockhandle
|
|
|
|
return (Nothing, True)
|
|
|
|
Just oldlockhandle -> ifM (checkSaneLock oldlckfile oldlockhandle)
|
|
|
|
( do
|
|
|
|
createtfile
|
|
|
|
return (Just (lockhandle, Just oldlockhandle), False)
|
|
|
|
, do
|
|
|
|
liftIO $ dropLock oldlockhandle
|
|
|
|
liftIO $ dropLock lockhandle
|
|
|
|
return (Nothing, True)
|
|
|
|
)
|
2017-05-25 20:02:17 +00:00
|
|
|
, do
|
|
|
|
liftIO $ dropLock lockhandle
|
|
|
|
return (Nothing, True)
|
2015-05-12 23:39:28 +00:00
|
|
|
)
|
2014-03-22 14:42:38 +00:00
|
|
|
#else
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
prep lckfile moldlckfile createtfile _mode = catchPermissionDenied (const prepfailed) $ do
|
|
|
|
createAnnexDirectory $ P.takeDirectory lckfile
|
|
|
|
catchMaybeIO (liftIO $ lockExclusive lckfile) >>= \case
|
|
|
|
Just (Just lockhandle) -> case moldlckfile of
|
|
|
|
Nothing -> do
|
|
|
|
createtfile
|
|
|
|
return (Just (lockhandle, Nothing), False)
|
|
|
|
Just oldlckfile -> do
|
|
|
|
createAnnexDirectory $ P.takeDirectory oldlckfile
|
|
|
|
catchMaybeIO (liftIO $ lockExclusive oldlckfile) >>= \case
|
|
|
|
Just (Just oldlockhandle) -> do
|
|
|
|
createtfile
|
|
|
|
return (Just (lockhandle, Just oldlockhandle), False)
|
|
|
|
_ -> do
|
|
|
|
liftIO $ dropLock lockhandle
|
|
|
|
return (Nothing, False)
|
|
|
|
_ -> return (Nothing, False)
|
2014-03-22 14:42:38 +00:00
|
|
|
#endif
|
create directory for transfer lock file, and catch perm error
Before, the call to mkProgressUpdater created the directory as a
side-effect, but since that ignored failure to create it, this led to
a "does not exist" exception when the transfer lock file was created,
rather than a permissions error.
So, make sure the directory exists before trying to lock the file in it.
When a PermissionDenied exception is caught, skip making the transfer lock.
This lets downloads from readonly remotes happen.
If an upload is being tried, and the lock file can't be written due to
permissions, then probably the actual transfer will fail for the same
reason, so I think it's ok that it continues w/o taking the lock in that
case.
2016-02-12 18:11:25 +00:00
|
|
|
prepfailed = return (Nothing, False)
|
|
|
|
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
cleanup _ _ _ Nothing = noop
|
|
|
|
cleanup tfile lckfile moldlckfile (Just (lockhandle, moldlockhandle)) = do
|
2020-10-29 18:20:57 +00:00
|
|
|
void $ tryIO $ R.removeLink tfile
|
2014-03-22 14:42:38 +00:00
|
|
|
#ifndef mingw32_HOST_OS
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
void $ tryIO $ R.removeLink lckfile
|
|
|
|
maybe noop (void . tryIO . R.removeLink) moldlckfile
|
|
|
|
maybe noop dropLock moldlockhandle
|
2015-05-12 23:36:16 +00:00
|
|
|
dropLock lockhandle
|
2014-03-22 14:42:38 +00:00
|
|
|
#else
|
|
|
|
{- Windows cannot delete the lockfile until the lock
|
|
|
|
- is closed. So it's possible to race with another
|
|
|
|
- process that takes the lock before it's removed,
|
|
|
|
- so ignore failure to remove.
|
|
|
|
-}
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
maybe noop dropLock moldlockhandle
|
2014-03-22 14:42:38 +00:00
|
|
|
dropLock lockhandle
|
fix transfer lock file for Download to not include uuid
While redundant concurrent transfers were already prevented in most
cases, it failed to prevent the case where two different repositories were
sending the same content to the same repository. By removing the uuid
from the transfer lock file for Download transfers, one repository
sending content will block the other one from also sending the same
content.
In order to interoperate with old git-annex, the old lock file is still
locked, as well as locking the new one. That added a lot of extra code
and work, and the plan is to eventually stop locking the old lock file,
at some point in time when an old git-annex process is unlikely to be
running at the same time.
Note that in the case of 2 repositories both doing eg
`git-annex copy foo --to origin`
the output is not that great:
copy b (to origin...)
transfer already in progress, or unable to take transfer lock
git-annex: transfer already in progress, or unable to take transfer lock
97% 966.81 MiB 534 GiB/s 0sp2pstdio: 1 failed
Lost connection (fd:14: hPutBuf: resource vanished (Broken pipe))
Transfer failed
Perhaps that output could be cleaned up? Anyway, it's a lot better than letting
the redundant transfer happen and then failing with an obscure error about
a temp file, which is what it did before. And it seems users don't often
try to do this, since nobody ever reported this bug to me before.
(The "97%" there is actually how far along the *other* transfer is.)
Sponsored-by: Joshua Antonishen on Patreon
2024-03-25 18:47:38 +00:00
|
|
|
void $ tryIO $ R.removeLink lckfile
|
|
|
|
maybe noop (void . tryIO . R.removeLink) moldlckfile
|
2014-03-22 14:42:38 +00:00
|
|
|
#endif
|
2020-09-04 16:46:37 +00:00
|
|
|
|
|
|
|
retry numretries oldinfo metervar run =
|
|
|
|
tryNonAsync run >>= \case
|
|
|
|
Right v
|
|
|
|
| observeBool v -> return v
|
|
|
|
| otherwise -> checkretry
|
|
|
|
Left e -> do
|
filter out control characters in warning messages
Converted warning and similar to use StringContainingQuotedPath. Most
warnings are static strings, some do refer to filepaths that need to be
quoted, and others don't need quoting.
Note that, since quote filters out control characters of even
UnquotedString, this makes all warnings safe, even when an attacker
sneaks in a control character in some other way.
When json is being output, no quoting is done, since json gets its own
quoting.
This does, as a side effect, make warning messages in json output not
be indented. The indentation is only needed to offset warning messages
underneath the display of the file they apply to, so that's ok.
Sponsored-by: Brett Eisenberg on Patreon
2023-04-10 18:47:32 +00:00
|
|
|
warning (UnquotedString (show e))
|
2020-09-04 16:46:37 +00:00
|
|
|
checkretry
|
2018-03-29 17:22:49 +00:00
|
|
|
where
|
|
|
|
checkretry = do
|
2017-12-05 19:00:50 +00:00
|
|
|
b <- getbytescomplete metervar
|
|
|
|
let newinfo = oldinfo { bytesComplete = Just b }
|
2020-09-04 16:46:37 +00:00
|
|
|
let !numretries' = succ numretries
|
|
|
|
ifM (retrydecider numretries' oldinfo newinfo)
|
|
|
|
( retry numretries' newinfo metervar run
|
2018-03-29 17:04:07 +00:00
|
|
|
, return observeFailure
|
|
|
|
)
|
2020-09-04 16:46:37 +00:00
|
|
|
|
avoid using temp file size when deciding whether to retry failed transfer
When stall detection is enabled, and a transfer is in progress,
it would display a doubled message:
(transfer already in progress, or unable to take transfer lock) (transfer already in progress, or unable to take transfer lock)
That happened because the forward retry decider had a start size of 0,
and an end size of whatever amount of the object the other process had
downloaded. So it incorrectly thought that the transferrer process had
made progress, when it had in fact immediately given up with that
message.
Instead, use the reported value from the progress meter. If a remote
does not report progress, this will mean it doesn't forward retry, in a
situation where it used to. But most remotes do report progress, and any
remote that does not can be fixed to, by using watchFileSize when
downloading. Also, some remotes might preallocate the temp file (eg
bittorrent), so relying on statting its size at this level to get
progress is dubious.
The same change was made to Annex/Transfer.hs, although only
Annex/TransferrerPool.hs needed to be changed to avoid the duplicate
message.
(An alternate fix would have been to start the retry decider with the
size of the object file before downloading begins, rather than 0.)
Sponsored-by: Brett Eisenberg on Patreon
2021-06-25 15:53:28 +00:00
|
|
|
getbytescomplete metervar = liftIO $
|
|
|
|
maybe 0 fromBytesProcessed <$> readTVarIO metervar
|
2014-03-22 14:42:38 +00:00
|
|
|
|
2021-02-03 19:35:32 +00:00
|
|
|
detectStallsAndSuggestConfig :: Maybe StallDetection -> TVar (Maybe BytesProcessed) -> Annex a -> Annex a
|
|
|
|
detectStallsAndSuggestConfig Nothing _ a = a
|
|
|
|
detectStallsAndSuggestConfig sd@(Just _) metervar a =
|
|
|
|
bracket setup cleanup (const a)
|
|
|
|
where
|
|
|
|
setup = do
|
|
|
|
v <- liftIO newEmptyTMVarIO
|
|
|
|
sdt <- liftIO $ async $ detectStalls sd metervar $
|
|
|
|
void $ atomically $ tryPutTMVar v True
|
|
|
|
wt <- liftIO . async =<< forkState (warnonstall v)
|
|
|
|
return (v, sdt, wt)
|
|
|
|
cleanup (v, sdt, wt) = do
|
|
|
|
liftIO $ uninterruptibleCancel sdt
|
|
|
|
void $ liftIO $ atomically $ tryPutTMVar v False
|
|
|
|
join (liftIO (wait wt))
|
|
|
|
warnonstall v = whenM (liftIO (atomically (takeTMVar v))) $
|
|
|
|
warning "Transfer seems to have stalled. To restart stalled transfers, configure annex.stalldetection"
|
|
|
|
|
|
|
|
{- Runs a transfer using a separate process, which lets detected stalls be
|
|
|
|
- canceled. -}
|
2020-12-08 19:22:18 +00:00
|
|
|
runTransferrer
|
|
|
|
:: StallDetection
|
|
|
|
-> Remote
|
|
|
|
-> Key
|
|
|
|
-> AssociatedFile
|
|
|
|
-> RetryDecider
|
|
|
|
-> Direction
|
|
|
|
-> NotifyWitness
|
|
|
|
-> Annex Bool
|
|
|
|
runTransferrer sd r k afile retrydecider direction _witness =
|
2023-03-27 19:10:46 +00:00
|
|
|
enteringStage (TransferStage direction) $ preCheckSecureHashes k Nothing $ do
|
2020-12-08 19:22:18 +00:00
|
|
|
info <- liftIO $ startTransferInfo afile
|
|
|
|
go 0 info
|
|
|
|
where
|
|
|
|
go numretries info =
|
|
|
|
withTransferrer (performTransfer (Just sd) AnnexLevel id (Just r) t info) >>= \case
|
|
|
|
Right () -> return True
|
|
|
|
Left newinfo -> do
|
|
|
|
let !numretries' = succ numretries
|
|
|
|
ifM (retrydecider numretries' info newinfo)
|
|
|
|
( go numretries' newinfo
|
|
|
|
, return False
|
|
|
|
)
|
|
|
|
t = Transfer direction (Remote.uuid r) (fromKey id k)
|
|
|
|
|
2017-02-27 19:21:24 +00:00
|
|
|
{- Avoid download and upload of keys with insecure content when
|
|
|
|
- annex.securehashesonly is configured.
|
|
|
|
-
|
|
|
|
- This is not a security check. Even if this let the content be
|
|
|
|
- downloaded, the actual security checks would prevent the content from
|
|
|
|
- being added to the repository. The only reason this is done here is to
|
|
|
|
- avoid transferring content that's going to be rejected anyway.
|
|
|
|
-
|
|
|
|
- We assume that, if annex.securehashesonly is set and the local repo
|
|
|
|
- still contains content using an insecure hash, remotes will likewise
|
|
|
|
- tend to be configured to reject it, so Upload is also prevented.
|
|
|
|
-}
|
2023-03-27 19:10:46 +00:00
|
|
|
preCheckSecureHashes :: Observable v => Key -> Maybe Backend -> Annex v -> Annex v
|
|
|
|
preCheckSecureHashes k meventualbackend a = case meventualbackend of
|
|
|
|
Just eventualbackend -> go
|
2024-02-29 21:21:29 +00:00
|
|
|
(pure (Types.Backend.isCryptographicallySecure eventualbackend))
|
2023-03-27 19:10:46 +00:00
|
|
|
(Types.Backend.backendVariety eventualbackend)
|
|
|
|
Nothing -> go
|
2024-02-29 21:21:29 +00:00
|
|
|
(isCryptographicallySecureKey k)
|
2023-03-27 19:10:46 +00:00
|
|
|
(fromKey keyVariety k)
|
2017-02-27 19:21:24 +00:00
|
|
|
where
|
2023-03-27 19:10:46 +00:00
|
|
|
go checksecure variety = ifM checksecure
|
|
|
|
( a
|
|
|
|
, ifM (annexSecureHashesOnly <$> Annex.getGitConfig)
|
|
|
|
( blocked variety
|
|
|
|
, a
|
|
|
|
)
|
|
|
|
)
|
|
|
|
blocked variety = do
|
filter out control characters in warning messages
Converted warning and similar to use StringContainingQuotedPath. Most
warnings are static strings, some do refer to filepaths that need to be
quoted, and others don't need quoting.
Note that, since quote filters out control characters of even
UnquotedString, this makes all warnings safe, even when an attacker
sneaks in a control character in some other way.
When json is being output, no quoting is done, since json gets its own
quoting.
This does, as a side effect, make warning messages in json output not
be indented. The indentation is only needed to offset warning messages
underneath the display of the file they apply to, so that's ok.
Sponsored-by: Brett Eisenberg on Patreon
2023-04-10 18:47:32 +00:00
|
|
|
warning $ UnquotedString $ "annex.securehashesonly blocked transfer of " ++ decodeBS (formatKeyVariety variety) ++ " key"
|
2023-03-27 19:10:46 +00:00
|
|
|
return observeFailure
|
2017-02-27 19:21:24 +00:00
|
|
|
|
2020-09-04 16:46:37 +00:00
|
|
|
type NumRetries = Integer
|
|
|
|
|
|
|
|
type RetryDecider = NumRetries -> TransferInfo -> TransferInfo -> Annex Bool
|
2018-03-29 17:04:07 +00:00
|
|
|
|
2020-09-04 16:47:53 +00:00
|
|
|
{- Both retry deciders are checked together, so if one chooses to delay,
|
|
|
|
- it will always take effect. -}
|
2018-03-29 17:04:07 +00:00
|
|
|
combineRetryDeciders :: RetryDecider -> RetryDecider -> RetryDecider
|
2020-09-04 16:47:53 +00:00
|
|
|
combineRetryDeciders a b = \n old new -> do
|
|
|
|
ar <- a n old new
|
|
|
|
br <- b n old new
|
|
|
|
return (ar || br)
|
2014-03-22 14:42:38 +00:00
|
|
|
|
|
|
|
noRetry :: RetryDecider
|
2020-09-04 16:46:37 +00:00
|
|
|
noRetry _ _ _ = pure False
|
2018-03-29 17:04:07 +00:00
|
|
|
|
|
|
|
stdRetry :: RetryDecider
|
|
|
|
stdRetry = combineRetryDeciders forwardRetry configuredRetry
|
2014-03-22 14:42:38 +00:00
|
|
|
|
2020-09-04 16:46:37 +00:00
|
|
|
{- Keep retrying failed transfers, as long as forward progress is being
|
|
|
|
- made.
|
|
|
|
-
|
|
|
|
- Up to a point -- while some remotes can resume where the previous
|
|
|
|
- transfer left off, and so it would make sense to keep retrying forever,
|
|
|
|
- other remotes restart each transfer from the beginning, and so even if
|
|
|
|
- forward progress is being made, it's not real progress. So, retry a
|
2020-09-04 19:16:40 +00:00
|
|
|
- maximum of 5 times by default.
|
2020-09-04 16:46:37 +00:00
|
|
|
-}
|
2014-03-22 14:42:38 +00:00
|
|
|
forwardRetry :: RetryDecider
|
2020-09-04 19:16:40 +00:00
|
|
|
forwardRetry numretries old new
|
|
|
|
| fromMaybe 0 (bytesComplete old) < fromMaybe 0 (bytesComplete new) =
|
|
|
|
(numretries <=) <$> maybe globalretrycfg pure remoteretrycfg
|
|
|
|
| otherwise = return False
|
|
|
|
where
|
|
|
|
globalretrycfg = fromMaybe 5 . annexForwardRetry
|
|
|
|
<$> Annex.getGitConfig
|
|
|
|
remoteretrycfg = remoteAnnexRetry =<<
|
|
|
|
(Remote.gitconfig <$> transferRemote new)
|
2018-03-29 17:04:07 +00:00
|
|
|
|
|
|
|
{- Retries a number of times with growing delays in between when enabled
|
|
|
|
- by git configuration. -}
|
|
|
|
configuredRetry :: RetryDecider
|
2020-09-04 16:47:53 +00:00
|
|
|
configuredRetry numretries _old new = do
|
2020-09-04 16:46:37 +00:00
|
|
|
(maxretries, Seconds initretrydelay) <- getcfg $
|
|
|
|
Remote.gitconfig <$> transferRemote new
|
|
|
|
if numretries < maxretries
|
|
|
|
then do
|
|
|
|
let retrydelay = Seconds (initretrydelay * 2^(numretries-1))
|
2023-04-10 21:03:41 +00:00
|
|
|
showSideAction $ UnquotedString $ "Delaying " ++ show (fromSeconds retrydelay) ++ "s before retrying."
|
2020-09-04 16:46:37 +00:00
|
|
|
liftIO $ threadDelaySeconds retrydelay
|
|
|
|
return True
|
|
|
|
else return False
|
2018-03-29 17:04:07 +00:00
|
|
|
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)
|
2016-09-06 16:42:50 +00:00
|
|
|
|
|
|
|
{- 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.
|
|
|
|
-
|
|
|
|
- The list should already be ordered by remote cost, and is normally
|
|
|
|
- tried in order. However, when concurrent jobs are running, they will
|
|
|
|
- be assigned different remotes of the same cost when possible. This can
|
|
|
|
- increase total transfer speed.
|
|
|
|
-}
|
|
|
|
pickRemote :: Observable v => [Remote] -> (Remote -> Annex v) -> Annex v
|
2020-09-16 15:41:28 +00:00
|
|
|
pickRemote l a = debugLocks $ go l =<< getConcurrency
|
2016-09-06 16:42:50 +00:00
|
|
|
where
|
|
|
|
go [] _ = return observeFailure
|
|
|
|
go (r:[]) _ = a r
|
2019-05-10 17:24:31 +00:00
|
|
|
go rs NonConcurrent = gononconcurrent rs
|
|
|
|
go rs (Concurrent n)
|
|
|
|
| n <= 1 = gononconcurrent rs
|
|
|
|
| otherwise = goconcurrent rs
|
|
|
|
go rs ConcurrentPerCpu = goconcurrent rs
|
|
|
|
|
|
|
|
gononconcurrent [] = return observeFailure
|
|
|
|
gononconcurrent (r:rs) = do
|
2016-09-06 16:42:50 +00:00
|
|
|
ok <- a r
|
|
|
|
if observeBool ok
|
|
|
|
then return ok
|
2019-05-10 17:24:31 +00:00
|
|
|
else gononconcurrent rs
|
|
|
|
|
|
|
|
goconcurrent rs = do
|
2021-04-02 19:26:21 +00:00
|
|
|
mv <- Annex.getRead Annex.activeremotes
|
2019-05-10 17:24:31 +00:00
|
|
|
active <- liftIO $ takeMVar mv
|
|
|
|
let rs' = sortBy (lessActiveFirst active) rs
|
|
|
|
goconcurrent' mv active rs'
|
|
|
|
|
|
|
|
goconcurrent' mv active [] = do
|
2016-09-06 16:42:50 +00:00
|
|
|
liftIO $ putMVar mv active
|
|
|
|
return observeFailure
|
2019-05-10 17:24:31 +00:00
|
|
|
goconcurrent' mv active (r:rs) = do
|
2017-03-08 18:49:30 +00:00
|
|
|
let !active' = M.insertWith (+) r 1 active
|
2016-09-06 16:42:50 +00:00
|
|
|
liftIO $ putMVar mv active'
|
|
|
|
let getnewactive = do
|
|
|
|
active'' <- liftIO $ takeMVar mv
|
2017-03-08 18:49:30 +00:00
|
|
|
let !active''' = M.update (\n -> if n > 1 then Just (n-1) else Nothing) r active''
|
2016-09-06 16:42:50 +00:00
|
|
|
return active'''
|
|
|
|
let removeactive = liftIO . putMVar mv =<< getnewactive
|
|
|
|
ok <- a r `onException` removeactive
|
|
|
|
if observeBool ok
|
|
|
|
then do
|
|
|
|
removeactive
|
|
|
|
return ok
|
|
|
|
else do
|
|
|
|
active'' <- getnewactive
|
|
|
|
-- Re-sort the remaining rs
|
|
|
|
-- because other threads could have
|
|
|
|
-- been assigned them in the meantime.
|
2017-03-08 18:49:30 +00:00
|
|
|
let rs' = sortBy (lessActiveFirst active'') rs
|
2019-05-10 17:24:31 +00:00
|
|
|
goconcurrent' mv active'' rs'
|
2016-09-06 16:42:50 +00:00
|
|
|
|
2017-03-08 18:49:30 +00:00
|
|
|
lessActiveFirst :: M.Map Remote Integer -> Remote -> Remote -> Ordering
|
|
|
|
lessActiveFirst active a b
|
|
|
|
| Remote.cost a == Remote.cost b = comparing (`M.lookup` active) a b
|
2018-08-03 17:06:06 +00:00
|
|
|
| otherwise = comparing Remote.cost a b
|