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で行われました。
問題を検出する方法
issueの最後の方に投稿されているamcheck extentionを使ったB-Treeインデックスの検査を実行してください。 PostgreSQLのスーパーユーザー(postgresユーザー)で実行する必要があります。
Mastodonでの解決方法
本件に対処するために、エラーを検出し、論理的に矛盾しているデータを統合し、インデックスを再作成するためのスクリプトが作成されています。ThibGさん、神ですね。
tootctl maintenance fix-duplicates
で、修復プロセスを実行できるようになる見込みです。(まだmasterにマージされていません)
もし早急に対応する必要がある場合、この修正をcherry-pickして適用することもできますが、行っている内容を理解できない場合はPostgreSQLやMastodonに詳しい人の助けを借りるようにしてください。