Skip to content

Commit 6e7f052

Browse files
cluster/scan_iter: fix iteration
1 parent d0e0126 commit 6e7f052

File tree

3 files changed

+56
-7
lines changed

3 files changed

+56
-7
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Add limited support for Lua scripting with RedisCluster
55
* Implement `.lock()` method on RedisCluster
66
* Fix cursor returned by SCAN for RedisCluster & change default target to PRIMARIES
7+
* Fix scan_iter for RedisCluster
78

89
* 4.1.3 (Feb 8, 2022)
910
* Fix flushdb and flushall (#1926)

redis/commands/cluster.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
from typing import Iterator, Union
2+
13
from redis.crc import key_slot
24
from redis.exceptions import RedisClusterException, RedisError
5+
from redis.typing import PatternT
36

47
from .core import (
58
ACLCommands,
@@ -206,6 +209,41 @@ def stralgo(
206209
**kwargs,
207210
)
208211

212+
def scan_iter(
213+
self,
214+
match: Union[PatternT, None] = None,
215+
count: Union[int, None] = None,
216+
_type: Union[str, None] = None,
217+
**kwargs,
218+
) -> Iterator:
219+
# Do the first query with cursor=0 for all nodes
220+
cursors, data = self.scan(match=match, count=count, _type=_type, **kwargs)
221+
yield from data
222+
223+
cursors = {name: cursor for name, cursor in cursors.items() if cursor != 0}
224+
if cursors:
225+
# Get nodes by name
226+
nodes = {name: self.get_node(node_name=name) for name in cursors.keys()}
227+
228+
# Iterate over each node till its cursor is 0
229+
kwargs.pop("target_nodes", None)
230+
while cursors:
231+
for name, cursor in cursors.items():
232+
cur, data = self.scan(
233+
cursor=cursor,
234+
match=match,
235+
count=count,
236+
_type=_type,
237+
target_nodes=nodes[name],
238+
**kwargs,
239+
)
240+
yield from data
241+
cursors[name] = cur[name]
242+
243+
cursors = {
244+
name: cursor for name, cursor in cursors.items() if cursor != 0
245+
}
246+
209247

210248
class RedisClusterCommands(
211249
ClusterMultiKeyCommands,

tests/test_cluster.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,13 +1810,23 @@ def test_cluster_scan_type(self, r):
18101810

18111811
@skip_if_server_version_lt("2.8.0")
18121812
def test_cluster_scan_iter(self, r):
1813-
r.set("a", 1)
1814-
r.set("b", 2)
1815-
r.set("c", 3)
1816-
keys = list(r.scan_iter(target_nodes="primaries"))
1817-
assert set(keys) == {b"a", b"b", b"c"}
1818-
keys = list(r.scan_iter(match="a", target_nodes="primaries"))
1819-
assert set(keys) == {b"a"}
1813+
keys_all = []
1814+
keys_1 = []
1815+
for i in range(100):
1816+
s = str(i)
1817+
r.set(s, 1)
1818+
keys_all.append(s.encode("utf-8"))
1819+
if s.startswith("1"):
1820+
keys_1.append(s.encode("utf-8"))
1821+
keys_all.sort()
1822+
keys_1.sort()
1823+
1824+
for target_nodes in ["primaries", "replicas"]:
1825+
keys = r.scan_iter(target_nodes=target_nodes)
1826+
assert sorted(keys) == keys_all
1827+
1828+
keys = r.scan_iter(match="1*", target_nodes=target_nodes)
1829+
assert sorted(keys) == keys_1
18201830

18211831
def test_cluster_randomkey(self, r):
18221832
node = r.get_node_from_key("{foo}")

0 commit comments

Comments
 (0)