From a52a58ece49a1b6dfa0c1c42a6231ee550a35041 Mon Sep 17 00:00:00 2001 From: Aaron <69273634+aaron-congo@users.noreply.github.com> Date: Mon, 27 May 2024 15:24:23 -0700 Subject: [PATCH] Python: add OBJECT FREQ command (#1472) Python: add OBJECT FREQ command (#311) --- CHANGELOG.md | 1 + python/python/glide/async_commands/core.py | 22 ++++++++++++++ .../glide/async_commands/transaction.py | 15 ++++++++++ python/python/tests/test_async_client.py | 20 +++++++++++++ python/python/tests/test_transaction.py | 29 ++++++++++++++++++- 5 files changed, 86 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f593bb5cc4..8322ff2c7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ #### Changes * Python: Added OBJECT ENCODING command ([#1471](https://github.com/aws/glide-for-redis/pull/1471)) +* Python: Added OBJECT FREQ command ([#1472](https://github.com/aws/glide-for-redis/pull/1472)) ## 0.4.0 (2024-05-26) diff --git a/python/python/glide/async_commands/core.py b/python/python/glide/async_commands/core.py index 6bfd25ea19..cd86ef09fb 100644 --- a/python/python/glide/async_commands/core.py +++ b/python/python/glide/async_commands/core.py @@ -3544,3 +3544,25 @@ async def object_encoding(self, key: str) -> Optional[str]: Optional[str], await self._execute_command(RequestType.ObjectEncoding, [key]), ) + + async def object_freq(self, key: str) -> int: + """ + Returns the logarithmic access frequency counter of a Redis object stored at `key`. + + See https://valkey.io/commands/object-freq for more details. + + Args: + key (str): The key of the object to get the logarithmic access frequency counter of. + + Returns: + int: If `key` exists, returns the logarithmic access frequency counter of the object stored at `key` as an + integer. Otherwise, returns None. + + Examples: + >>> await client.object_freq("my_hash") + 2 # The logarithmic access frequency counter of "my_hash" has a value of 2. + """ + return cast( + int, + await self._execute_command(RequestType.ObjectFreq, [key]), + ) diff --git a/python/python/glide/async_commands/transaction.py b/python/python/glide/async_commands/transaction.py index 127dfd201a..80130da109 100644 --- a/python/python/glide/async_commands/transaction.py +++ b/python/python/glide/async_commands/transaction.py @@ -2475,6 +2475,21 @@ def object_encoding(self: TTransaction, key: str) -> TTransaction: """ return self.append_command(RequestType.ObjectEncoding, [key]) + def object_freq(self: TTransaction, key: str) -> TTransaction: + """ + Returns the logarithmic access frequency counter of a Redis object stored at `key`. + + See https://valkey.io/commands/object-freq for more details. + + Args: + key (str): The key of the object to get the logarithmic access frequency counter of. + + Command response: + int: If `key` exists, returns the logarithmic access frequency counter of the object stored at `key` as an + integer. Otherwise, returns None. + """ + return self.append_command(RequestType.ObjectFreq, [key]) + class Transaction(BaseTransaction): """ diff --git a/python/python/tests/test_async_client.py b/python/python/tests/test_async_client.py index f28fb54abe..0e639a4afe 100644 --- a/python/python/tests/test_async_client.py +++ b/python/python/tests/test_async_client.py @@ -3393,6 +3393,26 @@ async def test_object_encoding(self, redis_client: TRedisClient): assert await redis_client.xadd(stream_key, [("field", "value")]) is not None assert await redis_client.object_encoding(stream_key) == "stream" + @pytest.mark.parametrize("cluster_mode", [True, False]) + @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) + async def test_object_freq(self, redis_client: TRedisClient): + key = get_random_string(10) + non_existing_key = get_random_string(10) + maxmemory_policy_key = "maxmemory-policy" + config = await redis_client.config_get([maxmemory_policy_key]) + maxmemory_policy = cast(str, config.get(maxmemory_policy_key)) + + try: + assert ( + await redis_client.config_set({maxmemory_policy_key: "allkeys-lfu"}) + == OK + ) + assert await redis_client.object_freq(non_existing_key) is None + assert await redis_client.set(key, "") == OK + assert await redis_client.object_freq(key) >= 0 + finally: + await redis_client.config_set({maxmemory_policy_key: maxmemory_policy}) + class TestMultiKeyCommandCrossSlot: @pytest.mark.parametrize("cluster_mode", [True]) diff --git a/python/python/tests/test_transaction.py b/python/python/tests/test_transaction.py index 54121ccb07..a8cb937a88 100644 --- a/python/python/tests/test_transaction.py +++ b/python/python/tests/test_transaction.py @@ -1,7 +1,7 @@ # Copyright GLIDE-for-Redis Project Contributors - SPDX Identifier: Apache-2.0 from datetime import datetime, timezone -from typing import List, Union +from typing import List, Union, cast import pytest from glide import RequestError @@ -501,3 +501,30 @@ async def test_transaction_chaining_calls(self, redis_client: TRedisClient): transaction.set(key, "value").get(key).delete([key]) assert await redis_client.exec(transaction) == [OK, "value", 1] + + # object_freq is not tested in transaction_test as it requires that we set and later restore the max memory policy + @pytest.mark.parametrize("cluster_mode", [True, False]) + @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) + async def test_transaction_object_freq( + self, redis_client: TRedisClient, cluster_mode: bool + ): + string_key = get_random_string(10) + maxmemory_policy_key = "maxmemory-policy" + config = await redis_client.config_get([maxmemory_policy_key]) + maxmemory_policy = cast(str, config.get(maxmemory_policy_key)) + + try: + transaction = ClusterTransaction() if cluster_mode else Transaction() + transaction.config_set({maxmemory_policy_key: "allkeys-lfu"}) + transaction.set(string_key, "foo") + transaction.object_freq(string_key) + + response = await redis_client.exec(transaction) + assert response is not None + assert len(response) == 3 + assert response[0] == OK + assert response[1] == OK + frequency = cast(int, response[2]) + assert frequency >= 0 + finally: + await redis_client.config_set({maxmemory_policy_key: maxmemory_policy})