Skip to content

Commit db0c2d4

Browse files
tonyseekjeffwidman
authored andcommitted
fix(recipe): Unexpected exceptions break TreeCache
1 parent 4456f18 commit db0c2d4

2 files changed

Lines changed: 30 additions & 14 deletions

File tree

kazoo/recipe/cache.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -268,14 +268,14 @@ def _refresh_children(self):
268268
# TODO max-depth checking support
269269
self._call_client('get_children', self._path)
270270

271-
def _call_client(self, method_name, path, *args):
271+
def _call_client(self, method_name, path):
272+
assert method_name in ('get', 'get_children', 'exists')
272273
self._tree._outstanding_ops += 1
273274
callback = functools.partial(
274275
self._tree._in_background, self._process_result,
275276
method_name, path)
276-
kwargs = {'watch': self._process_watch}
277277
method = getattr(self._tree._client, method_name + '_async')
278-
method(path, *args, **kwargs).rawlink(callback)
278+
method(path, watch=self._process_watch).rawlink(callback)
279279

280280
def _process_watch(self, watched_event):
281281
logger.debug('process_watch: %r', watched_event)
@@ -294,38 +294,35 @@ def _process_result(self, method_name, path, result):
294294
logger.debug('process_result: %s %s', method_name, path)
295295
if method_name == 'exists':
296296
assert self._parent is None, 'unexpected EXISTS on non-root'
297-
# the value of result will be set with `None` if node not exists.
298-
if result.get() is not None:
297+
# The result will be `None` if the node doesn't exist.
298+
if result.successful() and result.get() is not None:
299299
if self._state == self.STATE_DEAD:
300300
self._state = self.STATE_PENDING
301301
self.on_created()
302302
elif method_name == 'get_children':
303-
try:
303+
if result.successful():
304304
children = result.get()
305-
except NoNodeError:
306-
self.on_deleted()
307-
else:
308305
for child in sorted(children):
309306
full_path = os.path.join(path, child)
310307
if child not in self._children:
311308
node = TreeNode(self._tree, full_path, self)
312309
self._children[child] = node
313310
node.on_created()
311+
elif isinstance(result.exception, NoNodeError):
312+
self.on_deleted()
314313
elif method_name == 'get':
315-
try:
314+
if result.successful():
316315
data, stat = result.get()
317-
except NoNodeError:
318-
self.on_deleted()
319-
else:
320316
old_data, self._data = (
321317
self._data, NodeData.make(path, data, stat))
322-
323318
old_state, self._state = self._state, self.STATE_LIVE
324319
if old_state == self.STATE_LIVE:
325320
if old_data is None or old_data.stat.mzxid != stat.mzxid:
326321
self._publish_event(TreeEvent.NODE_UPDATED, self._data)
327322
else:
328323
self._publish_event(TreeEvent.NODE_ADDED, self._data)
324+
elif isinstance(result.exception, NoNodeError):
325+
self.on_deleted()
329326
else: # pragma: no cover
330327
logger.warning('unknown operation %s', method_name)
331328
self._tree._outstanding_ops -= 1

kazoo/tests/test_cache.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,25 @@ def test_exception_handler(self):
273273
self.cache.close()
274274
error_handler.assert_called_once_with(error_value)
275275

276+
def test_exception_suppressed(self):
277+
self.make_cache()
278+
self.wait_cache(since=TreeEvent.INITIALIZED)
279+
280+
# stoke up ConnectionClosedError
281+
self.client.stop()
282+
self.client.close()
283+
self.client.handler.start() # keep the async completion
284+
self.wait_cache(since=TreeEvent.CONNECTION_LOST)
285+
286+
with patch.object(TreeNode, 'on_created') as on_created:
287+
self.cache._root._call_client('exists', '/')
288+
self.cache._root._call_client('get', '/')
289+
self.cache._root._call_client('get_children', '/')
290+
291+
self.wait_cache(since=TreeEvent.INITIALIZED)
292+
on_created.assert_not_called()
293+
eq_(self.cache._outstanding_ops, 0)
294+
276295

277296
class FakeException(Exception):
278297
pass

0 commit comments

Comments
 (0)