changeset 23067:420a051616ce stable

bundle2: transmit exception during part generation If an exception is raised during a bundle2 part payload generation it is now recorded in the bundle. If such exception occurs, we capture it, transmit an abort exception through the bundle, cleanly close the current part payload and raise it again. This allow to generate valid bundle even in case of exception so that the consumer does not wait forever for a dead producer. This also allow to raise the exception during unbundling at the exact point it happened during bundling make debugging easier.
author Pierre-Yves David <pierre-yves.david@fb.com>
date Wed, 15 Oct 2014 03:52:20 -0700
parents ad144882318d
children fb3e63c603e8
files mercurial/bundle2.py tests/test-bundle2-format.t
diffstat 2 files changed, 19 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/bundle2.py	Tue Oct 14 10:47:47 2014 -0700
+++ b/mercurial/bundle2.py	Wed Oct 15 03:52:20 2014 -0700
@@ -145,6 +145,7 @@
 preserve.
 """
 
+import sys
 import util
 import struct
 import urllib
@@ -673,9 +674,22 @@
         yield _pack(_fpartheadersize, len(headerchunk))
         yield headerchunk
         ## payload
-        for chunk in self._payloadchunks():
-            yield _pack(_fpayloadsize, len(chunk))
-            yield chunk
+        try:
+            for chunk in self._payloadchunks():
+                yield _pack(_fpayloadsize, len(chunk))
+                yield chunk
+        except Exception, exc:
+            # backup exception data for later
+            exc_info = sys.exc_info()
+            msg = 'unexpected error: %s' % exc
+            interpart = bundlepart('b2x:error:abort', [('message', msg)])
+            interpart.id = 0
+            yield _pack(_fpayloadsize, -1)
+            for chunk in interpart.getchunks():
+                yield chunk
+            # abort current part payload
+            yield _pack(_fpayloadsize, 0)
+            raise exc_info[0], exc_info[1], exc_info[2]
         # end of payload
         yield _pack(_fpayloadsize, 0)
         self._generated = True
--- a/tests/test-bundle2-format.t	Tue Oct 14 10:47:47 2014 -0700
+++ b/tests/test-bundle2-format.t	Wed Oct 15 03:52:20 2014 -0700
@@ -777,25 +777,22 @@
 
 Check handling of exception during generation.
 ----------------------------------------------
-(is currently not right)
 
   $ hg bundle2 --genraise > ../genfailed.hg2
   abort: Someone set up us the bomb!
   [255]
 
 Should still be a valid bundle
-(is currently not right)
 
   $ cat ../genfailed.hg2
   HG2Y\x00\x00\x00\x00\x00\x00\x00\x11 (esc)
-  b2x:output\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
+  b2x:output\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00L\x0fb2x:error:abort\x00\x00\x00\x00\x01\x00\x07-messageunexpected error: Someone set up us the bomb!\x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
 
 And its handling on the other size raise a clean exception
-(is currently not right)
 
   $ cat ../genfailed.hg2 | hg unbundle2
   0 unread bytes
-  abort: stream ended unexpectedly (got 0 bytes, expected 4)
+  abort: unexpected error: Someone set up us the bomb!
   [255]