extend manifest with keys to be deleted

This will eventually be used to recover from an interrupted fullPush
and drop the old bundle keys it was unable to delete.

Sponsored-by: Luke T. Shumaker on Patreon
This commit is contained in:
Joey Hess 2024-05-13 09:03:43 -04:00
parent 1f62bc861a
commit 97b309b56e
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
3 changed files with 31 additions and 7 deletions

View file

@ -40,6 +40,7 @@ import Utility.Env
import Utility.Metered import Utility.Metered
import Network.URI import Network.URI
import Data.Either
import qualified Data.ByteString as B import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Char8 as B8
import qualified Data.Map.Strict as M import qualified Data.Map.Strict as M
@ -265,7 +266,7 @@ fullPush st rmt refs = guardPush st $ do
oldmanifest <- maybe (downloadManifest rmt) pure (manifestCache st) oldmanifest <- maybe (downloadManifest rmt) pure (manifestCache st)
let bs = map Git.Bundle.fullBundleSpec refs let bs = map Git.Bundle.fullBundleSpec refs
bundlekey <- generateAndUploadGitBundle rmt bs oldmanifest bundlekey <- generateAndUploadGitBundle rmt bs oldmanifest
uploadManifest rmt (Manifest [bundlekey]) uploadManifest rmt (Manifest [bundlekey] [])
ok <- allM (dropKey rmt) $ ok <- allM (dropKey rmt) $
filter (/= bundlekey) (inManifest oldmanifest) filter (/= bundlekey) (inManifest oldmanifest)
return (ok, st { manifestCache = Nothing }) return (ok, st { manifestCache = Nothing })
@ -285,7 +286,7 @@ incrementalPush st rmt oldtrackingrefs newtrackingrefs = guardPush st $ do
bs <- calc [] (M.toList newtrackingrefs) bs <- calc [] (M.toList newtrackingrefs)
oldmanifest <- maybe (downloadManifest rmt) pure (manifestCache st) oldmanifest <- maybe (downloadManifest rmt) pure (manifestCache st)
bundlekey <- generateAndUploadGitBundle rmt bs oldmanifest bundlekey <- generateAndUploadGitBundle rmt bs oldmanifest
uploadManifest rmt (Manifest [bundlekey]) uploadManifest rmt (Manifest [bundlekey] [])
return (True, st { manifestCache = Nothing }) return (True, st { manifestCache = Nothing })
where where
calc c [] = return (reverse c) calc c [] = return (reverse c)
@ -355,7 +356,7 @@ incrementalPush st rmt oldtrackingrefs newtrackingrefs = guardPush st $ do
pushEmpty :: State -> Remote -> Annex (Bool, State) pushEmpty :: State -> Remote -> Annex (Bool, State)
pushEmpty st rmt = do pushEmpty st rmt = do
manifest <- maybe (downloadManifest rmt) pure (manifestCache st) manifest <- maybe (downloadManifest rmt) pure (manifestCache st)
uploadManifest rmt (Manifest []) uploadManifest rmt (Manifest [] [])
ok <- allM (dropKey rmt) ok <- allM (dropKey rmt)
(genManifestKey (Remote.uuid rmt) : inManifest manifest) (genManifestKey (Remote.uuid rmt) : inManifest manifest)
return (ok, st { manifestCache = Nothing }) return (ok, st { manifestCache = Nothing })
@ -533,7 +534,14 @@ checkSpecialRemoteProblems rmt
| otherwise = Nothing | otherwise = Nothing
-- The manifest contains an ordered list of git bundle keys. -- The manifest contains an ordered list of git bundle keys.
newtype Manifest = Manifest { inManifest :: [Key] } --
-- There is a second list of git bundle keys that are no longer
-- used and should be deleted.
data Manifest =
Manifest
{ inManifest :: [Key]
, outManifest :: [Key]
}
-- Downloads the Manifest, or if it does not exist, returns an empty -- Downloads the Manifest, or if it does not exist, returns an empty
-- Manifest. -- Manifest.
@ -551,9 +559,12 @@ downloadManifest rmt = ifM (Remote.checkPresent rmt mk)
_ <- Remote.retrieveKeyFile rmt mk _ <- Remote.retrieveKeyFile rmt mk
(AssociatedFile Nothing) tmp (AssociatedFile Nothing) tmp
nullMeterUpdate Remote.NoVerify nullMeterUpdate Remote.NoVerify
ks <- map deserializeKey' . B8.lines <$> liftIO (B.readFile tmp) (outks, inks) <- partitionEithers . map parseline . B8.lines
Manifest <$> checkvalid [] ks <$> liftIO (B.readFile tmp)
, return (Manifest []) Manifest
<$> checkvalid [] inks
<*> checkvalid [] (filter (`notElem` inks) outks)
, return (Manifest [] [])
) )
where where
mk = genManifestKey (Remote.uuid rmt) mk = genManifestKey (Remote.uuid rmt)
@ -565,6 +576,12 @@ downloadManifest rmt = ifM (Remote.checkPresent rmt mk)
checkvalid _ (Nothing:_) = checkvalid _ (Nothing:_) =
giveup $ "Error parsing manifest " ++ serializeKey mk giveup $ "Error parsing manifest " ++ serializeKey mk
parseline l
| "-" `B.isPrefixOf` l =
Left $ deserializeKey' $ B.drop 1 l
| otherwise =
Right $ deserializeKey' l
-- Uploads the Manifest to the remote. -- Uploads the Manifest to the remote.
-- --
-- Throws errors if the remote cannot be accessed or the upload fails. -- Throws errors if the remote cannot be accessed or the upload fails.

View file

@ -12,6 +12,10 @@ GITBUNDLE--$UUID-sha256 is a git bundle.
An ordered list of bundle keys, one per line. An ordered list of bundle keys, one per line.
Additionally, there may be bundle keys that are prefixed with "-".
These keys are not part of the current content of the git remote
and are in the process of being deleted.
(Lines end with unix `"\n"`, not `"\r\n"`.) (Lines end with unix `"\n"`, not `"\r\n"`.)
# multiple GITMANIFEST files # multiple GITMANIFEST files

View file

@ -33,6 +33,9 @@ This is implememented and working. Remaining todo list for it:
to clone from a wrong url, since it fails to download to clone from a wrong url, since it fails to download
a manifest and so appears as if the remote is empty. a manifest and so appears as if the remote is empty.
* Improve recovery from interrupted push by using outManifest to clean up
after it. (Requires populating outManifest.)
* See XXX in uploadManifest about recovering from a situation * See XXX in uploadManifest about recovering from a situation
where the remote is left with a deleted manifest when a push where the remote is left with a deleted manifest when a push
is interrupted part way through. This should be recoverable is interrupted part way through. This should be recoverable