use InodeCache when dropping a key to see if a pointer file can be safely reset

The Keys database can hold multiple inode caches for a given key. One for
the annex object, and one for each pointer file, which may not be hard
linked to it.

Inode caches for a key are recorded when its content is added to the annex,
but only if it has known pointer files. This is to avoid the overhead of
maintaining the database when not needed.

When the smudge filter outputs a file's content, the inode cache is not
updated, because git's smudge interface doesn't let us write the file. So,
dropping will fall back to doing an expensive verification then. Ideally,
git's interface would be improved, and then the inode cache could be
updated then too.
This commit is contained in:
Joey Hess 2015-12-09 17:47:05 -04:00
parent 5e8c628d2e
commit ce73a96e4e
Failed to extract signature
6 changed files with 62 additions and 46 deletions

View file

@ -13,15 +13,17 @@
module Database.Keys (
DbHandle,
openDb,
flushDb,
closeDb,
shutdown,
addAssociatedFile,
getAssociatedFiles,
removeAssociatedFile,
setInodeCache,
getInodeCache,
storeInodeCaches,
addInodeCaches,
getInodeCaches,
removeInodeCaches,
AssociatedId,
DataId,
ContentId,
) where
import Database.Types
@ -35,6 +37,7 @@ import Annex.Perms
import Annex.LockFile
import Messages
import Utility.InodeCache
import Annex.InodeSentinal
import Database.Persist.TH
import Database.Esqueleto hiding (Key)
@ -44,10 +47,10 @@ Associated
key SKey
file FilePath
KeyFileIndex key file
Data
Content
key SKey
inodeCache SInodeCache
KeyIndex key
cache SInodeCache
KeyCacheIndex key cache
|]
{- Opens the database, creating it if it doesn't exist yet. -}
@ -62,7 +65,7 @@ openDb = withExclusiveLock gitAnnexKeysDbLock $ do
runMigrationSilent migrateKeysDb
setAnnexDirPerm dbdir
setAnnexFilePerm db
h <- liftIO $ H.openDb db "data"
h <- liftIO $ H.openDb db "content"
-- work around https://github.com/yesodweb/persistent/issues/474
liftIO setConsoleEncoding
@ -85,9 +88,12 @@ dbHandle = maybe startup return =<< Annex.getState Annex.keysdbhandle
Annex.changeState $ \s -> s { Annex.keysdbhandle = Just h }
return h
{- Flushes any changes made to the database. -}
flushDb :: Annex ()
flushDb = withDbHandle H.flushQueueDb
shutdown :: Annex ()
shutdown = maybe noop go =<< Annex.getState Annex.keysdbhandle
where
go h = do
Annex.changeState $ \s -> s { Annex.keysdbhandle = Nothing }
liftIO $ closeDb h
addAssociatedFile :: Key -> FilePath -> Annex ()
addAssociatedFile k f = withDbHandle $ \h -> H.queueDb h (\_ _ -> pure True) $ do
@ -113,21 +119,35 @@ getAssociatedFiles' sk = do
return $ map unValue l
removeAssociatedFile :: Key -> FilePath -> Annex ()
removeAssociatedFile k f = withDbHandle $ \h -> H.queueDb h (\_ _ -> pure True) $
removeAssociatedFile k f = withDbHandle $ \h -> H.queueDb h (\_ _ -> pure True) $
delete $ from $ \r -> do
where_ (r ^. AssociatedKey ==. val sk &&. r ^. AssociatedFile ==. val f)
where
sk = toSKey k
setInodeCache :: Key -> InodeCache -> Annex ()
setInodeCache k i = withDbHandle $ \h -> H.queueDb h (\_ _ -> pure True) $
void $ upsert (Data (toSKey k) (toSInodeCache i)) []
{- Stats the files, and stores their InodeCaches. -}
storeInodeCaches :: Key -> [FilePath] -> Annex ()
storeInodeCaches k fs = withTSDelta $ \d ->
addInodeCaches k . catMaybes =<< liftIO (mapM (`genInodeCache` d) fs)
getInodeCache :: Key -> Annex (Maybe (InodeCache))
getInodeCache k = withDbHandle $ \h -> H.queryDb h $ do
addInodeCaches :: Key -> [InodeCache] -> Annex ()
addInodeCaches k is = withDbHandle $ \h -> H.queueDb h (\_ _ -> pure True) $
forM_ is $ \i -> insertUnique $ Content (toSKey k) (toSInodeCache i)
{- A key may have multiple InodeCaches; one for the annex object, and one
- for each pointer file that is a copy of it. -}
getInodeCaches :: Key -> Annex [InodeCache]
getInodeCaches k = withDbHandle $ \h -> H.queryDb h $ do
l <- select $ from $ \r -> do
where_ (r ^. DataKey ==. val sk)
return (r ^. DataInodeCache)
return $ headMaybe $ map (fromSInodeCache. unValue) l
where_ (r ^. ContentKey ==. val sk)
return (r ^. ContentCache)
return $ map (fromSInodeCache. unValue) l
where
sk = toSKey k
removeInodeCaches :: Key -> Annex ()
removeInodeCaches k = withDbHandle $ \h -> H.queueDb h (\_ _ -> pure True) $
delete $ from $ \r -> do
where_ (r ^. ContentKey ==. val sk)
where
sk = toSKey k