feat(observability): record Redis connection state on failed slot operations - #7194
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Greptile SummaryAdds Redis connection-state diagnostics to usage-reservation failures without changing reservation control flow.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/lib/core/config/redis.ts | Adds read-only Redis connection diagnostics, lifecycle timestamps and counters, and sanitized URL-derived classifications. |
| apps/sim/lib/billing/calculations/usage-reservation.ts | Attaches connection diagnostics to failed reservation and refresh operations while retaining existing error propagation. |
| apps/sim/lib/core/config/redis.test.ts | Covers diagnostic states, lifecycle counters, URL classification, discarded clients, and non-throwing behavior. |
| apps/sim/lib/billing/calculations/usage-reservation.test.ts | Verifies that diagnostic logging does not replace the original Redis error object. |
| packages/testing/src/mocks/redis-config.mock.ts | Adds a resettable shared mock for the new diagnostic export. |
Sequence Diagram
sequenceDiagram
participant Workflow
participant Reservation as Usage Reservation
participant Redis
participant Diagnostics as Redis Diagnostics
participant Logger
Workflow->>Reservation: Reserve or refresh slot
Reservation->>Redis: Execute Redis operation
alt Redis operation succeeds
Redis-->>Reservation: Result
Reservation-->>Workflow: Continue
else Redis operation fails
Redis--xReservation: Original error
Reservation->>Diagnostics: describeRedisConnection()
Diagnostics-->>Reservation: Connection snapshot
Reservation->>Logger: Log operation, error, and snapshot
Reservation--xWorkflow: Rethrow original error
end
Reviews (9): Last reviewed commit: "feat(observability): record Redis connec..." | Re-trigger Greptile
There was a problem hiding this comment.
All reported issues were addressed across 4 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
36d47da to
9334a4d
Compare
9334a4d to
d387743
Compare
|
@cubic review |
|
@greptile review |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 5 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
You’re at about 95% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
…rations A Redis command that never gets a reply fails identically whether the connection was still being established, was reconnecting with the command parked in the offline queue, or was a socket that had silently died. ioredis reports all three the same way — `Error: Command timed out` with only its own timer frames in the stack, no app frame naming the call, and no lifecycle event saying which happened. Nothing recorded anywhere distinguishes them, so the cause can only be inferred from timing. Adds `describeRedisConnection()`: client status, connection and ready ages, offline-queue depth, lifecycle counters, and whether the configured host is an IP or a DNS name. `status` alone usually decides it; queue depth confirms, since a parked command was waiting on connection setup while one written to a `ready` socket that never answered means the socket died unreported. Host kind rules DNS resolution in or out, which no server-side telemetry can see. Attaches it to the usage-reservation slot operations — the first Redis calls a queued workflow makes, so an unusable connection surfaces there first. Connect and ready now log elapsed-since-construction, making the wait before a connection becomes usable directly measurable; today it is spent inside a command's deadline, where it reads as a command timeout rather than as connection latency. Purely additive. Redis call arguments are unchanged, the wrapper rethrows the original error object, and `describeRedisConnection` never throws — it runs inside catch blocks where a throw would replace the real failure. All three have tests that fail if the behavior is removed. Only non-sensitive facts are derived from REDIS_URL, which carries the AUTH token and is never logged.
d387743 to
2e58e0d
Compare
Summary
A Redis command that never gets a reply fails identically in three very different situations, and nothing we record distinguishes them:
status: connecting, non-zero queue depthstatus: reconnecting, non-zero queue depthstatus: ready, queue depth 0ioredis reports all three as
Error: Command timed outwith 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.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.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.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_URLcarries 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
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
statusto a constant breaks the discriminator tests, and leaking the URL into the snapshot breaks the secret test.bun run type-checkclean inapps/simandpackages/testing. 3149 tests pass acrosslib/core,lib/billing,lib/execution,background, plus 40 inpackages/testing.bun run lintand all 37 audits pass.Checklist