Skip to content

Commit fa45a31

Browse files
committed
perf(metrics): optimize command metrics with factory pattern and inline attributes
1 parent f0708bc commit fa45a31

File tree

3 files changed

+80
-42
lines changed

3 files changed

+80
-42
lines changed

packages/client/lib/client/index.ts

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,8 +1076,11 @@ export default class RedisClient<
10761076
args: ReadonlyArray<RedisArgument>,
10771077
options?: CommandOptions
10781078
): Promise<T> {
1079-
const clientAttributes = this._self._getClientOTelAttributes();
1080-
const recordOperation = OTelMetrics.instance.commandMetrics.createRecordOperationDuration(args, clientAttributes);
1079+
const recordOperation = OTelMetrics.instance.commandMetrics.createRecordOperationDuration(args, {
1080+
host: this._self.#socket.host,
1081+
port: this._self.#socket.port,
1082+
db: this._self.#selectedDB,
1083+
});
10811084

10821085
if (!this._self.#socket.isOpen) {
10831086
recordOperation(new ClientClosedError());
@@ -1098,27 +1101,40 @@ export default class RedisClient<
10981101

10991102
const promise = this._self.#queue.addCommand<T>(args, opts);
11001103

1101-
if (OTelMetrics.isInitialized()) {
1102-
OTelMetrics.instance.connectionAdvancedMetrics.recordPendingRequests(1, clientAttributes);
1104+
OTelMetrics.instance.connectionAdvancedMetrics.recordPendingRequests(1, {
1105+
host: this._self.#socket.host,
1106+
port: this._self.#socket.port,
1107+
db: this._self.#selectedDB,
1108+
});
11031109

1104-
const trackedPromise = promise
1105-
.then((reply) => {
1106-
recordOperation();
1107-
OTelMetrics.instance.connectionAdvancedMetrics.recordPendingRequests(-1, clientAttributes);
1108-
return reply;
1109-
})
1110-
.catch((err) => {
1111-
recordOperation(err);
1112-
OTelMetrics.instance.connectionAdvancedMetrics.recordPendingRequests(-1, clientAttributes);
1113-
throw err;
1114-
});
1110+
const trackedPromise = promise
1111+
.then((reply) => {
1112+
recordOperation();
1113+
OTelMetrics.instance.connectionAdvancedMetrics.recordPendingRequests(
1114+
-1,
1115+
{
1116+
host: this._self.#socket.host,
1117+
port: this._self.#socket.port,
1118+
db: this._self.#selectedDB,
1119+
}
1120+
);
1121+
return reply;
1122+
})
1123+
.catch((err) => {
1124+
recordOperation(err);
1125+
OTelMetrics.instance.connectionAdvancedMetrics.recordPendingRequests(
1126+
-1,
1127+
{
1128+
host: this._self.#socket.host,
1129+
port: this._self.#socket.port,
1130+
db: this._self.#selectedDB,
1131+
}
1132+
);
1133+
throw err;
1134+
});
11151135

1116-
this._self.#scheduleWrite();
1117-
return trackedPromise;
1118-
} else {
1119-
this._self.#scheduleWrite();
1120-
return promise;
1121-
}
1136+
this._self.#scheduleWrite();
1137+
return trackedPromise;
11221138
}
11231139

11241140
async SELECT(db: number): Promise<void> {

packages/client/lib/opentelemetry/metrics.ts

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,26 @@ import {
3434
class OTelCommandMetrics implements IOTelCommandMetrics {
3535
readonly #instruments: MetricInstruments;
3636
readonly #options: MetricOptions;
37+
public readonly createRecordOperationDuration: (
38+
args: ReadonlyArray<RedisArgument>,
39+
clientAttributes?: OTelClientAttributes
40+
) => (error?: Error) => void;
3741

3842
constructor(options: MetricOptions, instruments: MetricInstruments) {
3943
this.#options = options;
4044
this.#instruments = instruments;
41-
}
4245

43-
private isCommandExcluded(commandName: string) {
44-
return (
45-
// It's not explicitly included
46-
(this.#options.hasIncludeCommands &&
47-
!this.#options.includeCommands[commandName]) ||
48-
// it's explicitly excluded
49-
this.#options.excludeCommands[commandName]
50-
);
46+
// Build the appropriate function based on options
47+
if (options.hasIncludeCommands || options.hasExcludeCommands) {
48+
// Version with filtering
49+
this.createRecordOperationDuration = this.#createWithFiltering.bind(this);
50+
} else {
51+
this.createRecordOperationDuration =
52+
this.#createWithoutFiltering.bind(this);
53+
}
5154
}
5255

53-
public createRecordOperationDuration(
56+
#createWithFiltering(
5457
args: ReadonlyArray<RedisArgument>,
5558
clientAttributes?: OTelClientAttributes
5659
): (error?: Error) => void {
@@ -60,25 +63,45 @@ class OTelCommandMetrics implements IOTelCommandMetrics {
6063
return noopFunction;
6164
}
6265

63-
const startTime = performance.now();
66+
return this.#recordOperation(commandName, clientAttributes);
67+
}
6468

65-
const baseAttributes = {
66-
[OTEL_ATTRIBUTES.dbOperationName]: commandName,
67-
...parseClientAttributes(clientAttributes),
68-
};
69+
#createWithoutFiltering(
70+
args: ReadonlyArray<RedisArgument>,
71+
clientAttributes?: OTelClientAttributes
72+
): (error?: Error) => void {
73+
const commandName = args[0]?.toString() || "UNKNOWN";
74+
return this.#recordOperation(commandName, clientAttributes);
75+
}
76+
77+
#recordOperation(
78+
commandName: string,
79+
clientAttributes?: OTelClientAttributes
80+
): (error?: Error) => void {
81+
const startTime = performance.now();
6982

7083
return (error?: Error) => {
7184
this.#instruments.dbClientOperationDuration.record(
72-
(performance.now() - startTime) / 1000, // convert to seconds
85+
(performance.now() - startTime) / 1000,
7386
{
7487
...this.#options.attributes,
75-
...baseAttributes,
76-
// TODO add error types
88+
[OTEL_ATTRIBUTES.dbOperationName]: commandName,
89+
[OTEL_ATTRIBUTES.dbNamespace]: clientAttributes?.db,
90+
[OTEL_ATTRIBUTES.serverAddress]: clientAttributes?.host,
91+
[OTEL_ATTRIBUTES.serverPort]: clientAttributes?.port,
7792
...(error ? { [OTEL_ATTRIBUTES.errorType]: error.message } : {}),
7893
}
7994
);
8095
};
8196
}
97+
98+
private isCommandExcluded(commandName: string) {
99+
return (
100+
(this.#options.hasIncludeCommands &&
101+
!this.#options.includeCommands[commandName]) ||
102+
this.#options.excludeCommands[commandName]
103+
);
104+
}
82105
}
83106

84107
class OTelConnectionBasicMetrics implements IOTelConnectionBasicMetrics {
@@ -92,6 +115,7 @@ class OTelConnectionBasicMetrics implements IOTelConnectionBasicMetrics {
92115

93116
public recordConnectionCount(
94117
value: number,
118+
95119
clientAttributes?: OTelClientAttributes
96120
) {
97121
this.#instruments.dbClientConnectionCount.add(value, {

packages/client/lib/opentelemetry/types.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,9 +251,7 @@ export interface IOTelResiliencyMetrics {
251251
type: MetricErrorType,
252252
clientAttributes?: OTelClientAttributes
253253
): void;
254-
recordMaintenanceNotifications(
255-
clientAttributes?: OTelClientAttributes
256-
): void;
254+
recordMaintenanceNotifications(clientAttributes?: OTelClientAttributes): void;
257255
}
258256

259257
export interface IOTelMetrics {

0 commit comments

Comments
 (0)