Apollo DataSource类中Redis缓存正确用法及现有代码无返回问题排查
代码存在的问题及修复方案
你这段代码的核心错误点如下:
- 缺少异常捕获逻辑:整个async方法没有做错误捕获,无论是缓存读写抛出异常、数据库连接/查询报错,都会直接导致方法抛出未处理的拒绝,上层如果没有做catch处理就会直接中断,无法拿到返回值。
- 空结果缓存穿透问题:如果数据库中没有对应id的订单,
Order.findOne会返回null,此时如果传入了ttlInSeconds,你会直接把JSON.stringify(null)也就是字符串"null"写入缓存。后续相同id的请求会直接命中缓存返回null,即使后续数据库中新增了对应id的订单,在缓存过期前都会一直返回空结果。 - 参数兼容性问题:方法第二个参数采用对象解构赋值
{ttlInSeconds}={},如果调用时第二个参数传入了非对象类型(比如直接传数字order(123, 3600)),会直接抛出TypeError,导致方法执行中断。 - 缓存生效逻辑问题:如果没有传入
ttlInSeconds,所有查询结果都不会写入缓存,相当于缓存完全不生效,每次请求都会查数据库,如果你预期缓存默认生效,这也是逻辑偏差点。
修复后代码示例
async order(id, options = {}) { // 前置校验id合法性 if (!id) { throw new Error('订单id不能为空') } // 兼容直接传ttl数字、对象传参两种调用方式 const ttlInSeconds = typeof options === 'number' ? options : options.ttlInSeconds try { const cacheKey = this.cacheKey(id) const cachedOrder = await this.cache.get(cacheKey) if (cachedOrder) { const parsedOrder = JSON.parse(cachedOrder) return parsedOrder } const order = await this.models.Order.findOne({ where: { id } }) if (ttlInSeconds) { // 空值设置更短的缓存时间防穿透,避免长时间缓存无效结果 const cacheTtl = order ? ttlInSeconds : 30 await this.cache.set(cacheKey, JSON.stringify(order), { ttl: cacheTtl }) } return order } catch (err) { // 缓存异常时降级直接查数据库,避免缓存服务不可用导致整个业务中断 console.error('订单查询异常:', err) return await this.models.Order.findOne({ where: { id } }).catch(dbErr => { console.error('数据库查询失败:', dbErr) throw new Error('订单查询失败') }) } }
内容的提问来源于stack exchange,提问作者Ekaterina Janssen
相关产品推荐
相关产品推荐

