aboutsummaryrefslogtreecommitdiff
path: root/Assistant
diff options
context:
space:
mode:
Diffstat (limited to 'Assistant')
-rw-r--r--Assistant/DaemonStatus.hs30
-rw-r--r--Assistant/Drop.hs65
-rw-r--r--Assistant/MakeRemote.hs10
-rw-r--r--Assistant/Pairing/MakeRemote.hs2
-rw-r--r--Assistant/Sync.hs6
-rw-r--r--Assistant/Threads/Merger.hs2
-rw-r--r--Assistant/Threads/MountWatcher.hs2
-rw-r--r--Assistant/Threads/PairListener.hs12
-rw-r--r--Assistant/Threads/Pusher.hs2
-rw-r--r--Assistant/Threads/SanityChecker.hs2
-rw-r--r--Assistant/Threads/TransferPoller.hs2
-rw-r--r--Assistant/Threads/TransferScanner.hs62
-rw-r--r--Assistant/Threads/TransferWatcher.hs49
-rw-r--r--Assistant/Threads/Watcher.hs18
-rw-r--r--Assistant/Threads/WebApp.hs5
-rw-r--r--Assistant/TransferQueue.hs35
16 files changed, 214 insertions, 90 deletions
diff --git a/Assistant/DaemonStatus.hs b/Assistant/DaemonStatus.hs
index 11ea8676d..60b560b90 100644
--- a/Assistant/DaemonStatus.hs
+++ b/Assistant/DaemonStatus.hs
@@ -17,6 +17,8 @@ import Utility.NotificationBroadcaster
import Logs.Transfer
import Logs.Trust
import qualified Remote
+import qualified Types.Remote as Remote
+import Config
import Control.Concurrent.STM
import System.Posix.Types
@@ -39,8 +41,8 @@ data DaemonStatus = DaemonStatus
-- Messages to display to the user.
, alertMap :: AlertMap
, lastAlertId :: AlertId
- -- Ordered list of remotes to talk to.
- , knownRemotes :: [Remote]
+ -- Ordered list of remotes to sync with.
+ , syncRemotes :: [Remote]
-- Pairing request that is in progress.
, pairingInProgress :: Maybe PairingInProgress
-- Broadcasts notifications about all changes to the DaemonStatus
@@ -86,21 +88,21 @@ modifyDaemonStatus dstatus a = do
sendNotification $ changeNotifier s
return b
-{- Remotes ordered by cost, with dead ones thrown out. -}
-calcKnownRemotes :: Annex [Remote]
-calcKnownRemotes = do
- rs <- concat . Remote.byCost <$> Remote.enabledRemoteList
+{- Syncable remotes ordered by cost. -}
+calcSyncRemotes :: Annex [Remote]
+calcSyncRemotes = do
+ rs <- filterM (repoSyncable . Remote.repo) =<<
+ concat . Remote.byCost <$> Remote.enabledRemoteList
alive <- snd <$> trustPartition DeadTrusted (map Remote.uuid rs)
let good r = Remote.uuid r `elem` alive
return $ filter good rs
-{- Updates the cached ordered list of remotes from the list in Annex
- - state. -}
-updateKnownRemotes :: DaemonStatusHandle -> Annex ()
-updateKnownRemotes dstatus = do
- remotes <- calcKnownRemotes
+{- Updates the sycRemotes list from the list of all remotes in Annex state. -}
+updateSyncRemotes :: DaemonStatusHandle -> Annex ()
+updateSyncRemotes dstatus = do
+ remotes <- calcSyncRemotes
liftIO $ modifyDaemonStatus_ dstatus $
- \s -> s { knownRemotes = remotes }
+ \s -> s { syncRemotes = remotes }
{- Load any previous daemon status file, and store it in a MVar for this
- process to use as its DaemonStatus. Also gets current transfer status. -}
@@ -110,12 +112,12 @@ startDaemonStatus = do
status <- liftIO $
flip catchDefaultIO (readDaemonStatusFile file) =<< newDaemonStatus
transfers <- M.fromList <$> getTransfers
- remotes <- calcKnownRemotes
+ remotes <- calcSyncRemotes
liftIO $ atomically $ newTMVar status
{ scanComplete = False
, sanityCheckRunning = False
, currentTransfers = transfers
- , knownRemotes = remotes
+ , syncRemotes = remotes
}
{- Don't just dump out the structure, because it will change over time,
diff --git a/Assistant/Drop.hs b/Assistant/Drop.hs
new file mode 100644
index 000000000..cf20ef5b1
--- /dev/null
+++ b/Assistant/Drop.hs
@@ -0,0 +1,65 @@
+{- git-annex assistant dropping of unwanted content
+ -
+ - Copyright 2012 Joey Hess <joey@kitenet.net>
+ -
+ - Licensed under the GNU GPL version 3 or higher.
+ -}
+
+module Assistant.Drop where
+
+import Assistant.Common
+import Assistant.DaemonStatus
+import Logs.Location
+import Logs.Trust
+import Types.Remote (AssociatedFile)
+import qualified Remote
+import qualified Command.Drop
+import Command
+import Annex.Wanted
+import Config
+
+{- Drop from local and/or remote when allowed by the preferred content and
+ - numcopies settings. -}
+handleDrops :: DaemonStatusHandle -> Bool -> Key -> AssociatedFile -> Annex ()
+handleDrops _ _ _ Nothing = noop
+handleDrops dstatus fromhere key f = do
+ syncrs <- liftIO $ syncRemotes <$> getDaemonStatus dstatus
+ locs <- loggedLocations key
+ handleDrops' locs syncrs fromhere key f
+
+handleDrops' :: [UUID] -> [Remote] -> Bool -> Key -> AssociatedFile -> Annex ()
+handleDrops' _ _ _ _ Nothing = noop
+handleDrops' locs rs fromhere key (Just f)
+ | fromhere = do
+ n <- getcopies
+ if checkcopies n
+ then go rs =<< dropl n
+ else go rs n
+ | otherwise = go rs =<< getcopies
+ where
+ getcopies = do
+ have <- length . snd <$> trustPartition UnTrusted locs
+ numcopies <- getNumCopies =<< numCopies f
+ return (have, numcopies)
+ checkcopies (have, numcopies) = have > numcopies
+ decrcopies (have, numcopies) = (have - 1, numcopies)
+
+ go [] _ = noop
+ go (r:rest) n
+ | checkcopies n = dropr r n >>= go rest
+ | otherwise = noop
+
+ checkdrop n@(_, numcopies) u a =
+ ifM (wantDrop u (Just f))
+ ( ifM (doCommand $ a (Just numcopies))
+ ( return $ decrcopies n
+ , return n
+ )
+ , return n
+ )
+
+ dropl n = checkdrop n Nothing $ \numcopies ->
+ Command.Drop.startLocal f numcopies key
+
+ dropr r n = checkdrop n (Just $ Remote.uuid r) $ \numcopies ->
+ Command.Drop.startRemote f numcopies key r
diff --git a/Assistant/MakeRemote.hs b/Assistant/MakeRemote.hs
index 9184cb529..8aa7cb2e8 100644
--- a/Assistant/MakeRemote.hs
+++ b/Assistant/MakeRemote.hs
@@ -28,11 +28,12 @@ import qualified Data.Map as M
import Data.Char
{- Sets up and begins syncing with a new ssh or rsync remote. -}
-makeSshRemote :: ThreadState -> DaemonStatusHandle -> ScanRemoteMap -> Bool -> SshData -> IO ()
+makeSshRemote :: ThreadState -> DaemonStatusHandle -> ScanRemoteMap -> Bool -> SshData -> IO Remote
makeSshRemote st dstatus scanremotes forcersync sshdata = do
r <- runThreadState st $
addRemote $ maker (sshRepoName sshdata) sshurl
syncNewRemote st dstatus scanremotes r
+ return r
where
rsync = forcersync || rsyncOnly sshdata
maker
@@ -89,10 +90,11 @@ makeGitRemote basename location = makeRemote basename location $ \name ->
- Returns the name of the remote. -}
makeRemote :: String -> String -> (String -> Annex ()) -> Annex String
makeRemote basename location a = do
- r <- fromRepo id
- if not (any samelocation $ Git.remotes r)
+ g <- gitRepo
+ if not (any samelocation $ Git.remotes g)
then do
- let name = uniqueRemoteName basename 0 r
+
+ let name = uniqueRemoteName basename 0 g
a name
return name
else return basename
diff --git a/Assistant/Pairing/MakeRemote.hs b/Assistant/Pairing/MakeRemote.hs
index fae8c5ee3..ab0bef13c 100644
--- a/Assistant/Pairing/MakeRemote.hs
+++ b/Assistant/Pairing/MakeRemote.hs
@@ -46,7 +46,7 @@ finishedPairing st dstatus scanremotes msg keypair = do
, "git-annex-shell -c configlist " ++ T.unpack (sshDirectory sshdata)
]
""
- makeSshRemote st dstatus scanremotes False sshdata
+ void $ makeSshRemote st dstatus scanremotes False sshdata
{- Mostly a straightforward conversion. Except:
- * Determine the best hostname to use to contact the host.
diff --git a/Assistant/Sync.hs b/Assistant/Sync.hs
index 91ee1c219..6c167e2ea 100644
--- a/Assistant/Sync.hs
+++ b/Assistant/Sync.hs
@@ -84,7 +84,7 @@ reconnectRemotes threadname st dstatus scanremotes rs = void $
pushToRemotes :: ThreadName -> UTCTime -> ThreadState -> Maybe FailedPushMap -> [Remote] -> IO Bool
pushToRemotes threadname now st mpushmap remotes = do
(g, branch, u) <- runThreadState st $ (,,)
- <$> fromRepo id
+ <$> gitRepo
<*> inRepo Git.Branch.current
<*> getUUID
go True branch g u remotes
@@ -145,7 +145,7 @@ pushToRemotes threadname now st mpushmap remotes = do
{- Manually pull from remotes and merge their branches. -}
manualPull :: ThreadState -> Maybe Git.Ref -> [Remote] -> IO Bool
manualPull st currentbranch remotes = do
- g <- runThreadState st $ fromRepo id
+ g <- runThreadState st gitRepo
forM_ remotes $ \r ->
Git.Command.runBool "fetch" [Param $ Remote.name r] g
haddiverged <- runThreadState st Annex.Branch.forceUpdate
@@ -156,5 +156,5 @@ manualPull st currentbranch remotes = do
{- Start syncing a newly added remote, using a background thread. -}
syncNewRemote :: ThreadState -> DaemonStatusHandle -> ScanRemoteMap -> Remote -> IO ()
syncNewRemote st dstatus scanremotes remote = do
- runThreadState st $ updateKnownRemotes dstatus
+ runThreadState st $ updateSyncRemotes dstatus
void $ forkIO $ reconnectRemotes "SyncRemote" st dstatus scanremotes [remote]
diff --git a/Assistant/Threads/Merger.hs b/Assistant/Threads/Merger.hs
index 0349bb1f0..46f516262 100644
--- a/Assistant/Threads/Merger.hs
+++ b/Assistant/Threads/Merger.hs
@@ -25,7 +25,7 @@ thisThread = "Merger"
- pushes. -}
mergeThread :: ThreadState -> DaemonStatusHandle -> TransferQueue -> NamedThread
mergeThread st dstatus transferqueue = thread $ do
- g <- runThreadState st $ fromRepo id
+ g <- runThreadState st gitRepo
let dir = Git.localGitDir g </> "refs"
createDirectoryIfMissing True dir
let hook a = Just $ runHandler st dstatus transferqueue a
diff --git a/Assistant/Threads/MountWatcher.hs b/Assistant/Threads/MountWatcher.hs
index 79fcce08c..462f5843c 100644
--- a/Assistant/Threads/MountWatcher.hs
+++ b/Assistant/Threads/MountWatcher.hs
@@ -174,7 +174,7 @@ remotesUnder st dstatus dir = runThreadState st $ do
let (waschanged, rs') = unzip pairs
when (any id waschanged) $ do
Annex.changeState $ \s -> s { Annex.remotes = rs' }
- updateKnownRemotes dstatus
+ updateSyncRemotes dstatus
return $ map snd $ filter fst pairs
where
checkremote repotop r = case Remote.localpath r of
diff --git a/Assistant/Threads/PairListener.hs b/Assistant/Threads/PairListener.hs
index 9ce369032..9875dcb8a 100644
--- a/Assistant/Threads/PairListener.hs
+++ b/Assistant/Threads/PairListener.hs
@@ -17,6 +17,7 @@ import Assistant.DaemonStatus
import Assistant.WebApp
import Assistant.WebApp.Types
import Assistant.Alert
+import Utility.ThreadScheduler
import Network.Multicast
import Network.Socket
@@ -27,12 +28,17 @@ thisThread :: ThreadName
thisThread = "PairListener"
pairListenerThread :: ThreadState -> DaemonStatusHandle -> ScanRemoteMap -> UrlRenderer -> NamedThread
-pairListenerThread st dstatus scanremotes urlrenderer = thread $ withSocketsDo $ do
- sock <- multicastReceiver (multicastAddress $ IPv4Addr undefined) pairingPort
- go sock [] []
+pairListenerThread st dstatus scanremotes urlrenderer = thread $ withSocketsDo $
+ runEvery (Seconds 1) $ void $ tryIO $ do
+ sock <- getsock
+ go sock [] []
where
thread = NamedThread thisThread
+ {- Note this can crash if there's no network interface,
+ - or only one like lo that doesn't support multicast. -}
+ getsock = multicastReceiver (multicastAddress $ IPv4Addr undefined) pairingPort
+
go sock reqs cache = getmsg sock [] >>= \msg -> case readish msg of
Nothing -> go sock reqs cache
Just m -> do
diff --git a/Assistant/Threads/Pusher.hs b/Assistant/Threads/Pusher.hs
index dee563d74..4f3a2dd09 100644
--- a/Assistant/Threads/Pusher.hs
+++ b/Assistant/Threads/Pusher.hs
@@ -52,7 +52,7 @@ pushThread st dstatus commitchan pushmap = thread $ runEvery (Seconds 2) $ do
now <- getCurrentTime
if shouldPush now commits
then do
- remotes <- filter pushable . knownRemotes
+ remotes <- filter pushable . syncRemotes
<$> getDaemonStatus dstatus
unless (null remotes) $
void $ alertWhile dstatus (pushAlert remotes) $
diff --git a/Assistant/Threads/SanityChecker.hs b/Assistant/Threads/SanityChecker.hs
index f01b63de3..912270090 100644
--- a/Assistant/Threads/SanityChecker.hs
+++ b/Assistant/Threads/SanityChecker.hs
@@ -74,7 +74,7 @@ oneDay = 24 * 60 * 60
- will block the watcher. -}
check :: ThreadState -> DaemonStatusHandle -> TransferQueue -> ChangeChan -> IO Bool
check st dstatus transferqueue changechan = do
- g <- runThreadState st $ fromRepo id
+ g <- runThreadState st gitRepo
-- Find old unstaged symlinks, and add them to git.
(unstaged, cleanup) <- Git.LsFiles.notInRepo False ["."] g
now <- getPOSIXTime
diff --git a/Assistant/Threads/TransferPoller.hs b/Assistant/Threads/TransferPoller.hs
index 10ed7dd31..afead63ec 100644
--- a/Assistant/Threads/TransferPoller.hs
+++ b/Assistant/Threads/TransferPoller.hs
@@ -24,7 +24,7 @@ thisThread = "TransferPoller"
- of each transfer is complete. -}
transferPollerThread :: ThreadState -> DaemonStatusHandle -> NamedThread
transferPollerThread st dstatus = thread $ do
- g <- runThreadState st $ fromRepo id
+ g <- runThreadState st gitRepo
tn <- newNotificationHandle =<<
transferNotifier <$> getDaemonStatus dstatus
forever $ do
diff --git a/Assistant/Threads/TransferScanner.hs b/Assistant/Threads/TransferScanner.hs
index cb02ed2f2..631c36b02 100644
--- a/Assistant/Threads/TransferScanner.hs
+++ b/Assistant/Threads/TransferScanner.hs
@@ -13,6 +13,7 @@ import Assistant.TransferQueue
import Assistant.ThreadedMonad
import Assistant.DaemonStatus
import Assistant.Alert
+import Assistant.Drop
import Logs.Transfer
import Logs.Location
import Logs.Web (webUUID)
@@ -22,6 +23,7 @@ import Utility.ThreadScheduler
import qualified Git.LsFiles as LsFiles
import Command
import Annex.Content
+import Annex.Wanted
import qualified Data.Set as S
@@ -60,7 +62,7 @@ transferScannerThread st dstatus scanremotes transferqueue = thread $ do
- lost.
-}
startupScan = addScanRemotes scanremotes True
- =<< knownRemotes <$> getDaemonStatus dstatus
+ =<< syncRemotes <$> getDaemonStatus dstatus
{- This is a cheap scan for failed transfers involving a remote. -}
failedTransferScan :: ThreadState -> DaemonStatusHandle -> TransferQueue -> Remote -> IO ()
@@ -86,16 +88,26 @@ failedTransferScan st dstatus transferqueue r = do
transferqueue dstatus (associatedFile info) t r
{- This is a expensive scan through the full git work tree, finding
- - files to download from or upload to any of the remotes.
- -
- - The scan is blocked when the transfer queue gets too large. -}
+ - files to transfer. The scan is blocked when the transfer queue gets
+ - too large.
+ -
+ - This also finds files that are present either here or on a remote
+ - but that are not preferred content, and drops them. Searching for files
+ - to drop is done concurrently with the scan for transfers.
+ -
+ - TODO: It would be better to first drop as much as we can, before
+ - transferring much, to minimise disk use.
+ -}
expensiveScan :: ThreadState -> DaemonStatusHandle -> TransferQueue -> [Remote] -> IO ()
expensiveScan st dstatus transferqueue rs = unless onlyweb $ do
liftIO $ debug thisThread ["starting scan of", show visiblers]
void $ alertWhile dstatus (scanAlert visiblers) $ do
- g <- runThreadState st $ fromRepo id
+ g <- runThreadState st gitRepo
(files, cleanup) <- LsFiles.inRepo [] g
- go files
+ forM_ files $ \f -> do
+ ts <- runThreadState st $
+ ifAnnexed f (findtransfers f) (return [])
+ mapM_ (enqueue f) ts
void cleanup
return True
liftIO $ debug thisThread ["finished scan of", show visiblers]
@@ -103,26 +115,32 @@ expensiveScan st dstatus transferqueue rs = unless onlyweb $ do
onlyweb = all (== webUUID) $ map Remote.uuid rs
visiblers = let rs' = filter (not . Remote.readonly) rs
in if null rs' then rs else rs'
- go [] = noop
- go (f:fs) = do
- mapM_ (enqueue f) =<< catMaybes <$> runThreadState st
- (ifAnnexed f findtransfers $ return [])
- go fs
enqueue f (r, t) = do
debug thisThread ["queuing", show t]
queueTransferWhenSmall transferqueue dstatus (Just f) t r
- findtransfers (key, _) = do
+ findtransfers f (key, _) = do
locs <- loggedLocations key
- let use a = return $ map (a key locs) rs
- ifM (inAnnex key)
- ( use $ check Upload False
- , use $ check Download True
- )
- check direction want key locs r
- | direction == Upload && Remote.readonly r = Nothing
- | (Remote.uuid r `elem` locs) == want = Just
- (r, Transfer direction (Remote.uuid r) key)
- | otherwise = Nothing
+ {- The syncable remotes may have changed since this
+ - scan began. -}
+ syncrs <- liftIO $ syncRemotes <$> getDaemonStatus dstatus
+ present <- inAnnex key
+
+ handleDrops' locs syncrs present key (Just f)
+
+ let slocs = S.fromList locs
+ let use a = return $ catMaybes $ map (a key slocs) syncrs
+ if present
+ then filterM (wantSend (Just f) . Remote.uuid . fst)
+ =<< use (genTransfer Upload False)
+ else ifM (wantGet $ Just f)
+ ( use (genTransfer Download True) , return [] )
+
+genTransfer :: Direction -> Bool -> Key -> S.Set UUID -> Remote -> Maybe (Remote, Transfer)
+genTransfer direction want key slocs r
+ | direction == Upload && Remote.readonly r = Nothing
+ | (S.member (Remote.uuid r) slocs) == want = Just
+ (r, Transfer direction (Remote.uuid r) key)
+ | otherwise = Nothing
remoteHas :: Remote -> Key -> Annex Bool
remoteHas r key = elem
diff --git a/Assistant/Threads/TransferWatcher.hs b/Assistant/Threads/TransferWatcher.hs
index 33f0dacbe..168ff2688 100644
--- a/Assistant/Threads/TransferWatcher.hs
+++ b/Assistant/Threads/TransferWatcher.hs
@@ -11,12 +11,15 @@ import Assistant.Common
import Assistant.ThreadedMonad
import Assistant.DaemonStatus
import Assistant.TransferQueue
+import Assistant.Drop
import Annex.Content
import Logs.Transfer
import Utility.DirWatcher
import Utility.Types.DirWatcher
import qualified Remote
+import Control.Concurrent
+
thisThread :: ThreadName
thisThread = "TransferWatcher"
@@ -24,7 +27,7 @@ thisThread = "TransferWatcher"
- and updates the DaemonStatus's map of ongoing transfers. -}
transferWatcherThread :: ThreadState -> DaemonStatusHandle -> TransferQueue -> NamedThread
transferWatcherThread st dstatus transferqueue = thread $ do
- g <- runThreadState st $ fromRepo id
+ g <- runThreadState st gitRepo
let dir = gitAnnexTransferDir g
createDirectoryIfMissing True dir
let hook a = Just $ runHandler st dstatus transferqueue a
@@ -67,8 +70,8 @@ onAdd st dstatus _ file _ = case parseTransferFile file of
[ "transfer starting:"
, show t
]
- r <- headMaybe . filter (sameuuid t) . knownRemotes
- <$> getDaemonStatus dstatus
+ r <- headMaybe . filter (sameuuid t)
+ <$> runThreadState st Remote.remoteList
updateTransferInfo dstatus t info
{ transferRemote = r }
sameuuid t r = Remote.uuid r == transferUUID t
@@ -103,15 +106,31 @@ onDel st dstatus transferqueue file _ = case parseTransferFile file of
]
minfo <- removeTransfer dstatus t
- {- Queue uploads of files we successfully downloaded,
- - spreading them out to other reachable remotes. -}
- case (minfo, transferDirection t) of
- (Just info, Download) -> runThreadState st $
- whenM (inAnnex $ transferKey t) $
- queueTransfersMatching
- (/= transferUUID t)
- Later transferqueue dstatus
- (transferKey t)
- (associatedFile info)
- Upload
- _ -> noop
+ void $ forkIO $ do
+ {- XXX race workaround delay. The location
+ - log needs to be updated before finishedTransfer
+ - runs. -}
+ threadDelay 10000000 -- 10 seconds
+ finishedTransfer st dstatus transferqueue t minfo
+
+{- Queue uploads of files we successfully downloaded, spreading them
+ - out to other reachable remotes.
+ -
+ - Downloading a file may have caused a remote to not want it;
+ - so drop it from the remote.
+ -
+ - Uploading a file may cause the local repo, or some other remote to not
+ - want it; handle that too.
+ -}
+finishedTransfer :: ThreadState -> DaemonStatusHandle -> TransferQueue -> Transfer -> Maybe TransferInfo -> IO ()
+finishedTransfer st dstatus transferqueue t (Just info)
+ | transferDirection t == Download = runThreadState st $
+ whenM (inAnnex $ transferKey t) $ do
+ handleDrops dstatus False
+ (transferKey t) (associatedFile info)
+ queueTransfersMatching (/= transferUUID t)
+ Later transferqueue dstatus
+ (transferKey t) (associatedFile info) Upload
+ | otherwise = runThreadState st $
+ handleDrops dstatus True (transferKey t) (associatedFile info)
+finishedTransfer _ _ _ _ _ = noop
diff --git a/Assistant/Threads/Watcher.hs b/Assistant/Threads/Watcher.hs
index 1bf9e8581..310a6e984 100644
--- a/Assistant/Threads/Watcher.hs
+++ b/Assistant/Threads/Watcher.hs
@@ -20,6 +20,7 @@ import Assistant.DaemonStatus
import Assistant.Changes
import Assistant.TransferQueue
import Assistant.Alert
+import Assistant.Drop
import Logs.Transfer
import Utility.DirWatcher
import Utility.Types.DirWatcher
@@ -135,6 +136,7 @@ onAddSymlink threadname file filestatus dstatus transferqueue = go =<< Backend.l
liftIO $ debug threadname ["fix symlink", file]
liftIO $ removeFile file
liftIO $ createSymbolicLink link file
+ checkcontent key =<< liftIO (getDaemonStatus dstatus)
addlink link
)
go Nothing = do -- other symlink
@@ -146,7 +148,7 @@ onAddSymlink threadname file filestatus dstatus transferqueue = go =<< Backend.l
- and being re-added, or added when the watcher was
- not running. So they're normally restaged to make sure.
-
- - As an optimisation, during the status scan, avoid
+ - As an optimisation, during the startup scan, avoid
- restaging everything. Only links that were created since
- the last time the daemon was running are staged.
- (If the daemon has never ran before, avoid staging
@@ -174,12 +176,16 @@ onAddSymlink threadname file filestatus dstatus transferqueue = go =<< Backend.l
stageSymlink file sha
madeChange file LinkChange
- {- When a new link appears, after the startup scan,
- - try to get the key's content. -}
+ {- When a new link appears, or a link is changed,
+ - after the startup scan, handle getting or
+ - dropping the key's content. -}
checkcontent key daemonstatus
- | scanComplete daemonstatus = unlessM (inAnnex key) $
- queueTransfers Next transferqueue dstatus
- key (Just file) Download
+ | scanComplete daemonstatus = do
+ present <- inAnnex key
+ unless present $
+ queueTransfers Next transferqueue dstatus
+ key (Just file) Download
+ handleDrops dstatus present key (Just file)
| otherwise = noop
onDel :: Handler
diff --git a/Assistant/Threads/WebApp.hs b/Assistant/Threads/WebApp.hs
index cb5f58b2d..c33dc2103 100644
--- a/Assistant/Threads/WebApp.hs
+++ b/Assistant/Threads/WebApp.hs
@@ -5,7 +5,7 @@
- Licensed under the GNU GPL version 3 or higher.
-}
-{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes #-}
+{-# LANGUAGE TypeFamilies, QuasiQuotes, MultiParamTypeClasses, TemplateHaskell, OverloadedStrings, RankNTypes, CPP #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Assistant.Threads.WebApp where
@@ -17,10 +17,13 @@ import Assistant.WebApp.DashBoard
import Assistant.WebApp.SideBar
import Assistant.WebApp.Notifications
import Assistant.WebApp.Configurators
+import Assistant.WebApp.Configurators.Edit
import Assistant.WebApp.Configurators.Local
import Assistant.WebApp.Configurators.Ssh
import Assistant.WebApp.Configurators.Pairing
+#ifdef WITH_S3
import Assistant.WebApp.Configurators.S3
+#endif
import Assistant.WebApp.Documentation
import Assistant.WebApp.OtherRepos
import Assistant.ThreadedMonad
diff --git a/Assistant/TransferQueue.hs b/Assistant/TransferQueue.hs
index e2c3f167b..125b6d164 100644
--- a/Assistant/TransferQueue.hs
+++ b/Assistant/TransferQueue.hs
@@ -27,6 +27,7 @@ import Logs.Transfer
import Types.Remote
import qualified Remote
import qualified Types.Remote as Remote
+import Annex.Wanted
import Control.Concurrent.STM
import qualified Data.Map as M
@@ -56,22 +57,26 @@ stubInfo f r = stubTransferInfo
, associatedFile = f
}
-{- Adds transfers to queue for some of the known remotes. -}
+{- Adds transfers to queue for some of the known remotes.
+ - Honors preferred content settings, only transferring wanted files. -}
queueTransfers :: Schedule -> TransferQueue -> DaemonStatusHandle -> Key -> AssociatedFile -> Direction -> Annex ()
queueTransfers = queueTransfersMatching (const True)
{- Adds transfers to queue for some of the known remotes, that match a
- - condition. -}
+ - condition. Honors preferred content settings. -}
queueTransfersMatching :: (UUID -> Bool) -> Schedule -> TransferQueue -> DaemonStatusHandle -> Key -> AssociatedFile -> Direction -> Annex ()
-queueTransfersMatching matching schedule q dstatus k f direction = do
- rs <- sufficientremotes
- =<< knownRemotes <$> liftIO (getDaemonStatus dstatus)
- let matchingrs = filter (matching . Remote.uuid) rs
- if null matchingrs
- then defer
- else forM_ matchingrs $ \r -> liftIO $
- enqueue schedule q dstatus (gentransfer r) (stubInfo f r)
+queueTransfersMatching matching schedule q dstatus k f direction
+ | direction == Download = whenM (wantGet f) go
+ | otherwise = go
where
+ go = do
+ rs <- sufficientremotes
+ =<< syncRemotes <$> liftIO (getDaemonStatus dstatus)
+ let matchingrs = filter (matching . Remote.uuid) rs
+ if null matchingrs
+ then defer
+ else forM_ matchingrs $ \r -> liftIO $
+ enqueue schedule q dstatus (gentransfer r) (stubInfo f r)
sufficientremotes rs
{- Queue downloads from all remotes that
- have the key, with the cheapest ones first.
@@ -80,11 +85,9 @@ queueTransfersMatching matching schedule q dstatus k f direction = do
| direction == Download = do
uuids <- Remote.keyLocations k
return $ filter (\r -> uuid r `elem` uuids) rs
- {- TODO: Determine a smaller set of remotes that
- - can be uploaded to, in order to ensure all
- - remotes can access the content. Currently,
- - send to every remote we can. -}
- | otherwise = return $ filter (not . Remote.readonly) rs
+ {- Upload to all remotes that want the content. -}
+ | otherwise = filterM (wantSend f . Remote.uuid) $
+ filter (not . Remote.readonly) rs
gentransfer r = Transfer
{ transferDirection = direction
, transferKey = k
@@ -101,8 +104,8 @@ queueTransfersMatching matching schedule q dstatus k f direction = do
- any others in the list to try again later. -}
queueDeferredDownloads :: Schedule -> TransferQueue -> DaemonStatusHandle -> Annex ()
queueDeferredDownloads schedule q dstatus = do
- rs <- knownRemotes <$> liftIO (getDaemonStatus dstatus)
l <- liftIO $ atomically $ swapTVar (deferreddownloads q) []
+ rs <- syncRemotes <$> liftIO (getDaemonStatus dstatus)
left <- filterM (queue rs) l
unless (null left) $
liftIO $ atomically $ modifyTVar' (deferreddownloads q) $