Skip to content

Commit 908ff80

Browse files
logaretmclaudePavelPashov
authored
feat: implement diagnostic channels for observability (redis#3195)
* feat: add TracingChannel support with argument sanitization Add diagnostics_channel TracingChannel instrumentation for commands and connections. Wraps sendCommand, connect, pipeline, and MULTI with traceCommand/traceConnect alongside existing OTel metrics. - Sanitize args before emission using OTel redis-common rules - Include clientId from identity system in trace context - Add .catch(noop) for pipeline/MULTI to prevent unhandled rejections - Gate on hasSubscribers for zero-cost when no APM subscribes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: add comprehensive unit tests for sanitizeArgs Cover all serialization subsets (args=0, 1, 2, -1, default), edge cases (empty args, AUTH, HELLO, unknown commands, prefix matching like SETEX), case insensitivity, and non-string arg stringification. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: use ? placeholder for redacted args Matches the SQL parameterization convention used by db.query.text across APM tools (Sentry, Datadog, OTel). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: move OTel command metrics to TracingChannel subscribers Replace inline createRecordOperationDuration/createRecordBatchOperationDuration closures in sendCommand, _executePipeline, and _executeMulti with TracingChannel subscriptions in OTelCommandMetrics. - OTelCommandMetrics subscribes to node-redis:command and node-redis:batch channels - Command filtering (include/exclude) handled in the start subscriber - Batch metrics use new node-redis:batch channel wrapping pipeline/multi - Zero inline OTel metric closures remain in core client code - Noop classes no longer need closure methods - Tests rewritten to verify filtering through TC events Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: remove NoopCommandMetrics class No longer needed — when command metric group is disabled, no TC subscriptions are created, so hasSubscribers is false and traceCommand skips TracingChannel entirely. Zero cost without noops. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: move all inline OTel metrics to diagnostics_channel events Replace every OTelMetrics.instance.* call in core code with publish(CHANNELS.*, factory) point events. All observability data now flows through diagnostics_channel: - TracingChannel for async lifecycles (command, batch, connect) - Plain dc.channel for point events (connection ready/closed, errors, maintenance, pubsub, cache, command replies) Core code has zero OTelMetrics imports. The publish() function uses factory callbacks + hasSubscribers checks for zero-cost when no APM subscribes. Channel names are centralized in CHANNELS const map with a type-safe ChannelEvents interface. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: consolidate all OTel metric classes into channel subscribers Replace 6 individual metric classes (OTelConnectionBasicMetrics, OTelConnectionAdvancedMetrics, OTelResiliencyMetrics, OTelClientSideCacheMetrics, OTelPubSubMetrics, OTelStreamMetrics) with a single OTelChannelSubscribers class that subscribes to diagnostics_channel events. - All metric recording now happens via channel subscriptions - Noop classes eliminated — when a metric group is disabled, no subscription is created, zero overhead - noop-metrics.ts reduced to a single NoopOTelMetrics shell - IOTelMetrics interface simplified to just { commandMetrics } - recordCommandReplyMetrics removed — pubsub out and stream lag metrics now handled by channel subscribers on node-redis:command:reply Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * test: remove no-op channel publish tests These tests only verified that dc.channel().publish() doesn't crash, which is a Node.js guarantee, not our responsibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: use CHANNELS map consistently and tracingChannel API for subscriptions - Add TRACE_COMMAND, TRACE_BATCH, TRACE_CONNECT to CHANNELS map - Replace raw dc.subscribe() on sub-channel strings with dc.tracingChannel().subscribe() for proper TracingChannel usage - Replace all remaining string literals in metrics.ts with CHANNELS.* Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove dead types and interfaces from opentelemetry module Remove types that are no longer implemented by any class after the channel subscriber refactor: - RecordClientErrorContext, CommandReplyMetricHandler, ClientErrorOrigin, MetricErrorType, ConnectionCloseReason, CscResult, CscEvictionReason, ErrorCategory (type aliases) - METRIC_INSTRUMENT_TYPE (unused const) - RedisArgument import (no longer needed in types.ts) - Dead re-exports from opentelemetry/index.ts Const maps (CONNECTION_CLOSE_REASON, CSC_RESULT, etc.) are kept as they're used by tests and may be useful for external consumers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: deduplicate diagnostics_channel loading Export dc from tracing.ts and import it in metrics.ts instead of loading it independently in both modules. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: rename BatchTraceContext to BatchOperationContext Clarifies the distinction: BatchOperationContext is the batch operation as a whole (MULTI/PIPELINE), BatchCommandTraceContext is a single command within a batch. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove unused CONNECTION_WAIT_START event No subscriber existed for this event. The original redis#3110 code only recorded at wait end, not start. Remove the speculative event. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: restore wasReady guard on connection count decrement The original code only decremented db.client.connection.count when the socket was previously ready. Our refactoring lost this gate in destroySocket(), which could decrement below zero if destroy was called before the socket became ready. Add wasReady to the ConnectionClosedEvent so the subscriber can guard correctly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: export all channel names and event types for APM consumers Export CHANNELS map, ChannelEvents type map, and all individual event types so APM libraries can subscribe with proper typing: import { CHANNELS, type CommandTraceContext } from 'redis'; dc.subscribe(CHANNELS.ERROR, (ctx: ClientErrorEvent) => { ... }); Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: convert pool wait time to TracingChannel Replace CONNECTION_WAIT_END point event with TRACE_CONNECTION_WAIT TracingChannel. Pool wait is an async operation (start when no idle client, end when one becomes available) — TracingChannel captures the full lifecycle so APMs can create spans or measure duration. Core code emits lifecycle events only. Timing is computed by subscribers (OTel uses start/asyncEnd to measure duration). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: consistent serverPort typing across all event interfaces Use number | undefined everywhere instead of mixing with number?. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: add getTracingChannel helper with auto-resolved context types Eliminates repetitive ternary pattern. TracingChannelContextMap maps channel names to their context types so getTracingChannel infers the correct type without explicit generics. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: stop exporting dc, use getTracingChannel and getChannel instead metrics.ts no longer imports dc directly. All channel acquisition goes through the exported helpers which handle caching and undefined checks. dc remains private to tracing.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: unify trace functions into single generic trace() Replace traceCommand, traceBatch, traceConnect, traceConnectionWait with a single trace(CHANNELS.TRACE_*, fn, contextFactory) function. Channel name resolves the context type via TracingChannelContextMap. Also adds cache for getTracingChannel to avoid re-acquiring on every call, matching getChannel's caching pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: store unsubscriber closures instead of handler references Replace separate #subscriptions and #tracingChannels arrays with a single #unsubscribers list of cleanup closures. Simpler destroy(), no need to store channel/handler pairs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: delete noop-metrics.ts Inline the single remaining noop as an object literal in the default #instance. No noop classes or files remain. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: deduplicate COMMAND_REPLY events and merge subscriber Remove duplicate COMMAND_REPLY publish from sendCommand — typed commands already publish via _executeCommand. Also merge the two separate COMMAND_REPLY subscribers (pubsub out + streaming) into a single #subscribeCommandReply method. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: reject tracing promise on pool acquire timeout When the acquire timeout fires, rejectWait() is called so the TracingChannel error channel fires — APMs see the timeout as an error instead of an orphaned span that never closes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove noop-meter.ts When OTel is not initialized or disabled, skip instrument registration and subscriber creation entirely. No noop instruments needed — if there are no subscribers, channels are never fired. Removes 139-line noop-meter.ts and all noop fallback branches from instrument creation helpers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: sanitize COMMAND_REPLY args and catch pool trace promise - COMMAND_REPLY in _executeCommand now passes sanitizeArgs(parser.redisArgs) instead of raw args, preventing sensitive values from leaking to subscribers - Pool connection wait trace promise gets .catch(noop) to prevent unhandled rejection if a tracing subscriber throws Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: reject pending wait traces on pool destroy When pool.destroy() is called with tasks still queued, call rejectWait() on each pending task so the TracingChannel error channel fires and APM subscribers close their spans cleanly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: move const noop after imports in pool.ts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: E2E test failures from refactor - Tracing test: check isOpen before destroying already-closed client - OTel E2E tests: rewrite 5 tests that called removed methods (resiliencyMetrics.recordClientErrors, streamMetrics.recordStreamLag) to publish via channels instead - Connection closed metric: split subscriber for basic (connection count) and advanced (close reason) so tests enabling only one group see correct metrics Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: record connection wait time when client is immediately available The original code always recorded wait time, even for 0ms waits when a client was available. The TracingChannel refactor only traced the "no client, must wait" path. Add trace for immediate availability so the metric is always emitted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: clean up comments, remove em dashes, be concise Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(perf): optimize channel acquisition * refactor: deduplicate command error reporting, use TracingChannel error event Remove redundant publish(CHANNELS.ERROR) from sendCommand — the TracingChannel already emits error events for command failures. Wire OTel resiliency subscriber to commandTC.error for command-level error counts; CHANNELS.ERROR now only carries cluster/internal errors. Extract subscribeTC() helper to reduce TracingChannel subscription boilerplate and #recordError() to share error metric recording logic. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: filter redirection errors at call site, not in #recordError The previous approach unconditionally dropped all MOVED/ASK errors in #recordError, including cluster-origin ones that indicate slot migration. Move the filter to each call site so client-origin redirections are skipped while cluster-origin ones are still recorded. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use ctx.origin instead of ctx.internal for redirection deduplication internal and origin are separate fields — an error can be non-internal but still originate from the cluster. Use ctx.origin === 'client' as Pavel suggested to correctly identify client-origin redirections. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: guard against disabled metrics in OTelMetrics constructor Short-circuit with noop stubs when enabled is false, so metrics are not recorded even when meterProvider and enabledMetricGroups are provided. Update the disabled-metrics test to supply those options, verifying the guard works rather than passing vacuously. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: treat MULTI as atomic operation, remove per-command tracing Redis replies QUEUED for each command inside MULTI before EXEC runs, so per-command traces resolve as successful even when EXEC fails. Only the batch-level trace reflects the real outcome. Remove per-command tracing from _executeMulti and keep only the TRACE_BATCH span. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(otel): publish pool connection wait as a point event * chore: raise minimum supported Node.js version to 18.19.0 * fix(client): guard OTel metrics reset and gauge callbacks * docs: add diagnostics_channel docs * docs: clarify diagnostics tracing channel lifecycle --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Pavel Pashov <pavel.pashov@redis.com>
1 parent 2e167bb commit 908ff80

26 files changed

Lines changed: 1610 additions & 1365 deletions

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,10 @@ For SDK/provider/exporter setup, verification, and advanced configuration, see:
353353
- [OpenTelemetry Metrics docs](./docs/otel-metrics.md)
354354
- [OpenTelemetry Metrics example](./examples/otel-metrics.js)
355355

356+
### Diagnostics Channel
357+
358+
Node Redis publishes telemetry through Node.js [`diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html), enabling APM tools and custom instrumentation to observe commands, connections, and internal events. See the [Diagnostics Channel guide](./docs/diagnostics-channel.md) for the full channel reference and usage examples.
359+
356360
### Events
357361

358362
The Node Redis client class is an Nodejs EventEmitter and it emits an event each time the network status changes:

docs/diagnostics-channel.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Diagnostics Channel
2+
3+
Node Redis publishes telemetry through Node.js [`diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html), allowing APM tools and custom instrumentation to observe commands, connections, and internal events without modifying application code.
4+
5+
All channel name constants (`CHANNELS`) and payload types are exported from `@redis/client`.
6+
7+
```typescript
8+
import { CHANNELS, type CommandTraceContext } from "@redis/client";
9+
```
10+
11+
## Channel Types
12+
13+
### TracingChannels (async lifecycle)
14+
15+
Requires Node.js >= 18.19.0. These channels use Node.js `TracingChannel#tracePromise()` and emit `start`, `end`, `asyncStart`, `asyncEnd`, and `error` sub-events. `start`/`end` wrap the synchronous portion of the traced callback, while `asyncStart`/`asyncEnd` wrap the returned promise. Subscribe via `tracing:<name>:<event>`, for example `tracing:node-redis:command:start` or `tracing:node-redis:command:asyncEnd`.
16+
17+
```typescript
18+
import dc from "node:diagnostics_channel";
19+
20+
// Fired when the command starts.
21+
dc.subscribe("tracing:node-redis:command:start", ({ command, args }) => {
22+
console.log(`> ${command}`, args);
23+
});
24+
25+
// Fired when the async Redis operation settles (success or failure).
26+
dc.subscribe("tracing:node-redis:command:asyncEnd", ({ command }) => {
27+
console.log(`${command} settled`);
28+
});
29+
30+
dc.subscribe("tracing:node-redis:command:error", ({ command, error }) => {
31+
console.error(`${command} failed:`, error);
32+
});
33+
```
34+
35+
| Channel name | Payload | Description |
36+
| -------------------- | ------------------------------------------------ | ------------------------------------------ |
37+
| `node-redis:command` | `CommandTraceContext / BatchCommandTraceContext` | Individual command (standalone or pipeline) |
38+
| `node-redis:batch` | `BatchOperationContext` | MULTI/PIPELINE batch as a whole |
39+
| `node-redis:connect` | `ConnectTraceContext` | Socket connection attempt |
40+
41+
### Point-event channels (fire-and-forget)
42+
43+
Work on Node.js >= 16. Subscribe via `dc.subscribe('<name>', handler)`.
44+
45+
```typescript
46+
dc.subscribe("node-redis:connection:ready", ({ clientId, createTimeMs }) => {
47+
console.log(`Client ${clientId} connected in ${createTimeMs.toFixed(1)}ms`);
48+
});
49+
```
50+
51+
| Channel name | Payload | Description |
52+
| --------------------------------------- | ------------------------------- | ---------------------------------------- |
53+
| `node-redis:connection:ready` | `ConnectionReadyEvent` | Socket connected and ready |
54+
| `node-redis:connection:closed` | `ConnectionClosedEvent` | Socket closed |
55+
| `node-redis:connection:relaxed-timeout` | `ConnectionRelaxedTimeoutEvent` | Timeout relaxed/restored for maintenance |
56+
| `node-redis:connection:handoff` | `ConnectionHandoffEvent` | Maintenance handoff completed |
57+
| `node-redis:error` | `ClientErrorEvent` | Client or cluster error |
58+
| `node-redis:maintenance` | `MaintenanceNotificationEvent` | Maintenance push notification |
59+
| `node-redis:pubsub` | `PubSubMessageEvent` | Inbound PubSub message |
60+
| `node-redis:cache:request` | `CacheRequestEvent` | Client-side cache hit/miss |
61+
| `node-redis:cache:eviction` | `CacheEvictionEvent` | Cache entry evicted |
62+
| `node-redis:command:reply` | `CommandReplyEvent` | Command reply (for pubsub/streaming) |
63+
| `node-redis:pool:connection-wait` | `PoolConnectionWaitEvent` | Pool task acquired a client |

package-lock.json

Lines changed: 8 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/bloom/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"@redis/test-utils": "*"
2020
},
2121
"engines": {
22-
"node": ">= 18"
22+
"node": ">= 18.19.0"
2323
},
2424
"repository": {
2525
"type": "git",

packages/client/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,23 @@ export { SetOptions, CLIENT_KILL_FILTERS, FAILOVER_MODES, CLUSTER_SLOT_STATES, C
3737

3838
export { BasicClientSideCache, BasicPooledClientSideCache } from './lib/client/cache';
3939
export { OpenTelemetry } from './lib/opentelemetry';
40+
41+
export {
42+
CHANNELS,
43+
type ChannelEvents,
44+
type CommandTraceContext,
45+
type BatchCommandTraceContext,
46+
type BatchOperationContext,
47+
type ConnectTraceContext,
48+
type ConnectionReadyEvent,
49+
type ConnectionClosedEvent,
50+
type ConnectionRelaxedTimeoutEvent,
51+
type ConnectionHandoffEvent,
52+
type ClientErrorEvent,
53+
type MaintenanceNotificationEvent,
54+
type PubSubMessageEvent,
55+
type CacheRequestEvent,
56+
type CacheEvictionEvent,
57+
type CommandReplyEvent,
58+
type PoolConnectionWaitEvent,
59+
} from './lib/client/tracing';

packages/client/lib/client/cache.ts

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { EventEmitter } from 'stream';
22
import RedisClient from '.';
33
import { RedisArgument, ReplyUnion, TransformReply, TypeMapping } from '../RESP/types';
44
import { BasicCommandParser } from './parser';
5-
import { OTelMetrics, CSC_RESULT, CSC_EVICTION_REASON } from '../opentelemetry';
5+
import { publish, CHANNELS } from './tracing';
66

77
/**
88
* A snapshot of cache statistics.
@@ -558,33 +558,20 @@ export class BasicClientSideCache extends ClientSideCacheProvider {
558558
// If instanceof is "too slow", can add a "type" and then use an "as" cast to call proper getters.
559559
if (cacheEntry instanceof ClientSideCacheEntryValue) { // "2b1"
560560
this.#statsCounter.recordHits(1);
561-
OTelMetrics.instance.clientSideCacheMetrics.recordCacheRequest(
562-
CSC_RESULT.HIT,
563-
client._clientId,
564-
);
565-
OTelMetrics.instance.clientSideCacheMetrics.recordNetworkBytesSaved(
566-
cacheEntry.value,
567-
client._clientId,
568-
);
561+
publish(CHANNELS.CACHE_REQUEST, () => ({ result: 'hit', clientId: client._clientId }));
569562

570563
return structuredClone(cacheEntry.value);
571564
} else if (cacheEntry instanceof ClientSideCacheEntryPromise) { // 2b2
572565
// This counts as a miss since the value hasn't been fully loaded yet.
573566
this.#statsCounter.recordMisses(1);
574-
OTelMetrics.instance.clientSideCacheMetrics.recordCacheRequest(
575-
CSC_RESULT.MISS,
576-
client._clientId,
577-
);
567+
publish(CHANNELS.CACHE_REQUEST, () => ({ result: 'miss', clientId: client._clientId }));
578568
reply = await cacheEntry.promise;
579569
} else {
580570
throw new Error("unknown cache entry type");
581571
}
582572
} else { // 3/3a
583573
this.#statsCounter.recordMisses(1);
584-
OTelMetrics.instance.clientSideCacheMetrics.recordCacheRequest(
585-
CSC_RESULT.MISS,
586-
client._clientId,
587-
);
574+
publish(CHANNELS.CACHE_REQUEST, () => ({ result: 'miss', clientId: client._clientId }));
588575

589576
const startTime = performance.now();
590577
const promise = fn();
@@ -640,7 +627,7 @@ export class BasicClientSideCache extends ClientSideCacheProvider {
640627
this.clear(false);
641628
// Record invalidations as server-initiated evictions
642629
if (oldSize > 0) {
643-
OTelMetrics.instance.clientSideCacheMetrics.recordCacheEviction(CSC_EVICTION_REASON.INVALIDATION, oldSize);
630+
publish(CHANNELS.CACHE_EVICTION, () => ({ reason: 'invalidation', count: oldSize }));
644631
}
645632
this.emit("invalidate", key);
646633

@@ -661,7 +648,7 @@ export class BasicClientSideCache extends ClientSideCacheProvider {
661648
this.#keyToCacheKeySetMap.delete(key.toString());
662649
if (deletedCount > 0) {
663650
// Record invalidations as server-initiated evictions
664-
OTelMetrics.instance.clientSideCacheMetrics.recordCacheEviction(CSC_EVICTION_REASON.INVALIDATION, deletedCount);
651+
publish(CHANNELS.CACHE_EVICTION, () => ({ reason: 'invalidation', count: deletedCount }));
665652
}
666653
}
667654

@@ -692,7 +679,7 @@ export class BasicClientSideCache extends ClientSideCacheProvider {
692679
this.delete(cacheKey);
693680
this.#statsCounter.recordEvictions(1);
694681
// Entry failed validation - this is TTL expiry since invalidation marks are handled separately
695-
OTelMetrics.instance.clientSideCacheMetrics.recordCacheEviction(CSC_EVICTION_REASON.TTL);
682+
publish(CHANNELS.CACHE_EVICTION, () => ({ reason: 'ttl', count: 1 }));
696683
this.emit("cache-evict", cacheKey);
697684

698685
return undefined;
@@ -731,7 +718,7 @@ export class BasicClientSideCache extends ClientSideCacheProvider {
731718
this.deleteOldest();
732719
this.#statsCounter.recordEvictions(1);
733720
// Eviction due to cache capacity limit
734-
OTelMetrics.instance.clientSideCacheMetrics.recordCacheEviction(CSC_EVICTION_REASON.FULL);
721+
publish(CHANNELS.CACHE_EVICTION, () => ({ reason: 'full', count: 1 }));
735722
}
736723

737724
this.#cacheKeyToEntryMap.set(cacheKey, cacheEntry);

packages/client/lib/client/enterprise-maintenance-manager.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import { setTimeout } from "node:timers/promises";
77
import { RedisTcpSocketOptions } from "./socket";
88
import diagnostics_channel from "node:diagnostics_channel";
99
import { RedisArgument } from "../RESP/types";
10-
import { OTelMetrics } from "../opentelemetry";
11-
import { METRIC_ERROR_ORIGIN } from "../opentelemetry/types";
10+
import { publish, CHANNELS } from "./tracing";
1211

1312
type RedisType = RedisClient<any, any, any, any, any>;
1413

@@ -124,12 +123,12 @@ export default class EnterpriseMaintenanceManager {
124123
errorHandler: (error: Error) => {
125124
dbgMaintenance("handshake failed:", error);
126125

127-
OTelMetrics.instance.resiliencyMetrics.recordClientErrors({
126+
publish(CHANNELS.ERROR, () => ({
128127
error,
129-
origin: METRIC_ERROR_ORIGIN.CLIENT,
128+
origin: 'client',
130129
internal: true,
131130
clientId,
132-
});
131+
}));
133132

134133
if (options.maintNotifications === "enabled") {
135134
throw error;
@@ -160,10 +159,10 @@ export default class EnterpriseMaintenanceManager {
160159

161160
const type = String(push[0]);
162161

163-
OTelMetrics.instance.resiliencyMetrics.recordMaintenanceNotifications(
164-
type,
165-
this.#client._clientId,
166-
);
162+
publish(CHANNELS.MAINTENANCE, () => ({
163+
notification: type,
164+
clientId: this.#client._clientId,
165+
}));
167166

168167
emitDiagnostics({
169168
type,
@@ -306,9 +305,7 @@ export default class EnterpriseMaintenanceManager {
306305
dbgMaintenance("Resume writing");
307306
this.#client._unpause();
308307
this.#onMigrated();
309-
OTelMetrics.instance.connectionBasicMetrics.recordConnectionHandoff(
310-
this.#client._clientId,
311-
);
308+
publish(CHANNELS.CONNECTION_HANDOFF, () => ({ clientId: this.#client._clientId }));
312309
};
313310

314311
#onMigrating = () => {

0 commit comments

Comments
 (0)