Context
Redis operations are scattered across lib/redis.ts, lib/deviceToken.ts, lib/deviceCode.ts, and lib/deviceStatus.ts. Each module calls Redis directly with no retry logic, no error recovery, and no transaction support.
Issues:
getUserSettings extends TTL on every read but doesn't handle expiry failure
setUserSettings in deviceToken.ts partially updates settings (risk of losing fields)
- No connection error handling (Upstash outage = 500 errors everywhere)
- TTL values (90 days) duplicated across files
Proposed approach
- Create
lib/redis-ops.ts with typed wrappers for common patterns:
getWithTTLRefresh<T>(key, ttl, schema) - get + validate + refresh TTL
setWithTTL<T>(key, value, ttl) - set + TTL in pipeline
atomicUpdate<T>(key, updater) - read-modify-write in pipeline
- Add retry logic for transient Upstash errors (1 retry with backoff)
- Centralize TTL constants
- Use spread operator for partial settings updates to preserve all fields
Acceptance criteria
Context
Redis operations are scattered across
lib/redis.ts,lib/deviceToken.ts,lib/deviceCode.ts, andlib/deviceStatus.ts. Each module calls Redis directly with no retry logic, no error recovery, and no transaction support.Issues:
getUserSettingsextends TTL on every read but doesn't handle expiry failuresetUserSettingsindeviceToken.tspartially updates settings (risk of losing fields)Proposed approach
lib/redis-ops.tswith typed wrappers for common patterns:getWithTTLRefresh<T>(key, ttl, schema)- get + validate + refresh TTLsetWithTTL<T>(key, value, ttl)- set + TTL in pipelineatomicUpdate<T>(key, updater)- read-modify-write in pipelineAcceptance criteria