Merge lp:~cmiller/desktopcouch/reconnect-on-couch-death into lp:desktopcouch

Proposed by Chad Miller
Status: Merged
Approved by: Rodrigo Moya
Approved revision: 168
Merged at revision: 169
Proposed branch: lp:~cmiller/desktopcouch/reconnect-on-couch-death
Merge into: lp:desktopcouch
Diff against target: 93 lines (+39/-9)
2 files modified
desktopcouch/records/server.py (+11/-3)
desktopcouch/records/server_base.py (+28/-6)
To merge this branch: bzr merge lp:~cmiller/desktopcouch/reconnect-on-couch-death
Reviewer Review Type Date Requested Status
Rodrigo Moya (community) Approve
Tim Cole (community) Approve
Review via email: mp+30710@code.launchpad.net

Commit message

On couchdb failure, reconnect with logic that starts up the couchdb server again and uses the new port.

To post a comment you must log in.
Revision history for this message
Tim Cole (tcole) :
review: Approve
Revision history for this message
Rodrigo Moya (rodrigo-moya) :
review: Approve
169. By Chad Miller

Instead of giving python-couchdb view objects to the user, give an object we own that implements reconnecting to the server on errors, and proxies commands otherwise.

170. By Chad Miller

Fixes LP: Bug#522538.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'desktopcouch/records/server.py'
--- desktopcouch/records/server.py 2010-02-26 22:23:42 +0000
+++ desktopcouch/records/server.py 2010-08-23 22:54:43 +0000
@@ -49,9 +49,17 @@
49 def __init__(self, database, uri=None, record_factory=None, create=False,49 def __init__(self, database, uri=None, record_factory=None, create=False,
50 server_class=OAuthCapableServer, oauth_tokens=None,50 server_class=OAuthCapableServer, oauth_tokens=None,
51 ctx=desktopcouch.local_files.DEFAULT_CONTEXT):51 ctx=desktopcouch.local_files.DEFAULT_CONTEXT):
52 if not uri:52 self.ctx = ctx
53 port = desktopcouch.find_port(ctx=ctx)53 self.server_uri = uri
54 uri = "http://localhost:%s" % port
55 super(CouchDatabase, self).__init__(54 super(CouchDatabase, self).__init__(
56 database, uri, record_factory=record_factory, create=create,55 database, uri, record_factory=record_factory, create=create,
57 server_class=server_class, oauth_tokens=oauth_tokens, ctx=ctx)56 server_class=server_class, oauth_tokens=oauth_tokens, ctx=ctx)
57
58 def _reconnect(self):
59 if not self.server_uri:
60 port = desktopcouch.find_port(ctx=self.ctx)
61 uri = "http://localhost:%s" % port
62 else:
63 uri = self.server_uri
64 super(CouchDatabase, self)._reconnect(uri=uri)
65
5866
=== modified file 'desktopcouch/records/server_base.py'
--- desktopcouch/records/server_base.py 2010-07-02 15:32:25 +0000
+++ desktopcouch/records/server_base.py 2010-08-23 22:54:43 +0000
@@ -22,7 +22,7 @@
22"""The Desktop Couch Records API."""22"""The Desktop Couch Records API."""
2323
24import httplib2, urlparse, cgi, copy24import httplib2, urlparse, cgi, copy
25from time import time25from time import time, sleep
26import socket26import socket
2727
28# please keep desktopcouch python 2.5 compatible for now28# please keep desktopcouch python 2.5 compatible for now
@@ -170,15 +170,16 @@
170 continue170 continue
171 elif isinstance(e, socket.error):171 elif isinstance(e, socket.error):
172 logging.warn("Other socket error %s. Reconnecting.", e)172 logging.warn("Other socket error %s. Reconnecting.", e)
173 time.sleep(0.3) 173 sleep(0.3)
174 self._reconnect()174 self._reconnect()
175 continue175 continue
176 else:176 else:
177 raise177 raise
178178
179 def _reconnect(self):179 def _reconnect(self, uri=None):
180 logging.info("Connecting to %s." % (self.server_uri or "discovered local port",))180 logging.info("Connecting to %s." % (self.server_uri or "discovered local port",))
181 self._server = self._server_class(self.server_uri,181
182 self._server = self._server_class(uri or self.server_uri,
182 **self._server_class_extras)183 **self._server_class_extras)
183 if self._database_name not in self._server:184 if self._database_name not in self._server:
184 if self._create:185 if self._create:
@@ -432,12 +433,33 @@
432 def execute_view(self, view_name, design_doc=DEFAULT_DESIGN_DOCUMENT,433 def execute_view(self, view_name, design_doc=DEFAULT_DESIGN_DOCUMENT,
433 **params):434 **params):
434 """Execute view and return results."""435 """Execute view and return results."""
436
437 class ReconnectingViewWrapper:
438 """A view from python-couchdb is an object with attributes that
439 cause HTTP requests to be fired off when accessed. If we wish
440 to be able to reconnect on disappearance of the server, then
441 we must intercept calls from user to python-couchdb."""
442
443 def __init__(wrapper, view, *args, **kwargs):
444 wrapper.view = self.with_reconnects(view, *args, **kwargs)
445
446 def ___call__(wrapper, **options):
447 return self.with_reconnects(wrapper.view.__call__, **options)
448
449 def __iter__(wrapper):
450 return self.with_reconnects(wrapper.view.__iter__)
451
452 def __getitem__(wrapper, key):
453 return self.with_reconnects(wrapper.view.__getitem__, key)
454
435 if design_doc is None:455 if design_doc is None:
436 design_doc = view_name456 design_doc = view_name
437457
438 view_id_fmt = "_design/%(design_doc)s/_view/%(view_name)s"458 view_id_fmt = "_design/%(design_doc)s/_view/%(view_name)s"
439 return self.with_reconnects(self.db.view, view_id_fmt % locals(),459 wrapper = ReconnectingViewWrapper(self.db.view,
440 **params)460 view_id_fmt % locals(),
461 **params)
462 return wrapper
441463
442 def add_view(self, view_name, map_js, reduce_js,464 def add_view(self, view_name, map_js, reduce_js,
443 design_doc=DEFAULT_DESIGN_DOCUMENT):465 design_doc=DEFAULT_DESIGN_DOCUMENT):

Subscribers

People subscribed via source and target branches