업그레이드 중 테이블의 고유 제약 조건을 위반하는 중복 키 값

여러분 안녕하세요, 제가 관리하는 포럼 중 하나에서 이상한 문제가 발생했습니다.

업그레이드 중 관리 페이지(admin/upgrade)나 터미널에서 실행을 시도했는데 실패했고, 다음과 같은 오류가 발생했습니다:

--------------------------------------------------------------------------------
1 migrations failed!

Failed to migrate default
#<ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_screened_ip_addresses_on_ip_address"
DETAIL:  Key (ip_address)=(10.0.0.0/8) already exists.

다행히 포럼이 망가지지는 않았습니다. ./launcher restart app 명령으로 재시작했고(예전에 재시작 후 흰 화면이 뜰 때 ./launcher destroy app / ./launcher start app으로 문제를 해결한 적이 있습니다), 따라서 긴급한 상황은 아닙니다. 그래서 도움을 요청드립니다.

데이터베이스를 조사해 보니 10.0.0.0/8 IP에 대한 몇 가지 참조가 있는데, 이는 로그처럼 보입니다. 하지만 screened_ip_addresses 테이블에는 중복 항목이 없습니다.

--
-- TOC entry 6829 (class 0 OID 382198)
-- Dependencies: 657
-- Data for Name: screened_ip_addresses; Type: TABLE DATA; Schema: public; Owner: -
--

COPY public.screened_ip_addresses (id, ip_address, action_type, match_count, last_match_at, created_at, updated_at) FROM stdin;
236	10.0.0.0/8	2	0	\N	2020-05-24 19:44:41.587257	2020-05-24 19:44:41.587257
237	192.168.0.0/16	2	0	\N	2020-05-24 19:44:47.150337	2020-05-24 19:44:47.150337
239	172.16.0.0/12	2	0	\N	2020-05-24 19:44:57.347656	2020-05-24 19:44:57.347656
240	fc00::/7	2	0	\N	2020-05-24 19:45:02.270948	2020-05-24 19:45:02.270948
261	154.71.107.147	1	0	\N	2020-06-05 13:15:17.718236	2020-06-07 00:27:57.204765
257	154.126.107.81	1	0	\N	2020-06-02 09:51:31.191431	2020-06-07 00:27:58.538628
259	197.1.186.242	1	0	\N	2020-06-05 08:39:52.218198	2020-06-07 00:27:58.985867
258	89.158.72.7	1	0	\N	2020-06-02 20:44:41.584317	2020-06-07 00:27:59.542337
260	196.179.229.13	1	0	\N	2020-06-05 08:39:52.227515	2020-06-07 00:28:00.288445
238	127.0.0.0/8	2	0	\N	2020-05-24 19:44:52.369958	2020-05-24 19:44:52.369958

포럼 관리자는 /admin/logs/screened_ip_addresses 페이지에서 일부 IP를 정리하고 몇 개를 추가했다고 말했습니다. 하지만 그가 이 IP를 건드렸을 것 같지는 않습니다. 이상한 점은 이 IP가 14일 전에 추가되었다고 표시된다는 것입니다(마지막으로 업그레이드한 시기로, 아마도 postgresql 업그레이드였을 것입니다). 그런데 포럼은 2015년에 만든 것입니다. 다른 포럼에서는 생성 날짜를 기준으로 확인합니다.

즉, 테이블이 약간 어지러워진 것 같지만, 실제로는 그렇지 않아 보입니다.

SQL에는 능숙하지 않으므로 무리하게 시도하고 싶지 않습니다. 따라서 안전하게 업그레이드할 수 있도록 따를 절차에 대한 지침을 요청드립니다 :raised_hands:

/admin/logs/screened_ip_addresses 페이지를 정리하거나, 이 테이블 또는 다른 테이블의 항목을 삭제(purge)해야 할까요?

인덱스가 손상된 것 같습니다. 손상된 인덱스에 대한 다른 주제들을 살펴볼 수 있습니다.

먼저 인덱스를 재구성해 보시기 바랍니다. 또한 인덱스가 정상적으로 작동한다고 가정하기 때문에, 많은 검색에서 중복 항목을 찾지 못할 수 있다는 점도 유의하시기 바랍니다.

감사합니다. 확실하게 하기 위해, 다음과 같은 순서로 진행하면 될까요?

cd /var/discourse
./launcher enter app
su postgres
psql
\connect discourse
REINDEX SCHEMA CONCURRENTLY public;

만약 인덱스가 시스템상 정상으로 보여서 문제가 해결되지 않는다면, 이 테이블의 크기를 고려할 때 이 해결책이 실행 가능한가요?

TRUNCATE public.screened_ip_addresses

재인덱싱을 실행한 후, 해당 IP 주소를 수동으로 다시 추가하는 방식 말입니다.

지금 핸드폰을 쓰고 있어서 문서를 직접 확인해 봐야 할 것 같지만, public.screened_ip_addresses만 다시 생성하는 걸 추천해. 그렇게 하면 충돌 관련 에러가 나올 거야.

감사합니다. 명령어를 실행했는데 에러가 표시되지 않았습니다. 몇 시간 후 다시 재구성을 시도해 보고 결과를 확인해 보겠습니다.

수정: 재인덱싱이 아무 효과도 없었습니다.

1개의 마이그레이션이 실패했습니다!

Failed to migrate default
#<ActiveRecord::RecordNotUnique: PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint "index_screened_ip_addresses_on_ip_address"
DETAIL:  Key (ip_address)=(10.0.0.0/8) already exists.
>

앱을 재시작하고 /admin/logs/screened_ip_addresses의 모든 IP를 정리한 후 성공적으로 업그레이드했습니다.

IP들이 다시 복원되어 수동으로 추가할 필요가 없었습니다.

조금 지나치게 조심한 것 같지만, 제 포럼이 아니라서 문제를 일으키고 싶지 않았습니다. 도움을 주신 @pfaffman님께 감사합니다.

다음에 다시 발생하면 어떻게 해야 할지 알게 되었으니 그때 확인해 보겠습니다.

이 주제에 대한 마지막 업데이트는 관리/업그레이드 페이지를 통해 업그레이드했을 때였습니다. 이번에는 터미널로 재구축하여 확실하게 하려고 했습니다. 마이그레이션 문제는 없었습니다. 잘 진행되었습니다.

문제가 해결되었음을 확인합니다 :raised_hands: