Skip to content

Commit dae47b4

Browse files
authored
feat(client): add latency histogram (#3099)
* add latency histogram command, tests (##1955)
1 parent 38bfaa7 commit dae47b4

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import assert from "node:assert/strict";
2+
import testUtils, { GLOBAL } from "../test-utils";
3+
import LATENCY_HISTOGRAM from "./LATENCY_HISTOGRAM";
4+
import { parseArgs } from "./generic-transformers";
5+
6+
describe("LATENCY HISTOGRAM", () => {
7+
describe("transformArguments", () => {
8+
it("filtered by command set", () => {
9+
assert.deepEqual(parseArgs(LATENCY_HISTOGRAM, "set"), [
10+
"LATENCY",
11+
"HISTOGRAM",
12+
"set",
13+
]);
14+
});
15+
16+
it("unfiltered", () => {
17+
assert.deepEqual(parseArgs(LATENCY_HISTOGRAM), [
18+
"LATENCY",
19+
"HISTOGRAM",
20+
]);
21+
});
22+
});
23+
24+
describe("RESP 2", () => {
25+
testUtils.testWithClient(
26+
"unfiltered list",
27+
async (client) => {
28+
await client.configResetStat();
29+
await Promise.all([
30+
client.lPush("push-key", "hello "),
31+
client.set("set-key", "world!"),
32+
]);
33+
const histogram = await client.latencyHistogram();
34+
const commands = ["config|resetstat", "set", "lpush"];
35+
for (const command of commands) {
36+
assert.ok(typeof histogram[command]["calls"], "number");
37+
}
38+
},
39+
GLOBAL.SERVERS.OPEN,
40+
);
41+
42+
testUtils.testWithClient(
43+
"filtered by a command list",
44+
async (client) => {
45+
await client.configSet("latency-monitor-threshold", "100");
46+
await client.set("set-key", "hello");
47+
const histogram = await client.latencyHistogram("set");
48+
assert.ok(typeof histogram.set["calls"], "number");
49+
},
50+
GLOBAL.SERVERS.OPEN,
51+
);
52+
});
53+
54+
describe("RESP 3", () => {
55+
testUtils.testWithClient(
56+
"unfiltered list",
57+
async (client) => {
58+
await client.configResetStat();
59+
await Promise.all([
60+
client.lPush("push-key", "hello "),
61+
client.set("set-key", "world!"),
62+
]);
63+
const histogram = await client.latencyHistogram();
64+
const commands = ["config|resetstat", "set", "lpush"];
65+
for (const command of commands) {
66+
assert.ok(typeof histogram[command]["calls"], "number");
67+
}
68+
},
69+
{
70+
...GLOBAL.SERVERS.OPEN,
71+
clientOptions: {
72+
RESP: 3,
73+
},
74+
},
75+
);
76+
77+
testUtils.testWithClient(
78+
"filtered by a command list",
79+
async (client) => {
80+
await client.configSet("latency-monitor-threshold", "100");
81+
await client.set("set-key", "hello");
82+
const histogram = await client.latencyHistogram("set");
83+
assert.ok(typeof histogram.set["calls"], "number");
84+
},
85+
{
86+
...GLOBAL.SERVERS.OPEN,
87+
clientOptions: {
88+
RESP: 3,
89+
},
90+
},
91+
);
92+
});
93+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { CommandParser } from '../client/parser';
2+
import { Command } from '../RESP/types';
3+
import { transformTuplesToMap } from './generic-transformers';
4+
5+
type RawHistogram = [string, number, string, number[]];
6+
7+
type Histogram = Record<string, {
8+
calls: number;
9+
histogram_usec: Record<string, number>;
10+
}>;
11+
12+
const id = (n: number) => n;
13+
14+
export default {
15+
CACHEABLE: false,
16+
IS_READ_ONLY: true,
17+
/**
18+
* Constructs the LATENCY HISTOGRAM command
19+
*
20+
* @param parser - The command parser
21+
* @param commands - The list of redis commands to get histogram for
22+
* @see https://redis.io/docs/latest/commands/latency-histogram/
23+
*/
24+
parseCommand(parser: CommandParser, ...commands: string[]) {
25+
const args = ['LATENCY', 'HISTOGRAM'];
26+
if (commands.length !== 0) {
27+
args.push(...commands);
28+
}
29+
parser.push(...args);
30+
},
31+
transformReply: {
32+
2: (reply: (string | RawHistogram)[]): Histogram => {
33+
const result: Histogram = {};
34+
if (reply.length === 0) return result;
35+
for (let i = 1; i < reply.length; i += 2) {
36+
const histogram = reply[i] as RawHistogram;
37+
result[reply[i - 1] as string] = {
38+
calls: histogram[1],
39+
histogram_usec: transformTuplesToMap(histogram[3], id),
40+
};
41+
}
42+
return result;
43+
},
44+
3: undefined as unknown as () => Histogram,
45+
}
46+
} as const satisfies Command;

packages/client/lib/commands/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ import VREM from './VREM';
364364
import VSETATTR from './VSETATTR';
365365
import VSIM from './VSIM';
366366
import VSIM_WITHSCORES from './VSIM_WITHSCORES';
367+
import LATENCY_HISTOGRAM from './LATENCY_HISTOGRAM';
367368

368369
export {
369370
CLIENT_KILL_FILTERS,
@@ -722,6 +723,8 @@ export default {
722723
latencyGraph: LATENCY_GRAPH,
723724
LATENCY_HISTORY,
724725
latencyHistory: LATENCY_HISTORY,
726+
LATENCY_HISTOGRAM,
727+
latencyHistogram: LATENCY_HISTOGRAM,
725728
LATENCY_LATEST,
726729
latencyLatest: LATENCY_LATEST,
727730
LATENCY_RESET,

0 commit comments

Comments
 (0)