noellabo's tech blog

@noellaboの技術ブログ

PostgreSQLを動かしているOSをアップグレードするとデータベースが壊れることがある

TLDR

  • Locale data changes - PostgreSQL wiki を読むべし
  • glibcが2.28以前から以降に更新される際に、ロケールによる照合順序の変更の影響を受け、インデックスが壊れる
  • 誤ったパーティションに書き込んでしまうことがある
  • Ubuntu 18.04以前から18.10、20.04以降、Debian 9以前から10以降、CentOSの6から7(de_DE.UTF-8ロケールの場合のみ)、CentOS 7以前から8以降など
  • ストリーミングレプリケーションでこれらのバージョンを跨ぐとレプリカ側が壊れる

なんで?

  • データベースには、データを素早く取り出したり、不正なデータ(IDの重複、データ同士の関係の矛盾)が生じないようにするために、インデックスが使われています。
  • インデックスは多くの場合、データを並べ替えた際の順序に依存しています。(順番の定義が変わると異常動作します)
  • ロケールは、言語の特性を考慮するための仕組みです。たとえば日本語では、ひらがな、カタカナ、濁音などの照合順序(並べ順)に影響があります。
  • Cロケール・POSIXロケールと呼ばれる、言語の特性などを考えないものもあります。
  • どのロケールを使うかは、データベースクラスタを初期化する際に指定されています。
  • ロケールは、OSの提供する機能を用いています。
  • glibcのバージョン2.28以降で、ロケールを用いた並べ替えの順序が変更されています。

ということで、ロケールを用いていない(Cロケール・POSIXロケール)場合は影響ありませんが、OSのアップグレードに伴いロケールを用いた比較順序が変更されてしまうことで、インデックスが正しく機能しなくなります。

どうすればいいの?

対処方法がわからない場合は、むやみにOSをアップグレードしない方が良いでしょう。

  • 新OSの新しいデータベース環境を別マシンに構築し、ロジカルレプリケーションにて同期をとり、最小のダウンタイムで切替を行う
  • 事前にpg_dumpにてフルバックアップをとり、OSアップグレード後にリストアする
  • OSアップグレード後、データベースにデータを書き込む前に、影響の及ぶインデックスを再構築する

もし、既に対処を行わずにOSアップグレードを実行してしまっている場合は、重複レコードの発生などで論理的に壊れている可能性があります。 アプリケーションで対処方法が示されていない場合、開発元と連絡をとり、破損状況の確認と修復を行う必要があります。

Mastodonにおける対処については、このあとに説明があります。

pg_upgradeやpg_basebackupはバイナリで処理するため、インデックスが破損したまま引き継がれます。 pg_dumpやロジカルレプリケーションではインデックスのバイナリを再利用しないため、問題を回避できます。

Mastodonへの影響

インデックスの不具合に気付かずに運用を続けると、著しく速度低下したり、アカウントのレコードが重複するなど重篤な不具合が発生します。

アカウントのレコードが重複するとどうなるか。

  • あるユーザー名(noellabo)の記録が分裂し、ID: 1 のnoellabo、ID: 542 のnoellabo、ID: 84821 のnoellaboなどが記録されています
  • 20人のフォロワーは、ID: 1 のnoellaboをフォローしていると記録されます
  • 947人のフォロワーは、ID: 542 のnoellaboをフォローしていると記録されます
  • 69人のフォロワーは、ID: 84821 のnoellaboをフォローしていると記録されます
  • noellaboのフォロワー一覧を調べようと思った時に、どのnoellaboの記録が使われるかわかりませんし、どれを使っても正しい一覧は得られません
  • その他、さまざまな不具合の原因となります

アカウントはMastodonの基本的なオブジェクトで、フォローやブロック、投稿、お気に入り、その他、もの凄く沢山のデータに結びついています。 しかし、レコードが重複したままでは正しく機能しないので、分裂したレコードを統合しなければなりません。 これは、Mastodonのデータベース構造を正しく理解した上で、結びつきを強制的に正しい状態に変更しなければなりません。 ちょっと手作業で、自力で治せそうな内容じゃないですよね。

重複が発生する可能性があるのはアカウントだけでなく、直近のバージョンでは23種のテーブルが該当しています。

不具合は、Mastodonの特定バージョンへのアップグレードの際に、マイグレーションに失敗することで発覚することがあります。 しかし、すでにOSのアップグレードを行ってしまっている場合、潜在的に破損が進行しているにも関わらず、気付かないことがあります。 この問題は放置すると時間とともに症状が悪化するので、直ちに対処する必要があります。

本件に関する議論は、下記のissueで行われました。

github.com

問題を検出する方法

issueの最後の方に投稿されているamcheck extentionを使ったB-Treeインデックスの検査を実行してください。 PostgreSQLのスーパーユーザー(postgresユーザー)で実行する必要があります。

Mastodonでの解決方法

本件に対処するために、エラーを検出し、論理的に矛盾しているデータを統合し、インデックスを再作成するためのスクリプトが作成されています。ThibGさん、神ですね。 tootctl maintenance fix-duplicatesで、修復プロセスを実行できるようになる見込みです。(まだmasterにマージされていません)

github.com

もし早急に対応する必要がある場合、この修正をcherry-pickして適用することもできますが、行っている内容を理解できない場合はPostgreSQLやMastodonに詳しい人の助けを借りるようにしてください。