Skip to content

Commit

Permalink
Node: Add FUNCTION STATS command. (#2082)
Browse files Browse the repository at this point in the history
* Add `FUNCTION STATS` command.

Signed-off-by: Yury-Fridlyand <yury.fridlyand@improving.com>
  • Loading branch information
Yury-Fridlyand authored Aug 7, 2024
1 parent 4e28404 commit 80f8153
Show file tree
Hide file tree
Showing 9 changed files with 258 additions and 4 deletions.
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

0 comments on commit 80f8153

Please sign in to comment.