Skip to content

Commit e4d84b7

Browse files
authored
Merge branch 'master' into resp3
2 parents 98ddbc1 + 41c908e commit e4d84b7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+6623
-253
lines changed
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: 'Run node-redis tests'
2+
description: 'Runs node-redis tests against different Redis versions and configurations'
3+
inputs:
4+
repository:
5+
description: 'Repository to checkout'
6+
required: false
7+
ref:
8+
description: 'Branch to checkout'
9+
required: false
10+
node-version:
11+
description: 'Node version to use for running tests'
12+
required: true
13+
redis-tag:
14+
description: 'Redis image tag'
15+
required: false
16+
redis-version:
17+
description: 'Redis version to test against'
18+
required: false
19+
otel-authorization-token:
20+
description: 'Authorization token for OTEL metrics reporting'
21+
required: false
22+
run-codecov:
23+
description: 'Whether to upload coverage to Codecov'
24+
required: false
25+
default: 'true'
26+
runs:
27+
using: "composite"
28+
steps:
29+
- uses: actions/checkout@v4
30+
with:
31+
repository: ${{ inputs.repository }}
32+
ref: ${{ inputs.ref }}
33+
fetch-depth: 1
34+
- name: Use Node.js ${{ inputs.node-version }}
35+
uses: actions/setup-node@v4
36+
with:
37+
node-version: ${{ inputs.node-version }}
38+
- name: Update npm
39+
shell: bash
40+
run: npm i -g npm
41+
if: ${{ inputs.node-version <= 14 }}
42+
- name: Install Packages
43+
shell: bash
44+
run: npm ci
45+
- name: Build
46+
shell: bash
47+
run: npm run build
48+
- name: Run Tests
49+
shell: bash
50+
run: npm run test -ws --if-present -- --forbid-only --redis-tag=${{ inputs.redis-tag }} --redis-version=${{ inputs.redis-version }}
51+
- name: Upload to Codecov
52+
if: inputs.run-codecov == 'true'
53+
shell: bash
54+
run: |
55+
curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import
56+
curl -Os https://uploader.codecov.io/latest/linux/codecov
57+
curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM
58+
curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig
59+
gpgv codecov.SHA256SUM.sig codecov.SHA256SUM
60+
shasum -a 256 -c codecov.SHA256SUM
61+
chmod +x codecov
62+
./codecov
63+
- name: Self Report Metrics
64+
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && inputs.otel-authorization-token != ''
65+
id: self-report-metrics
66+
uses: redis-developer/cae-otel-ci-visibility@v2
67+
with:
68+
junit-xml-folder: "junit-results"
69+
otlp-endpoint: "https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/metrics"
70+
otlp-headers: "Authorization=Basic ${{inputs.otel-authorization-token}}"
71+
env:
72+
OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf"
73+
ACTIONS_STEP_DEBUG: "true"

.github/workflows/tests.yml

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,26 +35,10 @@ jobs:
3535
- uses: actions/checkout@v4
3636
with:
3737
fetch-depth: 1
38-
- name: Use Node.js ${{ matrix.node-version }}
39-
uses: actions/setup-node@v4
38+
- name: Run tests
39+
uses: ./.github/actions/run-tests
4040
with:
41-
node-version: ${{ matrix.node-version }}
42-
- name: Update npm
43-
run: npm i -g npm
44-
if: ${{ matrix.node-version <= 14 }}
45-
- name: Install Packages
46-
run: npm ci
47-
- name: Build
48-
run: npm run build
49-
- name: Run Tests
50-
run: npm run test -ws --if-present -- --forbid-only --redis-tag=${{ matrix.redis.tag }} --redis-version=${{ matrix.redis.version }}
51-
- name: Upload to Codecov
52-
run: |
53-
curl https://keybase.io/codecovsecurity/pgp_keys.asc | gpg --no-default-keyring --keyring trustedkeys.gpg --import
54-
curl -Os https://uploader.codecov.io/latest/linux/codecov
55-
curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM
56-
curl -Os https://uploader.codecov.io/latest/linux/codecov.SHA256SUM.sig
57-
gpgv codecov.SHA256SUM.sig codecov.SHA256SUM
58-
shasum -a 256 -c codecov.SHA256SUM
59-
chmod +x codecov
60-
./codecov
41+
node-version: ${{ matrix.node-version }}
42+
redis-tag: ${{ matrix.redis.tag }}
43+
redis-version: ${{ matrix.redis.version }}
44+
otel-authorization-token: ${{ secrets.SELF_CHECK_OTEL_AUTHORIZATION_TOKEN }}

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,17 @@ await client.hGet("key", "field"); // { field: <Buffer 76 61 6c 75 65> }
133133

134134
```
135135

136+
For commands that return serialized binary payloads, such as `DUMP`, map blob strings to `Buffer` before using the result with commands like `RESTORE`:
137+
138+
```typescript
139+
const binaryClient = createClient().withTypeMapping({
140+
[RESP_TYPES.BLOB_STRING]: Buffer
141+
});
142+
143+
const dump = await binaryClient.dump("source");
144+
await binaryClient.restore("destination", 0, dump);
145+
```
146+
136147
### Unsupported Redis Commands
137148

138149
If you want to run commands and/or use arguments that Node Redis doesn't know about (yet!) use `.sendCommand()`:
@@ -315,6 +326,37 @@ See the [Programmability overview](https://github.com/redis/node-redis/blob/mast
315326

316327
Check out the [Clustering Guide](https://github.com/redis/node-redis/blob/master/docs/clustering.md) when using Node Redis to connect to a Redis Cluster.
317328

329+
### OpenTelemetry
330+
331+
#### OpenTelemetry Metrics Instrumentation
332+
333+
```typescript
334+
import { createClient, OpenTelemetry } from "redis";
335+
336+
OpenTelemetry.init({
337+
metrics: {
338+
enabled: true
339+
}
340+
});
341+
342+
const client = createClient()
343+
344+
await client.connect();
345+
// ... use the client as usual
346+
```
347+
348+
**Important:** Initializing `OpenTelemetry` only enables node-redis metrics instrumentation and requires both `@opentelemetry/api` and an OpenTelemetry SDK configured in your application.
349+
350+
**Important:** Initialize `OpenTelemetry` before creating Redis clients.
351+
For SDK/provider/exporter setup, verification, and advanced configuration, see:
352+
353+
- [OpenTelemetry Metrics docs](./docs/otel-metrics.md)
354+
- [OpenTelemetry Metrics example](./examples/otel-metrics.js)
355+
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+
318360
### Events
319361

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

docs/command-options.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,37 @@ await proxyClient.get('key'); // `Buffer | null`
2020

2121
See [RESP](./RESP.md) for a full list of types.
2222

23+
### `DUMP`/`RESTORE` and Binary Data
24+
25+
[`DUMP`](https://redis.io/commands/dump/) returns serialized binary data. If blob strings are decoded as `string` (the default), the payload can be lossy and [`RESTORE`](https://redis.io/commands/restore/) may fail with `ERR DUMP payload version or checksum are wrong`.
26+
27+
Use `Buffer` mapping for blob strings:
28+
29+
```javascript
30+
import { createClient, RESP_TYPES } from 'redis';
31+
32+
const client = createClient().withTypeMapping({
33+
[RESP_TYPES.BLOB_STRING]: Buffer
34+
});
35+
36+
const dump = await client.dump('source');
37+
await client.restore('destination', 0, dump);
38+
```
39+
40+
You can also set it as a default:
41+
42+
```javascript
43+
const client = createClient({
44+
commandOptions: {
45+
typeMapping: {
46+
[RESP_TYPES.BLOB_STRING]: Buffer
47+
}
48+
}
49+
});
50+
```
51+
52+
In v5, use `withTypeMapping` or `createClient({ commandOptions: { typeMapping } })` instead of the v4 `client.commandOptions({ returnBuffers: true })` call style.
53+
2354
## Abort Signal
2455

2556
The client [batches commands](./FAQ.md#how-are-commands-batched) before sending them to Redis. Commands that haven't been written to the socket yet can be aborted using the [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) API:

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 |

0 commit comments

Comments
 (0)