From 99150ee6063c44e4d2dc3d0c9055c73d1d717160 Mon Sep 17 00:00:00 2001 From: Yury-Fridlyand Date: Tue, 20 Aug 2024 10:20:47 -0700 Subject: [PATCH 1/3] Signed-off-by: Yury-Fridlyand --- CHANGELOG.md | 1 + node/src/BaseClient.ts | 2 +- node/src/Transaction.ts | 31 ++++++++++++++++++++ node/tests/GlideClient.test.ts | 40 ++++++++++++++++++++++++++ node/tests/GlideClusterClient.test.ts | 41 +++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af4e2672e4..3e4588f128 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ #### Changes +* Node: Added FUNCTION DUMP and FUNCTION RESTORE commands (transaction) ([#2173](https://github.com/valkey-io/valkey-glide/pull/2173)) * Node: Added FUNCTION DUMP and FUNCTION RESTORE commands ([#2129](https://github.com/valkey-io/valkey-glide/pull/2129)) * Node: Added ZUNIONSTORE command ([#2145](https://github.com/valkey-io/valkey-glide/pull/2145)) * Node: Added XREADGROUP command ([#2124](https://github.com/valkey-io/valkey-glide/pull/2124)) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 68156ef365..a938757775 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -275,7 +275,7 @@ export type GlideString = string | Buffer; /** * Enum representing the different types of decoders. */ -export const enum Decoder { +export enum Decoder { /** * Decodes the response into a buffer array. */ diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 8d5d633fc9..678fe6dea9 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -27,6 +27,7 @@ import { FlushMode, FunctionListOptions, FunctionListResponse, // eslint-disable-line @typescript-eslint/no-unused-vars + FunctionRestorePolicy, FunctionStatsSingleResponse, // eslint-disable-line @typescript-eslint/no-unused-vars GeoAddOptions, GeoBoxShape, // eslint-disable-line @typescript-eslint/no-unused-vars @@ -97,9 +98,11 @@ import { createFlushAll, createFlushDB, createFunctionDelete, + createFunctionDump, createFunctionFlush, createFunctionList, createFunctionLoad, + createFunctionRestore, createFunctionStats, createGeoAdd, createGeoDist, @@ -3191,6 +3194,34 @@ export class BaseTransaction> { return this.addAndReturn(createFunctionStats()); } + /** + * Returns the serialized payload of all loaded libraries. + * + * @see {@link https://valkey.io/commands/function-dump/|valkey.io} for details. + * @remarks Since Valkey version 7.0.0. + * @remarks To execute a transaction with a `functionDump` command, the `exec` command requires `Decoder.Bytes` to handle the response. + * + * Command Response - The serialized payload of all loaded libraries. + */ + public functionDump(): T { + return this.addAndReturn(createFunctionDump()); + } + + /** + * Restores libraries from the serialized payload returned by {@link functionDump}. + * + * @see {@link https://valkey.io/commands/function-restore/|valkey.io} for details. + * @remarks Since Valkey version 7.0.0. + * + * @param payload - The serialized data from {@link functionDump}. + * @param policy - (Optional) A policy for handling existing libraries. + * + * Command Response - `"OK"`. + */ + public functionRestore(payload: Buffer, policy?: FunctionRestorePolicy): T { + return this.addAndReturn(createFunctionRestore(payload, policy)); + } + /** * Deletes all the keys of all the existing databases. This command never fails. * diff --git a/node/tests/GlideClient.test.ts b/node/tests/GlideClient.test.ts index 6c93134fae..5f1ccec953 100644 --- a/node/tests/GlideClient.test.ts +++ b/node/tests/GlideClient.test.ts @@ -1095,6 +1095,46 @@ describe("GlideClient", () => { }, ); + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + "function dump function restore in transaction %p", + async (protocol) => { + if (cluster.checkIfServerVersionLessThan("7.0.0")) return; + + const config = getClientConfigurationOption( + cluster.getAddresses(), + protocol, + ); + const client = await GlideClient.createClient(config); + expect(await client.functionFlush()).toEqual("OK"); + + try { + const name1 = "Foster"; + const name2 = "Dogster"; + // function returns first argument + const code = generateLuaLibCode( + name1, + new Map([[name2, "return args[1]"]]), + false, + ); + expect(await client.functionLoad(code)).toEqual(name1); + + // Verify functionDump + let transaction = new Transaction().functionDump(); + const result = await client.exec(transaction, Decoder.Bytes); + const data = result?.[0] as Buffer; + + // Verify functionRestore + transaction = new Transaction() + .functionRestore(data, FunctionRestorePolicy.REPLACE) + .fcall(name2, [], ["meow"]); + expect(await client.exec(transaction)).toEqual(["OK", "meow"]); + } finally { + expect(await client.functionFlush()).toEqual("OK"); + client.close(); + } + }, + ); + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( "sort sortstore sort_store sortro sort_ro sortreadonly test_%p", async (protocol) => { diff --git a/node/tests/GlideClusterClient.test.ts b/node/tests/GlideClusterClient.test.ts index 8cd18e943a..ecc3a4223c 100644 --- a/node/tests/GlideClusterClient.test.ts +++ b/node/tests/GlideClusterClient.test.ts @@ -1530,6 +1530,47 @@ describe("GlideClusterClient", () => { }, TIMEOUT, ); + it("function dump function restore in transaction %p", async () => { + if (cluster.checkIfServerVersionLessThan("7.0.0")) return; + + const config = getClientConfigurationOption( + cluster.getAddresses(), + protocol, + ); + const client = await GlideClusterClient.createClient(config); + expect(await client.functionFlush()).toEqual("OK"); + + try { + const name1 = "Foster"; + const name2 = "Dogster"; + // function returns first argument + const code = generateLuaLibCode( + name1, + new Map([[name2, "return args[1]"]]), + false, + ); + expect(await client.functionLoad(code)).toEqual(name1); + + // Verify functionDump + let transaction = new ClusterTransaction().functionDump(); + const result = await client.exec(transaction, { + decoder: Decoder.Bytes, + }); + const data = result?.[0] as Buffer; + + // Verify functionRestore + transaction = new ClusterTransaction() + .functionRestore(data, FunctionRestorePolicy.REPLACE) + .fcall(name2, [], ["meow"]); + expect(await client.exec(transaction)).toEqual([ + "OK", + "meow", + ]); + } finally { + expect(await client.functionFlush()).toEqual("OK"); + client.close(); + } + }); }, ); From 6364aba7e613f22072ac96cce2333f1eff760c99 Mon Sep 17 00:00:00 2001 From: Yury-Fridlyand Date: Tue, 20 Aug 2024 11:06:44 -0700 Subject: [PATCH 2/3] Signed-off-by: Yury-Fridlyand --- node/tests/GlideClusterClient.test.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/node/tests/GlideClusterClient.test.ts b/node/tests/GlideClusterClient.test.ts index ecc3a4223c..88c2af5076 100644 --- a/node/tests/GlideClusterClient.test.ts +++ b/node/tests/GlideClusterClient.test.ts @@ -25,6 +25,7 @@ import { ReturnType, Routes, ScoreFilter, + SlotKeyTypes, } from ".."; import { RedisCluster } from "../../utils/TestUtils.js"; import { @@ -1530,7 +1531,7 @@ describe("GlideClusterClient", () => { }, TIMEOUT, ); - it("function dump function restore in transaction %p", async () => { + it("function dump function restore in transaction", async () => { if (cluster.checkIfServerVersionLessThan("7.0.0")) return; const config = getClientConfigurationOption( @@ -1538,6 +1539,10 @@ describe("GlideClusterClient", () => { protocol, ); const client = await GlideClusterClient.createClient(config); + const route: SlotKeyTypes = { + key: uuidv4(), + type: "primarySlotKey", + }; expect(await client.functionFlush()).toEqual("OK"); try { @@ -1549,12 +1554,15 @@ describe("GlideClusterClient", () => { new Map([[name2, "return args[1]"]]), false, ); - expect(await client.functionLoad(code)).toEqual(name1); + expect( + await client.functionLoad(code, true, route), + ).toEqual(name1); // Verify functionDump let transaction = new ClusterTransaction().functionDump(); const result = await client.exec(transaction, { decoder: Decoder.Bytes, + route: route, }); const data = result?.[0] as Buffer; @@ -1562,10 +1570,9 @@ describe("GlideClusterClient", () => { transaction = new ClusterTransaction() .functionRestore(data, FunctionRestorePolicy.REPLACE) .fcall(name2, [], ["meow"]); - expect(await client.exec(transaction)).toEqual([ - "OK", - "meow", - ]); + expect( + await client.exec(transaction, { route: route }), + ).toEqual(["OK", "meow"]); } finally { expect(await client.functionFlush()).toEqual("OK"); client.close(); From f1fe01103c631586a741e73965f8032b68d94099 Mon Sep 17 00:00:00 2001 From: Yury-Fridlyand Date: Tue, 20 Aug 2024 11:50:31 -0700 Subject: [PATCH 3/3] Update CHANGELOG.md Co-authored-by: Andrew Carbonetto Signed-off-by: Yury-Fridlyand --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f086875224..cc28fe5898 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ #### Changes * Node: Added FUNCTION DUMP and FUNCTION RESTORE commands (transaction) ([#2173](https://github.com/valkey-io/valkey-glide/pull/2173)) -* Node: Added FUNCTION DUMP and FUNCTION RESTORE commands ([#2129](https://github.com/valkey-io/valkey-glide/pull/2129)) +* Node: Added FUNCTION DUMP and FUNCTION RESTORE commands ([#2129](https://github.com/valkey-io/valkey-glide/pull/2129), [#2173](https://github.com/valkey-io/valkey-glide/pull/2173)) * Node: Added ZUNIONSTORE command ([#2145](https://github.com/valkey-io/valkey-glide/pull/2145)) * Node: Added XREADGROUP command ([#2124](https://github.com/valkey-io/valkey-glide/pull/2124)) * Node: Added XINFO GROUPS command ([#2122](https://github.com/valkey-io/valkey-glide/pull/2122))