Skip to content

Commit 4e7d62b

Browse files
committed
Merge branch 'master' of github.com:redis/redis-py into vv-async-multi-exec-cluster
2 parents e846aab + 36619a5 commit 4e7d62b

File tree

5 files changed

+89
-11
lines changed

5 files changed

+89
-11
lines changed

README.md

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,17 @@ The Python interface to the Redis key-value store.
3131

3232
## Installation
3333

34-
Start a redis via docker:
34+
Start a redis via docker (for Redis versions >= 8.0):
3535

3636
``` bash
37-
docker run -p 6379:6379 -it redis/redis-stack:latest
37+
docker run -p 6379:6379 -it redis:latest
3838
```
3939

40+
Start a redis via docker (for Redis versions < 8.0):
41+
42+
``` bash
43+
docker run -p 6379:6379 -it redis/redis-stack:latest
44+
4045
To install redis-py, simply:
4146

4247
``` bash
@@ -54,15 +59,16 @@ Looking for a high-level library to handle object mapping? See [redis-om-python]
5459

5560
## Supported Redis Versions
5661

57-
The most recent version of this library supports redis version [5.0](https://github.com/redis/redis/blob/5.0/00-RELEASENOTES), [6.0](https://github.com/redis/redis/blob/6.0/00-RELEASENOTES), [6.2](https://github.com/redis/redis/blob/6.2/00-RELEASENOTES), [7.0](https://github.com/redis/redis/blob/7.0/00-RELEASENOTES), [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES) and [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES).
62+
The most recent version of this library supports Redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES) and [8.0](https://github.com/redis/redis/blob/8.0/00-RELEASENOTES).
5863

5964
The table below highlights version compatibility of the most-recent library versions and redis versions.
6065

6166
| Library version | Supported redis versions |
6267
|-----------------|-------------------|
6368
| 3.5.3 | <= 6.2 Family of releases |
6469
| >= 4.5.0 | Version 5.0 to 7.0 |
65-
| >= 5.0.0 | Version 5.0 to current |
70+
| >= 5.0.0 | Version 5.0 to 7.4 |
71+
| >= 6.0.0 | Version 7.2 to current |
6672

6773

6874
## Usage
@@ -152,8 +158,42 @@ The following example shows how to utilize [Redis Pub/Sub](https://redis.io/docs
152158
{'pattern': None, 'type': 'subscribe', 'channel': b'my-second-channel', 'data': 1}
153159
```
154160

161+
### Redis’ search and query capabilities default dialect
162+
163+
Release 6.0.0 introduces a client-side default dialect for Redis’ search and query capabilities.
164+
By default, the client now overrides the server-side dialect with version 2, automatically appending *DIALECT 2* to commands like *FT.AGGREGATE* and *FT.SEARCH*.
155165

156-
--------------------------
166+
**Important**: Be aware that the query dialect may impact the results returned. If needed, you can revert to a different dialect version by configuring the client accordingly.
167+
168+
``` python
169+
>>> from redis.commands.search.field import TextField
170+
>>> from redis.commands.search.query import Query
171+
>>> from redis.commands.search.index_definition import IndexDefinition
172+
>>> import redis
173+
174+
>>> r = redis.Redis(host='localhost', port=6379, db=0)
175+
>>> r.ft().create_index(
176+
>>> (TextField("name"), TextField("lastname")),
177+
>>> definition=IndexDefinition(prefix=["test:"]),
178+
>>> )
179+
180+
>>> r.hset("test:1", "name", "James")
181+
>>> r.hset("test:1", "lastname", "Brown")
182+
183+
>>> # Query with default DIALECT 2
184+
>>> query = "@name: James Brown"
185+
>>> q = Query(query)
186+
>>> res = r.ft().search(q)
187+
188+
>>> # Query with explicit DIALECT 1
189+
>>> query = "@name: James Brown"
190+
>>> q = Query(query).dialect(1)
191+
>>> res = r.ft().search(q)
192+
```
193+
194+
You can find further details in the [query dialect documentation](https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/dialects/).
195+
196+
---------------------------------------------
157197

158198
### Author
159199

@@ -169,4 +209,4 @@ Special thanks to:
169209
system.
170210
- Paul Hubbard for initial packaging support.
171211

172-
[![Redis](./docs/_static/logo-redis.svg)](https://redis.io)
212+
[![Redis](./docs/_static/logo-redis.svg)](https://redis.io)

docker-compose.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
---
2+
# image tag 8.0-RC2-pre is the one matching the 8.0 GA release
23
x-client-libs-stack-image: &client-libs-stack-image
3-
image: "redislabs/client-libs-test:${CLIENT_LIBS_TEST_STACK_IMAGE_TAG:-rs-7.4.0-v2}"
4+
image: "redislabs/client-libs-test:${CLIENT_LIBS_TEST_STACK_IMAGE_TAG:-8.0-RC2-pre}"
45

56
x-client-libs-image: &client-libs-image
6-
image: "redislabs/client-libs-test:${CLIENT_LIBS_TEST_IMAGE_TAG:-7.4.2}"
7+
image: "redislabs/client-libs-test:${CLIENT_LIBS_TEST_IMAGE_TAG:-8.0-RC2-pre}"
78

89
services:
910

redis/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def int_or_str(value):
4545
return value
4646

4747

48-
__version__ = "5.2.1"
48+
__version__ = "6.1.0"
4949
VERSION = tuple(map(int_or_str, __version__.split(".")))
5050

5151

redis/asyncio/cluster.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,14 @@ class RedisCluster(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterCommand
145145
| Enable read from replicas in READONLY mode and defines the load balancing
146146
strategy that will be used for cluster node selection.
147147
The data read from replicas is eventually consistent with the data in primary nodes.
148+
:param dynamic_startup_nodes:
149+
| Set the RedisCluster's startup nodes to all the discovered nodes.
150+
If true (default value), the cluster's discovered nodes will be used to
151+
determine the cluster nodes-slots mapping in the next topology refresh.
152+
It will remove the initial passed startup nodes if their endpoints aren't
153+
listed in the CLUSTER SLOTS output.
154+
If you use dynamic DNS endpoints for startup nodes but CLUSTER SLOTS lists
155+
specific IP addresses, it is best to set it to false.
148156
:param reinitialize_steps:
149157
| Specifies the number of MOVED errors that need to occur before reinitializing
150158
the whole cluster topology. If a MOVED error occurs and the cluster does not
@@ -261,6 +269,7 @@ def __init__(
261269
require_full_coverage: bool = True,
262270
read_from_replicas: bool = False,
263271
load_balancing_strategy: Optional[LoadBalancingStrategy] = None,
272+
dynamic_startup_nodes: bool = True,
264273
reinitialize_steps: int = 5,
265274
cluster_error_retry_attempts: int = 3,
266275
max_connections: int = 2**31,
@@ -399,6 +408,7 @@ def __init__(
399408
startup_nodes,
400409
require_full_coverage,
401410
kwargs,
411+
dynamic_startup_nodes=dynamic_startup_nodes,
402412
address_remap=address_remap,
403413
event_dispatcher=self._event_dispatcher,
404414
)
@@ -1200,6 +1210,7 @@ async def _mock(self, error: RedisError):
12001210

12011211
class NodesManager:
12021212
__slots__ = (
1213+
"_dynamic_startup_nodes",
12031214
"_moved_exception",
12041215
"_event_dispatcher",
12051216
"connection_kwargs",
@@ -1217,6 +1228,7 @@ def __init__(
12171228
startup_nodes: List["ClusterNode"],
12181229
require_full_coverage: bool,
12191230
connection_kwargs: Dict[str, Any],
1231+
dynamic_startup_nodes: bool = True,
12201232
address_remap: Optional[Callable[[Tuple[str, int]], Tuple[str, int]]] = None,
12211233
event_dispatcher: Optional[EventDispatcher] = None,
12221234
) -> None:
@@ -1229,6 +1241,8 @@ def __init__(
12291241
self.nodes_cache: Dict[str, "ClusterNode"] = {}
12301242
self.slots_cache: Dict[int, List["ClusterNode"]] = {}
12311243
self.read_load_balancer = LoadBalancer()
1244+
1245+
self._dynamic_startup_nodes: bool = dynamic_startup_nodes
12321246
self._moved_exception: MovedError = None
12331247
if event_dispatcher is None:
12341248
self._event_dispatcher = EventDispatcher()
@@ -1474,8 +1488,10 @@ async def initialize(self) -> None:
14741488
# Set the tmp variables to the real variables
14751489
self.slots_cache = tmp_slots
14761490
self.set_nodes(self.nodes_cache, tmp_nodes_cache, remove_old=True)
1477-
# Populate the startup nodes with all discovered nodes
1478-
self.set_nodes(self.startup_nodes, self.nodes_cache, remove_old=True)
1491+
1492+
if self._dynamic_startup_nodes:
1493+
# Populate the startup nodes with all discovered nodes
1494+
self.set_nodes(self.startup_nodes, self.nodes_cache, remove_old=True)
14791495

14801496
# Set the default node
14811497
self.default_node = self.get_nodes_by_server_type(PRIMARY)[0]

tests/test_asyncio/test_cluster.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2723,6 +2723,27 @@ def cmd_init_mock(self, r: ClusterNode) -> None:
27232723
assert rc.get_node(host=default_host, port=7001) is not None
27242724
assert rc.get_node(host=default_host, port=7002) is not None
27252725

2726+
@pytest.mark.parametrize("dynamic_startup_nodes", [True, False])
2727+
async def test_init_slots_dynamic_startup_nodes(self, dynamic_startup_nodes):
2728+
rc = await get_mocked_redis_client(
2729+
2730+
port=7000,
2731+
cluster_slots=default_cluster_slots,
2732+
dynamic_startup_nodes=dynamic_startup_nodes,
2733+
)
2734+
# Nodes are taken from default_cluster_slots
2735+
discovered_nodes = [
2736+
"127.0.0.1:7000",
2737+
"127.0.0.1:7001",
2738+
"127.0.0.1:7002",
2739+
"127.0.0.1:7003",
2740+
]
2741+
startup_nodes = list(rc.nodes_manager.startup_nodes.keys())
2742+
if dynamic_startup_nodes is True:
2743+
assert sorted(startup_nodes) == sorted(discovered_nodes)
2744+
else:
2745+
assert startup_nodes == ["[email protected]:7000"]
2746+
27262747

27272748
class TestClusterPipeline:
27282749
"""Tests for the ClusterPipeline class."""

0 commit comments

Comments
 (0)