@yroshcha/node-red-contrib-redis-full 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/icons/redis-icon.svg +7 -0
- package/icons/redis.png +0 -0
- package/package.json +38 -0
- package/redis.html +896 -0
- package/redis.js +1265 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
## 1.0.0 — 2026-08-10
|
|
6
|
+
|
|
7
|
+
First stable public release.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Generic Redis command, Pub/Sub, transaction, and cursor-scan nodes.
|
|
12
|
+
- Redis Streams publishing, consumer groups, manual ACK, manual recovery, dead-consumer cleanup, and stream metrics.
|
|
13
|
+
- Automatic abandoned-entry recovery through `XAUTOCLAIM`, PEL backpressure and alerts, exponential retry backoff with jitter, configurable delivery/batch/read pacing, and optional DLQ handling.
|
|
14
|
+
- Local consumer control through flow actions and the optional HTTP control plane: `pause`, `resume`, `status`, and `drain`.
|
|
15
|
+
- Controlled consumer startup through **Start paused (wait for resume)**.
|
|
16
|
+
|
|
17
|
+
### Security and reliability defaults
|
|
18
|
+
|
|
19
|
+
- HTTP Streams API is disabled by default. When enabled, token protection is optional for private networks and can be required explicitly.
|
|
20
|
+
- Automatic ACK is disabled by default; acknowledge only after successful processing.
|
|
21
|
+
- Unsafe stream trimming requires explicit confirmation.
|
|
22
|
+
|
|
23
|
+
### Compatibility
|
|
24
|
+
|
|
25
|
+
- Node.js 18 or later.
|
|
26
|
+
- Node-RED 3.x, 4.x, and 5.x.
|
|
27
|
+
- Redis 6.2 or later is recommended for `XAUTOCLAIM`; the remaining functionality supports Redis versions compatible with ioredis 5.
|
|
28
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yroshcha
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# @yroshcha/node-red-contrib-redis-full
|
|
2
|
+
|
|
3
|
+
Full Redis support for Node-RED: generic commands, Pub/Sub, scans, transactions, and production-oriented Redis Streams consumer groups.
|
|
4
|
+
|
|
5
|
+
## Palette identity
|
|
6
|
+
|
|
7
|
+
All worker nodes are in the **Redis Full** category, use a light node colour, and have the Redis icon. The package uses one shared `yroshcha-redis-config` node for regular commands, Streams, and production safeguards such as backoff/jitter, PEL alerts, dead-consumer cleanup, and DLQ handling.
|
|
8
|
+
|
|
9
|
+
It follows the Node-RED Redis palette convention: implementation lives in `redis.js` and `redis.html`; non-blocking nodes share one ioredis client while blocking operations receive their own connection.
|
|
10
|
+
|
|
11
|
+
## Node type isolation
|
|
12
|
+
|
|
13
|
+
Every node type is prefixed with `yroshcha-redis-*`; the palette category is uniquely named **Redis Full** and the package registration key is `yroshcha-redis`. It can coexist with `node-red-contrib-redis`, `@golfvert/node-red-redis`, and other Redis palettes in the same Node-RED installation.
|
|
14
|
+
|
|
15
|
+
## Nodes
|
|
16
|
+
|
|
17
|
+
| Palette label | Type | Redis command(s) | Purpose |
|
|
18
|
+
|---|---|---|---|
|
|
19
|
+
| `redis cmd` | `yroshcha-redis-command` | any | Runs a generic command through `.call()`. `Block` forces a dedicated connection for commands such as `BLPOP`, `BRPOP`, or `WAIT`. |
|
|
20
|
+
| `redis sub` | `yroshcha-redis-subscribe` | `SUBSCRIBE` / `PSUBSCRIBE` | Pub/Sub on a dedicated connection. Supports dynamic subscribe/unsubscribe through `msg.subscribe` and `msg.unsubscribe`. |
|
|
21
|
+
| `redis multi` | `yroshcha-redis-multi` | `MULTI` / `EXEC` | Atomic transaction from a command list. |
|
|
22
|
+
| `redis scan` | `yroshcha-redis-scan` | `SCAN` / `HSCAN` / `SSCAN` / `ZSCAN` | Cursor-based non-blocking scan. |
|
|
23
|
+
| `redis xadd` | `yroshcha-redis-stream-out` | `XADD` | Publishes to a stream. `MAXLEN` requires explicit unsafe confirmation. |
|
|
24
|
+
| `redis xreadgroup` | `yroshcha-redis-stream-in` | `XGROUP CREATE MKSTREAM`, `XREADGROUP`, `XAUTOCLAIM` | Consumer-group worker with blocking reads, automatic recovery, backoff/jitter, PEL alerting, DLQ, and backpressure. |
|
|
25
|
+
| `redis xack` | `yroshcha-redis-stream-ack` | `XACK` | Acknowledges one or more stream IDs. Uses `_streamKey` / `_streamGroup` from xreadgroup when its own fields are empty. |
|
|
26
|
+
| `redis xautoclaim` | `yroshcha-redis-stream-claim` | `XAUTOCLAIM` | Manually recovers abandoned PEL entries; processes the cursor incrementally. |
|
|
27
|
+
| `redis consumer gc` | `yroshcha-redis-stream-gc` | `XINFO CONSUMERS`, `XGROUP DELCONSUMER` | Removes idle consumer records that have no pending entries. |
|
|
28
|
+
| `redis stream metrics` | `yroshcha-redis-stream-metrics` | `XINFO GROUPS`, `XPENDING`, optional `XLEN` | Returns PEL, lag, consumer count, group cursor, and optional DLQ length. |
|
|
29
|
+
| `redis streams control` | `yroshcha-redis-stream-control` | — | Flow node for local `pause`, `resume`, `status`, and `drain` actions. |
|
|
30
|
+
| `redis streams API` | `yroshcha-redis-stream-api` | HTTP | Optional HTTP control plane: `GET /redis/streams/status` and `POST /redis/streams/control`. Disabled by default. |
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cd ~/.node-red
|
|
36
|
+
npm install @yroshcha/node-red-contrib-redis-full
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For a local archive, use `npm install /path/to/yroshcha-node-red-contrib-redis-full-1.0.0.tgz`.
|
|
40
|
+
|
|
41
|
+
## Connection model
|
|
42
|
+
|
|
43
|
+
- `yroshcha-redis-config` keeps one shared ioredis client (`getClient()`) for non-blocking nodes: `redis cmd` (without `Block`), `redis multi`, `redis scan`, `redis xadd`, `redis xack`, `redis xautoclaim`, `redis consumer gc`, and `redis stream metrics`.
|
|
44
|
+
- Blocking operations use a dedicated connection (`getDedicatedClient()`): `redis sub`, `redis xreadgroup`, and `redis cmd` when `Block` is enabled.
|
|
45
|
+
|
|
46
|
+
## Release status
|
|
47
|
+
|
|
48
|
+
**`1.0.0` is the first stable release.** See [CHANGELOG.md](CHANGELOG.md) for release notes and compatibility information.
|
|
49
|
+
|
|
50
|
+
## Production profile
|
|
51
|
+
|
|
52
|
+
The Redis client uses bounded failures: `connectTimeout` (10 s), `commandTimeout` (30 s), exponential reconnect up to `retryMaxDelay` (30 s), and at most three retries for an individual non-blocking request. Tune these values to your SLO rather than leaving commands unbounded.
|
|
53
|
+
|
|
54
|
+
Safe consumer defaults and important settings:
|
|
55
|
+
|
|
56
|
+
- **Auto ACK = false.** Acknowledge with `redis xack` only after business processing succeeds. Downstream must be idempotent: Streams provides at-least-once, not exactly-once, delivery.
|
|
57
|
+
- **Max pending = 1000** stops new intake once the group PEL reaches that ceiling. Choose it based on acceptable latency and downstream memory capacity.
|
|
58
|
+
- **Rate limit (msg/s) = 0** is unlimited. For example, `500` limits average downstream delivery to 500 events per second.
|
|
59
|
+
- **Read interval (ms) = 0** reads as fast as Redis responds. `1000` calls `XREADGROUP` no more often than once per second, independent of queue depth.
|
|
60
|
+
- **Batch wait (ms) = 0** emits one event at a time. With a positive value, the node emits an array once it has collected `COUNT` events or the wait expires. `msg.payload` and `msg.streamId` become arrays.
|
|
61
|
+
- **Batch interval (ms)** sets the minimum delay between emitted batches regardless of queue depth. For one batch of up to 50 entries per second, use `COUNT = 50` and `Batch interval = 1000`.
|
|
62
|
+
- **Max deliveries = 5.** After the limit is exceeded, an entry is atomically moved to `<stream>:dlq` and acknowledged. In Redis Cluster, source and DLQ keys must share a hash tag, for example `orders:{eu}` and `orders:{eu}:dlq`.
|
|
63
|
+
- **Start paused (wait for resume)** makes a consumer create/verify its group without calling `XREADGROUP`. It takes no work until a control-plane `resume` request arrives; use it for controlled pod startup after readiness checks.
|
|
64
|
+
- `redis xautoclaim` limits each manual recovery run through `Run limit` (default 1000), avoiding oversized Node-RED payloads.
|
|
65
|
+
- `MAXLEN` in `redis xadd` remains blocked until **Allow unsafe MAXLEN trim** is explicitly confirmed. Trimming can remove a payload still represented in a PEL.
|
|
66
|
+
|
|
67
|
+
## Production readiness boundaries
|
|
68
|
+
|
|
69
|
+
The package includes unit/contract checks (`npm test`), but throughput certification must be performed against the target Redis deployment and real flow. For 80–100 million events/day (roughly 0.9–1.2k events/s on average), complete these gates before release:
|
|
70
|
+
|
|
71
|
+
1. Run a soak test for at least 24 hours with peak load and production-sized payloads.
|
|
72
|
+
2. Test Redis failover, network interruption, and Node-RED rolling restarts without losing events or performing unintended duplicate acknowledgements.
|
|
73
|
+
3. Alert on PEL, DLQ, delivery count, Redis latency, event-loop lag, and heap usage.
|
|
74
|
+
4. Verify downstream idempotency and establish a DLQ replay procedure.
|
|
75
|
+
|
|
76
|
+
## Multi-pod / HPA consumer naming
|
|
77
|
+
|
|
78
|
+
`redis xreadgroup` and `redis xautoclaim` accept a **Consumer name**. When empty, the package uses `HOSTNAME-node.id`, which is unique per pod and per consumer node on the same host. Do **not** set the same static consumer name on multiple replicas in one group: pending recovery can otherwise mix unfinished entries between pods.
|
|
79
|
+
|
|
80
|
+
`process.pid` is not suitable as the fallback because it is commonly `1` in every container. When `HOSTNAME` is unavailable, the flow-file name plus `node.id` is used instead.
|
|
81
|
+
|
|
82
|
+
Each pod restart creates a new hostname and therefore a new consumer entry. Old consumers with `pending = 0` remain visible in `XINFO CONSUMERS` until `redis consumer gc` removes them.
|
|
83
|
+
|
|
84
|
+
## xreadgroup safeguards
|
|
85
|
+
|
|
86
|
+
### Backoff and jitter
|
|
87
|
+
|
|
88
|
+
After an error, the read loop uses exponential backoff with up to 30% random jitter:
|
|
89
|
+
|
|
90
|
+
- `initialBackoffMs` (default `500`) is the first delay.
|
|
91
|
+
- `maxBackoffMs` (default `30000`) is the cap.
|
|
92
|
+
- `backoffMultiplier` (default `2`) is applied after each failure.
|
|
93
|
+
- The delay resets after the first successful Redis call.
|
|
94
|
+
|
|
95
|
+
This avoids a thundering herd when many pods reconnect after a Redis restart or failover.
|
|
96
|
+
|
|
97
|
+
### PEL alert
|
|
98
|
+
|
|
99
|
+
An optional periodic `XPENDING` summary check runs on the shared client, so it does not interrupt the blocking read loop:
|
|
100
|
+
|
|
101
|
+
- `pelAlertThreshold` (default `0`) disables alerting at zero.
|
|
102
|
+
- `pelCheckIntervalMs` (default `30000`) is the check interval.
|
|
103
|
+
- When the threshold is exceeded, the node writes `node.warn(...)` and displays a yellow editor status until the lag recovers.
|
|
104
|
+
|
|
105
|
+
### Dead-consumer GC
|
|
106
|
+
|
|
107
|
+
Run `redis consumer gc` from an input message, typically a scheduled Inject node:
|
|
108
|
+
|
|
109
|
+
- Reads `XINFO CONSUMERS <stream> <group>`.
|
|
110
|
+
- Removes only consumers with `pending = 0` and `idle >= minIdleMs` (default 10 minutes), so active or recently restarted workers with unfinished work are not removed.
|
|
111
|
+
- Returns `msg.payload = { removed: [...], kept: [...] }` for logs and alerts.
|
|
112
|
+
|
|
113
|
+
## Choosing a node
|
|
114
|
+
|
|
115
|
+
- Read or write a single Redis value/command: `redis cmd`.
|
|
116
|
+
- Broadcast delivery without persistence: `redis sub`.
|
|
117
|
+
- Atomic command list without WATCH: `redis multi`.
|
|
118
|
+
- Scan keys or fields without blocking Redis: `redis scan`.
|
|
119
|
+
- Reliable delivery with consumer groups, ACK, and recovery: `redis xadd` → `redis xreadgroup` → `redis xack`. Keep `redis xautoclaim` for manual recovery and `redis consumer gc` for periodic cleanup.
|
|
120
|
+
|
|
121
|
+
## Example flow
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
[yroshcha-redis-config] ← shared configuration for all nodes below
|
|
125
|
+
|
|
126
|
+
[inject] -> [redis cmd: HGETALL] -> [function] -> [redis cmd: HSET]
|
|
127
|
+
|
|
128
|
+
[redis sub: app:notify] -> [function: process event]
|
|
129
|
+
|
|
130
|
+
[inject 30s] -> [redis xautoclaim] -> [function] -> [redis xack]
|
|
131
|
+
[inject 1h] -> [redis consumer gc] -> [function: log removed]
|
|
132
|
+
[redis xreadgroup] -> [function: business logic] -> [redis xack]
|
|
133
|
+
\-> (catch) -> no ACK -> recovered by xautoclaim
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## HTTP control plane
|
|
137
|
+
|
|
138
|
+
Add one `redis streams API` node and enable **Enable HTTP API**. In a private network, it works without a token by default; enable **Require API token** when protection is needed.
|
|
139
|
+
|
|
140
|
+
- `GET /redis/streams/status` returns local consumer states and their PEL.
|
|
141
|
+
- `POST /redis/streams/control` accepts `pause`, `resume`, `status`, or `drain`; filters are `stream`, `group`, and `nodeId`.
|
|
142
|
+
- For graceful scale-down, send `{"action":"drain","drainTimeoutMs":30000}` and terminate the instance only after `drained: true`.
|
|
143
|
+
- For controlled pod startup, enable **Start paused (wait for resume)** on the consumer. After readiness probes pass, send `{"action":"resume"}`. The setting is local to each consumer node.
|
|
144
|
+
|
|
145
|
+
`redis stream metrics` can measure DLQ size too: configure **DLQ stream** and read `msg.payload.deadLetterLength`.
|
|
146
|
+
|
|
147
|
+
## Deliberately not covered
|
|
148
|
+
|
|
149
|
+
- Sentinel: the config node supports only the `cluster` option (Redis Cluster through `Redis.Cluster`), not Sentinel-specific configuration.
|
|
150
|
+
- WATCH-based optimistic locking: `redis multi` is not a replacement for that pattern.
|
|
151
|
+
- Direct `prom-client` metrics: PEL alerts use `node.warn`; use `redis cmd: XPENDING` or `XINFO GROUPS` in a dedicated flow for Prometheus/Grafana collection.
|
|
152
|
+
|
|
153
|
+
## Next improvements
|
|
154
|
+
|
|
155
|
+
- Add Node-RED runtime/integration coverage with `node-red-node-test-helper` and testcontainers.
|
|
156
|
+
- Add Sentinel support to the config node.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
|
|
2
|
+
<g fill="none" stroke="#D82C20" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round">
|
|
3
|
+
<ellipse cx="20" cy="10" rx="14" ry="5"/>
|
|
4
|
+
<path d="M6 10 v9 c0 2.8 6.3 5 14 5 s14 -2.2 14 -5 v-9"/>
|
|
5
|
+
<path d="M6 19 v9 c0 2.8 6.3 5 14 5 s14 -2.2 14 -5 v-9"/>
|
|
6
|
+
</g>
|
|
7
|
+
</svg>
|
package/icons/redis.png
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@yroshcha/node-red-contrib-redis-full",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Production-oriented Redis palette for Node-RED: generic commands, Pub/Sub, scans, transactions, and Redis Streams consumer groups.",
|
|
5
|
+
"keywords": ["node-red", "redis", "redis-streams", "pubsub", "xadd", "xreadgroup", "ioredis"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "yroshcha",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/yroshcha/node-red-contrib-redis-full.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/yroshcha/node-red-contrib-redis-full/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/yroshcha/node-red-contrib-redis-full#readme",
|
|
16
|
+
"main": "redis.js",
|
|
17
|
+
"files": ["redis.js", "redis.html", "README.md", "CHANGELOG.md", "LICENSE", "icons"],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"node-red": ">=3 <6"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "node --test test/*.test.js",
|
|
26
|
+
"check": "node --check redis.js && npm test",
|
|
27
|
+
"prepack": "npm run check",
|
|
28
|
+
"prepublishOnly": "npm run check"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"ioredis": "^5.4.1"
|
|
32
|
+
},
|
|
33
|
+
"node-red": {
|
|
34
|
+
"nodes": {
|
|
35
|
+
"yroshcha-redis": "redis.js"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|