Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node: added FCALL and FCALL_RO commands #2011

Merged
merged 3 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* Node: Added FUNCTION LOAD command ([#1969](https://github.com/valkey-io/valkey-glide/pull/1969))
* Node: Added FUNCTION DELETE command ([#1990](https://github.com/valkey-io/valkey-glide/pull/1990))
* Node: Added FUNCTION FLUSH command ([#1984](https://github.com/valkey-io/valkey-glide/pull/1984))
* Node: Added FCALL and FCALL_RO commands ([#2011](https://github.com/valkey-io/valkey-glide/pull/2011))
* Node: Added ZMPOP command ([#1994](https://github.com/valkey-io/valkey-glide/pull/1994))

#### Fixes
Expand Down
63 changes: 61 additions & 2 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Buffer, BufferWriter, Reader, Writer } from "protobufjs";
import {
AggregationType,
ExpireOptions,
GeoUnit,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My VS Code automatically fix the order.

InsertPosition,
KeyWeight,
RangeByIndex,
Expand All @@ -34,10 +35,12 @@ import {
createExists,
createExpire,
createExpireAt,
createFCall,
createFCallReadOnly,
createGeoAdd,
createGeoDist,
createGeoPos,
createGeoHash,
createGeoPos,
createGet,
createGetBit,
createGetDel,
Expand Down Expand Up @@ -129,7 +132,6 @@ import {
createZRevRank,
createZRevRankWithScore,
createZScore,
GeoUnit,
} from "./Commands";
import { BitOffsetOptions } from "./commands/BitOffsetOptions";
import { GeoAddOptions } from "./commands/geospatial/GeoAddOptions";
Expand Down Expand Up @@ -3330,6 +3332,63 @@ export class BaseClient {
return this.createWritePromise(createObjectRefcount(key));
}

/**
* Invokes a previously loaded function.
*
* See https://valkey.io/commands/fcall/ for more details.
*
* since Valkey version 7.0.0.
*
* @remarks When in cluster mode, all `keys` must map to the same hash slot.
* @param func - The function name.
* @param keys - A list of `keys` accessed by the function. To ensure the correct execution of functions,
* all names of keys that a function accesses must be explicitly provided as `keys`.
* @param args - A list of `function` arguments and it should not represent names of keys.
* @returns The invoked function's return value.
*
* @example
* ```typescript
* const response = await client.fcall("Deep_Thought", [], []);
* console.log(response); // Output: Returns the function's return value.
* ```
*/
public fcall(
func: string,
keys: string[],
args: string[],
): Promise<string> {
return this.createWritePromise(createFCall(func, keys, args));
}

/**
* Invokes a previously loaded read-only function.
*
* See https://valkey.io/commands/fcall/ for more details.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valkey document currently doesn't list fcall_ro command.

*
* since Valkey version 7.0.0.
*
* @remarks When in cluster mode, all `keys` must map to the same hash slot.
* @param func - The function name.
* @param keys - A list of `keys` accessed by the function. To ensure the correct execution of functions,
* all names of keys that a function accesses must be explicitly provided as `keys`.
* @param args - A list of `function` arguments and it should not represent names of keys.
* @returns The invoked function's return value.
*
* @example
* ```typescript
* const response = await client.fcallReadOnly("Deep_Thought", ["key1"], ["Answer", "to", "the",
* "Ultimate", "Question", "of", "Life,", "the", "Universe,", "and", "Everything"]);
* console.log(response); // Output: 42 # The return value on the function that was executed.
* ```
*/
public fcallReadonly(
func: string,
keys: string[],
args: string[],
): Promise<string> {
return this.createWritePromise(createFCallReadOnly(func, keys, args));
}

/**
* Returns the index of the first occurrence of `element` inside the list specified by `key`. If no
* match is found, `null` is returned. If the `count` option is specified, then the function returns
Expand Down
48 changes: 37 additions & 11 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1584,6 +1584,32 @@ export function createBLPop(
return createCommand(RequestType.BLPop, args);
}

/**
* @internal
*/
export function createFCall(
func: string,
keys: string[],
args: string[],
): command_request.Command {
let params: string[] = [];
params = params.concat(func, keys.length.toString(), keys, args);
return createCommand(RequestType.FCall, params);
}

/**
* @internal
*/
export function createFCallReadOnly(
func: string,
keys: string[],
args: string[],
): command_request.Command {
let params: string[] = [];
params = params.concat(func, keys.length.toString(), keys, args);
return createCommand(RequestType.FCallReadOnly, params);
}

/**
* @internal
*/
Expand All @@ -1593,6 +1619,17 @@ export function createFunctionDelete(
return createCommand(RequestType.FunctionDelete, [libraryCode]);
}

/**
* @internal
*/
export function createFunctionFlush(mode?: FlushMode): command_request.Command {
if (mode) {
return createCommand(RequestType.FunctionFlush, [mode.toString()]);
} else {
return createCommand(RequestType.FunctionFlush, []);
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just group all function commands together.


/**
* @internal
*/
Expand All @@ -1616,17 +1653,6 @@ export function createBitCount(
return createCommand(RequestType.BitCount, args);
}

/**
* @internal
*/
export function createFunctionFlush(mode?: FlushMode): command_request.Command {
if (mode) {
return createCommand(RequestType.FunctionFlush, [mode.toString()]);
} else {
return createCommand(RequestType.FunctionFlush, []);
}
}

export type StreamReadOptions = {
/**
* If set, the read request will block for the set amount of milliseconds or
Expand Down
63 changes: 63 additions & 0 deletions node/src/GlideClusterClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
createCustomCommand,
createDBSize,
createEcho,
createFCall,
createFCallReadOnly,
createFlushAll,
createFlushDB,
createFunctionDelete,
Expand Down Expand Up @@ -659,6 +661,67 @@ export class GlideClusterClient extends BaseClient {
);
}

/**
* Invokes a previously loaded function.
*
* See https://valkey.io/commands/fcall/ for more details.
*
* since Valkey version 7.0.0.
*
* @param func - The function name.
* @param args - A list of `function` arguments and it should not represent names of keys.
* @param route - The command will be routed to a random node, unless `route` is provided, in which
* case the client will route the command to the nodes defined by `route`.
* @returns The invoked function's return value.
*
* @example
* ```typescript
* const response = await client.fcallWithRoute("Deep_Thought", [], "randomNode");
* console.log(response); // Output: Returns the function's return value.
* ```
*/
public fcallWithRoute(
func: string,
args: string[],
route?: Routes,
): Promise<ReturnType> {
return this.createWritePromise(
createFCall(func, [], args),
toProtobufRoute(route),
);
}

/**
* Invokes a previously loaded read-only function.
*
* See https://valkey.io/commands/fcall/ for more details.
*
* since Valkey version 7.0.0.
*
* @param func - The function name.
* @param args - A list of `function` arguments and it should not represent names of keys.
* @param route - The command will be routed to a random node, unless `route` is provided, in which
* case the client will route the command to the nodes defined by `route`.
* @returns The invoked function's return value.
*
* @example
* ```typescript
* const response = await client.fcallReadonlyWithRoute("Deep_Thought", ["Answer", "to", "the", "Ultimate",
* "Question", "of", "Life,", "the", "Universe,", "and", "Everything"], "randomNode");
* console.log(response); // Output: 42 # The return value on the function that was execute.
* ```
*/
public fcallReadonlyWithRoute(
func: string,
args: string[],
route?: Routes,
): Promise<ReturnType> {
return this.createWritePromise(
createFCallReadOnly(func, [], args),
toProtobufRoute(route),
);
}

/**
* Deletes a library and all its functions.
*
Expand Down
40 changes: 39 additions & 1 deletion node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import {
AggregationType,
ExpireOptions,
GeoUnit,
InfoOptions,
InsertPosition,
KeyWeight,
Expand Down Expand Up @@ -37,6 +38,8 @@ import {
createExists,
createExpire,
createExpireAt,
createFCall,
createFCallReadOnly,
createFlushAll,
createFlushDB,
createFunctionDelete,
Expand Down Expand Up @@ -142,7 +145,6 @@ import {
createZRevRank,
createZRevRankWithScore,
createZScore,
GeoUnit,
} from "./Commands";
import { command_request } from "./ProtobufMessage";
import { BitOffsetOptions } from "./commands/BitOffsetOptions";
Expand Down Expand Up @@ -1854,6 +1856,42 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
return this.addAndReturn(createLolwut(options));
}

/**
* Invokes a previously loaded function.
*
* See https://valkey.io/commands/fcall/ for more details.
*
* since Valkey version 7.0.0.
*
* @param func - The function name.
* @param keys - A list of `keys` accessed by the function. To ensure the correct execution of functions,
* all names of keys that a function accesses must be explicitly provided as `keys`.
* @param args - A list of `function` arguments and it should not represent names of keys.
*
* Command Response - The invoked function's return value.
*/
public fcall(func: string, keys: string[], args: string[]): T {
return this.addAndReturn(createFCall(func, keys, args));
}

/**
* Invokes a previously loaded read-only function.
*
* See https://valkey.io/commands/fcall/ for more details.
*
* since Valkey version 7.0.0.
*
* @param func - The function name.
* @param keys - A list of `keys` accessed by the function. To ensure the correct execution of functions,
* all names of keys that a function accesses must be explicitly provided as `keys`.
* @param args - A list of `function` arguments and it should not represent names of keys.
*
* Command Response - The invoked function's return value.
*/
public fcallReadonly(func: string, keys: string[], args: string[]): T {
return this.addAndReturn(createFCallReadOnly(func, keys, args));
}

/**
* Deletes a library and all its functions.
*
Expand Down
36 changes: 6 additions & 30 deletions node/tests/RedisClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,22 +397,10 @@ describe("GlideClient", () => {
checkSimple(await client.functionLoad(code)).toEqual(libName);

checkSimple(
await client.customCommand([
"FCALL",
funcName,
"0",
"one",
"two",
]),
await client.fcall(funcName, [], ["one", "two"]),
).toEqual("one");
checkSimple(
await client.customCommand([
"FCALL_RO",
funcName,
"0",
"one",
"two",
]),
await client.fcallReadonly(funcName, [], ["one", "two"]),
).toEqual("one");

// TODO verify with FUNCTION LIST
Expand Down Expand Up @@ -441,23 +429,11 @@ describe("GlideClient", () => {
libName,
);

expect(
await client.customCommand([
"FCALL",
func2Name,
"0",
"one",
"two",
]),
checkSimple(
await client.fcall(func2Name, [], ["one", "two"]),
).toEqual(2);
expect(
await client.customCommand([
"FCALL_RO",
func2Name,
"0",
"one",
"two",
]),
checkSimple(
await client.fcallReadonly(func2Name, [], ["one", "two"]),
).toEqual(2);
} finally {
expect(await client.functionFlush()).toEqual("OK");
Expand Down
Loading
Loading