Skip to content
This repository was archived by the owner on Jan 9, 2024. It is now read-only.

Commit df5ff0e

Browse files
committed
Merge branch 'unstable'
2 parents 5b105cb + 4fb6b47 commit df5ff0e

File tree

13 files changed

+149
-16
lines changed

13 files changed

+149
-16
lines changed

.travis.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@ python:
99
services:
1010
- redis-server
1111
install:
12-
- "if [[ $REDIS_VERSION == '3.0' ]]; then REDIS_VERSION=3.0.7 make redis-install; fi"
13-
- "if [[ $REDIS_VERSION == '3.2' ]]; then REDIS_VERSION=3.2.0-rc3 make redis-install; fi"
12+
- "if [[ $REDIS_VERSION == '3.0' ]]; then REDIS_VERSION=3.0 make redis-install; fi"
13+
- "if [[ $REDIS_VERSION == '3.2' ]]; then REDIS_VERSION=3.2 make redis-install; fi"
1414
- pip install -r dev-requirements.txt
1515
- pip install -e .
1616
- "if [[ $HIREDIS == '1' ]]; then pip install hiredis; fi"
1717
env:
18-
# Redis 3.0.7
18+
# Redis 3.0
1919
- HIREDIS=0 REDIS_VERSION=3.0
20-
# Redis 3.0.7 and HIREDIS
20+
# Redis 3.0 and HIREDIS
2121
- HIREDIS=1 REDIS_VERSION=3.0
22-
# Redis 3.2.0-rc3
22+
# Redis 3.2
2323
- HIREDIS=0 REDIS_VERSION=3.2
24-
# Redis 3.2.0-rc3 and HIREDIS
24+
# Redis 3.2 and HIREDIS
2525
- HIREDIS=1 REDIS_VERSION=3.2
2626
script:
2727
- make start

docs/authors.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ Authors who contributed code or testing:
2020
- gmolight - https://github.com/gmolight
2121
- baranbartu - https://github.com/baranbartu
2222
- monklof - https://github.com/monklof
23+
- dutradda - https://github.com/dutradda

docs/release-notes.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
Release Notes
22
=============
33

4+
1.3.2 (Nov 27, 2016)
5+
--------------------
6+
7+
* Fix a bug where from_url was not possible to use without passing in additional variables. Now it works as the same method from redis-py.
8+
Note that the same rules that is currently in place for passing ip addresses/dns names into startup_nodes variable apply the same way through
9+
the from_url method.
10+
* Added options to skip full coverage check. This flag is useful when the CONFIG redis command is disabled by the server.
11+
* Fixed a bug where method *CLUSTER SLOTS* would break in newer redis versions where node id is included in the reponse. Method is not compatible with both old and new redis versions.
12+
413

514
1.3.1 (Oct 13, 2016)
615
--------------------

docs/upgrading.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ Upgrading redis-py-cluster
33

44
This document describes what must be done when upgrading between different versions to ensure that code still works.
55

6+
7+
1.3.1 --> 1.3.2
8+
---------------
9+
10+
If your redis instance is configured to not have the `CONFIG ...` comannds enabled due to security reasons you need to pass this into the client object `skip_full_coverage_check=True`. Benefits is that the client class no longer requires the `CONFIG ...` commands to be enabled on the server. Downsides is that you can't use the option in your redis server and still use the same feature in this client.
11+
12+
13+
614
1.3.0 --> 1.3.1
715
---------------
816

ptp-debug.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44

55
# Note: decode_responses must be set to True when used with python3
66
rc = StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True)
7+
url_client = StrictRedisCluster.from_url('http://127.0.0.1:7000')
78

89
__import__('ptpdb').set_trace()

rediscluster/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
setattr(redis, "StrictClusterPipeline", StrictClusterPipeline)
1717

1818
# Major, Minor, Fix version
19-
__version__ = (1, 3, 1)
19+
__version__ = (1, 3, 2)
2020

2121
if sys.version_info[0:3] == (3, 4, 0):
2222
raise RuntimeError("CRITICAL: rediscluster do not work with python 3.4.0. Please use 3.4.1 or higher.")

rediscluster/client.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class StrictRedisCluster(StrictRedis):
113113
}
114114

115115
def __init__(self, host=None, port=None, startup_nodes=None, max_connections=32, max_connections_per_node=False, init_slot_cache=True,
116-
readonly_mode=False, reinitialize_steps=None, **kwargs):
116+
readonly_mode=False, reinitialize_steps=None, skip_full_coverage_check=False, **kwargs):
117117
"""
118118
:startup_nodes:
119119
List of nodes that initial bootstrapping can be done from
@@ -125,6 +125,9 @@ def __init__(self, host=None, port=None, startup_nodes=None, max_connections=32,
125125
Maximum number of connections that should be kept open at one time
126126
:readonly_mode:
127127
enable READONLY mode. You can read possibly stale data from slave.
128+
:skip_full_coverage_check:
129+
Skips the check of cluster-require-full-coverage config, useful for clusters
130+
without the CONFIG command (like aws)
128131
:**kwargs:
129132
Extra arguments that will be sent into StrictRedis instance when created
130133
(See Official redis-py doc for supported kwargs
@@ -156,6 +159,7 @@ def __init__(self, host=None, port=None, startup_nodes=None, max_connections=32,
156159
max_connections=max_connections,
157160
reinitialize_steps=reinitialize_steps,
158161
max_connections_per_node=max_connections_per_node,
162+
skip_full_coverage_check=skip_full_coverage_check,
159163
**kwargs
160164
)
161165

@@ -167,6 +171,30 @@ def __init__(self, host=None, port=None, startup_nodes=None, max_connections=32,
167171
self.response_callbacks = self.__class__.RESPONSE_CALLBACKS.copy()
168172
self.response_callbacks = dict_merge(self.response_callbacks, self.CLUSTER_COMMANDS_RESPONSE_CALLBACKS)
169173

174+
@classmethod
175+
def from_url(cls, url, db=None, skip_full_coverage_check=False, **kwargs):
176+
"""
177+
Return a Redis client object configured from the given URL, which must
178+
use either `the ``redis://`` scheme
179+
<http://www.iana.org/assignments/uri-schemes/prov/redis>`_ for RESP
180+
connections or the ``unix://`` scheme for Unix domain sockets.
181+
For example::
182+
redis://[:password]@localhost:6379/0
183+
unix://[:password]@/path/to/socket.sock?db=0
184+
There are several ways to specify a database number. The parse function
185+
will return the first specified option:
186+
1. A ``db`` querystring option, e.g. redis://localhost?db=0
187+
2. If using the redis:// scheme, the path argument of the url, e.g.
188+
redis://localhost/0
189+
3. The ``db`` argument to this function.
190+
If none of these options are specified, db=0 is used.
191+
Any additional querystring arguments and keyword arguments will be
192+
passed along to the ConnectionPool class's initializer. In the case
193+
of conflicting arguments, querystring arguments always win.
194+
"""
195+
connection_pool = ClusterConnectionPool.from_url(url, db=db, **kwargs)
196+
return cls(connection_pool=connection_pool, skip_full_coverage_check=skip_full_coverage_check)
197+
170198
def __repr__(self):
171199
"""
172200
"""

rediscluster/connection.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,32 @@ class ClusterConnectionPool(ConnectionPool):
7070
RedisClusterDefaultTimeout = None
7171

7272
def __init__(self, startup_nodes=None, init_slot_cache=True, connection_class=ClusterConnection,
73-
max_connections=None, max_connections_per_node=False, reinitialize_steps=None, **connection_kwargs):
73+
max_connections=None, max_connections_per_node=False, reinitialize_steps=None,
74+
skip_full_coverage_check=False, **connection_kwargs):
7475
"""
76+
:skip_full_coverage_check:
77+
Skips the check of cluster-require-full-coverage config, useful for clusters
78+
without the CONFIG command (like aws)
7579
"""
7680
super(ClusterConnectionPool, self).__init__(connection_class=connection_class, max_connections=max_connections)
7781

82+
# Special case to make from_url method compliant with cluster setting.
83+
# from_url method will send in the ip and port through a different variable then the
84+
# regular startup_nodes variable.
85+
if startup_nodes is None:
86+
if 'port' in connection_kwargs and 'host' in connection_kwargs:
87+
startup_nodes = [{
88+
'host': connection_kwargs.pop('host'),
89+
'port': str(connection_kwargs.pop('port')),
90+
}]
91+
92+
print(startup_nodes)
93+
7894
self.max_connections = max_connections or 2 ** 31
7995
self.max_connections_per_node = max_connections_per_node
8096

81-
self.nodes = NodeManager(startup_nodes, reinitialize_steps=reinitialize_steps, **connection_kwargs)
97+
self.nodes = NodeManager(startup_nodes, reinitialize_steps=reinitialize_steps,
98+
skip_full_coverage_check=skip_full_coverage_check, **connection_kwargs)
8299
if init_slot_cache:
83100
self.nodes.initialize()
84101

rediscluster/nodemanager.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ class NodeManager(object):
1919
"""
2020
RedisClusterHashSlots = 16384
2121

22-
def __init__(self, startup_nodes=None, reinitialize_steps=None, **connection_kwargs):
22+
def __init__(self, startup_nodes=None, reinitialize_steps=None, skip_full_coverage_check=False, **connection_kwargs):
2323
"""
24+
:skip_full_coverage_check:
25+
Skips the check of cluster-require-full-coverage config, useful for clusters
26+
without the CONFIG command (like aws)
2427
"""
2528
self.connection_kwargs = connection_kwargs
2629
self.nodes = {}
@@ -29,6 +32,7 @@ def __init__(self, startup_nodes=None, reinitialize_steps=None, **connection_kwa
2932
self.orig_startup_nodes = [node for node in self.startup_nodes]
3033
self.reinitialize_counter = 0
3134
self.reinitialize_steps = reinitialize_steps or 25
35+
self._skip_full_coverage_check = skip_full_coverage_check
3236

3337
if not self.startup_nodes:
3438
raise RedisClusterException("No startup nodes provided")
@@ -218,7 +222,10 @@ def initialize(self):
218222
self.populate_startup_nodes()
219223
self.refresh_table_asap = False
220224

221-
need_full_slots_coverage = self.cluster_require_full_coverage(nodes_cache)
225+
if self._skip_full_coverage_check:
226+
need_full_slots_coverage = False
227+
else:
228+
need_full_slots_coverage = self.cluster_require_full_coverage(nodes_cache)
222229

223230
# Validate if all slots are covered or if we should try next startup node
224231
for i in range(0, self.RedisClusterHashSlots):

rediscluster/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ def parse_cluster_slots(resp, **options):
125125
"""
126126
current_host = options.get('current_host', '')
127127

128-
def fix_server(host, port):
129-
return (host or current_host, port)
128+
def fix_server(*args):
129+
return (args[0] or current_host, args[1])
130130

131131
slots = {}
132132
for slot in resp:

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
setup(
2222
name="redis-py-cluster",
23-
version="1.3.1",
23+
version="1.3.2",
2424
description="Cluster library for redis 3.0.0 built on top of redis-py lib",
2525
long_description=readme + '\n\n' + history,
2626
author="Johan Andersson",

tests/test_cluster_obj.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
from tests.conftest import _get_client, skip_if_server_version_lt, skip_if_not_password_protected_nodes
1616

1717
# 3rd party imports
18-
from mock import patch, Mock
18+
from mock import patch, Mock, MagicMock
1919
from redis._compat import b, unicode
20+
from redis import StrictRedis
2021
import pytest
2122

2223
pytestmark = skip_if_server_version_lt('2.9.0')
@@ -107,6 +108,17 @@ def test_custom_connectionpool():
107108
assert {"host": h, "port": p} in c.connection_pool.nodes.startup_nodes
108109

109110

111+
@patch('rediscluster.nodemanager.StrictRedis', new=MagicMock())
112+
def test_skip_full_coverage_check():
113+
"""
114+
Test if the cluster_require_full_coverage NodeManager method was not called with the flag activated
115+
"""
116+
c = StrictRedisCluster("192.168.0.1", 7001, init_slot_cache=False, skip_full_coverage_check=True)
117+
c.connection_pool.nodes.cluster_require_full_coverage = MagicMock()
118+
c.connection_pool.nodes.initialize()
119+
assert not c.connection_pool.nodes.cluster_require_full_coverage.called
120+
121+
110122
def test_blocked_commands(r):
111123
"""
112124
These commands should be blocked and raise RedisClusterException

tests/test_utils.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,63 @@
1414
merge_result,
1515
first_key,
1616
clusterdown_wrapper,
17+
parse_cluster_slots,
1718
)
1819

1920
# 3rd party imports
2021
import pytest
2122
from redis._compat import unicode
2223

2324

25+
def test_parse_cluster_slots():
26+
"""
27+
Example raw output from redis cluster. Output is form a redis 3.2.x node
28+
that includes the id in the reponse. The test below that do not include the id
29+
is to validate that the code is compatible with redis versions that do not contain
30+
that value in the response from the server.
31+
32+
127.0.0.1:10000> cluster slots
33+
1) 1) (integer) 5461
34+
2) (integer) 10922
35+
3) 1) "10.0.0.1"
36+
2) (integer) 10000
37+
3) "3588b4cf9fc72d57bb262a024747797ead0cf7ea"
38+
4) 1) "10.0.0.4"
39+
2) (integer) 10000
40+
3) "a72c02c7d85f4ec3145ab2c411eefc0812aa96b0"
41+
2) 1) (integer) 10923
42+
2) (integer) 16383
43+
3) 1) "10.0.0.2"
44+
2) (integer) 10000
45+
3) "ffd36d8d7cb10d813f81f9662a835f6beea72677"
46+
4) 1) "10.0.0.5"
47+
2) (integer) 10000
48+
3) "5c15b69186017ddc25ebfac81e74694fc0c1a160"
49+
3) 1) (integer) 0
50+
2) (integer) 5460
51+
3) 1) "10.0.0.3"
52+
2) (integer) 10000
53+
3) "069cda388c7c41c62abe892d9e0a2d55fbf5ffd5"
54+
4) 1) "10.0.0.6"
55+
2) (integer) 10000
56+
3) "dc152a08b4cf1f2a0baf775fb86ad0938cb907dc"
57+
"""
58+
mock_response = [
59+
[0, 5460, ['172.17.0.2', 7000], ['172.17.0.2', 7003]],
60+
[5461, 10922, ['172.17.0.2', 7001], ['172.17.0.2', 7004]],
61+
[10923, 16383, ['172.17.0.2', 7002], ['172.17.0.2', 7005]]
62+
]
63+
parse_cluster_slots(mock_response)
64+
65+
extended_mock_response = [
66+
[0, 5460, ['172.17.0.2', 7000, 'ffd36d8d7cb10d813f81f9662a835f6beea72677'], ['172.17.0.2', 7003, '5c15b69186017ddc25ebfac81e74694fc0c1a160']],
67+
[5461, 10922, ['172.17.0.2', 7001, '069cda388c7c41c62abe892d9e0a2d55fbf5ffd5'], ['172.17.0.2', 7004, 'dc152a08b4cf1f2a0baf775fb86ad0938cb907dc']],
68+
[10923, 16383, ['172.17.0.2', 7002, '3588b4cf9fc72d57bb262a024747797ead0cf7ea'], ['172.17.0.2', 7005, 'a72c02c7d85f4ec3145ab2c411eefc0812aa96b0']]
69+
]
70+
71+
parse_cluster_slots(extended_mock_response)
72+
73+
2474
def test_string_keys_to():
2575
def mock_true():
2676
return True

0 commit comments

Comments
 (0)