Merge lp://staging/~mbp/bzr/2.5-client-reconnect-819604 into lp://staging/bzr

Proposed by Martin Pool
Status: Merged
Merged at revision: 6321
Proposed branch: lp://staging/~mbp/bzr/2.5-client-reconnect-819604
Merge into: lp://staging/bzr
Diff against target: 1557 lines (+917/-239) (has conflicts)
15 files modified
bzrlib/help_topics/en/debug-flags.txt (+2/-0)
bzrlib/smart/client.py (+208/-86)
bzrlib/smart/medium.py (+20/-5)
bzrlib/smart/protocol.py (+5/-3)
bzrlib/smart/request.py (+179/-105)
bzrlib/tests/servers.py (+75/-0)
bzrlib/tests/test_bundle.py (+7/-35)
bzrlib/tests/test_smart.py (+5/-2)
bzrlib/tests/test_smart_request.py (+10/-0)
bzrlib/tests/test_smart_transport.py (+376/-0)
doc/en/release-notes/bzr-2.1.txt (+5/-0)
doc/en/release-notes/bzr-2.2.txt (+5/-0)
doc/en/release-notes/bzr-2.3.txt (+5/-0)
doc/en/release-notes/bzr-2.4.txt (+7/-3)
doc/en/release-notes/bzr-2.5.txt (+8/-0)
Text conflict in bzrlib/smart/request.py
Text conflict in doc/en/release-notes/bzr-2.5.txt
To merge this branch: bzr merge lp://staging/~mbp/bzr/2.5-client-reconnect-819604
Reviewer Review Type Date Requested Status
Jelmer Vernooij (community) Approve
Review via email: mp+83555@code.staging.launchpad.net

Commit message

Bug #819604, allow clients to reconnect if they get ConnectionReset while trying to send an RPC.

Description of the change

Continues on from https://code.launchpad.net/~jelmer/bzr/2.5-client-reconnect-819604/+merge/83425

improving the DisconnectingTCPServer which was prone to hanging.

A few changes:

 * closing a socket will not interrupt a server that's accepting connections on that socket
 * with John's changes, the client does not connect exactly once

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve
Revision history for this message
Vincent Ladeuil (vila) wrote :

Focusing on ./bzr selftest -s bzrlib.tests.test_bundle.TestReadMergeableFromUrl.test_smart_server_connection_reset.

Running this command repeatedly shows that either the test succeeds in ~50ms OR ~10.000ms. I.e. the select timeout triggers.

With this patch:

   modified bzrlib/tests/servers.py

=== modified file 'bzrlib/tests/servers.py'
--- bzrlib/tests/servers.py 2011-11-28 07:52:49 +0000
+++ bzrlib/tests/servers.py 2011-11-28 09:33:09 +0000
@@ -40,10 +40,6 @@
         self.sock.setblocking(False)
         while not self._please_stop:
             try:
- # We can't just accept here, because accept is not interrupted
- # by the listen socket being asynchronously closed by
- # stop_server. However, select will be interrupted.
- select.select([fd], [fd], [fd], 10)
                 conn, addr = self.sock.accept()
             except (select.error, socket.error), e:
                 en = getattr(e, 'errno') or e[0]
@@ -68,6 +64,7 @@
             # just in case the test failed to do so.
             conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
             conn.connect(self.sock.getsockname())
+ conn.shutdown(socket.SHUT_RDWR)
             conn.close()
         except socket.error:
             pass

Running the same command 1.000 times reliably succeeds in ~50ms.

I don't think the select is worth it, what matters is to shutdown() the socket instead of just close()'ing it.

Revision history for this message
Vincent Ladeuil (vila) wrote :
Download full text (3.7 KiB)

More precisely, on top of jelmer's branch, the following patch also resists the 1.000 stress test. (note that I wrongly tested it with TestingThreadingTCPServer instead of TestingTCPServer in DisconnectingServer at first, for even more stress tests).

   modified bzrlib/tests/test_bundle.py

=== modified file 'bzrlib/tests/test_bundle.py'
--- bzrlib/tests/test_bundle.py 2011-10-05 12:16:17 +0000
+++ bzrlib/tests/test_bundle.py 2011-11-28 10:57:48 +0000
@@ -17,6 +17,7 @@
 from cStringIO import StringIO
 import os
 import socket
+import SocketServer
 import sys
 import threading

@@ -41,13 +42,12 @@
 from bzrlib.bundle.serializer.v4 import BundleSerializerV4
 from bzrlib.repofmt import knitrepo
 from bzrlib.tests import (
+ features,
+ test_commit,
     test_read_bundle,
- test_commit,
+ test_server,
     )
 from bzrlib.transform import TreeTransform
-from bzrlib.tests import (
- features,
- )

 def get_text(vf, key):
@@ -1836,7 +1836,7 @@
         bundle, then the ConnectionReset error should be propagated.
         """
         # Instantiate a server that will provoke a ConnectionReset
- sock_server = _DisconnectingTCPServer()
+ sock_server = DisconnectingServer()
         self.start_server(sock_server)
         # We don't really care what the url is since the server will close the
         # connection without interpreting it
@@ -1844,35 +1844,21 @@
         self.assertRaises(errors.ConnectionReset, read_mergeable_from_url, url)

-class _DisconnectingTCPServer(object):
- """A TCP server that immediately closes any connection made to it."""
-
- def start_server(self):
- self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- self.sock.bind(('127.0.0.1', 0))
- self.sock.listen(1)
- self.port = self.sock.getsockname()[1]
- self.thread = threading.Thread(
- name='%s (port %d)' % (self.__class__.__name__, self.port),
- target=self.accept_and_close)
- self.thread.start()
-
- def accept_and_close(self):
- conn, addr = self.sock.accept()
- conn.shutdown(socket.SHUT_RDWR)
- conn.close()
+class DisconnectingHandler(SocketServer.BaseRequestHandler):
+ """A request handler that immediately closes any connection made to it."""
+
+ def handle(self):
+ self.request.close()
+
+
+class DisconnectingServer(test_server.TestingTCPServerInAThread):
+
+ def __init__(self):
+ super(DisconnectingServer, self).__init__(
+ ('127.0.0.1', 0),
+ test_server.TestingTCPServer,
+ DisconnectingHandler)

     def get_url(self):
- return 'bzr://127.0.0.1:%d/' % (self.port,)
-
- def stop_server(self):
- try:
- # make sure the thread dies by connecting to the listening socket,
- # just in case the test failed to do so.
- conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- conn.connect(self.sock.getsockname())
- conn.close()
- except socket.error:
- pass
- self.sock.close()
- self.thread.join()
+ ...

Read more...

Revision history for this message
Martin Pool (mbp) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
The diff is not available at this time. You can reload the page or download it.