diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e5ae86e9b..3d664ac79e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Python: Added LMOVE and BLMOVE commands ([#1536](https://github.com/aws/glide-for-redis/pull/1536)) * Node: Added SUNIONSTORE command ([#1549](https://github.com/aws/glide-for-redis/pull/1549)) * Node: Added PFCOUNT command ([#1545](https://github.com/aws/glide-for-redis/pull/1545)) +* Node: Added OBJECT FREQ command ([#1542](https://github.com/aws/glide-for-redis/pull/1542)) ### Breaking Changes * Node: Update XREAD to return a Map of Map ([#1494](https://github.com/aws/glide-for-redis/pull/1494)) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index e42737a0d9..912e347121 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -56,6 +56,7 @@ import { createMGet, createMSet, createObjectEncoding, + createObjectFreq, createPExpire, createPExpireAt, createPTTL, @@ -2480,6 +2481,23 @@ export class BaseClient { return this.createWritePromise(createObjectEncoding(key)); } + /** Returns the logarithmic access frequency counter of a Redis object stored at `key`. + * + * See https://valkey.io/commands/object-freq for more details. + * + * @param key - The `key` of the object to get the logarithmic access frequency counter of. + * @returns - If `key` exists, returns the logarithmic access frequency counter of the object + * stored at `key` as a `number`. Otherwise, returns `null`. + * @example + * ```typescript + * const result = await client.object_freq("my_hash"); + * console.log(result); // Output: 2 - The logarithmic access frequency counter of "my_hash". + * ``` + */ + public object_freq(key: string): Promise { + return this.createWritePromise(createObjectFreq(key)); + } + /** * @internal */ diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 43137f1b2f..d32bfed331 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -1436,3 +1436,10 @@ export function createPfCount(keys: string[]): redis_request.Command { export function createObjectEncoding(key: string): redis_request.Command { return createCommand(RequestType.ObjectEncoding, [key]); } + +/** + * @internal + */ +export function createObjectFreq(key: string): redis_request.Command { + return createCommand(RequestType.ObjectFreq, [key]); +} diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 7f7e53c579..8855900add 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -58,6 +58,7 @@ import { createMGet, createMSet, createObjectEncoding, + createObjectFreq, createPExpire, createPExpireAt, createPTTL, @@ -1422,6 +1423,18 @@ export class BaseTransaction> { public object_encoding(key: string): T { return this.addAndReturn(createObjectEncoding(key)); } + + /** Returns the logarithmic access frequency counter of a Redis object stored at `key`. + * + * See https://valkey.io/commands/object-freq for more details. + * + * @param key - The `key` of the object to get the logarithmic access frequency counter of. + * Command Response - If `key` exists, returns the logarithmic access frequency counter of + * the object stored at `key` as a `number`. Otherwise, returns `null`. + */ + public object_freq(key: string): T { + return this.addAndReturn(createObjectFreq(key)); + } } /** diff --git a/node/tests/RedisClient.test.ts b/node/tests/RedisClient.test.ts index 5d62f0af8f..ff3f58a274 100644 --- a/node/tests/RedisClient.test.ts +++ b/node/tests/RedisClient.test.ts @@ -17,12 +17,12 @@ import { RedisCluster } from "../../utils/TestUtils.js"; import { redis_request } from "../src/ProtobufMessage"; import { runBaseTests } from "./SharedTests"; import { + convertStringArrayToBuffer, flushAndCloseClient, getClientConfigurationOption, parseCommandLineArgs, parseEndpoints, transactionTest, - convertStringArrayToBuffer, } from "./TestUtilities"; /* eslint-disable @typescript-eslint/no-var-requires */ @@ -180,6 +180,47 @@ describe("RedisClient", () => { }, ); + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + "object freq transaction test_%p", + async (protocol) => { + const client = await RedisClient.createClient( + getClientConfigurationOption(cluster.getAddresses(), protocol), + ); + + const key = uuidv4(); + const maxmemoryPolicyKey = "maxmemory-policy"; + const config = await client.configGet([maxmemoryPolicyKey]); + const maxmemoryPolicy = String(config[maxmemoryPolicyKey]); + + try { + const transaction = new Transaction(); + transaction.configSet({ + [maxmemoryPolicyKey]: "allkeys-lfu", + }); + transaction.set(key, "foo"); + transaction.object_freq(key); + + const response = await client.exec(transaction); + expect(response).not.toBeNull(); + + if (response != null) { + expect(response.length).toEqual(3); + expect(response[0]).toEqual("OK"); + expect(response[1]).toEqual("OK"); + expect(response[2]).toBeGreaterThanOrEqual(0); + } + } finally { + expect( + await client.configSet({ + [maxmemoryPolicyKey]: maxmemoryPolicy, + }), + ).toEqual("OK"); + } + + client.close(); + }, + ); + runBaseTests({ init: async (protocol, clientName?) => { const options = getClientConfigurationOption( diff --git a/node/tests/RedisClusterClient.test.ts b/node/tests/RedisClusterClient.test.ts index 915e3d387e..7794aa70d8 100644 --- a/node/tests/RedisClusterClient.test.ts +++ b/node/tests/RedisClusterClient.test.ts @@ -324,4 +324,45 @@ describe("RedisClusterClient", () => { client.close(); }, ); + + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + "object freq transaction test_%p", + async (protocol) => { + const client = await RedisClusterClient.createClient( + getClientConfigurationOption(cluster.getAddresses(), protocol), + ); + + const key = uuidv4(); + const maxmemoryPolicyKey = "maxmemory-policy"; + const config = await client.configGet([maxmemoryPolicyKey]); + const maxmemoryPolicy = String(config[maxmemoryPolicyKey]); + + try { + const transaction = new ClusterTransaction(); + transaction.configSet({ + [maxmemoryPolicyKey]: "allkeys-lfu", + }); + transaction.set(key, "foo"); + transaction.object_freq(key); + + const response = await client.exec(transaction); + expect(response).not.toBeNull(); + + if (response != null) { + expect(response.length).toEqual(3); + expect(response[0]).toEqual("OK"); + expect(response[1]).toEqual("OK"); + expect(response[2]).toBeGreaterThanOrEqual(0); + } + } finally { + expect( + await client.configSet({ + [maxmemoryPolicyKey]: maxmemoryPolicy, + }), + ).toEqual("OK"); + } + + client.close(); + }, + ); }); diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index 58f05d5abb..f01bc0c70b 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -3079,6 +3079,41 @@ export function runBaseTests(config: { }, config.timeout, ); + + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + "object freq test_%p", + async (protocol) => { + await runTest(async (client: BaseClient) => { + const key = uuidv4(); + const nonExistingKey = uuidv4(); + const maxmemoryPolicyKey = "maxmemory-policy"; + const config = await client.configGet([maxmemoryPolicyKey]); + const maxmemoryPolicy = String(config[maxmemoryPolicyKey]); + + try { + expect( + await client.configSet({ + [maxmemoryPolicyKey]: "allkeys-lfu", + }), + ).toEqual("OK"); + expect(await client.object_freq(nonExistingKey)).toEqual( + null, + ); + expect(await client.set(key, "foobar")).toEqual("OK"); + expect( + await client.object_freq(key), + ).toBeGreaterThanOrEqual(0); + } finally { + expect( + await client.configSet({ + [maxmemoryPolicyKey]: maxmemoryPolicy, + }), + ).toEqual("OK"); + } + }, protocol); + }, + config.timeout, + ); } export function runCommonTests(config: {