Skip to content
This repository has been archived by the owner on Jan 27, 2021. It is now read-only.

Commit

Permalink
Merge pull request #10 from zopefoundation/fixes-2014-08-27
Browse files Browse the repository at this point in the history
Fixed, especially for hang on reconnect. (Don't know why travis shows as failing. The last runs there are green.)
  • Loading branch information
Jim Fulton committed Aug 28, 2014
2 parents d150e34 + 9d8a77c commit 05ace0f
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 16 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
@@ -1,4 +1,7 @@
language: python
env:
- PYTHON_VER=2.6
- PYTHON_VER=2.7
install:
- python bootstrap.py
- bin/buildout
Expand Down
8 changes: 8 additions & 0 deletions src/zc/zk/README.txt
Expand Up @@ -1251,6 +1251,14 @@ more, use the help function::
Change History
==============

- Fixed: ZooKeeper operations (including closing ZooKeeper
connections) hung after network failures if ZooKeeper sessions were
lost and ephemeral nodes (for registered services) had to be
re-registered.

- Fixed: Didn't properly handle None values for node data returned by
Kazoo 2.0.

2.0.0 (2014-06-02)
==================

Expand Down
15 changes: 10 additions & 5 deletions src/zc/zk/__init__.py
Expand Up @@ -41,7 +41,7 @@ def encode(props):
return json.dumps(props, separators=(',',':'))

def decode(sdata, path='?'):
s = sdata.strip()
s = sdata and sdata.strip()
if not s:
data = {}
elif s.startswith('{') and s.endswith('}'):
Expand Down Expand Up @@ -141,15 +141,20 @@ def __init__(
self.state = None

def watch_session(state):
restore = False
logger.info("watch_session %s" % state)
if state == kazoo.protocol.states.KazooState.CONNECTED:
if self.state == kazoo.protocol.states.KazooState.LOST:
logger.warning("session lost")
restore = self.state == kazoo.protocol.states.KazooState.LOST
logger.info('connected')
self.state = state

if restore:
@zc.thread.Thread
def restore():
for path, data in self.ephemeral.items():
logger.info("restoring ephemeral %s", path)
self.create(
path, data['data'], data['acl'], ephemeral=True)
logger.info('connected')
self.state = state

client.add_listener(watch_session)

Expand Down
32 changes: 21 additions & 11 deletions src/zc/zk/disconnectiontests.py
Expand Up @@ -79,12 +79,16 @@ def log_ephemeral_restoration_on_session_timeout():
>>> handler.clear()
>>> zk.client.lose_session()
>>> print handler
zc.zk WARNING
session lost
zc.zk INFO
restoring ephemeral /fooservice/providers/test
watch_session SUSPENDED
zc.zk INFO
watch_session LOST
zc.zk INFO
watch_session CONNECTED
zc.zk INFO
connected
zc.zk INFO
restoring ephemeral /fooservice/providers/test
>>> zk.close()
"""
Expand Down Expand Up @@ -138,19 +142,25 @@ def session_timeout_with_child_and_data_watchers():
['providers', 'x']
>>> print handler
zc.zk WARNING
session lost
zc.zk INFO
watch_session SUSPENDED
zc.zk INFO
watch_session LOST
zc.zk INFO
watch_session CONNECTED
zc.zk INFO
connected
If changes are made while we're disconnected, we'll still see them:
>>> @zk.client.lose_session
... def _():
... zk2 = zc.zk.ZooKeeper('zookeeper.example.com:2181')
... zk2.set('/fooservice', '{"test": 1}')
... zk2.create('/fooservice/y')
... zk2.close()
>>> if True:
... @zk.client.lose_session
... def _():
... zk2 = zc.zk.ZooKeeper('zookeeper.example.com:2181')
... zk2.set('/fooservice', '{"test": 1}')
... zk2.create('/fooservice/y')
... zk2.close()
... import time; time.sleep(1) # threads :/
properties changed True
children changed True
Expand Down
Expand Up @@ -41,6 +41,11 @@ Some custom data and acl:
a = 1
pid = 9999
>>> zk.client.lose_session()

There be threading thar, so wait:

>>> import time; time.sleep(1)

>>> zk.print_tree('/fooservice/providers')
/providers
/test
Expand Down
11 changes: 11 additions & 0 deletions src/zc/zk/tests.py
Expand Up @@ -1031,6 +1031,7 @@ def test_property_loops():
[(u'a', 1), (u'b', 2)]
>>> zk.close()
INFO watch_session SUSPENDED
>>> logger.uninstall()
"""

Expand Down Expand Up @@ -1116,6 +1117,16 @@ def backward_compatible_create_recursive():
>>> zk.close()
"""

def node_data_may_be_None():
"""
>>> zk = zc.zk.ZooKeeper('zookeeper.example.com:2181')
>>> _ = zk.client.create('/empty', None)
>>> zk.get_properties('/empty')
{}
>>> zk.close()
"""


event = threading.Event()
def check_async(show=True, expected_status=0):
event.clear()
Expand Down

0 comments on commit 05ace0f

Please sign in to comment.