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.

같은 사용자 이름을 가진 두 계정이 어떻게 존재할 수 있는지 모르겠습니다.

사이트가 완전히 다운된 상태입니다. 이제 어떻게 해야 하나요?

감사합니다,
Gunnar

./launcher start app 명령으로 사이트를 다시 구동할 수 있을 것 같습니다.

그 다음 중복 항목을 삭제하는 방법에 대해 확인해 보세요:

및

아마도 제공해야 할 추가 정보:

  • 이는 단일 컨테이너 설치입니다.
  • 첫 번째 재구축은 오류 없이 완료되었습니다. 이 오류는 첫 번째 재구축이 성공적으로 완료된 후 두 번째 재구축을 실행할 때 발생했습니다.

감사합니다,
Gunnar

디스크 공간이 충분한가요? PostgreSQL 13 버전 업데이트와 관련된 오류인 것으로 보입니다. 이 업그레이드 방법을 따랐나요?

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

포럼에서 해당 오류를 검색해 보면 과거에 공유된 많은 정보와 가능한 해결 방법을 찾을 수 있습니다.

이 방법을 시도해 보세요.

포럼에서 게시한 오류를 검색하여 다른 사용자에게 효과가 있었던 다양한 해결책을 확인해 보세요.

postgres_data_old을 다시 postgres_data로 이름 변경한 후, ./launcher start app을 실행하여 이전 이미지를 시작함으로써 사이트를 다시 가동했습니다.

즉, 다시 처음부터 시작해야 하는 상황이 되었지만, 적어도 사이트는 정상 작동하고 있습니다.

중복 키를 찾기 위해 다음을 입력해 보았습니다:

select * from users WHERE username = 'DaveW';

(username = DaveW는 이 스레드의 첫 번째 게시물에서 오류 메시지에 표시된 문제 키였습니다.)

명령을 실행한 결과, 단 한 줄, 즉 한 명의 사용자만 반환되었습니다. 제가 놓치고 있는 것이 무엇일까요?

감사합니다,
Gunnar

DB에서 대소문자가 다르게 저장된 두 개의 항목이 있습니다:

제 사이트를 방문하셨나요? :wink:

말씀하신 내용을 이해했습니다. GUI에서 검색을 하면 실제로 두 개가 보이긴 합니다. 하지만 관리자로 로그인하여 두 항목 모두를 클릭해서 상세 페이지를 불러오면, 두 계정이 동일한 사용자로 보입니다. 두 계정 사이에 다른 단 하나의 세부 정보도 찾을 수 없습니다. 기본적으로 그중 어느 것을 클릭하든, 전체 이름이 입력된 두 번째 계정의 데이터가 표시됩니다.

해당 사용자는 이전에 게시물을 작성한 적이 없고 한동안 접속하지 않았기 때문에, 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

물론이죠! :grinning:

이것은 19732를 삭제해야 하지만, 부작용이 전혀 없을 것이라고 보장할 수는 없습니다. 어쨌든, 계속 진행하기 전에 백업을 권장합니다.

중복된 항목 중 하나의 사용자 이름을 변경할 수도 있습니다.

네.

변경한 후, 업그레이드를 처음부터 다시 시작할 수 있을까요? 즉, rebuild를 두 번 실행하는 방식이요.

감사합니다!
Gunna

그렇게 하면 작동할 것입니다. 중복된 사용자가 다른 곳에 없는 한 말이죠.

결국 중복 사용자가 최소 한 명은 있는 것으로 나타났습니다.

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

데이터베이스에서 중복 항목을 스캔할 방법이 있을까요? 사용자뿐만 아니라 업그레이드 중 이 오류를 유발할 수 있는 모든 테이블에 대해요.

다시 시도하기 전에 모두 찾아서 수정하고 싶습니다.

또한 이것이 업그레이드 과정의 일부가 되는 것이 좋겠습니다. 먼저 스캔을 실행한 후, 어떤 변경 사항도 적용되기 전에 프로세스를 중단하는 방식이죠. 사용자에게 경고를 하고, 중복 항목 목록과 함께 메타(meta)에 있는 수정 방법 안내 페이지로 연결되는 링크를 제공하는 것이 어떨까요?

더 덜 침습적인 접근 방식은 업그레이드 전에 데이터베이스를 다시 색인화하는 것이며, 이는 수정이 필요한 항목을 알려줍니다.

저는 데이터베이스 전문가가 아니기 때문에 그게 좀 무섭게 느껴집니다. 그리고 제가 올바르게 이해했다면, 그렇게 하면 중복된 인덱스가 생겨서 수동으로 삭제해야 한다는 뜻인가요?

게다가 여기가 이상한 부분입니다. 이전 중복 항목에서 제 사이트에서 발견했던 것과 마찬가지로, 이번에는 GUI에서 중복된 사용자 RobS를 발견했습니다. 하지만, 이전과 마찬가지로 그중 어느 것을 클릭하든 결국 둘 중 하나(사용자 #2)의 프로필 페이지로 이동했습니다. 즉:

  • GUI에서 RobS라는 이름의 사용자를 나열합니다. 두 명을 찾습니다. 둘 다 다른 통계를 가지고 있음을 확인합니다.
  • 사용자 #1을 클릭합니다. 사용자 #2에 속하는 프로필과 통계를 확인합니다.
  • 사용자 #2를 클릭합니다. 사용자 #2에 속하는 프로필과 통계를 확인합니다.

select * from users WHERE username_lower = 'robs';

1행 반환: 사용자 #2처럼 보이는 것(날짜가 일치합니다).

select * from users WHERE username = 'RobS';

역시 1행만 반환: 사용자 #1처럼 보이는 것(다시, 날짜가 일치합니다). 이 사용자는 다른 사용자와 ID가 다릅니다.

출력 결과를 판단하면, 둘 다 같은 username과 username_lower를 가지고 있었음에도 불구하고 각 SELECT 문에서 1행만 반환되었습니다. 제 데이터베이스가 심각한 상태인가요?

다시 인덱싱하면 이 문제가 해결될까요?

다시 재구축을 시도했는데, 사용자 이름이 "drc"인 또 다른 중복 항목을 발견했습니다. GUI에서 다음 두 항목을 찾았습니다:

Deborah C
David C

두 사용자 중 하나를 클릭해 프로필 "카드"에서 세부 정보를 확인해 보세요. 어떤 사용자를 클릭하든 Deborah C의 세부 정보가 표시됩니다.

이런 일이 어떻게 발생했는지 전혀 모르겠지만, 지금 가장 좋은 대응 방법은 무엇일까요? 이 문제를 해결할 때까지 업그레이드를 포기했습니다.

재인덱싱은 어떻게 수행해야 하나요? 이렇게 해야 하나요?

REINDEX SCHEMA CONCURRENTLY public;

이렇게 하면 어떤 키가 중복인지 알려줄까요?

다음 쿼리가 무엇을 반환할지 궁금합니다:

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

사용자 이름 항목에 공백이 있거나, 비인쇄 가능 문자/유니코드가 들어 있는 것 같은 어리석은 문제는 아닐 텐데요?

네, 가능하다고 생각합니다. 그리고 더 나은 방법이 있는지 저도 잘 모르겠습니다. 제 테스트 인스턴스에서는 아무 출력 없이 실행만 되었습니다. 프로덕션 사이트를 업데이트했을 때 중복된 항목이 하나뿐이었기 때문에, 제 경험은 여러분의 상황에 좋은 예시가 아닐 수 있습니다. 어쨌든 데이터베이스 작업을 하기 전에 반드시 백업하세요—보통 DigitalOcean에서 스냅샷을 만들어 두면 문제가 생겼을 때 복구가 빠르고 쉽습니다.

과거에 비슷한 문제를 해결했던 방식이 바로 이런 것 같네요. 이전에도 비슷한 방식으로 해결한 적이 있습니다. 인덱스를 재구성하면 실패할 때 중복된 항목이 알려주니, 그 항목을 수정한 뒤 다시 시도하면 됩니다. 이 과정을 반복하세요.

감사합니다. 실패했을 때, 불완전한 새 인덱스를 어떻게 찾아서 제거하나요? 실패한 테이블 이름에 "_ccnew"를 추가하는 것만으로 충분한가요?

예를 들어, 중복 항목 때문에 "users"에서 충돌이 발생하면, 먼저 중복을 수정한 다음:

DROP index ‘users_ccnew’;

을 실행하면 되는 건가요?

그리고 다시 시도하면 되는 건가요? 그렇게 간단한 건가요?

감사합니다,
Gunnar

아, 네. 현재 동일한 username과 username_lower를 가진 두 개의 유효한(활성 상태) 사용자를 발견한 상황에 처해 있습니다:

  id   | username | (redacted) | username_lower |
 42379 | DrC      | (redacted) | drc            |
 47695 | DRC      | (redacted) | drc            |

두 번째 사용자의 username과 username_lower를 모두 변경해야 할 것 같습니다. psql에서 어떻게 하면 될까요?

감사합니다!
Gunnar