Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fully resolve and randomize the client host list at connect time #372

Merged
merged 10 commits into from
Aug 5, 2016
13 changes: 13 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
Changelog
=========

2.X.Y (TBD)
-----------

Features
********

Bug Handling
************
- #372: fully resolve multiple records for hosts in the zookeeper connection string

Documentation
*************

2.2.1 (2015-06-17)
------------------

Expand Down
2 changes: 1 addition & 1 deletion ensure-zookeeper-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ HERE=`pwd`
ZOO_BASE_DIR="$HERE/zookeeper"
ZOOKEEPER_VERSION=${ZOOKEEPER_VERSION:-3.4.6}
ZOOKEEPER_PATH="$ZOO_BASE_DIR/$ZOOKEEPER_VERSION"
ZOO_MIRROR_URL="http://apache.osuosl.org/"
ZOO_MIRROR_URL="http://archive.apache.org/dist"


function download_zookeeper(){
Expand Down
8 changes: 5 additions & 3 deletions kazoo/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,10 +366,12 @@ def set_hosts(self, hosts, randomize_hosts=None):

"""

if randomize_hosts is None:
randomize_hosts = self.randomize_hosts
# Change the client setting for randomization if specified
if randomize_hosts is not None:
self.randomize_hosts = randomize_hosts

self.hosts, chroot = collect_hosts(hosts, randomize_hosts)
# Randomizing the list will be done at connect time
self.hosts, chroot = collect_hosts(hosts)

if chroot:
new_chroot = normpath(chroot)
Expand Down
7 changes: 1 addition & 6 deletions kazoo/hosts.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import random

from six.moves import urllib_parse


def collect_hosts(hosts, randomize=True):
def collect_hosts(hosts):
"""Collect a set of hosts and an optional chroot from a string."""
host_ports, chroot = hosts.partition("/")[::2]
chroot = "/" + chroot if chroot else None
Expand All @@ -19,7 +17,4 @@ def collect_hosts(hosts, randomize=True):
port = int(res.port) if res.port else 2181
result.append((host.strip(), port))

if randomize:
random.shuffle(result)

return result, chroot
22 changes: 21 additions & 1 deletion kazoo/protocol/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -478,10 +478,30 @@ def zk_loop(self):
self.client._session_callback(KeeperState.CLOSED)
self.logger.log(BLATHER, 'Connection stopped')

def _expand_client_hosts(self):
# Expand the entire list in advance so we can randomize it if needed
host_ports = []
for host, port in self.client.hosts:
try:
for rhost in socket.getaddrinfo(host.strip(), port, 0, 0, socket.IPPROTO_TCP):
host_ports.append((rhost[4][0], rhost[4][1]))
except socket.gaierror:
# Skip hosts that don't resolve
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do u think about logging this? Seems like a warning would at least be useful (as someone giving an unresolveable host seems not that good).

pass
if self.client.randomize_hosts:
random.shuffle(host_ports)
return host_ports

def _connect_loop(self, retry):
# Iterate through the hosts a full cycle before starting over
status = None
for host, port in self.client.hosts:
host_ports = self._expand_client_hosts()

# Check for an empty hostlist, indicating none resolved
if len(host_ports) == 0:
return STOP_CONNECTING

for host, port in host_ports:
if self.client._stopped.is_set():
status = STOP_CONNECTING
break
Expand Down