Repository navigation
Add RESP3 - #2127
Add RESP3#2127
Conversation
6225346 to
fed5a8e
Compare
Coverage Report for CI Build 29090997366Warning Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes. Warning No base build found for commit Coverage: 89.501%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats💛 - Coveralls |
733a65a to
396b20e
Compare
Add a replyMapping option for RESP3 connections, keeping legacy RESP2-like reply shapes by default while allowing native RESP3 map and double replies. Also update RESP3 subscriber-mode handling so regular commands remain usable while subscribed, preserve object reply encoding conversion, harden RESP3 map object decoding for special keys, and add migration notes plus functional coverage for reply mapping and RESP2/RESP3 parity.
Add RESP3 reply-mapping return types and the generic plumbing that wires them through the client: - bin codegen (returnTypes/template) emits RespShape/Resp2/Resp3 variants and regenerates RedisCommander return types - ReplyMapping type helpers in types.ts - Redis/Cluster gain a ReplyMapping type parameter inferred from the options literal, threaded through duplicate() - cluster passthrough of protocol/replyMapping to subscriber/refresher nodes
Move the active protocol into the connection condition and add an internal handshake gate so user commands cannot race ahead of the ready check. If the server rejects HELLO 3, downgrade the connection to RESP2 in place (re-running the handshake only when auth was bundled) instead of failing the connection. - Condition gains protocol/handshake; DataHandler reads condition.protocol - connectHandler runs the handshake async, gates writability, and recovers from protocol-negotiation errors - isResp2SubscriberMode derives protocol from the condition - exclude HELLO from auto-pipelining - tests for protocol downgrade and connect-state writability
READONLY is already sent as part of the connection handshake, so skip the second ready-handler dispatch and assert it only runs once before user commands. Pipeline commands continue to receive reply context through the Redis send path.
396b20e to
ab56a5e
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Reviewed by Cursor Bugbot for commit ded6122. Configure here.
| #decodeNull() { | ||
| this.#cursor += 2; // skip \r\n | ||
| return null; | ||
| } |
There was a problem hiding this comment.
Null reply split parse bug
Medium Severity
The RESP3 null decoder always advances the cursor by two bytes without waiting for a complete _ terminator sequence. When _ and \r\n arrive in separate TCP chunks, the cursor moves past the available data and the parser can misalign on the next read, corrupting command pairing or triggering fatal protocol errors.
Reviewed by Cursor Bugbot for commit ded6122. Configure here.
…er stack overflow The RESP3 rewrite (redis#2127) replaced redis-parser with a new Decoder, and as part of that already wraps decoder.write() in try/catch, routing failures through returnFatalError -> recoverFromFatalError the same way other parser-detected fatal errors are handled. The new Decoder still recurses once per level of RESP aggregate nesting with no depth limit (confirmed directly against lib/resp/decoder.ts), so a sufficiently deeply nested reply still throws a RangeError - it's just already caught and routed through the existing recovery path instead of crashing the process. There was no test pinning this down, so this adds one. Fixes redis#2108
# [6.0.0-beta.1](v5.11.1...v6.0.0-beta.1) (2026-07-29) * Add RESP3 ([#2127](#2127)) ([5d0862e](5d0862e)) ### Bug Fixes * clear stale socket timeout on reconnect ([#2148](#2148)) ([6455dbe](6455dbe)) * **cluster:** recreate stale connection on circular MOVED ([#2135](#2135)) ([08c8967](08c8967)) * **cluster:** validate MOVED slot to prevent Array.prototype pollution ([#2151](#2151)) ([9618206](9618206)), closes [#1267](#1267) * **command:** serialize large integer arguments in decimal notation ([#2136](#2136)) ([09b8d04](09b8d04)) * **redis:** keep reconnecting when connection closes during client setup ([#2099](#2099)) ([#2123](#2123)) ([f9a66bc](f9a66bc)) * **sentinel:** preserve zero preferred slave priority ([#2129](#2129)) ([a3f9f2d](a3f9f2d)) * **tracing:** redact values for GETSET and PSETEX ([#2134](#2134)) ([832765d](832765d)) ### Features * add LMOVEM and BLMOVEM command support ([#2144](#2144)) ([c26af46](c26af46)) * add Redis 8.10 set cardinality commands ([#2143](#2143)) ([301099b](301099b)) * support MAXCOUNT and MAXSIZE for stream reads ([#2142](#2142)) ([ae5e41b](ae5e41b)) ### BREAKING CHANGES * ioredis now requires Node.js 20 or newer and uses RESP3 by default. Set `protocol: 2` to retain the v5 wire protocol.
|
🎉 This PR is included in version 6.0.0-beta.1 🎉 The release is available on: Your semantic-release bot 📦🚀 |
# [6.0.0](v5.11.1...v6.0.0) (2026-07-31) * Add RESP3 ([#2127](#2127)) ([5d0862e](5d0862e)) ### Bug Fixes * clear stale socket timeout on reconnect ([#2148](#2148)) ([6455dbe](6455dbe)) * **cluster:** recreate stale connection on circular MOVED ([#2135](#2135)) ([08c8967](08c8967)) * **cluster:** validate MOVED slot to prevent Array.prototype pollution ([#2151](#2151)) ([9618206](9618206)), closes [#1267](#1267) * **command:** serialize large integer arguments in decimal notation ([#2136](#2136)) ([09b8d04](09b8d04)) * **redis:** keep reconnecting when connection closes during client setup ([#2099](#2099)) ([#2123](#2123)) ([f9a66bc](f9a66bc)) * **sentinel:** preserve zero preferred slave priority ([#2129](#2129)) ([a3f9f2d](a3f9f2d)) * **tracing:** redact values for GETSET and PSETEX ([#2134](#2134)) ([832765d](832765d)) * **types:** export ScanStreamOptions, RedisStatus and ClusterStatus ([#2158](#2158)) ([cf3bf71](cf3bf71)) ### Features * add LMOVEM and BLMOVEM command support ([#2144](#2144)) ([c26af46](c26af46)) * add Redis 8.10 set cardinality commands ([#2143](#2143)) ([301099b](301099b)) * himport managed fieldsets ([#2159](#2159)) ([729f174](729f174)) * improve default connection resilience ([#2160](#2160)) ([6d0716e](6d0716e)) * support MAXCOUNT and MAXSIZE for stream reads ([#2142](#2142)) ([ae5e41b](ae5e41b)) ### BREAKING CHANGES * ioredis now requires Node.js 20 or newer and uses RESP3 by default. Set `protocol: 2` to retain the v5 wire protocol.
|
🎉 This PR is included in version 6.0.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
…er stack overflow (#2138) The RESP3 rewrite (#2127) replaced redis-parser with a new Decoder, and as part of that already wraps decoder.write() in try/catch, routing failures through returnFatalError -> recoverFromFatalError the same way other parser-detected fatal errors are handled. The new Decoder still recurses once per level of RESP aggregate nesting with no depth limit (confirmed directly against lib/resp/decoder.ts), so a sufficiently deeply nested reply still throws a RangeError - it's just already caught and routed through the existing recovery path instead of crashing the process. There was no test pinning this down, so this adds one. Fixes #2108


Note
High Risk
Touches connection handshake, wire parsing, pub/sub routing, and default protocol behavior—any regression affects all commands; RESP3 subscriber semantics and reply-shape changes can break callers expecting v5 RESP2-only behavior.
Overview
This is a major protocol and typing upgrade for v6: connections default to RESP3 (
HELLO 3), fall back to RESP2 when the server cannot negotiate, and exposeprotocolplusreplyMapping("legacy"vs"resp3") to control how maps and doubles appear in JavaScript.Runtime:
redis-parseris replaced by an in-tree RESP3 decoder (lib/resp/decoder.ts).DataHandlerroutes push frames for pub/sub on RESP3, skips RESP2-style subscriber reply sniffing on protocol 3, and applies legacy vs resp3 type mappings at parse time. Connect handshake is reworked around orderedHELLO/auth/select/client/readonly/setup with a handshake gate so user commands cannot race ahead of readiness; unsupported RESP3 triggers downgrade and a warning when"resp3"mapping was requested.Replies: Command reply transformers are context-aware (protocol + mapping) for sorted-set pairs, streams (
xread/xreadgroup), andvsim. Generated commander types gainResp2/Resp3/RespShapebranches and many per-command return type splits inbin/returnTypes.js.Ecosystem: README documents RESP3 and v6 (Node ≥20); release pipeline generates
lib/utils/version.tsforCLIENT SETINFOinstead of reading package metadata at runtime. Cluster/Sentinel paths propagate protocol options; cluster slot refresh uses explicit RESP2/legacy on a duplicate connection.Reviewed by Cursor Bugbot for commit ded6122. Bugbot is set up for automated code reviews on this repo. Configure here.