1
=== modified file 'desktopcouch/records/server_base.py'
2
--- old/desktopcouch/records/server_base.py 2010-04-08 15:27:33 +0000
3
+++ new/desktopcouch/records/server_base.py 2010-04-19 16:07:11 +0000
5
from couchdb.client import ResourceNotFound, ResourceConflict, uri as couchdburi
6
from couchdb.design import ViewDefinition
7
from record import Record
10
#DEFAULT_DESIGN_DOCUMENT = "design"
11
DEFAULT_DESIGN_DOCUMENT = None # each view in its own eponymous design doc.
14
def __init__(self, database, uri, record_factory=None, create=False,
15
server_class=Server, **server_class_extras):
16
+ self._database_name = database
17
+ self._create = create
18
+ self._server_class = server_class
19
+ self._server_class_extras = server_class_extras
20
+ self.record_factory = record_factory or Record
22
- self._server = server_class(self.server_uri, **server_class_extras)
23
- if database not in self._server:
25
- self._server.create(database)
27
- raise NoSuchDatabase(database)
28
- self.db = self._server[database]
29
- self.record_factory = record_factory or Record
31
self._changes_since = self.db.info()["update_seq"]
32
self._changes_last_used = 0 # Immediate run works.
35
+ def _is_bug_lp539674(ex):
36
+ return isinstance(ex, AttributeError) and \
37
+ ex.args == ("'NoneType' object has no attribute 'makefile'",)
39
+ def with_reconnects(self, func, *args, **kwargs):
40
+ for retry in (2, 1, None):
42
+ return func(*args, **kwargs)
43
+ except Exception, e:
44
+ if self._is_bug_lp539674(e) and retry:
45
+ logging.warn("DB connection timed out. Reconnecting.")
51
+ def _reconnect(self):
52
+ self._server = self._server_class(self.server_uri,
53
+ **self._server_class_extras)
54
+ if self._database_name not in self._server:
56
+ self._server.create(self._database_name)
58
+ raise NoSuchDatabase(self._database_name)
59
+ self.db = self._server[self._database_name]
61
def _temporary_query(self, map_fun, reduce_fun=None, language='javascript',
62
wrapper=None, **options):
63
"""Pass-through to CouchDB library. Deprecated."""
64
- return self.db.query(map_fun, reduce_fun, language,
65
+ return self.with_reconnects(self.db.query, map_fun, reduce_fun, language,
68
def get_record(self, record_id):
69
"""Get a record from back end storage."""
71
- couch_record = self.db[record_id]
72
+ couch_record = self.with_reconnects(self.db.__getitem__, record_id)
73
except ResourceNotFound:
77
# Do not rely on couchdb to create an ID for us.
78
from uuid import uuid4
79
record.record_id = uuid4().hex
80
- self.db[record.record_id] = record._data
81
+ self.with_reconnects(self.db.__setitem__,
82
+ record.record_id, record._data)
84
for attachment_name in record.list_attachments():
85
data, content_type = record.attachment_data(attachment_name)
86
- self.db.put_attachment(
87
+ self.with_reconnects(self.db.put_attachment,
88
record._data, data, attachment_name, content_type)
90
return record.record_id
92
# although with a single record we need to test for the
93
# revisison, with a batch we do not, but we have to make sure
94
# that we did not get an error
95
- batch_put_result = self.db.update([record._data for record in batch])
96
+ batch_put_result = self.with_reconnects(
97
+ self.db.update, [record._data for record in batch])
98
for current_tuple in batch_put_result:
99
success, docid, rev_or_exc = current_tuple
102
record._data["_rev"] = rev_or_exc
103
for attachment_name in record.list_attachments():
104
data, content_type = record.attachment_data(attachment_name)
105
- self.db.put_attachment(
106
+ self.with_reconnects(self.db.put_attachment,
107
{"_id":record.record_id, "_rev":record["_rev"]},
108
data, attachment_name, content_type)
109
# all success record have the blobs added we return result of
111
# as far as we know. (If they're not, we'll get a
112
# ResourceConflict later on, from which we can recover.)
113
if cached_record is None:
114
- cached_record = self.db[record_id]
115
+ cached_record = self.with_reconnects(self.db.__getitem__,
117
if isinstance(cached_record, Record):
118
cached_record = cached_record._data
119
record = copy.deepcopy(cached_record)
120
@@ -294,13 +322,15 @@
124
- self.db[record_id] = record
125
+ self.with_reconnects(self.db.__setitem__, record_id,
127
except ResourceConflict:
128
# We got a conflict, meaning the record has
129
# changed in the database since we last loaded it
130
# into memory. Let's get a fresh copy and try
132
- record = self.db[record_id]
133
+ record = self.with_reconnects(self.db.__getitem__,
136
# If we get here, nothing remains to be done, and we can
137
# take a well deserved break.
138
@@ -308,17 +338,17 @@
140
def delete_record(self, record_id):
141
"""Delete record with given id"""
142
- record = self.db[record_id]
143
+ record = self.with_reconnects(self.db.__getitem__, record_id)
144
record.setdefault('application_annotations', {}).setdefault(
145
'Ubuntu One', {}).setdefault('private_application_annotations', {})[
147
- self.db[record_id] = record
148
+ self.with_reconnects(self.db.__setitem__, record_id, record)
150
def record_exists(self, record_id):
151
"""Check if record with given id exists."""
152
if record_id not in self.db:
154
- record = self.db[record_id]
155
+ record = self.with_reconnects(self.db.__getitem__, record_id)
156
return not row_is_deleted(record)
158
def delete_view(self, view_name, design_doc=DEFAULT_DESIGN_DOCUMENT):
160
# No atomic updates. Only read & mutate & write. Le sigh.
161
# First, get current contents.
163
- view_container = self.db[doc_id]["views"]
164
+ view_container = self.with_reconnects(
165
+ self.db.__getitem__, doc_id)["views"]
166
except (KeyError, ResourceNotFound):
171
# Remove design document. This assumes there are only views in
172
# design documents. :(
173
- del self.db[doc_id]
174
+ self.with_reconnects(self.db.__delitem__, doc_id)
176
assert not self.view_exists(view_name, design_doc)
179
design_doc = view_name
181
view_id_fmt = "_design/%(design_doc)s/_view/%(view_name)s"
182
- return self.db.view(view_id_fmt % locals(), **params)
183
+ return self.with_reconnects(self.db.view, view_id_fmt % locals(),
186
def add_view(self, view_name, map_js, reduce_js,
187
design_doc=DEFAULT_DESIGN_DOCUMENT):
189
doc_id = "_design/%(design_doc)s" % locals()
192
- view_container = self.db[doc_id]["views"]
194
+ self.with_reconnects(self.db.__getitem__, doc_id)["views"]
195
return view_name in view_container
196
except (KeyError, ResourceNotFound):
200
doc_id = "_design/%(design_doc)s" % locals()
202
- return list(self.db[doc_id]["views"])
203
+ return list(self.with_reconnects(self.db.__getitem__, doc_id)["views"])
204
except (KeyError, ResourceNotFound):