[PATCH 2 of 2] bundle2: transmit exception during part generation

Pierre-Yves David pierre-yves.david at ens-lyon.org
Tue Oct 21 15:08:46 CDT 2014

# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at fb.com>
# Date 1413370340 25200
#      Wed Oct 15 03:52:20 2014 -0700
# Branch stable
# Node ID bd35fb4708eda7d548a34ef6ff504505cf0e85cb
# Parent  1c1b98ea77615d601d8234f70728da9a21692c93
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.

diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -143,10 +143,11 @@ process is aborted, the full bundle is s
 channel usable. But none of the part read from an abort are processed. In the
 future, dropping the stream may become an option for channel we do not care to
+import sys
 import util
 import struct
 import urllib
 import string
 import obsolete
@@ -671,13 +672,26 @@ class bundlepart(object):
         ## finalize header
         headerchunk = ''.join(header)
         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:
+            # 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
     def _payloadchunks(self):
diff --git a/tests/test-bundle2-format.t b/tests/test-bundle2-format.t
--- a/tests/test-bundle2-format.t
+++ b/tests/test-bundle2-format.t
@@ -775,28 +775,25 @@ with reply
   added 0 changesets with 0 changes to 3 files
   \x00\x00\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
 Check handling of exception during generation.
-(is currently not right)
   $ hg bundle2 --genraise > ../genfailed.hg2
   abort: Someone set up us the bomb!
 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 us up 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 us up the bomb!
   $ cd ..

More information about the Mercurial-devel mailing list