Mercurial > hg-stable
comparison mercurial/bundle2.py @ 32024:ad41739c6b2b
bundle2: ignore errors seeking a bundle after an exception (issue4784)
Many have seen a "stream ended unexpectedly" error. This message is
raised from changegroup.readexactly() when a read(n) operation fails
to return exactly N bytes.
I believe most occurrences of this error in the wild stem from
the code changed in this patch. Before, if bundle2's part applicator
raised an Exception when processing/applying parts, the exception
handler would attempt to iterate the remaining parts. If I/O
during this iteration failed, it would likely raise the
"stream ended unexpectedly" exception.
The problem with this approach is that if we already encountered
an I/O error iterating the bundle2 data during application, then
any further I/O would almost certainly fail. If the original stream
were closed, changegroup.readexactly() would obtain an empty
string, triggering "stream ended unexpectedly" with "got 0." This is
the error message that users would see. What's worse is that the
original I/O related exception would be lost since a new exception
would be raised. This made debugging the actual I/O failure
effectively impossible.
This patch changes the exception handler for bundle2 application to
ignore errors when seeking the underlying stream. When the underlying
error is I/O related, the seek should fail fast and the original
exception will be re-raised. The output changes in
test-http-bad-server.t demonstrate this.
When the underlying error is not I/O related and the stream can be
seeked, the old behavior is preserved.
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Sun, 16 Apr 2017 11:55:08 -0700 |
parents | a02e773008f5 |
children | 99515353c72a 76f9a0009b4b |
comparison
equal
deleted
inserted
replaced
32023:a29580905771 | 32024:ad41739c6b2b |
---|---|
352 nbpart = 0 | 352 nbpart = 0 |
353 try: | 353 try: |
354 for nbpart, part in iterparts: | 354 for nbpart, part in iterparts: |
355 _processpart(op, part) | 355 _processpart(op, part) |
356 except Exception as exc: | 356 except Exception as exc: |
357 for nbpart, part in iterparts: | 357 # Any exceptions seeking to the end of the bundle at this point are |
358 # consume the bundle content | 358 # almost certainly related to the underlying stream being bad. |
359 part.seek(0, 2) | 359 # And, chances are that the exception we're handling is related to |
360 # getting in that bad state. So, we swallow the seeking error and | |
361 # re-raise the original error. | |
362 seekerror = False | |
363 try: | |
364 for nbpart, part in iterparts: | |
365 # consume the bundle content | |
366 part.seek(0, 2) | |
367 except Exception: | |
368 seekerror = True | |
369 | |
360 # Small hack to let caller code distinguish exceptions from bundle2 | 370 # Small hack to let caller code distinguish exceptions from bundle2 |
361 # processing from processing the old format. This is mostly | 371 # processing from processing the old format. This is mostly |
362 # needed to handle different return codes to unbundle according to the | 372 # needed to handle different return codes to unbundle according to the |
363 # type of bundle. We should probably clean up or drop this return code | 373 # type of bundle. We should probably clean up or drop this return code |
364 # craziness in a future version. | 374 # craziness in a future version. |
368 if op.reply is not None: | 378 if op.reply is not None: |
369 salvaged = op.reply.salvageoutput() | 379 salvaged = op.reply.salvageoutput() |
370 replycaps = op.reply.capabilities | 380 replycaps = op.reply.capabilities |
371 exc._replycaps = replycaps | 381 exc._replycaps = replycaps |
372 exc._bundle2salvagedoutput = salvaged | 382 exc._bundle2salvagedoutput = salvaged |
373 raise | 383 |
384 # Re-raising from a variable loses the original stack. So only use | |
385 # that form if we need to. | |
386 if seekerror: | |
387 raise exc | |
388 else: | |
389 raise | |
374 finally: | 390 finally: |
375 repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart) | 391 repo.ui.debug('bundle2-input-bundle: %i parts total\n' % nbpart) |
376 | 392 |
377 return op | 393 return op |
378 | 394 |