Patchwork [2,of,2] bundle2: transmit exception during part generation

login
register
mail settings
Submitter Pierre-Yves David
Date Oct. 17, 2014, 5:50 p.m.
Message ID <66ddaa0106002186ea5d.1413568236@marginatus.alto.octopoid.net>
Download mbox | patch
Permalink /patch/6384/
State Superseded
Commit 420a051616ceab0403c146040cbf8f022523f814
Headers show

Comments

Pierre-Yves David - Oct. 17, 2014, 5:50 p.m.
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@fb.com>
# Date 1413370340 25200
#      Wed Oct 15 03:52:20 2014 -0700
# Node ID 66ddaa0106002186ea5d72fc80511bbe38c50012
# Parent  4ab700f8a545b047ffeb5096e04ce56b67d2808d
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.

Patch

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
 preserve.
 """
 
+import sys
 import util
 import struct
 import urllib
 import string
 import obsolete
@@ -670,13 +671,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!
   [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 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!
   [255]
 
 
   $ cd ..