IndexedDB 使用引起的线上事故

问题出现

某晚,告警平台发出消息。登录sentry查看发现一个ip短时间内报出了1.3k个error。具体报错信息为 QuotaExceededError,分类为未分类,有效信息不多。

立即百度报错关键词,于此关联词较多的为localStorage。结合最近期上线记录,想到了新使用的API--基于localforage封装的Store.js。

localforage&使用

localforage通过异步存储来改进应用程序的离线体验。能存储多种类型的数据,而不仅仅是字符串。采用优雅降级,优先使用 IndexedDB 或 WebSQL,若浏览器不支持,则使用 localStorage。

除此之外,还可设置过期时间expire,在getItem之前会做是否清除判断。

基于以上优点,localforage完美适用于此次业务场景:

  1. 此次本地储存的数据需要设置过期时间,超过指定时间后清除重新调用最新数据。
  2. 后期可能会有大量数据需要本地存储,此次需求恰好为边缘场景,便使用localforage测试一下IndexedDB的存储效果。
  3. IndexedDB为异步操作,不会阻塞主线程。

问题排查

兼容性问题?

首先怀疑的便是兼容性问题,但是localforage有优雅降级机制,开发过程中也通过真机测试,移动端、pc端都会兼容IndexedDB。这也石锤了一个问题,长期以来使用localStorage并未出现此问题,那么应该就是IndexedDB使用方式不对。

复现问题

既然推断是IndexedDB抛出的异常,便尝试去复现错误。但是需求上线两周,只有三个ip报过此错误,在手机pc进行各种暴力操作后均未复现,那么会不会是手机的问题呢。

随后找到一个内存最小的测试机,竟然剩余空间还有20GB,o(╥﹏╥)o。于是下载了一天的游戏后,将内存占用至只剩100MB左右,终于成功复现。

问题原因

成功复现错误后,查阅了相关资料,找出了IndexedDB异常的原因。

不同于浏览器会为localStorage预留5MB左右的储存空间,而IndexedDB的配额是动态计算的,虽然浏览器实现各不相同,但可用存储量通常取决于设备上可用的存储量。

如何查看有多少可用存储空间?

if (navigator.storage && navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -> Number of bytes used.
  // quota.quota -> Maximum number of bytes available.
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`You've used ${percentageUsed}% of the available storage.`);
  const remaining = quota.quota - quota.usage;
  console.log(`You can write up to ${remaining} more bytes.`);
}
复制代码

在问题复现阶段,当手机内存为1GB时,webview的剩余储存空间为500MB左右,随着手机内存的不断减少,浏览器的配额也会减少,直到手机内存为300MB左右时,配额降为0,当写入的数据量大于剩余配额时,便会抛出QuotaExceededError

总结&反思

  1. 内存严重不足的极端情况会导致IndexedDB使用异常。
  2. 使用新API时应该做一些异常处理。以此次举例,虽然localforage有降级机制,但是在支持IndexedDB的端遇到IndexedDB报错时并不会自动降为localStorage,需要在开发过程中手动处理。
  3. 遇到开源轮子使用异常时,先去issues看一眼,此次问题排查走了不少弯路,最后也是在gitlab上找到的突破口。

image.png

参考资料

web.dev/storage-for… 深度好文 推荐阅读

developer.mozilla.org/zh-CN/docs/…

猜你喜欢

转载自juejin.im/post/7033303792443473934