initial try at using storeExportWithContentIdentifier

Untested, and I'm not sure about the locking of the ContentIdentifier db.
This commit is contained in:
Joey Hess 2019-03-04 17:50:41 -04:00
parent b67fa2180e
commit cd3a2b023a
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
6 changed files with 96 additions and 56 deletions

View file

@ -14,6 +14,7 @@ module Annex.LockFile (
fromLockCache, fromLockCache,
withSharedLock, withSharedLock,
withExclusiveLock, withExclusiveLock,
takeExclusiveLock,
tryExclusiveLock, tryExclusiveLock,
) where ) where
@ -77,11 +78,18 @@ withSharedLock getlockfile a = debugLocks $ do
{- Runs an action with an exclusive lock held. If the lock is already {- Runs an action with an exclusive lock held. If the lock is already
- held, blocks until it becomes free. -} - held, blocks until it becomes free. -}
withExclusiveLock :: (Git.Repo -> FilePath) -> Annex a -> Annex a withExclusiveLock :: (Git.Repo -> FilePath) -> Annex a -> Annex a
withExclusiveLock getlockfile a = debugLocks $ do withExclusiveLock getlockfile a = bracket
(takeExclusiveLock getlockfile)
(liftIO . dropLock)
(const a)
{- Takes an exclusive lock, blocking until it's free. -}
takeExclusiveLock :: (Git.Repo -> FilePath) -> Annex LockHandle
takeExclusiveLock getlockfile = debugLocks $ do
lockfile <- fromRepo getlockfile lockfile <- fromRepo getlockfile
createAnnexDirectory $ takeDirectory lockfile createAnnexDirectory $ takeDirectory lockfile
mode <- annexFileMode mode <- annexFileMode
bracket (lock mode lockfile) (liftIO . dropLock) (const a) lock mode lockfile
where where
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
lock mode = noUmask mode . lockExclusive (Just mode) lock mode = noUmask mode . lockExclusive (Just mode)

View file

@ -20,6 +20,7 @@ module Database.ContentIdentifier (
closeDb, closeDb,
flushDbQueue, flushDbQueue,
recordContentIdentifier, recordContentIdentifier,
getContentIdentifiers,
getContentIdentifierKeys, getContentIdentifierKeys,
ContentIdentifiersId, ContentIdentifiersId,
) where ) where
@ -82,11 +83,8 @@ recordContentIdentifier h u cid k = queueDb h $ do
getContentIdentifiers :: ContentIdentifierHandle -> UUID -> Key -> IO [ContentIdentifier] getContentIdentifiers :: ContentIdentifierHandle -> UUID -> Key -> IO [ContentIdentifier]
getContentIdentifiers (ContentIdentifierHandle h) u k = H.queryDbQueue h $ do getContentIdentifiers (ContentIdentifierHandle h) u k = H.queryDbQueue h $ do
l <- selectList l <- selectList [ContentIdentifiersKey ==. toSKey k] []
[ ContentIdentifiersCid ==. cid return $ map (contentIdentifiersCid . entityVal) l
, ContentIdentifiersKey ==. toSKey k
] []
return $ map (ContentIdentifiersCid . entityVal) l
getContentIdentifierKeys :: ContentIdentifierHandle -> UUID -> ContentIdentifier -> IO [Key] getContentIdentifierKeys :: ContentIdentifierHandle -> UUID -> ContentIdentifier -> IO [Key]
getContentIdentifierKeys (ContentIdentifierHandle h) u cid = getContentIdentifierKeys (ContentIdentifierHandle h) u cid =

View file

@ -163,9 +163,9 @@ getExportedLocation (ExportHandle h _) k = H.queryDbQueue h $ do
- Note that this does not see recently queued changes. - Note that this does not see recently queued changes.
-} -}
getExportedKey :: ExportHandle -> ExportLocation -> IO [Key] getExportedKey :: ExportHandle -> ExportLocation -> IO [Key]
getExportedKey ExportHandle h _) el = H.queryDbQueue h $ do getExportedKey (ExportHandle h _) el = H.queryDbQueue h $ do
l <- selectList [ExportedFile ==. ef] [] l <- selectList [ExportedFile ==. ef] []
return $ map (fromSKey . exportedKey . entityVal) l return $ map (fromIKey . exportedKey . entityVal) l
where where
ef = toSFilePath (fromExportLocation el) ef = toSFilePath (fromExportLocation el)

View file

@ -5,7 +5,7 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
module Remote.Helper.ExportImport where module Remote.Helper.ExportImport where
@ -15,9 +15,11 @@ import Types.Backend
import Types.Key import Types.Key
import Backend import Backend
import Remote.Helper.Encryptable (isEncrypted) import Remote.Helper.Encryptable (isEncrypted)
import Database.Export import qualified Database.Export as Export
import qualified Database.ContentIdentifier as ContentIdentifier
import Annex.Export import Annex.Export
import Annex.Import import Annex.Import
import Annex.LockFile
import Config import Config
import Git.Types (fromRef) import Git.Types (fromRef)
import Logs.Export import Logs.Export
@ -91,51 +93,81 @@ adjustExportImportRemoteType rt = rt { setup = setup' }
then giveup "cannot enable importtree=yes without also enabling exporttree=yes" then giveup "cannot enable importtree=yes without also enabling exporttree=yes"
else setup rt st mu cp c gc else setup rt st mu cp c gc
-- | If the remote is importSupported, and importtree=yes, adjust the -- | Adjust a remote to support exporttree=yes and importree=yes.
-- remote to be an import.
-- --
-- (This relies on all import remotes also being export remotes, -- Note that all remotes with importree=yes also have exporttree=yes.
-- so adjustExportable will adjust the remote actions to use the adjustExportImport :: Remote -> Annex Remote
-- exported/imported tree.) adjustExportImport r = case M.lookup "exporttree" (config r) of
adjustImportable :: Remote -> Annex Remote Nothing -> return $ notexport r
adjustImportable r Just c -> case yesNo c of
| importTree (config r) = Just True -> ifM (isExportSupported r)
ifM (isExportSupported r) ( do
( return r exportdb <- Export.openDb (uuid r)
, notimport r' <- isexport exportdb
if importTree (config r)
then isimport r' exportdb
else return r'
, return $ notexport r
) )
| otherwise = notimport Just False -> return $ notexport r
Nothing -> do
warning $ "bad exporttree value for " ++ name r ++ ", assuming not an export"
return $ notexport r
where where
notimport = return $ r notexport r' = notimport r'
{ exportActions = exportUnsupported
, remotetype = (remotetype r')
{ exportSupported = exportUnsupported
}
}
notimport r' = r'
{ importActions = importUnsupported { importActions = importUnsupported
, remotetype = (remotetype r) , remotetype = (remotetype r')
{ importSupported = importUnsupported { importSupported = importUnsupported
} }
} }
-- | If the remote is exportSupported, and exporttree=yes, adjust the isimport r' exportdb = do
-- remote to be an export. lcklckv <- liftIO newEmptyTMVarIO
adjustExportable :: Remote -> Annex Remote dbtv <- liftIO newEmptyTMVarIO
adjustExportable r = case M.lookup "exporttree" (config r) of let store f k loc p = do
Nothing -> notexport -- Only open the database once it's needed, since
Just c -> case yesNo c of -- we have to take an exclusive write lock.
Just True -> ifM (isExportSupported r) -- The write lock will remain held while the
( isexport -- process is running.
, notexport db <- liftIO (atomically (tryReadTMVar dbtv)) >>= \case
Just (db, _lck) -> return db
-- let only one thread take the lock
Nothing -> ifM (liftIO $ atomically $ tryPutTMVar lcklckv ())
( do
lck <- takeExclusiveLock gitAnnexContentIdentifierLock
db <- ContentIdentifier.openDb
liftIO $ atomically (putTMVar dbtv (db, lck))
return db
-- loser waits for winner to open
-- the db and can then also use its
-- handle
, liftIO $ fst <$> atomically (readTMVar dbtv)
) )
Just False -> notexport
Nothing -> do ks <- liftIO $ Export.getExportedKey exportdb loc
warning $ "bad exporttree value for " ++ name r ++ ", assuming not an export" oldcids <- liftIO $ concat
notexport <$> mapM (ContentIdentifier.getContentIdentifiers db (uuid r')) ks
where storeExportWithContentIdentifier (importActions r') f k loc oldcids p >>= \case
notexport = return $ r Nothing -> return False
{ exportActions = exportUnsupported Just newcid -> do
, remotetype = (remotetype r) liftIO $ ContentIdentifier.recordContentIdentifier db (uuid r') newcid k
{ exportSupported = exportUnsupported -- TODO update git-annex branch
return True
return $ r'
{ exportActions = (exportActions r')
{ storeExport = store
} }
} }
isexport = do
db <- openDb (uuid r) isexport db = do
updateflag <- liftIO $ newTVarIO Nothing updateflag <- liftIO $ newTVarIO Nothing
-- When multiple threads run this, all except the first -- When multiple threads run this, all except the first
@ -163,13 +195,13 @@ adjustExportable r = case M.lookup "exporttree" (config r) of
let getexportlocs = \k -> do let getexportlocs = \k -> do
bracket startupdateonce doneupdateonce $ \updatenow -> bracket startupdateonce doneupdateonce $ \updatenow ->
when updatenow $ when updatenow $
updateExportTreeFromLog db >>= \case Export.updateExportTreeFromLog db >>= \case
ExportUpdateSuccess -> return () Export.ExportUpdateSuccess -> return ()
ExportUpdateConflict -> do Export.ExportUpdateConflict -> do
warnExportConflict r warnExportConflict r
liftIO $ atomically $ liftIO $ atomically $
writeTVar exportinconflict True writeTVar exportinconflict True
liftIO $ getExportTree db k liftIO $ Export.getExportTree db k
return $ r return $ r
-- Storing a key on an export could be implemented, -- Storing a key on an export could be implemented,

View file

@ -106,10 +106,7 @@ remoteGen m t g = do
let c = fromMaybe M.empty $ M.lookup u m let c = fromMaybe M.empty $ M.lookup u m
generate t g u c gc >>= \case generate t g u c gc >>= \case
Nothing -> return Nothing Nothing -> return Nothing
Just r -> do Just r -> Just <$> adjustExportImport (adjustReadOnly (addHooks r))
r' <- adjustImportable (adjustReadOnly (addHooks r))
r'' <- adjustExportable r'
return $ Just r''
{- Updates a local git Remote, re-reading its git config. -} {- Updates a local git Remote, re-reading its git config. -}
updateRemote :: Remote -> Annex (Maybe Remote) updateRemote :: Remote -> Annex (Maybe Remote)

View file

@ -10,12 +10,17 @@ this.
## implementation notes ## implementation notes
* Should the ContentIdentifier db be multiwriter? It would simplify
the situation with the long-lived lock of it in adjustExportImport
* fix TODO in adjustExportImport
* Test export to importtree=yes remote and make sure it uses
storeExportWithContentIdentifier correctly.
* Need to support annex-tracking-branch configuration, which documentation * Need to support annex-tracking-branch configuration, which documentation
says makes git-annex sync and assistant do imports. says makes git-annex sync and assistant do imports.
* export needs to use storeExportWithContentIdentifierM for importtree=yes
remotes
* Database.ContentIdentifier needs a way to update the database with * Database.ContentIdentifier needs a way to update the database with
information coming from the git-annex branch. This will allow multiple information coming from the git-annex branch. This will allow multiple
clones to import from the same remote, and share content identifier clones to import from the same remote, and share content identifier