diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c0c9fdeb1..f9491973e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * Python: Added ZINTER, ZUNION commands ([#1478](https://github.com/aws/glide-for-redis/pull/1478)) * Python: Added SINTERCARD command ([#1511](https://github.com/aws/glide-for-redis/pull/1511)) * Python: Added SORT command ([#1439](https://github.com/aws/glide-for-redis/pull/1439)) +* Node: Added OBJECT ENCODING command ([#1518](https://github.com/aws/glide-for-redis/pull/1518)) ### 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 4f0b0aee47..5bfadd5188 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -53,6 +53,7 @@ import { createLTrim, createMGet, createMSet, + createObjectEncoding, createPExpire, createPExpireAt, createPTTL, @@ -2380,6 +2381,23 @@ export class BaseClient { return this.createWritePromise(createPfAdd(key, elements)); } + /** Returns the internal encoding for the Redis object stored at `key`. + * + * See https://valkey.io/commands/object-encoding for more details. + * + * @param key - The `key` of the object to get the internal encoding of. + * @returns - If `key` exists, returns the internal encoding of the object stored at `key` as a string. + * Otherwise, returns None. + * @example + * ```typescript + * const result = await client.object_encoding("my_hash"); + * console.log(result); // Output: "listpack" + * ``` + */ + public object_encoding(key: string): Promise { + return this.createWritePromise(createObjectEncoding(key)); + } + /** * @internal */ diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 0853bafb5a..0cddd74bbe 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -1368,3 +1368,10 @@ export function createPfAdd( const args = [key, ...elements]; return createCommand(RequestType.PfAdd, args); } + +/** + * @internal + */ +export function createObjectEncoding(key: string): redis_request.Command { + return createCommand(RequestType.ObjectEncoding, [key]); +} diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 8b46534aac..e5442e481a 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -55,6 +55,7 @@ import { createLTrim, createMGet, createMSet, + createObjectEncoding, createPExpire, createPExpireAt, createPTTL, @@ -1351,6 +1352,18 @@ export class BaseTransaction> { public pfadd(key: string, elements: string[]): T { return this.addAndReturn(createPfAdd(key, elements)); } + + /** Returns the internal encoding for the Redis object stored at `key`. + * + * See https://valkey.io/commands/object-encoding for more details. + * + * @param key - The `key` of the object to get the internal encoding of. + * Command Response - If `key` exists, returns the internal encoding of the object stored at `key` as a string. + * Otherwise, returns None. + */ + public object_encoding(key: string): T { + return this.addAndReturn(createObjectEncoding(key)); + } } /** diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index efd7059a67..1844c8c51b 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -2701,6 +2701,149 @@ export function runBaseTests(config: { }, config.timeout, ); + + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + "object encoding test_%p", + async (protocol) => { + await runTest(async (client: BaseClient) => { + const string_key = uuidv4(); + const list_key = uuidv4(); + const hashtable_key = uuidv4(); + const intset_key = uuidv4(); + const set_listpack_key = uuidv4(); + const hash_hashtable_key = uuidv4(); + const hash_listpack_key = uuidv4(); + const skiplist_key = uuidv4(); + const zset_listpack_key = uuidv4(); + const stream_key = uuidv4(); + const non_existing_key = uuidv4(); + const versionLessThan7 = + await checkIfServerVersionLessThan("7.0.0"); + const versionLessThan72 = + await checkIfServerVersionLessThan("7.2.0"); + + expect(await client.object_encoding(non_existing_key)).toEqual( + null, + ); + + expect( + await client.set( + string_key, + "a really loooooooooooooooooooooooooooooooooooooooong value", + ), + ).toEqual("OK"); + expect(await client.object_encoding(string_key)).toEqual("raw"); + + expect(await client.set(string_key, "2")).toEqual("OK"); + expect(await client.object_encoding(string_key)).toEqual("int"); + + expect(await client.set(string_key, "value")).toEqual("OK"); + expect(await client.object_encoding(string_key)).toEqual( + "embstr", + ); + + expect(await client.lpush(list_key, ["1"])).toEqual(1); + + if (versionLessThan7) { + expect(await client.object_encoding(list_key)).toEqual( + "quicklist", + ); + } else { + expect(await client.object_encoding(list_key)).toEqual( + "listpack", + ); + } + + // The default value of set-max-intset-entries is 512 + for (let i = 0; i < 513; i++) { + expect( + await client.sadd(hashtable_key, [String(i)]), + ).toEqual(1); + } + + expect(await client.object_encoding(hashtable_key)).toEqual( + "hashtable", + ); + + expect(await client.sadd(intset_key, ["1"])).toEqual(1); + expect(await client.object_encoding(intset_key)).toEqual( + "intset", + ); + + expect(await client.sadd(set_listpack_key, ["foo"])).toEqual(1); + + if (versionLessThan72) { + expect( + await client.object_encoding(set_listpack_key), + ).toEqual("hashtable"); + } else { + expect( + await client.object_encoding(set_listpack_key), + ).toEqual("listpack"); + } + + // The default value of hash-max-listpack-entries is 512 + for (let i = 0; i < 513; i++) { + expect( + await client.hset(hash_hashtable_key, { + [String(i)]: "2", + }), + ).toEqual(1); + } + + expect( + await client.object_encoding(hash_hashtable_key), + ).toEqual("hashtable"); + + expect( + await client.hset(hash_listpack_key, { "1": "2" }), + ).toEqual(1); + + if (versionLessThan7) { + expect( + await client.object_encoding(hash_listpack_key), + ).toEqual("ziplist"); + } else { + expect( + await client.object_encoding(hash_listpack_key), + ).toEqual("listpack"); + } + + // The default value of zset-max-listpack-entries is 128 + for (let i = 0; i < 129; i++) { + expect( + await client.zadd(skiplist_key, { [String(i)]: 2.0 }), + ).toEqual(1); + } + + expect(await client.object_encoding(skiplist_key)).toEqual( + "skiplist", + ); + + expect( + await client.zadd(zset_listpack_key, { "1": 2.0 }), + ).toEqual(1); + + if (versionLessThan7) { + expect( + await client.object_encoding(zset_listpack_key), + ).toEqual("ziplist"); + } else { + expect( + await client.object_encoding(zset_listpack_key), + ).toEqual("listpack"); + } + + expect( + await client.xadd(stream_key, [["field", "value"]]), + ).not.toBeNull(); + expect(await client.object_encoding(stream_key)).toEqual( + "stream", + ); + }, protocol); + }, + config.timeout, + ); } export function runCommonTests(config: { diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts index b6fdcd9890..6daeb88fe7 100644 --- a/node/tests/TestUtilities.ts +++ b/node/tests/TestUtilities.ts @@ -234,6 +234,8 @@ export async function transactionTest( const args: ReturnType[] = []; baseTransaction.set(key1, "bar"); args.push("OK"); + baseTransaction.object_encoding(key1); + args.push("embstr"); baseTransaction.type(key1); args.push("string"); baseTransaction.echo(value);