Skip to content

Commit 742b13b

Browse files
Adding special handling of client_tracking_on and client_tracking_off for cluster clients. Marking those two functions as deprecated for cluster - embedded client-side caching feature should be used instead. (#3858)
* Adding special handling of client_tracking_on and client_tracking_off for cluster clients. Marking those two functions as deprecated for cluster - embedded client-side caching feature should be used instead. * Update tests/test_cluster.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update redis/commands/cluster.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update redis/commands/cluster.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fixing linters --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 26b9021 commit 742b13b

File tree

4 files changed

+174
-2
lines changed

4 files changed

+174
-2
lines changed

redis/cluster.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1141,7 +1141,7 @@ def _get_command_keys(self, *args):
11411141
redis_conn = self.get_default_node().redis_connection
11421142
return self.commands_parser.get_keys(redis_conn, *args)
11431143

1144-
def determine_slot(self, *args) -> int:
1144+
def determine_slot(self, *args) -> Optional[int]:
11451145
"""
11461146
Figure out what slot to use based on args.
11471147
@@ -1156,6 +1156,12 @@ def determine_slot(self, *args) -> int:
11561156

11571157
# Get the keys in the command
11581158

1159+
# CLIENT TRACKING is a special case.
1160+
# It doesn't have any keys, it needs to be sent to the provided nodes
1161+
# By default it will be sent to all nodes.
1162+
if command.upper() == "CLIENT TRACKING":
1163+
return None
1164+
11591165
# EVAL and EVALSHA are common enough that it's wasteful to go to the
11601166
# redis server to parse the keys. Besides, there is a bug in redis<7.0
11611167
# where `self._get_command_keys()` fails anyway. So, we special case

redis/commands/cluster.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
Mapping,
1212
NoReturn,
1313
Optional,
14+
Sequence,
1415
Union,
1516
)
1617

@@ -25,6 +26,7 @@
2526
PatternT,
2627
ResponseT,
2728
)
29+
from redis.utils import deprecated_function
2830

2931
from .core import (
3032
ACLCommands,
@@ -755,6 +757,76 @@ def readwrite(self, target_nodes: Optional["TargetNodesT"] = None) -> ResponseT:
755757
self.read_from_replicas = False
756758
return self.execute_command("READWRITE", target_nodes=target_nodes)
757759

760+
@deprecated_function(
761+
version="7.2.0",
762+
reason="Use client-side caching feature instead.",
763+
)
764+
def client_tracking_on(
765+
self,
766+
clientid: Optional[int] = None,
767+
prefix: Sequence[KeyT] = [],
768+
bcast: bool = False,
769+
optin: bool = False,
770+
optout: bool = False,
771+
noloop: bool = False,
772+
target_nodes: Optional["TargetNodesT"] = "all",
773+
) -> ResponseT:
774+
"""
775+
Enables the tracking feature of the Redis server, that is used
776+
for server assisted client side caching.
777+
778+
When clientid is provided - in target_nodes only the node that owns the
779+
connection with this id should be provided.
780+
When clientid is not provided - target_nodes can be any node.
781+
782+
For more information see https://redis.io/commands/client-tracking
783+
"""
784+
return self.client_tracking(
785+
True,
786+
clientid,
787+
prefix,
788+
bcast,
789+
optin,
790+
optout,
791+
noloop,
792+
target_nodes=target_nodes,
793+
)
794+
795+
@deprecated_function(
796+
version="7.2.0",
797+
reason="Use client-side caching feature instead.",
798+
)
799+
def client_tracking_off(
800+
self,
801+
clientid: Optional[int] = None,
802+
prefix: Sequence[KeyT] = [],
803+
bcast: bool = False,
804+
optin: bool = False,
805+
optout: bool = False,
806+
noloop: bool = False,
807+
target_nodes: Optional["TargetNodesT"] = "all",
808+
) -> ResponseT:
809+
"""
810+
Disables the tracking feature of the Redis server, that is used
811+
for server assisted client side caching.
812+
813+
When clientid is provided - in target_nodes only the node that owns the
814+
connection with this id should be provided.
815+
When clientid is not provided - target_nodes can be any node.
816+
817+
For more information see https://redis.io/commands/client-tracking
818+
"""
819+
return self.client_tracking(
820+
False,
821+
clientid,
822+
prefix,
823+
bcast,
824+
optin,
825+
optout,
826+
noloop,
827+
target_nodes=target_nodes,
828+
)
829+
758830

759831
class AsyncClusterManagementCommands(
760832
ClusterManagementCommands, AsyncManagementCommands
@@ -782,6 +854,76 @@ async def cluster_delslots(self, *slots: EncodableT) -> List[bool]:
782854
)
783855
)
784856

857+
@deprecated_function(
858+
version="7.2.0",
859+
reason="Use client-side caching feature instead.",
860+
)
861+
async def client_tracking_on(
862+
self,
863+
clientid: Optional[int] = None,
864+
prefix: Sequence[KeyT] = [],
865+
bcast: bool = False,
866+
optin: bool = False,
867+
optout: bool = False,
868+
noloop: bool = False,
869+
target_nodes: Optional["TargetNodesT"] = "all",
870+
) -> ResponseT:
871+
"""
872+
Enables the tracking feature of the Redis server, that is used
873+
for server assisted client side caching.
874+
875+
When clientid is provided - in target_nodes only the node that owns the
876+
connection with this id should be provided.
877+
When clientid is not provided - target_nodes can be any node.
878+
879+
For more information see https://redis.io/commands/client-tracking
880+
"""
881+
return await self.client_tracking(
882+
True,
883+
clientid,
884+
prefix,
885+
bcast,
886+
optin,
887+
optout,
888+
noloop,
889+
target_nodes=target_nodes,
890+
)
891+
892+
@deprecated_function(
893+
version="7.2.0",
894+
reason="Use client-side caching feature instead.",
895+
)
896+
async def client_tracking_off(
897+
self,
898+
clientid: Optional[int] = None,
899+
prefix: Sequence[KeyT] = [],
900+
bcast: bool = False,
901+
optin: bool = False,
902+
optout: bool = False,
903+
noloop: bool = False,
904+
target_nodes: Optional["TargetNodesT"] = "all",
905+
) -> ResponseT:
906+
"""
907+
Disables the tracking feature of the Redis server, that is used
908+
for server assisted client side caching.
909+
910+
When clientid is provided - in target_nodes only the node that owns the
911+
connection with this id should be provided.
912+
When clientid is not provided - target_nodes can be any node.
913+
914+
For more information see https://redis.io/commands/client-tracking
915+
"""
916+
return await self.client_tracking(
917+
False,
918+
clientid,
919+
prefix,
920+
bcast,
921+
optin,
922+
optout,
923+
noloop,
924+
target_nodes=target_nodes,
925+
)
926+
785927

786928
class ClusterDataAccessCommands(DataAccessCommands):
787929
"""

redis/commands/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ def client_tracking(
685685
if noloop:
686686
pieces.append("NOLOOP")
687687

688-
return self.execute_command("CLIENT TRACKING", *pieces)
688+
return self.execute_command("CLIENT TRACKING", *pieces, **kwargs)
689689

690690
def client_trackinginfo(self, **kwargs) -> ResponseT:
691691
"""

tests/test_cluster.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,30 @@ def test_client_trackinginfo(self, r):
17421742
assert len(res) > 2
17431743
assert "prefixes" in res or b"prefixes" in res
17441744

1745+
@skip_if_server_version_lt("6.0.0")
1746+
@skip_if_redis_enterprise()
1747+
def test_client_tracking(self, r):
1748+
# simple case - will execute on all nodes
1749+
assert r.client_tracking_on()
1750+
assert r.client_tracking_off()
1751+
1752+
# id based
1753+
node = r.get_default_node()
1754+
# when id is provided - the command should be sent to the node that
1755+
# owns the connection with this id
1756+
client_id = node.redis_connection.client_id()
1757+
assert r.client_tracking_on(clientid=client_id, target_nodes=node)
1758+
assert r.client_tracking_off(clientid=client_id, target_nodes=node)
1759+
1760+
# execute with client id and prefixes and bcast
1761+
assert r.client_tracking_on(
1762+
clientid=client_id, prefix=["foo", "bar"], bcast=True, target_nodes=node
1763+
)
1764+
1765+
# now with some prefixes and without bcast
1766+
with pytest.raises(DataError):
1767+
assert r.client_tracking_on(prefix=["foo", "bar", "blee"])
1768+
17451769
@skip_if_server_version_lt("2.9.50")
17461770
def test_client_pause(self, r):
17471771
node = r.get_primaries()[0]

0 commit comments

Comments
 (0)