Repository navigation
add a redis streams transport as @node-ts/bus-redis - #359
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ms durations Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ry to its visibility timeout Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e test listeners Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #347
Summary
Adds
RedisTransportas a new@node-ts/bus-redis: each service's queue is a Redis Stream read by a consumer group, on Redis 7+ or Valkey 7.2+. It replaces the 0.x Redis lists transport that had the same name.Background
#347 asks for a Redis Streams transport in the monorepo, to replace
node-ts/bus-redis(bus-core 1.x, inversify, unchanged since 2021). The maintainer accepted the design first; it's in this comment. It differs from the issue in three places: node-redis 6 rather than 5, no Sentinel in v1, and a dead letter stream per queue rather than a shared one.Problem
There's no Redis transport for the current bus. Teams that already run Redis or Valkey need another broker, or have to stay on the unmaintained 0.x package.
Approach
Package:
packages/bus-redis, withredis(node-redis)^6as a peer dependency. The transport makes its own clients with RESP2, so the reply shapes don't depend on node-redis' defaults. It reads on a second, duplicated connection that blocks for at most 1 s.Frozen connections: node-redis' command timeout stops covering a command once it's written to the socket. Without more, a frozen or half-open connection would leave a read,
stop()anddispose()waiting forever. So:PING, and reconnects after a socket timeout;PINGevery 5 s. Without a reply in 15 s, it destroys the connection, which fails what's pending, and replaces it. It runs fromconnect()todisconnect()and never overlaps itself;stop()gives a read in flight 3 s, then destroys the reader, and gives handing back messages another 3 s. The bus then waits for messages being handled, whose settling on a frozen connection fails once the watchdog replaces it, up to 20 s later;disconnect()gives leaving the group andclose()3 s each, then destroys the connection;stop()anddispose()finish.Keys:
<prefix>:{<queue>}:queue(a stream, with consumer group<queue>),:delayed(a sorted set),:dead-letter(a stream), and<prefix>:subscriptions:<$name>(a set). The default prefix isbus.{<queue>}hash tag, and every script touches one queue's keys, so Redis Cluster stays possible later.queueNameandkeyPrefixcan't contain{or}(InvalidRedisKeyName).Sending:
SMEMBERSon the subscription set, then oneMULTIwith anXADD … NOMKSTREAMper subscriber, so every subscriber gets the message or none does.sendToAddressisXADD NOMKSTREAM, which throwsEndpointNotFoundwhen the stream doesn't exist, and creates nothing.Receiving:
visibilityTimeoutMs(default 30 s), withXPENDING … IDLE+XCLAIM. Once a minute, it removes stale consumers that have nothing pending.failedAttempts= thefailedAttemptsfield + deliveries − 1, so a receipt that timed out counts, as on SQS and Postgres.stop()hands back entries it read but never gave to a worker, without counting an attempt.disconnect()leaves the consumer group, but only when nothing is pending to it.Retries and dead letters:
failmoves the message to the queue's dead letter stream with abus-failurefield, trimmed withMINID ~todeadLetterRetentionMs(default 14 days;0/Infinitykeep everything).Commands used: only Redis 6.2/7.0 commands (no
XACKDEL/XNACK/XREADGROUP CLAIM), so Valkey works.Provisioning:
provision()runsXGROUP CREATE … 0 MKSTREAM(BUSYGROUPcounts as success) andSADDs the subscriptions. A send-only bus or a dry run makes no calls.initialize()only checks, withXINFO GROUPSandSISMEMBER, and throwsResourcesNotProvisioned.redis-acldocument. The integration test runs a bus as an ACL user with exactly those rules.Infra:
redis:8on host port 6380 andvalkey/valkey:8on 6381, withREDIS_URLintest.env.UNLINKtheir own keys. They never flush.Docs:
/transports/redispage: keys, provisioning and ACLs, keeping messages safe (noeviction, AOF, async replication), redrive, and migrating from 0.x.MIGRATING.mdentry, and the root README.CLAUDE.mdfiles.Changeset: a minor changeset with a
**Breaking:**note for 0.x users.Tests:
transportTestspasses on Redis 8 and Valkey 8. The other integration tests cover:EndpointNotFound;Unit tests cover the plan, ACL escaping and config validation, including rejecting fractional durations (Redis needs whole milliseconds).
Maintainer actions after release (not done here):
Release it together with the other packages. It starts at
0.0.0, and npm already has@node-ts/bus-redis0.1.1–0.1.9. Released with the others under the linked versions, with master's pending major changesets, its first version is 2.0.0. Released on its own, it would fall below or clash with 0.1.x. Check theversion packagesPR.Deprecate only the old range:
npm deprecate @node-ts/bus-redis@"<1.0.0" "Replaced by @node-ts/bus-redis 2.x, a Redis Streams transport: https://node-ts.github.io/bus/transports/redis". A barenpm deprecate @node-ts/bus-rediswould deprecate the new versions too. The old versions are two lines: 0.1.8 (latest, bus-core 1.x,new RedisTransport({ connectionString })) and 0.1.1–0.1.7 and 0.1.9 (the0.xtag, bus-core 0.6 with inversify'sBusRedisModule). The migration docs cover both.Archive the old repo:
node-ts/bus-redis.Badge: the docs page's npm badge shows 0.1.8 until the release.
This is original work under the clean-room policy, not ported, translated or copied from another messaging framework
Added a changeset (
pnpm changeset) for user-facing changes to published packages, or none is neededDocs: updated
docs/for user-facing changes, or none needed🤖 Generated with Claude Code