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: Add FUNCTION STATS command. #2082

Merged
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
@@ -1,4 +1,5 @@
#### Changes
* Node: Added FUNCTION STATS commands ([#2082](https://github.com/valkey-io/valkey-glide/pull/2082))
* Node: Added XCLAIM command ([#2092](https://github.com/valkey-io/valkey-glide/pull/2092))
* Node: Added EXPIRETIME and PEXPIRETIME commands ([#2063](https://github.com/valkey-io/valkey-glide/pull/2063))
* Node: Added SORT commands ([#2028](https://github.com/valkey-io/valkey-glide/pull/2028))
Expand Down
2 changes: 2 additions & 0 deletions node/npm/glide/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ function initialize() {
GlideClientConfiguration,
FunctionListOptions,
FunctionListResponse,
FunctionStatsResponse,
SlotIdTypes,
SlotKeyTypes,
RouteByAddress,
Expand Down Expand Up @@ -195,6 +196,7 @@ function initialize() {
GlideClientConfiguration,
FunctionListOptions,
FunctionListResponse,
FunctionStatsResponse,
SlotIdTypes,
SlotKeyTypes,
RouteByAddress,
Expand Down
13 changes: 13 additions & 0 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2149,6 +2149,19 @@ export function createFunctionList(
return createCommand(RequestType.FunctionList, args);
}

/** Type of the response of `FUNCTION STATS` command. */
export type FunctionStatsResponse = Record<
string,
| null
| Record<string, string | string[] | number>
| Record<string, Record<string, number>>
>;

/** @internal */
export function createFunctionStats(): command_request.Command {
return createCommand(RequestType.FunctionStats, []);
}

/**
* Represents offsets specifying a string interval to analyze in the {@link BaseClient.bitcount|bitcount} command. The offsets are
* zero-based indexes, with `0` being the first index of the string, `1` being the next index and so on.
Expand Down
54 changes: 54 additions & 0 deletions node/src/GlideClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
FlushMode,
FunctionListOptions,
FunctionListResponse,
FunctionStatsResponse,
InfoOptions,
LolwutOptions,
SortOptions,
Expand All @@ -33,6 +34,7 @@ import {
createFunctionFlush,
createFunctionList,
createFunctionLoad,
createFunctionStats,
createInfo,
createLastSave,
createLolwut,
Expand Down Expand Up @@ -545,6 +547,58 @@ export class GlideClient extends BaseClient {
return this.createWritePromise(createFunctionList(options));
}

/**
* Returns information about the function that's currently running and information about the
* available execution engines.
*
* See https://valkey.io/commands/function-stats/ for details.
*
* since Valkey version 7.0.0.
*
* @returns A `Record` with two keys:
* - `"running_script"` with information about the running script.
* - `"engines"` with information about available engines and their stats.
*
* See example for more details.
*
* @example
* ```typescript
* const response = await client.functionStats();
* console.log(response); // Output:
* // {
* // "running_script":
* // {
* // "name": "deep_thought",
* // "command": ["fcall", "deep_thought", "0"],
* // "duration_ms": 5008
* // },
* // "engines":
* // {
* // "LUA":
* // {
* // "libraries_count": 2,
* // "functions_count": 3
* // }
* // }
* // }
* // Output if no scripts running:
* // {
* // "running_script": null
* // "engines":
* // {
* // "LUA":
* // {
* // "libraries_count": 2,
* // "functions_count": 3
* // }
* // }
* // }
* ```
*/
public async functionStats(): Promise<FunctionStatsResponse> {
return this.createWritePromise(createFunctionStats());
}

/**
* Deletes all the keys of all the existing databases. This command never fails.
*
Expand Down
63 changes: 62 additions & 1 deletion node/src/GlideClusterClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
FlushMode,
FunctionListOptions,
FunctionListResponse,
FunctionStatsResponse,
InfoOptions,
LolwutOptions,
SortClusterOptions,
Expand All @@ -35,6 +36,7 @@ import {
createFunctionFlush,
createFunctionList,
createFunctionLoad,
createFunctionStats,
createInfo,
createLastSave,
createLolwut,
Expand Down Expand Up @@ -834,7 +836,7 @@ export class GlideClusterClient extends BaseClient {
* since Valkey version 7.0.0.
*
* @param mode - The flushing mode, could be either {@link FlushMode.SYNC} or {@link FlushMode.ASYNC}.
* @param route - The command will be routed to all primary node, unless `route` is provided, in which
* @param route - The command will be routed to all primary nodes, unless `route` is provided, in which
* case the client will route the command to the nodes defined by `route`.
* @returns A simple OK response.
*
Expand Down Expand Up @@ -892,6 +894,65 @@ export class GlideClusterClient extends BaseClient {
);
}

/**
* Returns information about the function that's currently running and information about the
* available execution engines.
*
* See https://valkey.io/commands/function-stats/ for details.
*
* since Valkey version 7.0.0.
*
* @param route - The client will route the command to the nodes defined by `route`.
* If not defined, the command will be routed to all primary nodes.
* @returns A `Record` with two keys:
* - `"running_script"` with information about the running script.
* - `"engines"` with information about available engines and their stats.
*
* See example for more details.
*
* @example
* ```typescript
* const response = await client.functionStats("randomNode");
* console.log(response); // Output:
* // {
* // "running_script":
* // {
* // "name": "deep_thought",
* // "command": ["fcall", "deep_thought", "0"],
* // "duration_ms": 5008
* // },
* // "engines":
* // {
* // "LUA":
* // {
* // "libraries_count": 2,
* // "functions_count": 3
* // }
* // }
* // }
* // Output if no scripts running:
* // {
* // "running_script": null
* // "engines":
* // {
* // "LUA":
* // {
* // "libraries_count": 2,
* // "functions_count": 3
* // }
* // }
* // }
* ```
*/
public async functionStats(
route?: Routes,
): Promise<ClusterResponse<FunctionStatsResponse>> {
return this.createWritePromise(
createFunctionStats(),
toProtobufRoute(route),
);
}

/**
* Deletes all the keys of all the existing databases. This command never fails.
*
Expand Down
19 changes: 19 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
FlushMode,
FunctionListOptions,
FunctionListResponse, // eslint-disable-line @typescript-eslint/no-unused-vars
FunctionStatsResponse, // eslint-disable-line @typescript-eslint/no-unused-vars
GeoAddOptions,
GeoBoxShape, // eslint-disable-line @typescript-eslint/no-unused-vars
GeoCircleShape, // eslint-disable-line @typescript-eslint/no-unused-vars
Expand Down Expand Up @@ -87,6 +88,7 @@ import {
createFunctionFlush,
createFunctionList,
createFunctionLoad,
createFunctionStats,
createGeoAdd,
createGeoDist,
createGeoHash,
Expand Down Expand Up @@ -2492,6 +2494,23 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
return this.addAndReturn(createFunctionList(options));
}

/**
* Returns information about the function that's currently running and information about the
* available execution engines.
*
* See https://valkey.io/commands/function-stats/ for details.
*
* since Valkey version 7.0.0.
*
* Command Response - A `Record` of type {@link FunctionStatsResponse} with two keys:
*
* - `"running_script"` with information about the running script.
* - `"engines"` with information about available engines and their stats.
*/
public functionStats(): T {
return this.addAndReturn(createFunctionStats());
}

/**
* Deletes all the keys of all the existing databases. This command never fails.
*
Expand Down
11 changes: 10 additions & 1 deletion node/tests/GlideClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { command_request } from "../src/ProtobufMessage";
import { runBaseTests } from "./SharedTests";
import {
checkFunctionListResponse,
checkFunctionStatsResponse,
convertStringArrayToBuffer,
flushAndCloseClient,
generateLuaLibCode,
Expand Down Expand Up @@ -507,7 +508,7 @@ describe("GlideClient", () => {
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
"function load test_%p",
"function load function list function stats test_%p",
async (protocol) => {
if (cluster.checkIfServerVersionLessThan("7.0.0")) return;

Expand All @@ -534,6 +535,9 @@ describe("GlideClient", () => {
await client.fcallReadonly(funcName, [], ["one", "two"]),
).toEqual("one");

let functionStats = await client.functionStats();
checkFunctionStatsResponse(functionStats, [], 1, 1);

let functionList = await client.functionList({
libNamePattern: libName,
});
Expand Down Expand Up @@ -592,6 +596,9 @@ describe("GlideClient", () => {
newCode,
);

functionStats = await client.functionStats();
checkFunctionStatsResponse(functionStats, [], 1, 2);

expect(
await client.fcall(func2Name, [], ["one", "two"]),
).toEqual(2);
Expand All @@ -600,6 +607,8 @@ describe("GlideClient", () => {
).toEqual(2);
} finally {
expect(await client.functionFlush()).toEqual("OK");
const functionStats = await client.functionStats();
checkFunctionStatsResponse(functionStats, [], 0, 0);
client.close();
}
},
Expand Down
51 changes: 49 additions & 2 deletions node/tests/GlideClusterClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,17 @@ import {
ScoreFilter,
} from "..";
import { RedisCluster } from "../../utils/TestUtils.js";
import { FlushMode, GeoUnit, SortOrder } from "../build-ts/src/Commands";
import {
FlushMode,
FunctionStatsResponse,
GeoUnit,
SortOrder,
} from "../build-ts/src/Commands";
import { runBaseTests } from "./SharedTests";
import {
checkClusterResponse,
checkFunctionListResponse,
checkFunctionStatsResponse,
flushAndCloseClient,
generateLuaLibCode,
getClientConfigurationOption,
Expand Down Expand Up @@ -739,7 +745,7 @@ describe("GlideClusterClient", () => {
"Single node route = %s",
(singleNodeRoute) => {
it(
"function load and function list",
"function load function list function stats",
async () => {
if (cluster.checkIfServerVersionLessThan("7.0.0"))
return;
Expand Down Expand Up @@ -775,6 +781,21 @@ describe("GlideClusterClient", () => {
singleNodeRoute,
(value) => expect(value).toEqual([]),
);

let functionStats =
await client.functionStats(route);
checkClusterResponse(
functionStats as object,
singleNodeRoute,
(value) =>
checkFunctionStatsResponse(
value as FunctionStatsResponse,
[],
0,
0,
),
);

// load the library
expect(await client.functionLoad(code)).toEqual(
libName,
Expand Down Expand Up @@ -803,6 +824,19 @@ describe("GlideClusterClient", () => {
expectedFlags,
),
);
functionStats =
await client.functionStats(route);
checkClusterResponse(
functionStats as object,
singleNodeRoute,
(value) =>
checkFunctionStatsResponse(
value as FunctionStatsResponse,
[],
1,
1,
),
);

// call functions from that library to confirm that it works
let fcall = await client.fcallWithRoute(
Expand Down Expand Up @@ -881,6 +915,19 @@ describe("GlideClusterClient", () => {
newCode,
),
);
functionStats =
await client.functionStats(route);
checkClusterResponse(
functionStats as object,
singleNodeRoute,
(value) =>
checkFunctionStatsResponse(
value as FunctionStatsResponse,
[],
1,
2,
),
);

fcall = await client.fcallWithRoute(
func2Name,
Expand Down
Loading
Loading