Fix stalls in S3 when transferring encrypted data.

Stalls were caused by code that did approximatly:

content' <- liftIO $ withEncryptedContent cipher content return
store content'

The return evaluated without actually reading content from S3,
and so the cleanup code began waiting on gpg to exit before
gpg could send all its data.

Fixing it involved moving the `store` type action into the IO monad:

liftIO $ withEncryptedContent cipher content store

Which was a bit of a pain to do, thank you type system, but
avoids the problem as now the whole content is consumed, and
stored, before cleanup.
This commit is contained in:
Joey Hess 2011-04-19 14:45:19 -04:00
parent 1687fecd33
commit a441e08da1
4 changed files with 50 additions and 34 deletions

View file

@ -199,7 +199,10 @@ gpgPipeStrict params input = do
return output return output
{- Runs gpg with a cipher and some parameters, feeding it an input, {- Runs gpg with a cipher and some parameters, feeding it an input,
- and passing a handle to its output to an action. -} - and passing a handle to its output to an action.
-
- Note that to avoid deadlock with the cleanup stage,
- the action must fully consume gpg's input before returning. -}
gpgCipherHandle :: [CommandParam] -> Cipher -> L.ByteString -> (Handle -> IO a) -> IO a gpgCipherHandle :: [CommandParam] -> Cipher -> L.ByteString -> (Handle -> IO a) -> IO a
gpgCipherHandle params c input a = do gpgCipherHandle params c input a = do
-- pipe the passphrase into gpg on a fd -- pipe the passphrase into gpg on a fd

View file

@ -100,13 +100,13 @@ s3Setup u c = do
loc <- liftIO $ getBucketLocation conn bucket loc <- liftIO $ getBucketLocation conn bucket
case loc of case loc of
Right _ -> return () Right _ -> return ()
Left err@(NetworkError _) -> error $ prettyReqError err Left err@(NetworkError _) -> s3Error err
Left (AWSError _ _) -> do Left (AWSError _ _) -> do
showNote $ "creating bucket in " ++ datacenter showNote $ "creating bucket in " ++ datacenter
res <- liftIO $ createBucketIn conn bucket datacenter res <- liftIO $ createBucketIn conn bucket datacenter
case res of case res of
Right _ -> return () Right _ -> return ()
Left err -> error $ prettyReqError err Left err -> s3Error err
gitConfigSpecialRemote u fullconfig "s3" "true" gitConfigSpecialRemote u fullconfig "s3" "true"
return fullconfig return fullconfig
@ -141,33 +141,32 @@ checkPresent r k = s3Action r noconn $ \(conn, bucket) -> do
case res of case res of
Right _ -> return $ Right True Right _ -> return $ Right True
Left (AWSError _ _) -> return $ Right False Left (AWSError _ _) -> return $ Right False
Left e -> return $ Left (error $ prettyReqError e) Left e -> return $ Left (s3Error e)
where where
noconn = Left $ error "S3 not configured" noconn = Left $ error "S3 not configured"
store :: Remote Annex -> Key -> Annex Bool store :: Remote Annex -> Key -> Annex Bool
store r k = storeHelper r k =<< lazyKeyContent k store r k = s3Action r False $ \(conn, bucket) -> do
content <- lazyKeyContent k
res <- liftIO $ storeHelper (conn, bucket) r k content
s3Bool res
storeEncrypted :: Remote Annex -> (Cipher, Key) -> Key -> Annex Bool storeEncrypted :: Remote Annex -> (Cipher, Key) -> Key -> Annex Bool
storeEncrypted r (cipher, enck) k = do storeEncrypted r (cipher, enck) k = s3Action r False $ \(conn, bucket) -> do
content <- lazyKeyContent k content <- lazyKeyContent k
content' <- liftIO $ withEncryptedContent cipher content return res <- liftIO $ withEncryptedContent cipher content $ \s -> do
storeHelper r enck content' storeHelper (conn, bucket) r enck s
s3Bool res
lazyKeyContent :: Key -> Annex L.ByteString lazyKeyContent :: Key -> Annex L.ByteString
lazyKeyContent k = do lazyKeyContent k = do
g <- Annex.gitRepo g <- Annex.gitRepo
liftIO $ L.readFile $ gitAnnexLocation g k liftIO $ L.readFile $ gitAnnexLocation g k
storeHelper :: Remote Annex -> Key -> L.ByteString -> Annex Bool storeHelper :: (AWSConnection, String) -> Remote Annex -> Key -> L.ByteString -> IO (AWSResult ())
storeHelper r k content = s3Action r False $ \(conn, bucket) -> do storeHelper (conn, bucket) r k content = do
let object = setStorageClass storageclass $ bucketKey bucket k content let object = setStorageClass storageclass $ bucketKey bucket k content
res <- liftIO $ sendObject conn object sendObject conn object
case res of
Right _ -> return True
Left e -> do
warning $ prettyReqError e
return False
where where
storageclass = storageclass =
case fromJust $ M.lookup "storageclass" $ fromJust $ config r of case fromJust $ M.lookup "storageclass" $ fromJust $ config r of
@ -175,30 +174,41 @@ storeHelper r k content = s3Action r False $ \(conn, bucket) -> do
_ -> STANDARD _ -> STANDARD
retrieve :: Remote Annex -> Key -> FilePath -> Annex Bool retrieve :: Remote Annex -> Key -> FilePath -> Annex Bool
retrieve = retrieveHelper (return . obj_data) retrieve r k f = s3Action r False $ \(conn, bucket) -> do
retrieveEncrypted :: Remote Annex -> (Cipher, Key) -> FilePath -> Annex Bool
retrieveEncrypted r (cipher, enck) f = retrieveHelper decrypt r enck f
where
decrypt o = withDecryptedContent cipher (obj_data o) return
retrieveHelper :: (S3Object -> IO L.ByteString) -> Remote Annex -> Key -> FilePath -> Annex Bool
retrieveHelper a r k f = s3Action r False $ \(conn, bucket) -> do
res <- liftIO $ getObject conn $ bucketKey bucket k L.empty res <- liftIO $ getObject conn $ bucketKey bucket k L.empty
case res of case res of
Right o -> do Right o -> do
content <- liftIO $ a o liftIO $ L.writeFile f $ obj_data o
liftIO $ L.writeFile f content
return True return True
Left e -> do Left e -> s3Warning e
warning $ prettyReqError e
return False retrieveEncrypted :: Remote Annex -> (Cipher, Key) -> FilePath -> Annex Bool
retrieveEncrypted r (cipher, enck) f = s3Action r False $ \(conn, bucket) -> do
res <- liftIO $ getObject conn $ bucketKey bucket enck L.empty
case res of
Right o -> liftIO $
withDecryptedContent cipher (obj_data o) $ \content -> do
L.writeFile f content
return True
Left e -> s3Warning e
remove :: Remote Annex -> Key -> Annex Bool remove :: Remote Annex -> Key -> Annex Bool
remove r k = s3Action r False $ \(conn, bucket) -> do remove r k = s3Action r False $ \(conn, bucket) -> do
res <- liftIO $ deleteObject conn $ bucketKey bucket k L.empty res <- liftIO $ deleteObject conn $ bucketKey bucket k L.empty
case res of case res of
Right _ -> return True Right _ -> return True
Left e -> do Left e -> s3Warning e
warning $ prettyReqError e
return False s3Warning :: ReqError -> Annex Bool
s3Warning e = do
warning $ prettyReqError e
return False
s3Error :: ReqError -> a
s3Error e = error $ prettyReqError e
s3Bool :: AWSResult () -> Annex Bool
s3Bool res = do
case res of
Right _ -> return True
Left e -> s3Warning e

1
debian/changelog vendored
View file

@ -3,6 +3,7 @@ git-annex (0.20110418) UNRELEASED; urgency=low
* Don't run gpg in batch mode, so it can prompt for passphrase when * Don't run gpg in batch mode, so it can prompt for passphrase when
there is no agent. there is no agent.
* Add missing build dep on dataenc. * Add missing build dep on dataenc.
* Fix stalls in S3 when transferring encrypted data.
-- Joey Hess <joeyh@debian.org> Sun, 17 Apr 2011 14:29:49 -0400 -- Joey Hess <joeyh@debian.org> Sun, 17 Apr 2011 14:29:49 -0400

View file

@ -5,3 +5,5 @@ dialup.
There was a similar issue with bup, which I fixed by forking a process There was a similar issue with bup, which I fixed by forking a process
rather than using a thread to do some IO. Probably need the same here. rather than using a thread to do some IO. Probably need the same here.
--[[Joey]] --[[Joey]]
[[done]] --[[Joey]]