Tailscale 追踪到 SQLite 16 年前的 Bug

Tailscale Traces Database Corruption to 16y/o SQLite WAL-Reset Bug

Tailscale 追踪到 SQLite 16 年前的 Bug

去年,Tailscale 遭遇了持续数月的服务不稳定,根源竟深藏于 SQLite 数据库中。在经历了 19 次数据库损坏事件后,我们投入大量精力进行排查,最终锁定了一个存在 16 年的 SQLite WAL-Reset 漏洞。虽然我们的架构设计遵循了 SQLite 的最佳实践,但这个深埋的 Bug 导致数据在特定场景下凭空消失。通过与 SQLite 核心开发团队的合作,我们不仅修复了问题,还协助定位了该底层缺陷。这次经历让我们深刻意识到,即使是看似“无聊”的成熟技术,在极端规模下也可能暴露出意想不到的隐患。

在两次事件中,我们的事务日志无法顺利重放,深入检查后发现,由某个事务写入并提交的数据,在后续事务中竟然不可见,仿佛凭空消失且未触发任何错误。
  1. simonw

    我们资助了开源的 SQLite VFS 包装层,它几乎立刻帮助隔离了竞态条件,未来也将有助于追踪类似的 Bug。

    这是一个公司资助开源的有趣案例——在这个案例中,他们付费开发了一个全新的、非常特定的调试工具。

  2. calmingsolitude

    文章写得真好,读起来很享受。

    > A single Go process exclusively accesses that database, and serves the control plane for those tailnets. This single-writer design is exactly how SQLite is meant to be used.

    这句话让我以为写入逻辑和检查点逻辑都在同一个数据库连接上,所以我很好奇数据竞态究竟是如何发生的。然而,SQLite 页面 [0] 上的 Bug 详情指出,只有在存在多个打开的连接时才会发生这种情况,因此写入器和检查点器一定是在不同的线程上。

    [0] https://sqlite.org/wal.html#the_wal_reset_bug

  3. andai

    SQLite:9200 万行测试用例

    Dijkstra:测试只能证明 Bug 的存在,永远无法证明其不存在!

  4. throw0101a

    也许可以参考最近的一段视频 "Reliability Lessons From SQLite - Richard Hipp | SSW 2026":

    > Abstract: SQLite is a C-language library that implements a self-contained, in-process relational database engine supporting full-featured SQL, an advanced query planner, and ACID transactions. By many estimates, SQLite is the most widely used software library in the world today.

    > Over its 26-year history, SQLite has gained a reputation as software that "just works". This talk goes over the design choices and development practices that have, at least in the opinion of the lead developer, resulted in that reputation.

    * https://www.youtube.com/watch?v=V_qzqY1bb7I

  5. bobtheborg

    读起来很棒。很高兴他们花时间讲述了这个故事。(也很高兴他们作为一家营利性公司,与 SQLite 签订了支持合同。希望即便这个问题已经解决,他们也能继续这样做。)

  6. procflora

    文章写得很好,我也很欣赏 SQLite 对 Bug 的解释。Tailscale 对此事的态度也显得非常酷(比如付费开发 VFS 包装层等)。

    不过,我希望能听到更多关于他们决定如此频繁地进行检查点的讨论,正是这个决定让他们走上了这条路。推测这是为了保持 WAL 极小,从而实现极快的恢复。我想这大概是为了缓解将 DBMS 插入网络层所带来的某些负面影响吧?这可是个棘手的问题。好奇这与典型的 etcd 快照频率相比如何。

  7. bch

    这真的、真的很有趣——多么一次胜利的冒险。

    几点(非常、非常、非常吹毛求疵的)观察:

    > We wanted a way to restore service that didn’t involve rolling back to the last known-good backup (which would lose a lot of data) or repairing the known-corrupted database (which was potentially risky).

    (我加粗了重点)——应该是“有风险”,而不是“可能有风险”——然后“计算风险期”开始,就变成了“可能有问题”。

    在 SQLite 报告 [0](11.2 节)中,我希望他们少淡化一些——先提一下罕见性,再讲技术细节——我和几位开发者/前开发者很熟,对他们的技能和成就抱有极大的尊重(并由此延伸,对我没有直接打交道的开发者也抱有同样的信心),对 SQLite 这一巨大成就充满欣赏和喜爱,等等……这是世界级的作品。也许 11.2 节并不是针对我写的,或者我太过挑剔了。为了公平起见,对于这样一个有趣的问题/修复方案,这只是一个微不足道的抱怨。希望我的评论不会成为败兴之物。

    最后关于 Bug 修复的一点 [1]——唉。部署修复后却发现全是红灯,那种感觉一定很糟糕吧——这也是一个教训 [2],告诫我们不要“反正都在这儿了”就把其他改动偷偷塞进变更集里。很高兴结果并非灾难性的,但这确实导致了 SQLite 罕见(我一时想不起其他例子)的召回 [3]。它当时是在向 sa 抛出错误 […]

  8. sandeepkd

    >In our control plane, we take manual control of the checkpoint process so we can run fast and consistent backups.

    > running boring technology in a non-standard way is a risk.

    这是一篇很好的文章,也提醒我们行业中的专家正在逐渐流失。我不是 DBA,但过去至少听过几次这种行为,被告知要避免。这只是那些因为专家避开了它、而普通人又没机会深入接触,从而未能被记录下来的一类事情罢了。

同日更多故事

2026-08-12