Skip to content

Version 5.0.0: remove callback support and upgrade to node-redis v6 - #77

Merged
freshlogic merged 21 commits into
mainfrom
v5.0.0
Oct 15, 2026
Merged

freshlogic merged 21 commits into
mainfrom
v5.0.0

Conversation

@freshlogic

@freshlogic freshlogic commented Sep 1, 2026 •

Copy link
Copy Markdown
Member

Removes callback support entirely and upgrades the underlying Redis client from node-redis v3 to v6. See the v4 to v5 migration guide.

Changed

  • Callbacks are gone. Every function returns a promise. v5 doesn't detect leftover v4 callbacks; the migration guide covers finding them on v4 first.
  • Cache-miss functions must be async (or plain-return). That covers fetch, fetchAndRefresh, bulkFetch, and semaphore.retrieveOrCreate's size option.
  • node-redis v3 → v6.3.0. The client connects automatically, bulkSet leans on v6 auto-pipelining instead of multi, and injected clients must be v6 clients. The constructor takes a node-redis v6 options object, passed to redis.createClient() untouched (Take a node-redis options object and nothing else #79). v4's (port, [host, [options]]) signature and node-redis v3 option names are not supported.

Added

  • pettyCache.close() — stops the background refresh intervals started by fetchAndRefresh and closes the Redis client.

Before merging

Two things outside this repo have to land first, or the fleet breaks on the bump:

  1. ~217 callback-style call sites across 14 repos still need converting. Biggest: mediocre-sdk (99), admin.stores.com (24), meh.com (22), runbook (16), analytics-service (13).
  2. Nine repos require('redis') without declaring it and get it transitively from petty-cache, using the v3 API in routes/status.js. Bumping petty-cache swaps them to v6 and breaks /status. PRs for those are open separately.

Notes

  • 149 tests, 100% line and branch coverage.
  • The last commit fixes a bug that would have silently misconfigured every caller: the legacy positional branch gated on typeof port === 'number', but the fleet passes process.env.redisPort, a string. That fell through to redis.createClient('6380'), which v6 ignores — dropping host, password, and every translated option, and connecting to a default localhost:6379 with no error.
  • node-redis v6's default reconnect strategy already matches what Never let node_redis abandon a client #76 hand-rolls for v3 (exponential backoff to 2s plus jitter, retrying forever), so that PR does not need forward-porting wholesale. It does give up permanently on SocketTimeoutError, though, which is worth a follow-up socket.reconnectStrategy here.

🤖 Generated with Claude Code

freshlogic and others added 11 commits August 4, 2026 01:05
Every function is now a plain async function returning a promise; the
executor pattern, callback bridges, and deprecation warnings are gone.
Passing a callback rejects with a TypeError pointing at the returned
promise, rather than silently ignoring a callback that would never be
invoked. Cache-miss functions and retrieveOrCreate's size option must
be async (or plain-return) functions.

The test suite is converted to async/await throughout: callback-only
and duplicate tests are removed, error-injection stub tests use
assert.rejects, and a guard test covers callback rejection for all
seventeen methods. Line and branch coverage remain at 100%.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Commands use node-redis's native promises directly, so the per-call
promisify wrappers are gone; bulkSet pipelines through multi() instead
of the removed batch(). The constructor connects the client
automatically (commands issued while connecting are queued by the
client), still accepts the legacy (port, [host, [options]]) signature
by translating it to node-redis options including auth_pass to
password, and connects injected clients that aren't already open.

Test stubs return promises to match the v6 client API, and the raw
client tests use the camelCase v6 commands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Individual PSETEX commands issued in the same tick are automatically
pipelined into a single round trip by node-redis, so Promise.all over
plain pSetEx calls replaces the explicit multi()/execAsPipeline()
machinery with the same wire behavior and matching error semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stops the background refresh intervals started by fetchAndRefresh and
gracefully closes the Redis client connection via node-redis v6's
close(), giving services and tests a clean shutdown path for the first
time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unscoped push + pull_request fired two Test runs per PR commit, doubling
live-API load and requiring both twin checks to pass before merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The constructor now translates auth_pass, connect_timeout, db,
enable_offline_queue, family, host, path, port, socket_initial_delay,
socket_keepalive, and tls whether passed positionally or as an options
object — the v3 object form with root host/port previously fell through
to v6 untranslated and silently connected to localhost. Legacy options
with no v6 equivalent (retry_strategy, return_buffers, and friends)
throw a TypeError instead of being silently ignored.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Brings in #73 (invoke callbacks exactly once, outside of the promise chain).
Nothing from it applies here: v5 removed callback support, so all 17 conflicted
hunks resolve to v5's side, the invokeCallback helper would be dead code, and
the new child-process tests exercise a bridge that no longer exists. v5 already
covers this surface with "should reject callback-style usage with a TypeError".
The CHANGELOG entry is dropped for the same reason and stays on main, where it
will be versioned when the 4.0.x patch is cut.

The merged tree is byte-identical to v5.0.0's previous tip; this commit only
records the ancestry so the next merge from main doesn't re-conflict on the
same 17 hunks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Brings in 4.0.1. Conflicts resolved by keeping v5.0.0's version and
retaining both CHANGELOG entries. The 4.0.1 double-callback fix needs no
counterpart here — v5 removed the callback bridges it patched, so
invokeCallback would be dead code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The legacy (port, [host, [options]]) branch gated on typeof === 'number',
but every caller passes process.env.redisPort, which is a string. A string
fell through to redis.createClient(port), which node-redis v6 silently
ignores — dropping the host, password, and every translated option, and
yielding a default localhost:6379 client with no error. node-redis v3
accepted the string, so this only broke on the v6 upgrade.

Also bumps redis to ~6.2.1, the latest v6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coveralls

coveralls commented Sep 1, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 38826776683

Coverage remained the same at 100.0%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: 338 of 338 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 849
Covered Lines: 849
Line Coverage: 100.0%
Relevant Branches: 224
Covered Branches: 224
Branch Coverage: 100.0%
Branches in Coverage %: Yes
Coverage Strength: 52.48 hits per line

💛 - Coveralls

freshlogic and others added 10 commits September 1, 2026 23:48
Removes the (port, host, options) signature and the translation of v3
option names. Both now throw a TypeError naming what to change.

Translating was the wrong call. v6 ignores unknown top-level options, so
anything the translator missed produced an unauthenticated client pointed
at localhost:6379 — a cache that never hits, never errors, and looks
healthy. Two separate bugs in that layer surfaced in as many days: a
numeric string port fell through the dispatch, and then an unset port did
the same thing while also discarding the host and password that followed
it. Every fleet call site passes process.env.redisPort, which is not set on
any App Service, so both were live.

A translator that silently degrades is worse than no translator. Callers
now migrate once, at construction, with the offending option named:

  TypeError: petty-cache v5 takes a node-redis options object.
  auth_pass, host, port are node-redis v3 options and would be ignored;
  see docs/v4-to-v5.md.

new PettyCache(redisClient) and new PettyCache() are unchanged. Drops
isPort and translateLegacyOptions — 122 lines out of index.js, 100% line
and branch coverage held across 172 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The guide said the translation of v3 option names was "gone", but that
translation only ever existed on this unreleased branch — a reader coming
from v4 has never seen it and cannot tell what was removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drops the legacy-option list and the construction-time guards. v5 takes a
node-redis options object; callers migrate once, and the migration guide
carries the v3 to v6 mapping.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Take a node-redis options object and nothing else
node-redis v6 silently ignores v4's (port, host, options) arguments and
connects to localhost:6379 with no password. CI runs Redis there, so a
Dependabot bump to v5 would pass CI and then point production at a Redis
that isn't there. Throwing at construction makes those bumps fail loudly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reverts 98c8843. v5 is a clean break: the constructor takes a node-redis
options object and nothing else, as decided in 1dad4a3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drops assertNoCallback and the ...rest parameters that only fed it: v5
doesn't detect v4 callback calls, and the migration guide covers them.
Removes the v4-era '(promises)' copies of tests that the callback tests
became when they were converted, and the blank lines left behind by
earlier removals. Docs no longer promise a TypeError for callbacks, and
the guide's fire-and-forget example now shows the v4 to v5 change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@freshlogic
freshlogic merged commit 2fbd450 into main Oct 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants