復元失敗 - 一意インデックスを作成できませんでした

こんにちは、バックアップの復元が以下のエラーで失敗しました:

CREATE INDEX
ERROR:  一意インデックス "index_incoming_referers_on_path_and_incoming_domain_id" の作成に失敗しました
DETAIL:  キー (path, incoming_domain_id)=(/m/search, 25) が重複しています。
EXCEPTION: psql が失敗しました: DETAIL:  キー (path, incoming_domain_id)=(/m/search, 25) が重複しています。

類似のトピックを見つけましたが、何をするべきか理解できませんでした:https://meta.discourse.org/t/getting-this-error-during-restore-could-not-create-unique-index/

初心者向けに、手順を踏んだ解決策をご教示いただければ幸いです。

「いいね!」 2

この incoming_referers テーブルに関する問題は、最近何度か発生しています。なぜその特定のテーブルが問題を引き起こしているのかはわかりませんが、問題が関連している可能性が高いようです。Discourse チームの他の誰かが、重複レコードが作成される原因についてアイデアを持っているかもしれません。

バックアップファイルを作成したサイトにまだアクセスできますか?もし可能であれば、データベースから重複レコードを削除し、その後新しいバックアップファイルを作成することで修正できます。そのためには、古いサーバーに SSH で接続し、/var/discourse ディレクトリに移動します:

cd /var/discourse

次に、以下のコマンドを実行します。

./launcher enter app

その後、Rails コンソールに入ります。

rails c

次に、以下のようなプロンプトが表示されるはずです。

[1] pry(main)>

Rails コンソールから以下のコマンドを実行し、何を返すか教えてください:

IncomingReferer.where(path: "/m/search")

これは、2 つ以上のレコードを含む配列を返すはずです。

「いいね!」 2

ありがとうございます。
明日の朝に実行し、結果を報告いたします。

「いいね!」 1

これは古いインストールからのものです。レコードが1つだけでしょうか?

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> [#<IncomingReferer:0x00005638d834b130
  id: 5153,
  path: "/m/search",
  incoming_domain_id: 25>]
[2] pry(main)>

編集: 新しいサーバーでも試しました。表示は以下の通りです:

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> []
[2] pry(main)>

ご確認ありがとうございます!あなたが得られた結果は、私が今日早些頃別のサイトで確認したものと同じです。これは解決可能な問題ですが、弊社のエンジニアの一人に状況を調べてもらうよう手配しようと思います。

「いいね!」 2

サーバーを移動させた主な理由は、サポート終了が控えていたDebian 8を使用していたためです。
このリストアの問題に伴い、同じサーバー上でDebian 9へのアップグレードを選択しました。無事に完了しましたので、当面はひとまず安心です。
サポートいただき、ありがとうございます。

この行を置き換えてください

インデックスが機能すると仮定しないように、あいまい検索を行う必要があります。先頭にパーセント記号が一つあれば十分だと思います。

余分なレコードを削除するだけです。ただし、正しく行うには、このテーブルを参照している他のテーブルも更新する必要があります。いくつかの異なるテーブルが関連しているため、毎回調べています。

この問題はサードパーティの拡張機能のせいにされていますが、あまり意味がわかりません。Postgres の不具合に違いないようですが、わかりません。月に数回この問題に遭遇します(スコア)が、複数のサイトで発生しているようです。

「いいね!」 5

重複キーの問題も発生しています。公式の修正方法が文書化されていますか?

discourse=# REINDEX SCHEMA CONCURRENTLY public;
    ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_ccnew"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1905) is duplicated.

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> [#<IncomingReferer:0x0000557176d3f210 id: 44231, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557176d925c8 id: 42228, path: "/m/search", incoming_domain_id: 26>]

サーバーをその場でアップグレードしたため、もはや新しいサーバーに復元することはできませんが、好奇心から試してみたところ、あいまい検索で一致するレコードは見つかりませんでした。

[1] pry(main)> IncomingReferer.where(path: "%/m/search%")
=> []
[2] pry(main)> IncomingReferer.where(path: "%/m/search")
=> []
[3] pry(main)> IncomingReferer.where(path: "/m/search%")
=> []

ワイルドカードを機能させるには、LIKE を使用する必要があります。

IncomingReferer.where("path LIKE '%/m/search%'")
「いいね!」 4

それにより、重複するキーがさらにいくつか見つかりました。

[1] pry(main)> IncomingReferer.where("path LIKE '%/m/search%'")
=> [#<IncomingReferer:0x0000557eaa7ed488 id: 408, path: "/m/search", incoming_domain_id: 26>,
 #<IncomingReferer:0x0000557eaabd80c0 id: 1508, path: "/m/search", incoming_domain_id: 45>,
 #<IncomingReferer:0x0000557eaabe3268 id: 2216, path: "/m/search", incoming_domain_id: 420>,
 #<IncomingReferer:0x0000557eaabe2f20 id: 3081, path: "/m/search", incoming_domain_id: 230>,
 #<IncomingReferer:0x0000557eaabe2c00 id: 33210, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557eaabe2908 id: 44231, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557eaabe27c8 id: 42228, path: "/m/search", incoming_domain_id: 26>]
「いいね!」 2

重複行はすべて削除してしまえばいいでしょう。この情報にはほとんど価値がありません。

「いいね!」 1

喜んでお手伝いします。正しいコマンドを教えていただけますか?PostgreSQL は特に詳しくありませんが、SQL は知っています。

そう聞いて安心しました。私はそれらとリンクしている別のテーブルを地道に更新していました。それが何だったか全く思い出せないので、非常に面倒で、何度も何度も最初からやり直す羽目になっています。

IncomingReferer.find(44231).destroy
IncomingReferer.find(42228).destroy
「いいね!」 2

2 つの重複を削除したのは成功しましたが、その後にインデックスを再構築すると新しいエラーが発生しました。これは重大な問題でしょうか?どのように修正し、その検索 3433 の行を削除すればよいでしょうか?

[1] pry(main)> IncomingReferer.find(44231).destroy
=> #<IncomingReferer:0x000055734c65d8e8 id: 44231, path: "/m/search", incoming_domain_id: 4>
[2] pry(main)> IncomingReferer.find(42228).destroy
=> #<IncomingReferer:0x000055734cd81a70 id: 42228, path: "/m/search", incoming_domain_id: 26>
postgres=# \connect discourse
You are now connected to database "discourse" as user "postgres".
discourse=# REINDEX SCHEMA CONCURRENTLY public;
WARNING:  cannot reindex invalid index "public.incoming_referers_pkey_ccnew" concurrently, skipping
WARNING:  cannot reindex invalid index "public.index_incoming_referers_on_path_and_incoming_domain_id_ccnew" concurrently, skipping
WARNING:  cannot reindex invalid index "pg_toast.pg_toast_2782645_index_ccnew" concurrently, skipping
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_ccnew1"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 3433) is duplicated.
CONTEXT:  parallel worker
「いいね!」 1

ここは作成を処理するコードです…これは適切に処理しているはずですが、必要であれば ON CONFLICT による挿入に変更することもできますか?

「いいね!」 4

4 つのインデックスを手動で再構築しようとしましたが、2 つは成功し、2 つは失敗しました。重複している 2 行を削除すべきでしょうか?

discourse=# REINDEX INDEX CONCURRENTLY "public"."incoming_referers_pkey_ccnew";
REINDEX
discourse=# REINDEX INDEX CONCURRENTLY "public"."index_incoming_referers_on_path_and_incoming_domain_id_ccnew";
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_cc_ccnew"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1861) is duplicated.
discourse=# REINDEX INDEX CONCURRENTLY "pg_toast"."pg_toast_2782645_index_ccnew";
REINDEX
discourse=# REINDEX INDEX CONCURRENTLY "index_incoming_referers_on_path_and_incoming_domain_id_ccnew1";
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_c_ccnew1"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1905) is duplicated.
「いいね!」 1

はい、重複行を削除してください。

@riking pg のインデックス破損は Discourse のバグではなく、pg のバグです。その INSERT のパフォーマンスを改善することはできますが、pg のバグ自体は pg 側で修正する必要があります。

私の推測では、これはデータベースエンジンの強制シャットダウン(例えば停電など)に関連するものではないかと思います。

「いいね!」 2

それは妥当な説明ですね。./launcher shutdown app(または再構築)は、Postgres に対してクリーンなシャットダウンを実行しているのでしょうか?ああ、でも、自動更新が Docker コンテナのクリーンなシャットダウン方法を知らないはずだ、と私は確信しています。