HN 日本語サマリー

← 一覧へ戻る
インフラ・DevOps

Tailscale、データベース破損の原因を16年前のSQLite WAL-Resetバグに特定

Tailscale Traces Database Corruption to 16y/o SQLite WAL-Reset Bug (tailscale.com)

1129 pointsby ropbear215 コメント

要約

Tailscaleは、昨年後半から発生していたサービス不安定の原因が、16年前から存在するSQLiteのWAL-Resetバグであることを突き止めました。このバグは、データベースの破損を引き起こし、復旧のために数ヶ月にわたる詳細なフォレンジック調査が必要でした。Tailscaleは、このバグの特定と修正に成功し、SQLite開発者との協力により、データベースのトランザクションログを記録する新しいパイプラインを構築しました。

全文翻訳

ブログ|インサイト 2026年8月12日 16年前のSQLiteバグを特定する方法 当社の稼働時間は、昨年末にかけて非常に不安定でした。この傾向はステータスページで確認できますが、その不安定さは新年に入っても続きました。これらの停止の多くは、SQLiteの奥深くに潜む単一のバグによって引き起こされていました。それを特定するには、数ヶ月にわたる集中的なフォレンジック調査が必要でした。夏に入り、バグを発見し、それを理解したこと、そしてさらに重要なことに、それを修正したことに自信を持っています。お客様はTailscaleが信頼性の高いサービスであることを期待されていると承知しており、数ヶ月間その約束を果たせませんでした。それは混乱を招くものであり、申し訳ありませんでした。何が起こったのか、どのように対応したのか、そして最終的にSQLiteデータベースの中核にある長年のバグの発見にどのように貢献したのかを説明するために、このブログ記事を公開します。 Tailscaleのデータベースアーキテクチャ 当社のクライアントは、単一の公開エンドポイント(controlplane.tailscale.com)として当社のコントロールプレーンとやり取りしますが、内部的には、コントロールプレーンは一連のコーディネーションサーバー(または「シャード」)に分割されています。各テールネットは、一度に1つの内部シャード上に存在しますが、それらの間でシームレスに移行できます。これらのシャードは内部実装の詳細です。テールネットがどのシャードにあるかはわかりませんし、知る必要もありません。各シャードには、そのシャード上のテールネットに関するすべての情報を保持するSQLiteデータベースがあります。単一のGoプロセスがそのデータベースに排他的にアクセスし、それらのテールネットのコントロールプレーンを提供します。この単一書き込み設計は、まさにSQLiteが使用されるように設計されている方法です。当社は2022年からSQLiteを主要データベースとして使用しており、よく知られており、信頼性が高く、広く使用されているため、それを選択しました。SQLiteは「退屈なテクノロジー」であり、良い意味でそうです。多くの企業が、より大規模なデプロイメントでSQLiteを問題なく使用しており、当社も同様のストレスフリーな使用を期待していました。現在のバックアップパイプラインでは、数分ごとにデータベースの完全なスナップショットを取得し、SQLiteファイルをS3バケットにアップロードしています。このセットアップは、2023年初頭から問題なく実行されていました。昨年8月に、それらのS3バックアップを読み取るデータパイプラインが、当社のデータベースの1つでエラーを報告しました。当社は、バックアップに対してSQLiteのPRAGMA integrity_checkコマンドを実行し、それが実際に破損していることを発見しました。SQLiteの破損は可能ですが、非常に珍しく、通常の操作では遭遇すべきではありません。影響を受けたデータベースを修復し、原因を調査しましたが、効果はありませんでした。大規模に運用する場合、まれなイベントでも一定の頻度で発生する可能性があるため、それが再び、再び、再び、再び発生しても驚くべきではなかったはずです。最終的に根本的なバグを解決する前に、当社は6ヶ月間に19件の別々のデータベース破損インシデントに直面しました。「データベース破損」という言葉を聞くと、データ損失を心配するのは自然です。当社のコントロールプレーンは設定データのみを処理するため、これらのデータベースにはテールネットとデバイスに関するメタデータが含まれていますが、プライベート暗号化キーやネットワークトラフィックは含まれていません。初期のインシデントでは、復旧プロセスにより、新しく追加されたデバイスや設定変更の一部が永続せず、少量のメタデータを再入力する必要がありました。破損が発生するたびに、データベースを修復または復元する間、シャード上のコントロールプレーンプロセスを停止する必要がありました。これは、その復旧ウィンドウ中にコントロールプレーン全体が消滅したため、そのシャード上のテールネットにとって苦痛でした。初期のインシデントでは、ダウンタイムは1時間以上でしたが、その後のインシデントで徐々に復旧プロセスを迅速化しました。各テールネットはメッシュネットワークであり、デバイスは互いにピアツーピアのWireGuard®接続を確立します。デバイスがテールネットに参加すると、新しい接続を確立する前にコントロールプレーンから他のデバイスのリストを取得する必要があります。そのため、デバイスがSQLiteのダウンタイム中にオンラインになった場合、接続できませんでした。データベースが修復されている間、オンラインのデバイスは互いに接続されたままでしたが、ネットワークの変更を学習することはできませんでした。それらのテールネットは、Webベースの管理コンソールとTailscale APIへのアクセスも一時的に失いました。信頼性への広範な影響もあります。少数のテールネットのみが影響を受けた場合でも、グローバルインシデントをステータスページに投稿しました。多くの人が、自分に影響を与えなかったインシデントのステータスページイベントを見ました。実際、ほとんどのシャードとテールネットはデータベース破損インシデントに関与しませんでした。それにもかかわらず、繰り返し発生するダウンタイムは、直接影響を受けたかどうかに関わらず、信頼を損ないます。破損の最初のインスタンスから、これが当社の信頼性に対する深刻な脅威であることを認識しており、問題に多くのエンジニアリング時間を費やしましたが、修正は容易ではありませんでした。 障害を見つけようとする試み このバグは、当社の最初の試みのすべてに抵抗しました。最近の変更を確認しましたが、関連性があると思われるものはありませんでした。当社のSQLiteとやり取りする低レベルコードは長年前にすべて書かれ、それまで問題がなかったため、誰もそれを扱っていませんでした。当社はそのコードすべてを徹底的に再レビューし、見落とされたバグを探しましたが、当社が見ていた破損を引き起こすようなものは何も見つかりませんでした。破損インシデント間の共通の要因を探しましたが、見つけることができませんでした。単一のシャード、顧客、テールネット機能、時刻、または負荷レベルに結びついていませんでした。何がその動作を引き起こしているのか、当社は途方に暮れていました。この信頼性の高いトリガー条件の欠如は、バグを合成的に再現できないことを意味しました。代わりに、ライブ環境にパッシブなフォレンジックテレメトリを展開して、破損を現行犯で捕らえることに頼らざるを得ませんでした。データベースの問題に対するライブ診断の収集は、当社がやりたくなかったことの最後でしたが、他に選択肢はありませんでした。追加の複雑さとして、破損は定期的なスケジュールで発生しませんでした。インシデントは数時間おきに発生することもあれば、数週間おきに発生することもありました。次の診断ダンプを取得できる時期がわからなかったため、進捗を予測したり、さらなる作業を計画したりするのが困難でした。10月から12月にかけて、破損インシデントがなかった6週間の期間がありましたが、それらは歓迎されないクリスマスプレゼントとして戻ってきました。これは迅速かつ容易な修正ではないため、SQLite開発者にプロフェッショナルサポート契約を依頼しました。これは素晴らしい決断でした。これにより、彼らの深い専門知識と経験に直接アクセスでき、当社のアーキテクチャとインシデントについて多くの詳細な技術的議論を行いました。TailscaleエンジニアリングとSQLiteコア開発者の間で、破損の原因となっている可能性のあるいくつかの理論をマッピングしました。これには、close()での破損したPOSIXロック、SQLiteが所有するメモリの誤管理、またはスレッドセーフティを無効にしながらSQLiteを複数のスレッドから誤って使用することなどが含まれます。各インシデントの後、当社はより多くのデータを収集し、より多くの診断を追加し、これらの理論を体系的に除外しました。当社は徐々に真のバグに収束していきました。 鳴らなかったトランザクション 当社が根本原因を調査している間も、当社は実行するライブプラットフォームを持っていました。当社は、復旧を自動化し、ダウンタイムを最小限に抑えるために、積極的な措置を取りました:破損に遭遇した場合に即座にハードストップするようにコントロールプレーンシャードを設定するバックアップを継続的にPRAGMA integrity_checkで実行する自動バックアップモニターを展開するランブックとオンコールトレーニングを改善するこれらの取り組みにより、応答時間を1時間未満に短縮しました。そして、予期しない手がかりを発見しました。当社は、サービスを、既知の良いバックアップにロールバックする(多くのデータを失うことになる)か、破損したとわかっているデータベースを修復する(潜在的にリスクがある)ことに関係しない方法で復元する方法を望んでいました。これを行うために、当社はトランザクションログパイプラインを構築しました。データベースを変更したすべてのSQLステートメントを、別のログファイルにストリーミングしました。SQLiteは単一書き込みデータベースであり、シリアライズ可能なトランザクションを持つため、当社のトランザクション履歴は完全に線形的で決定論的でした。(これはPostgresやMySQLのようなマルチライターデータベースでは真実ではありません。)それらのトランザ