Skip to content

Commit 36d47da

Browse files
committed
chore(observability): restore behavior guards and tidy placement
Audit cleanups on the diff itself, no functional change. The revert took two tests with it that were unrelated to what it reverted: that describeRedisConnection never throws, since it runs inside catch blocks where a throw would replace the real failure, and that the wrapper rethrows the original error object rather than substituting its own. Both are the properties that make this instrumentation rather than a behavior change, so both are back. The diagnostics helper had also been inserted between reserveExecutionSlot's TSDoc and the function, orphaning that doc onto the helper. Moved above it. In-body block comments converted to line comments, matching how the rest of this file annotates statements.
1 parent 9c13632 commit 36d47da

4 files changed

Lines changed: 39 additions & 21 deletions

File tree

apps/sim/lib/billing/calculations/usage-reservation.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,13 @@ describe('usage-reservation', () => {
339339
})
340340

341341
describe('refreshExecutionSlotExpiry', () => {
342+
it('rethrows the original error object rather than the diagnostic wrapper', async () => {
343+
const original = Object.assign(new Error('Command timed out'), { code: 'ETIMEDOUT' })
344+
getMock.mockRejectedValueOnce(original)
345+
346+
await expect(refreshExecutionSlotExpiry('exec-1', Date.now() + 60_000)).rejects.toBe(original)
347+
})
348+
342349
it('refreshes only the locally owned slot and matching pointer', async () => {
343350
evalMock.mockResolvedValueOnce(1).mockResolvedValueOnce(1)
344351
await reserveExecutionSlot(memberParams)

apps/sim/lib/billing/calculations/usage-reservation.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -425,14 +425,6 @@ export type ReserveExecutionSlotResult =
425425
reason: ReservationDenialReason
426426
}
427427

428-
/**
429-
* Atomic admission reservation that closes the usage-cap check-then-use race.
430-
*
431-
* Billing-disabled and self-hosted deployments are no-ops. Hosted deployments
432-
* require Redis and fail closed when reservation or pointer ownership cannot be
433-
* proven. A newly-created local reservation is rolled back if pointer
434-
* registration fails; TTL is only the bounded crash fallback.
435-
*/
436428
/**
437429
* Records connection state alongside a failed slot operation.
438430
*
@@ -461,6 +453,14 @@ async function withReservationDiagnostics<T>(
461453
}
462454
}
463455

456+
/**
457+
* Atomic admission reservation that closes the usage-cap check-then-use race.
458+
*
459+
* Billing-disabled and self-hosted deployments are no-ops. Hosted deployments
460+
* require Redis and fail closed when reservation or pointer ownership cannot be
461+
* proven. A newly-created local reservation is rolled back if pointer
462+
* registration fails; TTL is only the bounded crash fallback.
463+
*/
464464
export async function reserveExecutionSlot(
465465
params: ReserveExecutionSlotParams
466466
): Promise<ReserveExecutionSlotResult> {

apps/sim/lib/core/config/redis.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,22 @@ describe('redis config', () => {
225225
expect(JSON.stringify(d)).not.toContain('10.0.0.5')
226226
})
227227

228+
it('never throws, so it cannot mask the error it is describing', () => {
229+
// Called from catch blocks: a throw here would replace the real failure.
230+
mockEnv.REDIS_URL = undefined
231+
expect(() => describeRedisConnection()).not.toThrow()
232+
233+
mockEnv.REDIS_URL = 'not a url'
234+
expect(() => describeRedisConnection()).not.toThrow()
235+
expect(describeRedisConnection().hostKind).toBe('unknown')
236+
237+
// rediss:// to a bare IP with no REDIS_TLS_SERVERNAME makes the URL
238+
// resolution throw; the snapshot must still come back.
239+
mockEnv.REDIS_URL = 'rediss://10.0.0.5:6379'
240+
mockEnv.REDIS_TLS_SERVERNAME = undefined
241+
expect(() => describeRedisConnection()).not.toThrow()
242+
})
243+
228244
it('does not date a connection that has been discarded', async () => {
229245
mockRedisInstance.status = 'ready'
230246
getRedisClient()

apps/sim/lib/core/config/redis.ts

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,10 @@ function describeRedisUrl(
137137
// WHATWG keeps IPv6 literals bracketed in `hostname`; `isIP` wants them bare.
138138
const host = parsed.hostname.replace(/^\[|\]$/g, '')
139139
const tls = parsed.protocol === 'rediss:'
140-
/**
141-
* `sniOverride` deliberately mirrors `resolveRedisTlsOptions`, which tests
142-
* for IPv4 only. So an IPv6 literal over TLS reports `hostKind: 'ip'` with
143-
* `sniOverride: false` — not a contradiction but the useful reading, since
144-
* that combination is a connection whose certificate cannot verify.
145-
*/
140+
// `sniOverride` deliberately mirrors `resolveRedisTlsOptions`, which tests for
141+
// IPv4 only. So an IPv6 literal over TLS reports `hostKind: 'ip'` with
142+
// `sniOverride: false` — not a contradiction but the useful reading, since that
143+
// combination is a connection whose certificate cannot verify.
146144
return {
147145
hostKind: isIP(host) === 0 ? 'dns' : 'ip',
148146
tls,
@@ -172,13 +170,10 @@ export function describeRedisConnection(): RedisConnectionDiagnostics {
172170
// double-cast-allowed: ioredis omits offlineQueue from its public type, and its depth is what separates a command waiting on connection setup from one written to a live socket
173171
const queued = (client as unknown as OfflineQueueView | null)?.offlineQueue?.length
174172

175-
/**
176-
* Ages describe the client that is currently held. A discarded client leaves
177-
* its timestamps behind until the next `getRedisClient()` rebuilds them, and
178-
* reporting those against `no-client` would date a connection that no longer
179-
* exists — precisely the wrong answer for the investigation this exists to
180-
* support. The counters below are deliberately cumulative for the process.
181-
*/
173+
// Ages describe the client currently held. A discarded client leaves its
174+
// timestamps behind until the next `getRedisClient()` rebuilds them, and
175+
// reporting those against `no-client` would date a connection that no longer
176+
// exists. The counters below are deliberately cumulative for the process.
182177
const ageOf = (at: number | null) => (client === null || at === null ? null : now - at)
183178

184179
return {

0 commit comments

Comments
 (0)