Skip to content

Commit 88b657a

Browse files
BrianEaton1StephenSorriaux
authored andcommitted
feat(recipe): add support for curator SharedCount recipe (#559)
* feat(recipe): add support for curator SharedCount recipe This feature allows Java clients using curator's SharedCount recipe and python clients using kazoo's Counter recipe to read and write from the same path without receiving type errors. example use: counter = zk.Counter("/curator", support_curator=True) counter += 2 counter -= 1 counter.value == 1 counter.pre_value == 2 counter.post_value == 1 Closes #558
1 parent cd49b3f commit 88b657a

File tree

2 files changed

+48
-5
lines changed

2 files changed

+48
-5
lines changed

kazoo/recipe/counter.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"""
77
from kazoo.exceptions import BadVersionError
88
from kazoo.retry import ForceRetryError
9-
9+
import struct
1010

1111
class Counter(object):
1212
"""Kazoo Counter
@@ -19,6 +19,12 @@ class Counter(object):
1919
`type(counter.default)(value)` both using an ascii encoding. As
2020
such other data types might be used for the counter value.
2121
22+
If you would like to support clients updating the same znode path using
23+
either kazoo's counter recipe or curator's SharedCount recipe, you will
24+
need to enable the support_curator flag. This flag limits
25+
support to integers only and does not use ascii encoding as described
26+
above.
27+
2228
Counter changes can raise
2329
:class:`~kazoo.exceptions.BadVersionError` if the retry policy
2430
wasn't able to apply a change.
@@ -42,22 +48,35 @@ class Counter(object):
4248
counter.pre_value == 1.0
4349
counter.post_value == 3.0
4450
51+
counter = zk.Counter("/curator", support_curator=True)
52+
counter += 2
53+
counter -= 1
54+
counter.value == 1
55+
counter.pre_value == 2
56+
counter.post_value == 1
57+
4558
"""
46-
def __init__(self, client, path, default=0):
59+
def __init__(self, client, path, default=0, support_curator=False):
4760
"""Create a Kazoo Counter
4861
4962
:param client: A :class:`~kazoo.client.KazooClient` instance.
5063
:param path: The counter path to use.
51-
:param default: The default value.
64+
:param default: The default value to use for new counter paths.
65+
:param support_curator: Enable if support for curator's SharedCount
66+
recipe is desired.
5267
5368
"""
5469
self.client = client
5570
self.path = path
5671
self.default = default
5772
self.default_type = type(default)
73+
self.support_curator = support_curator
5874
self._ensured_path = False
5975
self.pre_value = None
6076
self.post_value = None
77+
if self.support_curator and not isinstance(self.default, int):
78+
raise TypeError("when support_curator is enabled the default "
79+
"type must be an int")
6180

6281
def _ensure_node(self):
6382
if not self._ensured_path:
@@ -68,7 +87,10 @@ def _ensure_node(self):
6887
def _value(self):
6988
self._ensure_node()
7089
old, stat = self.client.get(self.path)
71-
old = old.decode('ascii') if old != b'' else self.default
90+
if self.support_curator:
91+
old = struct.unpack(">i", old)[0] if old != b'' else self.default
92+
else:
93+
old = old.decode('ascii') if old != b'' else self.default
7294
version = stat.version
7395
data = self.default_type(old)
7496
return data, version
@@ -86,7 +108,10 @@ def _change(self, value):
86108
def _inner_change(self, value):
87109
self.pre_value, version = self._value()
88110
post_value = self.pre_value + value
89-
data = repr(post_value).encode('ascii')
111+
if self.support_curator:
112+
data = struct.pack(">i", post_value)
113+
else:
114+
data = repr(post_value).encode('ascii')
90115
try:
91116
self.client.set(self.path, data, version=version)
92117
except BadVersionError: # pragma: nocover

kazoo/tests/test_counter.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,22 @@ def test_int_counter(self):
2121
counter - 1
2222
eq_(counter.value, -1)
2323

24+
def test_int_curator_counter(self):
25+
counter = self._makeOne(support_curator=True)
26+
eq_(counter.value, 0)
27+
counter += 2
28+
counter + 1
29+
eq_(counter.value, 3)
30+
counter -= 3
31+
counter - 1
32+
eq_(counter.value, -1)
33+
counter += 1
34+
counter += 2147483647
35+
eq_(counter.value, 2147483647)
36+
counter -= 2147483647
37+
counter -= 2147483647
38+
eq_(counter.value, -2147483647)
39+
2440
def test_float_counter(self):
2541
counter = self._makeOne(default=0.0)
2642
eq_(counter.value, 0.0)
@@ -33,6 +49,8 @@ def test_errors(self):
3349
counter = self._makeOne()
3450
self.assertRaises(TypeError, counter.__add__, 2.1)
3551
self.assertRaises(TypeError, counter.__add__, b"a")
52+
with self.assertRaises(TypeError):
53+
counter = self._makeOne(default=0.0, support_curator=True)
3654

3755
def test_pre_post_values(self):
3856
counter = self._makeOne()

0 commit comments

Comments
 (0)