feat(observability): record Redis connection state on failed slot operations
Summary
A Redis command that never gets a reply fails identically in three very different situations, and nothing we record distinguishes them:
| Situation | What we'd want to see |
|---|---|
| Connection still being established | status: connecting, non-zero queue depth |
| Reconnecting, command parked in the offline queue | status: reconnecting, non-zero queue depth |
| Socket silently dead | status: ready, queue depth 0 |
ioredis reports all three as Error: Command timed out with only its own timer frames in the stack — no app frame naming the call, no lifecycle event saying which happened. Today the cause can only be inferred from timing.
- Adds
describeRedisConnection()— client status, connection and ready ages, offline-queue depth, lifecycle counters (connects/reconnects/errors + last error), and whether the configured host is an IP literal or a DNS name. - Attaches it to the usage-reservation slot operations. Those are the first Redis calls a queued workflow makes, so an unusable connection surfaces there first.
connectandreadynow log elapsed-since-construction, making the wait before a connection becomes usable directly measurable. Today that wait is spent inside a command's deadline, where it reads as a command timeout rather than as connection latency.- Extends the shared
redisConfigMock, which documents that every export of the real module is present.
Why these fields
statusis usually decisive on its own.queuedCommandsconfirms it: a command parked in the offline queue was waiting on connection setup, whereas one written to areadysocket that never answered means the socket died without anything reporting it. ioredis has no public accessor for the offline queue, so this reads it through a narrow interface with adouble-cast-allowedannotation.hostKindrules DNS resolution in or out. No server-side telemetry can see a slow or retrying resolve — not VPC flow logs, not NLB counters, not cache metrics — so without this it stays a permanent unknown.
Security
REDIS_URL carries the AUTH token and is never logged. Only derived facts (IP vs DNS, TLS on/off, SNI override in play) are emitted. There's a test asserting the snapshot cannot contain the host, and I verified it fails if the URL is leaked into the payload.
Type of Change
- Observability / diagnostics (no behavior change)
Testing
No behavior change: this adds a read-only snapshot and log fields. No timeouts, retry policy, or control flow were touched.
New tests cover no-client, connecting vs ready, offline-queue depth, lifecycle counting, IP vs DNS classification, and the secret-leak guard. Verified each fails for the right reason — forcing status to a constant breaks the discriminator tests, and leaking the URL into the snapshot breaks the secret test.
bun run type-check clean in apps/sim and packages/testing. 3149 tests pass across lib/core, lib/billing, lib/execution, background, plus 40 in packages/testing. bun run lint and all 37 audits pass.
Checklist
- Code follows project style guidelines
- Self-reviewed my changes
- Tests added/updated and passing
- No new warnings introduced
- I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)