2.7.0.beta2 のアップグレード中に ERROR: duplicate key が発生しました

2.7.0.beta1 からアップグレードしたところ、以下のエラーメッセージが表示されました。

2021-01-22 20:16:22.015 UTC [4055] discourse@discourse LOG:  duration: 75335.241 ms  statement: UPDATE notifications SET processed = true
2021-01-22 20:16:23.792 UTC [4055] discourse@discourse LOG:  duration: 1776.591 ms  statement: ALTER TABLE "notifications" ALTER COLUMN "processed" SET NOT NULL
2021-01-22 20:16:25.198 UTC [4055] discourse@discourse LOG:  duration: 1323.298 ms  statement: CREATE  INDEX  "index_notifications_on_processed" ON "notifications"  ("processed")
2021-01-22 20:16:25.458 UTC [4055] discourse@discourse LOG:  duration: 241.063 ms  statement: CREATE TABLE "user_notification_schedules" ("id" bigserial primary key, "user_id" integer NOT NULL, "enabled" boolean DEFAULT FALSE NOT NULL, "day_0_start_time" integer NOT NULL, "day_0_end_time" integer NOT NULL, "day_1_start_time" integer NOT NULL, "day_1_end_time" integer NOT NULL, "day_2_start_time" integer NOT NULL, "day_2_end_time" integer NOT NULL, "day_3_start_time" integer NOT NULL, "day_3_end_time" integer NOT NULL, "day_4_start_time" integer NOT NULL, "day_4_end_time" integer NOT NULL, "day_5_start_time" integer NOT NULL, "day_5_end_time" integer NOT NULL, "day_6_start_time" integer NOT NULL, "day_6_end_time" integer NOT NULL)
2021-01-22 20:16:25.560 UTC [4055] discourse@discourse LOG:  duration: 100.868 ms  statement: CREATE  INDEX  "index_user_notification_schedules_on_user_id" ON "user_notification_schedules"  ("user_id")
2021-01-22 20:16:25.782 UTC [4055] discourse@discourse LOG:  duration: 142.180 ms  statement: CREATE  INDEX  "index_do_not_disturb_timings_on_scheduled" ON "do_not_disturb_timings"  ("scheduled")
2021-01-22 20:16:26.414 UTC [4055] discourse@discourse LOG:  duration: 361.514 ms  statement: UPDATE users
	SET locale = 'en_GB'
	WHERE locale = 'en'
	
2021-01-22 20:16:26.656 UTC [4055] discourse@discourse LOG:  duration: 132.778 ms  statement: UPDATE theme_translation_overrides
	SET locale = 'en_GB'
	WHERE locale = 'en'
	
2021-01-22 20:16:42.745 UTC [4055] discourse@discourse ERROR:  duplicate key value violates unique constraint "index_users_on_username"
2021-01-22 20:16:42.745 UTC [4055] discourse@discourse DETAIL:  Key (username)=(DaveW) already exists.
2021-01-22 20:16:42.745 UTC [4055] discourse@discourse STATEMENT:  UPDATE users
	SET locale = 'en'
	WHERE locale = 'en_US'
	
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_users_on_username"
DETAIL:  Key (username)=(DaveW) already exists.

そして、出力の最後には以下が表示されました。

I, [2021-01-22T20:16:42.805286 #1] INFO -- : Terminating async processes

I, [2021-01-22T20:16:42.805333 #1] INFO -- : Sending INT to HOME=/var/lib/postgresql USER=postgres exec chpst -u postgres:postgres:ssl-cert -U postgres:postgres:ssl-cert /usr/lib/postgresql/13/bin/postmaster -D /etc/postgresql/13/main pid: 49

I, [2021-01-22T20:16:42.805381 #1] INFO -- : Sending TERM to exec chpst -u redis -U redis /usr/bin/redis-server /etc/redis/redis.conf pid: 166

166:signal-handler (1611346602) Received SIGTERM scheduling shutdown...

2021-01-22 20:16:42.805 UTC [49] LOG: received fast shutdown request

2021-01-22 20:16:42.835 UTC [49] LOG: aborting any active transactions

2021-01-22 20:16:42.857 UTC [49] LOG: background worker "logical replication launcher" (PID 58) exited with exit code 1

166:M 22 Jan 2021 20:16:42.876 # User requested shutdown...

166:M 22 Jan 2021 20:16:42.876 * Saving the final RDB snapshot before exiting.

166:M 22 Jan 2021 20:16:44.758 * DB saved on disk

166:M 22 Jan 2021 20:16:44.758 # Redis is now ready to exit, bye bye...

2021-01-22 20:16:45.563 UTC [53] LOG: shutting down

I, [2021-01-22T20:16:52.806177 #1] INFO -- : HOME=/var/lib/postgresql USER=postgres exec chpst -u postgres:postgres:ssl-cert -U postgres:postgres:ssl-cert /usr/lib/postgresql/13/bin/postmaster -D /etc/postgresql/13/main pid:49 did not terminate cleanly, forcing termination!

FAILED

--------------------

Pups::ExecError: cd /var/www/discourse && su discourse -c 'bundle exec rake db:migrate' failed with return #<Process::Status: pid 4032 exit 1>

Location of failure: /pups/lib/pups/exec_command.rb:112:in `spawn'

exec failed with the params {"cd"=>"$home", "hook"=>"db_migrate", "cmd"=>["su discourse -c 'bundle exec rake db:migrate'"]}

d627ad17d1f22d839a7dc8099878e6272eb3ea1772539f6628e2a23dd830aca2

** FAILED TO BOOTSTRAP ** please scroll up and look for earlier error messages, there may be more than one.

./discourse-doctor may help diagnose the problem.

同じユーザー名を持つアカウントが2つ存在するなんて、どうすればいいのかわかりません。

当サイトのサイトが完全にダウンしています。今どうすればよいでしょうか?

よろしくお願いいたします、
Gunnar

「いいね!」 2

./launcher start app でサイトを再起動できると思います。

その後、重複データを削除する方法を確認してください。

および

「いいね!」 5

追加で提供するべき情報があります:

  • これは単一のコンテナインストールです。
  • 最初の再構築はエラーなく完了しました。このエラーは、最初の再構築が正常に完了した後、2 回目の再構築を実行した際に発生しました。

よろしくお願いいたします、
グンナー

「いいね!」 1

ディスク容量は十分ですか?おそらく、必要な PostgreSQL のバージョン 13 への更新 に関連するエラーです。このアップグレード手順に従いましたか

cd /var/discourse
git pull
./launcher rebuild app

そのエラーでフォーラムを検索すると、過去の情報や考えられる修正方法が多数見つかります。

これを試してみてください。

投稿したエラーでフォーラムを検索し、他の人が有効だった修正方法を確認してください。

「いいね!」 1

postgres_data_oldpostgres_data に名前を戻し、./launcher start app を実行して古いイメージを起動することで、サイトを復旧させることができました。

これで最初の状態に戻ってしまいましたが、少なくともサイトは起動しています。

重複キーを探そうとして、以下のコマンドを入力してみました。

select * from users WHERE username = 'DaveW';

(このスレッドの最初の投稿で表示されたエラーメッセージによると、問題のキーは username = DaveW でした。)

コマンドは1行、つまり1人のユーザーのみを返しました。何が抜けているのでしょうか?

よろしくお願いいたします、
Gunnar

「いいね!」 1

DB には 2 つ存在し、大文字小文字が異なる可能性があります:

「いいね!」 1

私のサイトをご覧になりましたか?:wink:

おっしゃる通りです。GUI で検索すると確かに2つ表示されます。しかし、管理者として両方をクリックして詳細ページを開くと、同じユーザーのようです。2つの間で異なる詳細は1つも見つかりません。つまり、どちらをクリックしても、フルネームが入力されている2つ目のユーザーのデータが表示されます。

そのユーザーは投稿をしたこともなく、久しくアクセスもしていなかったため、GUI からアカウントを削除しました。

面白いことに、リストの最初のユーザー(フルネームなしのほう)はまだ残っています。しかし、今そのユーザーをクリックすると何も起こりません。ダイアログが開こうとしてすぐに閉じる様子が見えます。

データベースを直接検索して username = DaveW で探すと、0 行が返されます。しかし、以下のクエリで検索すると:

select * from users WHERE name = 'DaveW';

(username ではなく name で検索)1 行が返されます:

 19732 | DaveW    | 2016-11-15 12:43:02.708166 | 2016-11-15 12:43:02.708166 | DaveW |                    0 |                |               |      | t      | davew          | 2016-11-15 12:43:02.708166 | f     | 2017-06-01 18:09:45.018058 |           1 | f        |                |             |                   |              |                |               |     0 |          0 |            | f         |       |                    |        |                  |                         | f      |               |               |                          |                           | 
(1 row)

username フィールドに DaveW(同じスペル)が含まれていることに注目してください。このアカウントはもう一方のアカウントより3年も古いものです。

以下のコマンドでこれを削除しても問題ありませんか?

DELETE from users WHERE id = 19732;

よろしければご回答ください。

Gunnar

「いいね!」 1

もちろん!:grinning:

これで 19732 は削除されるはずです。ただし、何らかの悪影響がないとは保証できません。いずれにせよ、先に進む前にバックアップを取っておくことをお勧めします。

「いいね!」 3

重複しているユーザーのユーザー名を変更することもできます。

「いいね!」 2

はい。

変更後、アップグレードを最初からやり直せるでしょうか?つまり、rebuild を2回実行できるのでしょうか?

ありがとうございます!
Gunnar

「いいね!」 4

それで問題ないはずです。ただし、他の重複ユーザーが存在しないか確認してください。

「いいね!」 2

実は、少なくとも1人は存在するようです。

PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_users_on_username_lower"
DETAIL:  Key (username_lower)=(robs) already exists.

データベース内の重複をスキャンする方法はありますか?ユーザーだけでなく、アップグレード中にこのエラーを引き起こす可能性のあるすべてのテーブルを対象にしたいです。

再試行する前に、それらすべてを見つけて修正したいと考えています。

また、これがアップグレードプロセスの一部になることも望ましいでしょう。まずスキャンを実行し、変更が行われる前に処理を停止するのです。ユーザーに警告し、重複のリストと、修正方法に関するメタページへのリンクを提供すればよいのではないでしょうか。

「いいね!」 2

より侵襲性の低いアプローチとしては、アップグレード前にデータベースを再インデックス化することがあり、修正が必要なインデックスが特定できるはずです。

「いいね!」 2

私はデータベースの専門家ではないので、それは少し怖く見えます。私の読み方が正しければ、その操作をすると重複するインデックスが生成され、それらを手動で削除する必要があるのでしょうか?

それとは別に、ここがおかしい部分です。前の重複問題であなたが私のサイトで見つけたのと同様に、今 GUI 内で重複したユーザー「RobS」を見つけました。しかし、前と同様に、どちらをクリックしても、結局は片方のプロファイルページにしか移動しません。つまり:

  • GUI で「RobS」という名前のユーザー一覧を表示します。2 人いることがわかります。両者の統計データが異なることも確認できます。
  • ユーザー #1 をクリックします。表示されるプロファイルと統計データは、実はユーザー #2 に属するものです。
  • ユーザー #2 をクリックします。表示されるプロファイルと統計データは、ユーザー #2 に属するものです。

select * from users WHERE username_lower = 'robs';

このクエリは 1 行を返します:ユーザー #2 のように見える行(日付が一致しています)。

select * from users WHERE username = 'RobS';

このクエリも 1 行のみを返します:ユーザー #1 のように見える行(これも日付が一致しています)。このユーザーは、もう一方とは異なる ID を持っています。

出力結果から判断すると、両者とも同じ usernameusername_lower を持っていたはずなのに、各 SELECT 文では 1 行しか返されていません。私のデータベースは深刻な問題を抱えているのでしょうか?

再インデックス化でこの問題は解決するでしょうか?

「いいね!」 2

再度ビルドを試みたところ、ユーザー名「drc」の重複がさらに見つかりました。GUI では、以下の 2 名が表示されました。

Deborah C
David C

いずれかのユーザーをクリックして、プロフィールの「カード」で詳細を確認してみてください。どちらをクリックしても、Deborah C の詳細が表示されてしまいます。

これがどうして起きたのかはわかりませんが、次に取るべき最善の手段は何でしょうか?この問題を解決できるまで、アップグレードは中断しています。

どのように再インデックスを作成すればよいでしょうか?例えば、以下のようなコマンドでしょうか?

REINDEX SCHEMA CONCURRENTLY public;

これによって、どのキーが重複しているかを確認できるのでしょうか?

「いいね!」 1

以下のクエリが何を返すか気になります:

select id, username FROM users WHERE username_lower = 'robs' OR username = 'RobS':

単に、ユーザー名エントリのいずれかに空白が含まれている、あるいは何らかの非印刷文字や Unicode 文字が入っているといった単純な問題でしょうか?

「いいね!」 1

はい、そうだと思いますが、より良い方法があるかどうかはわかりません。私のテスト環境では、出力なしで実行されるだけです。本番サイトを更新した際には、対応すべき重複が1つしかなかったため、私の経験があなたの状況に当てはまるとは限りません。いずれにせよ、データベース操作を行う前には必ずバックアップを取ってください。私は通常、DigitalOcean でスナップショットを作成しておくので、問題が発生した場合の復旧が迅速かつ簡単に行えます。

「いいね!」 1

以前も同様の問題を このように解決した と思います。インデックスを再構築し、失敗した際に重複しているアイテムが表示されるので、それを修正して再度試します。これを繰り返してください。

「いいね!」 2

ありがとうございます。失敗した場合、不完全な新しいインデックスをどのように見つけて削除すればよいでしょうか?失敗したテーブル名に「_ccnew」を追加するだけでよいのでしょうか?

つまり、「users」テーブルで重複エラーによりクラッシュした場合、まず重複を修正し、その後:

DROP index ‘users_ccnew’;

を実行する。その後、手順を繰り返す。これでよいのでしょうか?

よろしくお願いいたします、
Gunnar

「いいね!」 1

ああ、そうですか。現在、同じユーザー名と username_lower を持つ2人の有効(かつアクティブ)なユーザーが存在する状況に陥っています。

  id   | username | (非表示) | username_lower |
 42379 | DrC      | (非表示) | drc            |
 47695 | DRC      | (非表示) | drc            |

2番目のユーザーの username と username_lower の両方を変更する必要があるようです。psql でどのように行えばよいでしょうか?

よろしくお願いいたします。
Gunnar

「いいね!」 1