중복 헤더가 있으면 이메일 알림이 실패합니다

버그 설명은 Email notifications fail if duplicate headers exist - #14 by simonk 에서 확인하실 수 있습니다.
위 내용은 @pfaffman 이 추가한 것입니다.

원본 텍스트는 다음과 같습니다.

안녕하세요,

저는 몇 년째 비교적 작은 규모의 Discourse 인스턴스를 운영 중입니다. 트래픽이 꽤 낮은 편이라, 몇 달 전부터 이메일(알림, 다이제스트) 전송이 명백히 실패하기 시작했다는 사실을 알아차리는 데 시간이 걸렸습니다. 추적을 해본 결과, 2022-10-22경 2.8.0.beta7로 업그레이드한 시점으로 가리키며, 그 이전에는 2.8.0.beta4를 사용していました. 적어도 해당 인스턴스에서 게시글이나 메시지에 대한 이메일을 받은 적이 없습니다.

이메일들이 Sidekiq에 쌓여 있으며, 제가 관련 지을 수 없고 검색해도 딱 맞는 내용을 찾을 수 없는 메시지가 포함되어 있습니다. undefined method 메시지에 대한 보고가 있었지만, 제 상황과는 어떤 조건도 일치하지 않습니다. (TLS 문제가 아니며, 메일 서버 타임아웃도 아니고, 이벤트 플러그인 문제도 아니며, 보안 미디어 수정 사항은 이미 적용되어 있어야 합니다 — 게다가 정확한 에러 메시지도 달랐습니다.)

Sidekiq 에러:

Wrapped NoMethodError: undefined method `value' for #<Array:0x00007f7fd5277d68> Did you mean? values_at

#<Array: 이후 부분은 각 보류 중인 이메일마다 다릅니다. 지난밤에 새 VM으로 Discourse를 재설치하고 최신 백업에서 복원했지만, 이메일 문제는 데이터와 함께 복원된 것 같습니다 :thinking:

에러율이 10월 말부터 상승하기 시작했으므로, 이 문제가 2.8.0.beta7에서 도입된 것이라고 꽤 확신합니다:

문제 해결을 위한 도움이나 추가 디버깅에 대한 힌트는 매우 감사하겠습니다.

1개의 좋아요

설치된 플러그인이 있나요? 표준이 아닌 플러그인이 있다면 제거해야 합니다.

이미 시도해 보았는데, 문제가 여전히 지속됩니다 :frowning:

## Plugins go here
## see https://meta.discourse.org/t/19157 for details
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

## Any custom commands to run after building

수정: 참고로(FTR):

사용하려는 플러그인들:

root@discourse:/var/discourse# grep -B5 "git clone" containers/app.yml-with-plugins 
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/discourse/discourse-spoiler-alert.git
          - git clone https://github.com/discourse/discourse-prometheus.git
          - git clone https://github.com/discourse/discourse-solved.git
          - git clone https://github.com/discourse/discourse-chat-integration.git
          - git clone https://github.com/discourse/discourse-voting.git
          - git clone https://github.com/discourse/discourse-checklist.git
          - git clone https://github.com/discourse/discourse-whos-online.git
          - git clone https://github.com/discourse/discourse-calendar.git
          - git clone https://github.com/discourse/discourse-affiliate.git
          - git clone https://github.com/discourse/discourse-reactions.git
          - git clone https://github.com/discourse/discourse-surveys.git

기존 설치에는 다음이 포함되어 있습니다:

root@discourse-old:/var/discourse# grep -B5 "git clone" containers/app.yml
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/discourse/discourse-spoiler-alert.git
          - git clone https://github.com/discourse/discourse-prometheus.git
          - git clone https://github.com/discourse/discourse-solved.git
          - git clone https://github.com/discourse/discourse-chat-integration.git
          - git clone https://github.com/discourse/discourse-voting.git
          - git clone https://github.com/discourse/discourse-checklist.git
          - git clone https://github.com/davidtaylorhq/discourse-whos-online.git

이것이 표준 설치인가요? 데이터 컨테이너를 별도로 사용 중이라면 재구축이 되었나요? PostgreSQL이 최신 버전인가요? (코드에서 이 부분을 확인하는 로직이 아직 있는 것 같습니다.)

네, 표준 설치입니다. AFAICS(제가 보는 바로는) 현재 실행 중인 컨테이너는 하나뿐입니다. (새 VM을 생성하고, DNS를 재지정하고, apt update, apt dist-upgrade, 재부팅, git pull, ./discourse-setup, 웹 기반 설정, 백업 업로드, 백업 복원, 메일 재활성화, 그리고 다시 에러를 확인했습니다.) 참고로 기존 설치에서는 백업 링크를 메일로 전송할 수 있었고, 새 설치에서도 테스트 메일 전송은 여전히 정상 작동합니다 — 게시글 관련 메일만 실패하는 것으로 보입니다.

더 정확히 말하면, Discourse를 설치한 최소한의 Debian 10 환경입니다:

root@discourse:~# history
    1  apt update
    2  apt dist-upgrade
    3  reboot ; exit
    4  git clone https://github.com/discourse/discourse_docker.git /var/discourse
    5  apt install git rsync
    6  git clone https://github.com/discourse/discourse_docker.git /var/discourse
    7  cd /var/discourse
       [attach /dev/vdb, fdisk, mkfs.ext4, mount as /var/lib/docker]
   18  apt-get install git apt-transport-https ca-certificates curl gnupg2 software-properties-common -y
   19  df -h
   20  curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | apt-key add -
   21  add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) stable"
   22  apt-get update -y
   23  apt-get install docker-ce -y
   24  ./discourse-setup

그리고 컨테이너 하나:

root@discourse:~# docker ps
CONTAINER ID   IMAGE                 COMMAND        CREATED        STATUS        PORTS                                                                      NAMES
ac408a70305d   local_discourse/app   "/sbin/boot"   12 hours ago   Up 12 hours   0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   app

수정:

root@discourse:~# apt update
Hit:1 https://download.docker.com/linux/debian buster InRelease
Hit:2 http://deb.debian.org/debian buster InRelease
Get:3 http://deb.debian.org/debian buster-updates InRelease [51,9 kB]
Get:4 http://security.debian.org/debian-security buster/updates InRelease [65,4 kB]
Fetched 117 kB in 2s (60,5 kB/s)    
Reading package lists... Done
Building dependency tree       
Reading state information... Done
All packages are up to date.

그러나 최초 복원 작업은 실제로 실패했습니다. database_restorer.rb가 restore_dump 단계에서 post_id 3841의 중복 링크로 인해 실패했습니다. 결국 2017년 게시글에 있는 해당 링크들을 이전 설치 환경에서 캡처한 스크린샷으로 대체하고 다시 백업을 수행한 후, 비로소 새 설치 환경에 백업을 복원할 수 있었습니다. Postgres에 대해 언급하셨듯이, 이 문제는 CREATE INDEX 실행 중 ERROR: could not create unique index "unique_post_links" 오류와 함께 발생했습니다. 추가 정보: EXCEPTION: psql failed: DETAIL: Key (topic_id, post_id, url)=(1300, 3841, [redacted]) is duplicated.

이것이 직접적인 관련은 없다고 생각하지만, 언급할 가치가 있어 공유합니다.

자, 아무도 쉽게 해결하는 방법을 모른다면, 이 문제를 제대로 디버깅해 보겠습니다. 하지만 저는 여러분의 도움이 필요합니다. Discourse는 제가 익숙하지 않은 여러 기술을 사용하는 상당히 복잡한 애플리케이션이니까요. 그래서: 메일은 어떻게 "Sidekiq로 전송"되는 건가요?

Discourse의 어떤 구성 요소가 Sidekiq에 “value” 메서드를 제공하고 있나요?

업그레이드 이후 이메일 알림이 작동하지 않게 되면서, Discourse는 안타깝게도 이제 거의 쓸모가 없습니다. 즉각적인 주의가 필요한 대신, 이제 주제에 대한 반응은 며칠씩 걸리거나, 아예 아무도 반응하지 않습니다. 2022년에 사람들이 능동적으로 사이트를 확인하기 때문이죠. 알림이 없으면 ⇒ 아무 일도 일어나지 않은 것 같으니 ⇒ 사이트를 확인할 필요도 없다 :frowning:

1개의 좋아요

이것은 손상된 인덱스처럼 보입니다. Postgres 13에서 이런 현상을 본 적이 없는 것 같습니다. 아마도 이전 사이트에서 이를 수정한 뒤, 백업과 복원을 통해 새 사이트로 업그레이드한 것이 아닌가 싶습니다.

문제가 알림을 전송하는 코드 내에 있는 것처럼 보이지만, 결국 sidekiq와 관련된 문제인 것 같습니다.

말씀드린 것처럼, 오래된 사이트는 여전히 실행 중이며, 백업을 받아 신선한 설치 환경에 적용했지만 복구가 실패했습니다. 복구까지 성공할 때까지 문제의 원인으로 지목된 포스트를 수정했습니다. 그런데 신선한 설치 환경에서도 Sidekiq 문제가 다시 발생하거나 여전히 지속되고 있습니다.

오래된 사이트도 Postgres 13을 실행 중입니다(하지만 수년 전으로 거슬러 올라가므로, 처음부터 이 버전으로 시작하지는 않았을 가능성이 큽니다 :slightly_smiling_face:)

root@discourse-old:/var/discourse# ./launcher enter app
x86_64 arch detected.
root@discourse-app:/var/www/discourse# psql --version
psql (PostgreSQL) 13.5 (Debian 13.5-1.pgdg110+1)

따라서 이 포스트의 마무리 코멘트에 따르면, Discourse 데이터베이스가 손상될 수 있으며 — 수정도 가능합니다.

새로운 사용자로 시도해 보니, 가입 이메일은 정상적으로 수신됩니다. 하지만 자신의 포스트에 대한 답글 알림은 오지 않으며, Sidekiq에서 오류가 발생합니다.

따라서 제 생각에는 Discourse가 Sidekiq에 알림을 보내라고 지시할 때(가입 메일과 달리) 잘못된 정보를 전달하고 있는 것 같습니다. 더 깊이 디버깅하려면 어떻게 해야 하나요?

좋습니다. 인덱스가 복구되었다면, 이는 value 메서드가 있는 모델 대신 배열이 무엇인가에 전달되고 있음을 시사합니다. 문제는 본질적으로 Sidekiq에 있는 것이 아니라, Sidekiq가 호출하도록 하는 함수에 있습니다.

즉, 단일 항목이 아닌 배열을 반환하는 어떤 것이 호출되고 있는 것 같습니다. 다만 정확히 무엇인지는 추측할 수 없습니다. /var/discourse/shared/standalone/logs/rails/production.log (또는 제 기억이나 손가락이 틀렸다면 매우 유사한 경로)를 확인해 보셔야 할 것 같습니다. 그런 다음 해당 로그에서 그 오류를 찾아보거나(또는 오류가 다시 발생하도록 만들어 파일 끝에 위치하도록 하세요). 거기서 무엇이 실패하고 있는지에 대한 더 많은 정보를 얻을 수 있을 것입니다.

감사합니다. 하지만 실제로는 많은 정보를 주지 못하고 있어요:

Started POST "/sidekiq/retries" for 185.39.142.187 at 2022-04-11 16:31:35 +0000
start
Started GET "/sidekiq/retries" for 185.39.142.187 at 2022-04-11 16:31:35 +0000
  Rendered email/notification.html.erb (Duration: 42.8ms | Allocations: 4323)
  Rendered layouts/email_template.html.erb (Duration: 0.3ms | Allocations: 29)
Job exception: undefined method `value' for #<Array:0x00007ff393af6c78>
Did you mean?  values_at

fail

shared/standalone/log/rails/production_errors.log 파일은 비어 있습니다.

오류가 관리자 → 로그 → 오류 로그에 표시됩니까? 그렇다면 전체 스택 트레이스를 확인할 수 있으며, 이것이 도움이 될 수 있습니다.

1개의 좋아요

오, 좋다 — 맞습니다:

즉, 제가 이해한 대로,

 433    def header_value(name)
 434      header = @message.header[name]
 435      return nil unless header
*436      header.value
 437    end

이 부분은 – Discourse 코드에서 – Sidekiq가 중단되는 곳인가요?

이것은 … 에서 호출됩니다

 228      MessageBuilder.custom_headers(SiteSetting.email_custom_headers).each do |key, _|
*229        value = header_value(key)
 230
 231        # Remove Auto-Submitted header for group private message emails, it does
 232        # not make sense there and may hurt deliverability.

그러면 사용자 정의 헤더일 수 있나요?

실제로 거기에 항목이 있습니다:

Screenshot 2022-04-12 at 11-27-31 Administration - Freifunk Kreis GT

기본값으로 리셋했고 … 실제로 독자들의 확인을 통해 이메일 알림이 다시 전송되고 있는 것 같습니다.

감사합니다!

다만 한 가지 질문이 남아 있습니다: 왜 »Auto-Submitted: auto-generated|Precedence: bulk«가 이 실패를 일으키나요? 사용자 정의 헤더는 »|«로 구분되어야 한다고 되어 있습니다.

1개의 좋아요

(참고: Ruby 프로그래머가 아닙니다)

Discourse가 사용 중인 메일 라이브러리에서 특히 불쾌한 동작인 것 같습니다. 다음은 header_value 함수입니다:

제가 아는 한, @message.header[name]는 이 메서드를 호출하고 있습니다:

https://www.rubydoc.info/github/mikel/mail/Mail%2FHeader:[]

RFC에 따르면 많은 필드가 여러 번 나타날 수 있으며, 헤더가 하나만 있거나 일치하는 헤더가 여러 개인 경우, 헤더 상단에서 하단으로 순서대로 배열된 값의 배열을 반환합니다.

Discourse는 자동으로 Precedence 헤더를 설정하므로, email_custom_headers 설정을 통해 추가하는 헤더와 합쳐져 이제 두 개의 Precedence 헤더가 있게 되며, @message.header["Precedence"]는 문자열 대신 배열을 반환합니다.

email_custom_headers에 메시지 객체에 이미 존재하는 헤더가 포함된 경우 이 버그가 발생한다고 생각합니다.

5개의 좋아요

제 생각에는 지금 일어나고 있는 상황이 바로 이런 것 같습니다(제가 어딘가가 단일 항목이 아니라 배열이라고 제안했지만, 그게 어떻게 사실일 수 있는지 상상할 수 없었습니다). 그리고 이는 버그입니다.

이 주제의 제목과 카테고리를 변경하겠습니다.

3개의 좋아요

오늘 이 문제에 대한 수정 사항을 병합했습니다. 중복 헤더를 감지하면 사이트 설정의 사용자 정의 헤더 대신 Discourse 코어에 정의된 헤더를 사용하도록 했습니다:

4개의 좋아요

이 주제는 2일 후 자동으로 닫혔습니다. 더 이상 답변을 게시할 수 없습니다.