You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
improvement(redis): warm the shared connection at process start (#7683)
* improvement(redis): warm the shared connection at process start
Establishing a connection is far more expensive than the commands that run over
it, so it should cost once per process rather than once per unit of work. It
also needs its own budget: `commandTimeout` is armed before ioredis checks
whether the socket is writable, so a first command issued against a client still
shaking hands spends that budget waiting to connect and fails as a command
timeout from a server that never received it. A run's first Redis call is
typically a lock acquire, which is exactly where that surfaces.
`warmRedisConnection` resolves once the connection is usable, or `false` when
Redis is unconfigured or the wait ran out. It never throws and never rejects —
a Trigger.dev `init` hook that throws fails the whole run attempt, and a
warm-up is an optimization, so failing to warm must cost nothing beyond the
connection staying cold. The deadline is its own, and its timer is unref'd so a
pending warm-up can never hold a process open.
The in-flight warm-up is keyed on the client it is warming, which is what makes
a replacement re-warm. That keying is the only mechanism: clearing by hand at
every site that drops the client is an invariant that rots the first time one
forgets.
Trigger.dev awaits it in the global `init` hook so the connection is up before
`run()` issues anything; Next starts it without awaiting so boot never waits on
Redis to serve requests that do not touch it.
Gives the shared Redis mock a real listener registry so lifecycle events can be
driven in tests. `on` stays a spy — tests read `on.mock.calls` to reach the
handlers the client registered.
* fix(testing): scope mock Redis listeners to the client that registered them
The listener registry outlived the spies: `vi.clearAllMocks()` and
`clearRedisMocks` reset call history but left handlers registered, so they
accumulated across tests and a later `emit` could reach handlers belonging to a
client the test under way never created.
Adds `removeAllListeners`, which real clients have, and drops listeners in
`clearRedisMocks` alongside spy history. Where one mock instance stands in for
every client a module constructs, the registry is now emptied per construction —
a real client starts with none, so binding listener lifetime to construction
makes the isolation automatic rather than something each test has to remember.
Covers the mock's event behavior in the testing package, where it lives.
0 commit comments