Rev 4885: Merge 2.1-client-read-reconnect-819604 in http://bazaar.launchpad.net/~jameinel/bzr/2.1-all-reconnect-819604
John Arbash Meinel
john at arbash-meinel.com
Wed Sep 12 07:07:52 UTC 2012
At http://bazaar.launchpad.net/~jameinel/bzr/2.1-all-reconnect-819604
------------------------------------------------------------
revno: 4885 [merge]
revision-id: john at arbash-meinel.com-20120912070732-8xh7kllfxa6qr3ax
parent: john at arbash-meinel.com-20120912070702-frcohrl82jacgvq1
parent: john at arbash-meinel.com-20111010130807-jg97k047bl5d0x76
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 2.1-all-reconnect-819604
timestamp: Wed 2012-09-12 11:07:32 +0400
message:
Merge 2.1-client-read-reconnect-819604
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/smart/client.py client.py-20061116014825-2k6ada6xgulslami-1
bzrlib/tests/test_smart_transport.py test_ssh_transport.py-20060608202016-c25gvf1ob7ypbus6-2
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS 2012-02-02 14:08:45 +0000
+++ b/NEWS 2012-09-12 07:07:32 +0000
@@ -43,6 +43,11 @@
(John Arbash Meinel, #609187, #812928)
+* Teach the bzr client how to reconnect if we get ``ConnectionReset``
+ while making an RPC request. This doesn't handle all possible network
+ disconnects, but it should at least handle when the server is asked to
+ shutdown gracefully. (John Arbash Meinel, #819604)
+
Improvements
************
=== modified file 'bzrlib/smart/client.py'
--- a/bzrlib/smart/client.py 2011-10-10 12:49:14 +0000
+++ b/bzrlib/smart/client.py 2011-10-10 13:04:31 +0000
@@ -14,6 +14,11 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+from bzrlib import lazy_import
+lazy_import.lazy_import(globals(), """
+from bzrlib.smart import request as _mod_request
+""")
+
import bzrlib
from bzrlib.smart import message, protocol
from bzrlib import (
@@ -149,6 +154,22 @@
else:
return self._call(protocol_version)
+ def _is_safe_to_send_twice(self):
+ """Check if the current method is re-entrant safe."""
+ if self.body_stream is not None or 'noretry' in debug.debug_flags:
+ # We can't restart a body stream that has already been consumed.
+ return False
+ request_type = _mod_request.request_handlers.get_info(self.method)
+ if request_type in ('read', 'idem', 'semi'):
+ return True
+ # If we have gotten this far, 'stream' cannot be retried, because we
+ # already consumed the local stream.
+ if request_type in ('semivfs', 'mutate', 'stream'):
+ return False
+ trace.mutter('Unknown request type: %s for method %s'
+ % (request_type, self.method))
+ return False
+
def _run_call_hooks(self):
if not _SmartClient.hooks['call']:
return
@@ -164,8 +185,21 @@
where the code will be to retry requests if the connection is closed.
"""
response_handler = self._send(protocol_version)
- response_tuple = response_handler.read_response_tuple(
- expect_body=self.expect_response_body)
+ try:
+ response_tuple = response_handler.read_response_tuple(
+ expect_body=self.expect_response_body)
+ except errors.ConnectionReset, e:
+ self.client._medium.reset()
+ if not self._is_safe_to_send_twice():
+ raise
+ trace.warning('ConnectionReset reading response for %r, retrying'
+ % (self.method,))
+ trace.log_exception_quietly()
+ encoder, response_handler = self._construct_protocol(
+ protocol_version)
+ self._send_no_retry(encoder)
+ response_tuple = response_handler.read_response_tuple(
+ expect_body=self.expect_response_body)
return (response_tuple, response_handler)
def _call_determining_protocol_version(self):
=== modified file 'bzrlib/tests/test_smart_transport.py'
--- a/bzrlib/tests/test_smart_transport.py 2011-10-10 12:49:14 +0000
+++ b/bzrlib/tests/test_smart_transport.py 2011-10-10 13:00:28 +0000
@@ -3382,10 +3382,10 @@
class Test_SmartClientRequest(tests.TestCase):
- def make_client_with_failing_medium(self, fail_at_write=True):
- response = StringIO()
+ def make_client_with_failing_medium(self, fail_at_write=True, response=''):
+ response_io = StringIO(response)
output = StringIO()
- vendor = FirstRejectedStringIOSSHVendor(response, output,
+ vendor = FirstRejectedStringIOSSHVendor(response_io, output,
fail_at_write=fail_at_write)
client_medium = medium.SmartSSHClientMedium(
'a host', 'a port', 'a user', 'a pass', 'base', vendor,
@@ -3393,6 +3393,41 @@
smart_client = client._SmartClient(client_medium, headers={})
return output, vendor, smart_client
+ def make_response(self, args, body=None, body_stream=None):
+ response_io = StringIO()
+ response = _mod_request.SuccessfulSmartServerResponse(args, body=body,
+ body_stream=body_stream)
+ responder = protocol.ProtocolThreeResponder(response_io.write)
+ responder.send_response(response)
+ return response_io.getvalue()
+
+ def test__call_doesnt_retry_append(self):
+ response = self.make_response(('appended', '8'))
+ output, vendor, smart_client = self.make_client_with_failing_medium(
+ fail_at_write=False, response=response)
+ smart_request = client._SmartClientRequest(smart_client, 'append',
+ ('foo', ''), body='content\n')
+ self.assertRaises(errors.ConnectionReset, smart_request._call, 3)
+
+ def test__call_retries_get_bytes(self):
+ response = self.make_response(('ok',), 'content\n')
+ output, vendor, smart_client = self.make_client_with_failing_medium(
+ fail_at_write=False, response=response)
+ smart_request = client._SmartClientRequest(smart_client, 'get',
+ ('foo',))
+ response, response_handler = smart_request._call(3)
+ self.assertEqual(('ok',), response)
+ self.assertEqual('content\n', response_handler.read_body_bytes())
+
+ def test__call_noretry_get_bytes(self):
+ debug.debug_flags.add('noretry')
+ response = self.make_response(('ok',), 'content\n')
+ output, vendor, smart_client = self.make_client_with_failing_medium(
+ fail_at_write=False, response=response)
+ smart_request = client._SmartClientRequest(smart_client, 'get',
+ ('foo',))
+ self.assertRaises(errors.ConnectionReset, smart_request._call, 3)
+
def test__send_no_retry_pipes(self):
client_read, server_write = create_file_pipes()
server_read, client_write = create_file_pipes()
More information about the bazaar-commits
mailing list