You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
122 lines
3.8 KiB
122 lines
3.8 KiB
"""Party
|
|
|
|
:Maintainer: Ben Bangert <ben@groovie.org>
|
|
:Status: Production
|
|
|
|
A Zookeeper pool of party members. The :class:`Party` object can be
|
|
used for determining members of a party.
|
|
|
|
"""
|
|
import uuid
|
|
|
|
from kazoo.exceptions import NodeExistsError, NoNodeError
|
|
|
|
|
|
class BaseParty(object):
|
|
"""Base implementation of a party."""
|
|
|
|
def __init__(self, client, path, identifier=None):
|
|
"""
|
|
:param client: A :class:`~kazoo.client.KazooClient` instance.
|
|
:param path: The party path to use.
|
|
:param identifier: An identifier to use for this member of the
|
|
party when participating.
|
|
|
|
"""
|
|
self.client = client
|
|
self.path = path
|
|
self.data = str(identifier or "").encode("utf-8")
|
|
self.ensured_path = False
|
|
self.participating = False
|
|
|
|
def _ensure_parent(self):
|
|
if not self.ensured_path:
|
|
# make sure our parent node exists
|
|
self.client.ensure_path(self.path)
|
|
self.ensured_path = True
|
|
|
|
def join(self):
|
|
"""Join the party"""
|
|
return self.client.retry(self._inner_join)
|
|
|
|
def _inner_join(self):
|
|
self._ensure_parent()
|
|
try:
|
|
self.client.create(self.create_path, self.data, ephemeral=True)
|
|
self.participating = True
|
|
except NodeExistsError:
|
|
# node was already created, perhaps we are recovering from a
|
|
# suspended connection
|
|
self.participating = True
|
|
|
|
def leave(self):
|
|
"""Leave the party"""
|
|
self.participating = False
|
|
return self.client.retry(self._inner_leave)
|
|
|
|
def _inner_leave(self):
|
|
try:
|
|
self.client.delete(self.create_path)
|
|
except NoNodeError:
|
|
return False
|
|
return True
|
|
|
|
def __len__(self):
|
|
"""Return a count of participating clients"""
|
|
self._ensure_parent()
|
|
return len(self._get_children())
|
|
|
|
def _get_children(self):
|
|
return self.client.retry(self.client.get_children, self.path)
|
|
|
|
|
|
class Party(BaseParty):
|
|
"""Simple pool of participating processes"""
|
|
|
|
_NODE_NAME = "__party__"
|
|
|
|
def __init__(self, client, path, identifier=None):
|
|
BaseParty.__init__(self, client, path, identifier=identifier)
|
|
self.node = uuid.uuid4().hex + self._NODE_NAME
|
|
self.create_path = self.path + "/" + self.node
|
|
|
|
def __iter__(self):
|
|
"""Get a list of participating clients' data values"""
|
|
self._ensure_parent()
|
|
children = self._get_children()
|
|
for child in children:
|
|
try:
|
|
d, _ = self.client.retry(
|
|
self.client.get, self.path + "/" + child
|
|
)
|
|
yield d.decode("utf-8")
|
|
except NoNodeError: # pragma: nocover
|
|
pass
|
|
|
|
def _get_children(self):
|
|
children = BaseParty._get_children(self)
|
|
return [c for c in children if self._NODE_NAME in c]
|
|
|
|
|
|
class ShallowParty(BaseParty):
|
|
"""Simple shallow pool of participating processes
|
|
|
|
This differs from the :class:`Party` as the identifier is used in
|
|
the name of the party node itself, rather than the data. This
|
|
places some restrictions on the length as it must be a valid
|
|
Zookeeper node (an alphanumeric string), but reduces the overhead
|
|
of getting a list of participants to a single Zookeeper call.
|
|
|
|
"""
|
|
|
|
def __init__(self, client, path, identifier=None):
|
|
BaseParty.__init__(self, client, path, identifier=identifier)
|
|
self.node = "-".join([uuid.uuid4().hex, self.data.decode("utf-8")])
|
|
self.create_path = self.path + "/" + self.node
|
|
|
|
def __iter__(self):
|
|
"""Get a list of participating clients' identifiers"""
|
|
self._ensure_parent()
|
|
children = self._get_children()
|
|
for child in children:
|
|
yield child[child.find("-") + 1 :]
|