Rake:rebake 실행 시 PG::ConnectionBad: PQsocket 오류로 충돌

200,000개 게시글이 있는 포럼을 새 서버로 마이그레이션했습니다. 다운타임이 발생하지 않도록 라이브 사이트는 읽기 전용 모드로 전환해 두었습니다.

사용자가 설치 화면이나 복원 과정에서 발생할 수 있는 문제를 보지 않도록 백업을 다른 서브도메인(dev.example.com 같은)에 복원했습니다.

복원이 완료되자마자 DNS를 새 서버로 가리키고 app.yml 파일의 도메인을 정상적인 forum.example.com으로 변경했습니다.

그런데 기존 게시글의 이모티콘(스마일리)이 전부 dev.example.com 서버를 가리키고 있어서 rake:rebake를 실행했습니다.

약 1,000~2,000개 게시글을 처리한 후 데이터베이스 연결 관련 오류로 크래시가 발생합니다.

아래는 일부 로그 내용입니다:

/usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.4/lib/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
/usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.4/lib/bundler/cli.rb:34:in `dispatch'
/usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.4/lib/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
/usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.4/lib/bundler/cli.rb:28:in `start'
/usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.4/exe/bundle:45:in `block in <top (required)>'
/usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.4/lib/bundler/friendly_errors.rb:117:in `with_friendly_errors'
/usr/local/lib/ruby/gems/3.2.0/gems/bundler-2.4.4/exe/bundle:33:in `<top (required)>'
/usr/local/bin/bundle:25:in `load'
/usr/local/bin/bundle:25:in `<main>'
     1999 / 200968 (  1.0%)
Failed to rebake (topic_id: 78730, post_id: 210607)
PG::ConnectionBad: PQsocket() can't get socket descriptor
/var/www/discourse/lib/tasks/posts.rake:108:in `rebake_posts_all_sites'
/var/www/discourse/lib/tasks/posts.rake:7:in `block in <main>'
/usr/local/bin/bundle:25:in `load'
/usr/local/bin/bundle:25:in `<main>'

Caused by:
PG::ConnectionBad: PQsocket() can't get socket descriptor

현재는 dev.example.com 도메인을 forum.example.com 도메인으로 리다이렉트하여 이미지 로딩은 해결했지만, 이는 임시 조치일 뿐입니다.

이 오류를 해결하여 모든 게시글을 리베이킹(rebake)할 수 있는 방법이 있을까요? 데이터베이스에 부하가 너무 많이 걸리는 것일까요?

먼저 도메인 이름 변경 또는 Discourse 이름 변경 문서를 참고해 주세요 (다른 해결 방법으로는 백업 후 새 호스트명으로 복원하는 것이 있습니다).

데이터베이스 연결이 소진되고 있는 것으로 추정되지만, 제가 예상하는 빠른 오류 유형과는 다릅니다.

이것은 표준 설치 환경인가요, 아니면 다른 PG 서버를 사용하고 계신가요?

링크들 감사합니다. DigitalOcean 드롭렛(“Premium AMD”, 4GB RAM, 2 vCPUs)에 표준 Docker로 설치되어 있습니다.

말씀해 주신 링크의 지침을 따랐습니다. 설정에서 잘못된 URL을 몇 개 발견해서, 안전을 위해 그것들을 수정한 후 포럼을 다시 빌드했습니다.

그런 다음 다음과 같은 명령을 실행했습니다:

discourse remap dev.example.com forum.example.com

해당 명령이 다음과 같은 오류로 충돌했습니다:

Error: ERROR:  duplicate key value violates unique constraint "unique_post_links"
DETAIL:  Key (topic_id, post_id, url)=(78821, 207117, https://forum.example.com/t/the-slug/78946/7) already exists.

그래서 해당 URL(https://forum.example.com/t/the-slug/78946/7)을 링크한 게시물을 임시로 삭제하고, 명령을 다시 실행했는데 이번에는 충돌 없이 작동했습니다.

그 후 다시 rake posts:rebake를 실행했습니다.

아래와 같이 몇몇 게시물에서는 실패했지만, 계속 진행되었습니다(해당 게시물들의 HTML은 수동으로 다시 빌드했습니다):

Rebaking post markdown for 'default'
     2273 / 200996 (  1.1%)
Failed to rebake (topic_id: 66586, post_id: 210353)
JavaScript was terminated (either by timeout or explicitly)

마지막으로 11,000개 게시물에 도달하기 직전에 다음과 같은 오류로 충돌했습니다:

/usr/local/bin/bundle:25:in `<main>'
    10996 / 200996 (  5.5%)
Failed to rebake (topic_id: 76678, post_id: 200988)
PG::ConnectionBad: PQsocket() can't get socket descriptor
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/pg.rb:69:in `exec_params'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/pg.rb:69:in `exec_params'
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.4.1/lib/active_record/connection_adapters/postgresql_adapter.rb:768:in `block (2 levels) in exec_no_cache'

Uptime Robot으로부터 사이트가 다운되었다는 알림을 받았기 때문에, 서버 전체가 오프라인이 된 것 같았습니다.

이 명령을 실행하기에 서버 사양이 부족하다고 생각하시나요? :thinking:

정상적으로 RAM 사용률이 80%를 넘고, 명령 실행 중에는 100%까지 치솟습니다. 아마 메모리가 부족해서 그런 것 같습니다.

로컬 디스크가 있다면 스왑을 추가할 수 있으며, 이를 통해 메모리 고갈을 방지할 수 있습니다(여기서 문제가 발생하는 원인이 메모리 고갈인지 여부에 관계없이). free 명령어의 출력은 어떻게 되나요? dmesg 출력에서 oom 또는 memory 항목을 확인할 수 있나요?

현재 다음과 같이 표시됩니다:

               total        used        free      shared  buff/cache   available
Mem:           3.8Gi       2.1Gi       160Mi       1.0Gi       1.6Gi       488Mi
Swap:             0B          0B          0B

oom은 보이지 않지만, 메모리 예약 및 해제와 관련된 몇몇 위치에 memory라는 단어가 나타납니다.

서버는 4GB RAM으로 생성되었기 때문에 Discourse는 스왑 파일을 자동으로 생성하지 않았습니다. 추가하는 것이 가치가 있다고 보시나요?

디스크 공간이 충분하다면, 2GB 정도의 스왑을 추가하는 것이 분명히 가치가 있습니다.

또 다른 방법은 대규모 작업이 실행되는 동안 사용량을 모니터링하는 것입니다. vmstat 5 5를 사용하고, 필요에 따라 파일로 로그를 기록하는 것이 좋습니다. si 또는 so 컬럼에 큰 값이 나타나지 않기를 바라야 하며, swpd 컬럼이 스왑 용량에 근접하지 않는지 확인해야 합니다.

아래 게시물을 참고해 보세요:

(데이터베이스 시스템이 어떤 자원을 소진하고 있는 것 같지만, 그 부분에 대해서는 제가 아는 바가 없습니다.)

감사합니다. 오늘 나중에 시도해 볼게요. 지금은 50GB가 비어 있어요.

2GB 스왑파일을 추가했는데, 이로 인해 문제가 해결된 것 같습니다. 리베이킹은 아직 20%밖에 진행되지 않았지만, 지금까지 오류는 전혀 없었고 RAM 사용량은 100%를 약간 밑도는 수준입니다.

도와주셔서 두 분 모두 감사합니다.

좋습니다! 참고로

  • vmstat 또는 free (또는 top)에서 swap이 거의 소진되고 있는 것으로 보인다면, 작업이 실행 중이어도 swap을 더 추가할 수 있습니다.
  • 신중하게 진행하면, 더 큰 RAM을 가진 인스턴스로 일시적으로 업그레이드할 수 있습니다. 이는 일부 비용이 들지만, 몇 시간 동안만 유지하면 됩니다. 더 큰 디스크를 가진 인스턴스로 이동하지 않는 것이 중요합니다. 이는 되돌릴 수 없기 때문입니다. (더 많은 RAM은 작업이 풀 스피드로 실행되도록 허용하는 반면, modest한 RAM과 많은 swap은 성능 저하를 초래할 수 있으며, 작업 완료에 더 오래 걸릴 수 있습니다.)

그렇게 하려고 했지만, 서버를 꺼야 했기 때문에 고민이 되었습니다. 서버를 이전할 때 이미 사용자들이 번거로운 “읽기 전용” 기간과 다운타임을 겪었거든요. :sweat:

어젯밤에는 잠을 자야 해서 작업을 끝내지 못했지만, 지금은 다시 실행 중입니다. 지금까지 30% 진행되었고, 오류는 없습니다.

vmstat나 유사한 도구로 상황을 주시해 두는 것이 좋습니다. 이 작업은 실행 시간이 매우 길기 때문에 다시 시작해야 하는 상황이 발생하면 안 되니까요. 안전을 위해 스왑 공간을 2GB 정도 더 추가하는 것이 나을 것 같습니다.

감사합니다. 가끔 vmstat로 확인했습니다. tmux 세션에서 실행해서 잠시 노트북을 덮고 떠날 수 있었습니다. 명령이 실행되는 데 약 8~9시간이 걸렸지만, 모든 작업이 오류 없이 완료되었습니다.