Discourse 이메일 메시지가 잘못 스레드 처리됩니다

아래 글의 어조가 다소 강할 수 있어 미리 양해의 말씀을 드립니다. 저도 조금 지쳐서 그런 말투가 나온 것입니다.

Michael Brown 님이 Discourse Meta에서 2022년 7월 27일 14:06에 작성:

죄송합니다. 이제야 내용을 파악하고 있는데, 몇 가지 생각이 있어서 적어봅니다. 일부는 이미 다뤄진 내용이기도 하고요…

여기서 어려운 점은 Discourse에서 외부로 발송되는 것이 수신된 메시지와는 다른 메시지라는 것입니다. 여기에는 다른 메타데이터(이 목적상 To/From/Reply-to/Unsubscribe 등)와 다른 본문(사용자별로 커스터마이징된 것으로 보입니다? 메일링 리스트 모드에서는 이것이 발생하지 않는 건가요?)이 포함됩니다.

정확히 메시지는 무엇일까요? 5322 규격을 절대 기준으로 삼아보자면:

메시지는 헤더 필드로 구성되며, 선택적으로 메시지 본문을 포함할 수 있습니다.

“Message-ID:” 필드는 특정 메시지의 특정 버전을 가리키는 고유한 메시지 식별자를 제공합니다.
[강조는 필자]

이 "특정 버전"이라는 표현 때문에, 다른 Message-ID를 사용하여 수신된 메시지를 재전송하는 것은 적절하지 않다고 생각합니다. 다만, Discourse를 "포럼 소프트웨어"가 아닌 "메일링 리스트 소프트웨어"로 보는 관점으로 전환하면 그렇게 하는 것이 어느 정도 합리적이므로, 왜 그렇게 생각하시는지 이해는 합니다.

글쎄요, 안타깝게도 이것은 지나치게 문자 그대로의 해석, 혹은 존재하지 않는 문맥을 읽는 것에서 비롯된 것 같습니다.

모든 이메일 메시지는 메일 시스템이 이를 전달하는 과정에서 헤더가 수정됩니다. 적어도 Received: 헤더는 각 단계에서 추가되고, 여러 시스템은 스팸 필터링 결과와 서명을 나타내는 다양한 헤더를 추가합니다. 이러한 것들은 message-id 변경을 유발하지 않으며, 실제로 그렇게 하면 message-id가 완전히 기능을 상실하게 됩니다.

내용에 관해서는 이미 언급했듯이, 거의 모든 메일링 리스트는 본문 텍스트에 내용을 추가합니다. 보통 리스트 관리 페이지 링크나 구독 해지 링크가 포함된 푸터입니다. 이것들도 message-id 변경을 유발하지 않습니다.

사실, 메시지를 전달하는 거의 모든 것이 message-id를 변경하지 않습니다. 그렇게 하면 최종 사용자 클라이언트에서의 스레딩(threading)과 중복 제거가 깨지기 때문입니다.

제가 바로 인용하려던 내용을 인용하고 계시네요. :slight_smile:

5322에는 또한 다음과 같이 명시되어 있습니다:

메시지가 "변경"되는 경우가 많지만, 그러한 변경은 해당 메시지의 새로운 인스턴스로 간주되지 않으므로, 메시지는 새로운 메시지 식별자를 받지 않습니다. 예를 들어, 메시지가 전송 시스템에 도입될 때 추적 필드(3.6.7절 참조)와 재전송 필드(3.6.6절 참조)와 같은 추가 헤더 필드가 앞에 붙는 경우가 많습니다. 이러한 헤더 필드의 추가는 메시지의 동일성을 변경하지 않으므로 원래의 “Message-ID:” 필드가 유지됩니다. 모든 경우에서, 메시지의 발신자가 전달하고자 하는 의미(즉, 이것이 동일한 메시지인지 다른 메시지인지)가 “Message-ID:” 필드가 변경되는지 여부를 결정하며, 메시지에 나타나는(또는 나타나지 않는) 특정 구문적 차이가 아닙니다.

결국 메시지의 발신자가 Discourse를 통해 메시지를 발송할 때 변경되는지 여부에 달려있다고 생각합니다.

여기서 오해하신 것 같습니다. 강조하겠습니다:

모든 경우에서, 메시지의 발신자가 전달하고자 하는 의미(즉, 이것이 동일한 메시지인지 다른 메시지인지)가 "Message-ID:"
필드가 변경되는지 여부를 결정합니다

_발신자_는 Discourse와 같은 MTA(메일 전송 에이전트)가 아니라 작성자입니다.

제가 이메일로 Discourse에 게시글을 올리면, 제 메시지가 의미적으로 그대로 독자에게 전달되기를 바랍니다. 구독 해지 링크와 같은 부가적인 내용은 제가 메시지에서 말한 것의 의미론적 내용을 변경하지 않습니다.

여전히 _동일한 메시지_입니다.

아마도 Resent-Message-ID와 같은 필드를 사용해야 하지 않을까요?

절대 아닙니다. 그것들은 _사용자_가 메시지를 재제출할 때 사용하는 것입니다. 예를 들어, 제가 메시지를 다른 사람에게 전달(forward)할 때 말입니다. 메일 릴레이(리스트와 Discourse와 같은)를 위한 것이 아닙니다.

처음부터 822 규격까지 항상 있었습니다. 다만 나중에 말씀하신 대로 업데이트는 되었습니다.

아프네요. 그때는 USENET 전용인 줄 알았습니다. 제가 잘못 알고 있었습니다.

5322는 또한 Discourse와 Github가 사용하는 방식에 대해 직접적으로 언급합니다:

“In-Reply-To:” 필드는 새로운 메시지가 답장인 메시지(또는 메시지들)를 식별하는 데 사용될 수 있으며, “References:” 필드는 대화의 "스레드"를 식별하는 데 사용될 수 있습니다.

아마도 약간 부적절하게, “Thread Identifier” 헤더의 부재로 인해 그렇습니다. 하지만 이 해석이 RFC 작성자의 의도와 일치하지 않을 수 있습니다… "References"는 있지만 "In-Reply-To"는 없는 메시지에 대해서는 다루지 않고 있습니다.

저에게는 두 필드가 동일한 정보를 다루는 것으로 보입니다:

  • References는 OP(첫 번째 게시물)까지의 선형(보통) 스레드를 보여줍니다.
  • In-Reply-To는 부모 메시지를 보여주고, OP까지의 이전 메시지와 함께 동일한 스레드를 암시합니다.

여기서 까다로운 부분은 하나의 이메일을 발송하는 것이 아니라, 수신자별로 하나씩 N개를 발송한다는 것입니다. 그렇게 해야 개별 메타데이터(Unsubscribe 등)가 정확해지니까요.

이것은 까다로운 문제가 아닙니다. 메시지의 의미는 동일하며, 커스터마이징은 사소하고 의미론적으로 무관합니다. 그것들은 새롭거나 서로 다른 message-id를 정당화하지 않습니다.

그리고 네, 테스트 중에 스팸 판정이 Message-ID와 연결될 것이라는 강력한 징후를 보았습니다. 나중에 다시 발견되면(같은 사용자 또는 다른 사용자) 스팸으로 분류될 가능성이 훨씬 더 높아졌거든요.

이러한 사례를 몇 가지 보여줄 수 있을까요? message-id는 최종 사용자 측에서 중복 제거를 허용하기 때문입니다. 또한 많은 “안티스팸” 조치가 잘못된 엉터리라는 점을 명심하십시오. 터무니없는 이유로 잠재적 스팸으로 거부당한 것들이 얼마나 많은지… 깨진 스팸 오분류 문제를 우회하기 위해 이메일을 깨뜨리는 것은 현명한 선택이 아닙니다.

오늘날까지 저는 GMail 주소를 가진 사람들에게 CC를 보내지 않습니다. GMail의 스팸 필터링은 저를 알고 있고 메시지를 그냥 버리거든요. 리스트에만 보내면 그들은 메시지를 받습니다. GMail 주소를 CC로 추가하면 (a) 스팸으로 표시되고 (b) 메일링 리스트 메시지도 또 스팸으로 표시됩니다(동일한 message-id!). 최종 사용자는 제 메시지를 볼 수 없습니다. 이 논리는 완전히 터무니없고 수정 불가능합니다.

[quote=“Cameron Simpson, post:22, topic:233499,
username:cameron-simpson”]
수신자별 구독 해지 링크를 추가하고 원래의 message-id를 유지하는 것에 대해 저는 완전히 괜찮습니다. 각 메시지 사본에 개별 message-id를 부여하여 스레딩을 잃는 것보다 이득이 훨씬 훨씬 큽니다.
[/quote]

솔직히 말하면, 여기에서의 이득은 특정 메일 클라이언트에서 이메일을 올바르게 스레딩하는 것에 전적으로 관련되어 있으며, 이는 전달률(deliverability)을 희생하는 것입니다.

아쉬운 한숨. 모든 메일 클라이언트에 해당합니다. 그리고 Python 커뮤니티 쪽에서 Discourse로 오지 않겠다고 말하는 주요 이유 중 하나는 이메일 쪽 스레딩이 깨져 있기 때문입니다. 많은 사람들이 포럼을 사용하지 않습니다. 각 포럼을 _방문_해야 하니까요. 이메일은 사용자에게 옵니다. 그들은 자기들이 선호하는 리더와 자기들이 선호하는 에디터를 사용할 수 있고, 스레딩은 사람들이 토론의 흐름을 명확하게 볼 수 있게 해줍니다. 제대로 작동할 때 말입니다.

현재의 topic/#{topic_id}/#{post_id}.s#{sender_user_id}r#{receiver_user_id}는 적어도 사용자의 메일함에서 일관성을 유지합니다. 그 전제에 따르면

제 가장 큰 우려는 전달률입니다 - 주요 제공업체로부터 가시성이 전혀 없는 상황에서 이메일을 전달하는 것은 이미 어려운 일입니다.

증거를 보고 싶습니다. 메일링 리스트는 전 세계적으로 이를 올바르게 처리합니다. Discourse는 분명하고 객관적으로 이를 깨뜨립니다. 저는 이를 수정하려고 노력하고 있습니다.

여기서 두 가지 근본적인 문제를 다시 강조하겠습니다:

  • OP의 In-Reply-To와 References는 가상의 “OP 이전” “토픽” message-id를 인용하므로, 이메일 사용자는 시작 메시지(OP)가 있는 스레드를 갖지 못합니다 - OP를 포함하여 _모든 것_이 후속 답변처럼 보입니다.
  • Discourse를 통해 수신된 이메일과 CC 등을 통해 직접 수신된 이메일은 의미론적으로 동일한 메시지임에도 불구하고 다른 message-id를 가지고 있습니다. 이는 스레딩과 중복 제거를 깨뜨립니다.

하지만 메일링 리스트 모드에서 Discourse가 메일링 리스트 소프트웨어처럼 더 많이 행동하도록 만드는 것에 대한 강력한 논리는 봅니다. @martin 메일링 리스트 모드에서는 메시지 본문을 커스터마이징하지 않는다고 믿습니다. 메일링 리스트 모드에서 Message-ID를 보존하고 재사용하는 것에 대해 더 엄격한 접근을 취하는 것이 합리적이라고 생각하시나요?

Python 커뮤니티 쪽에는 "메일링 리스트 모드"가 너무 많은 정보 홍수(firehose)라고 느끼는 사람들이 있습니다. 그들은 모든 것이 아니라 대상화된 토픽에 대한 이메일만 받고 싶어합니다. message-id 처리는 이메일 쪽의 모든 경우에 동일해야 합니다.

저는 discuss.python.org에서 "메일링 리스트 모드"를 사용하는 사람입니다. 하지만 여기(discourse.org)에서는 켰다가 _즉시 다시 껐습니다. 여기서는 대상화된 모드가 필요합니다.

4개의 좋아요

Michael Brown이 Discourse Meta에서 2022년 7월 27일 22:37에 작성:

아! 이미 topic/#{topic_id}/#{post_id}.s#{sender_user_id}r#{receiver_user_id} 형식을 사용하고 있다고 생각했네요.

{receiver_user_id}를 사용하면 동일한 원본 게시글에 대해 각 수신자별로 고유한 메시지 ID가 생성됩니다. 이는 수신자가 Discourse 외부에서 소통하거나 Discourse를 경유하지 않고 사본을 받을 경우 문제가 됩니다.

이메일의 고유성 및 전달률에 대한 우려와 메일링 리스트 모드에 대한 우려 사이의 균형을 맞추기 위해, 메일링 리스트 모드가 비활성화되어 있을 때는 (2)번, 활성화되어 있을 때는 (3)번 방식을 적용하는 것이 좋다고 생각합니다.

최근 제 게시물에서 언급했듯이, 메일링 리스트 모드는 Discourse 이메일 수신 방식 중 하나의 변형만을 다룹니다. 이메일 수신자가 메일링 리스트 모드에 있든, 특정 주제/태그에 한해 이메일을 받는 모드에 있든 동일한 우려 사항이 적용됩니다.

마찬가지로 References 헤더의 경우, 주제 내 1번 게시글에는 이 헤더가 존재하지 않도록 하는 것이 좋다고 생각합니다.

In-Reply-To 헤더도 마찬가지입니다. 두 헤더 모두 존재하려면 가상의 OP(첫 번째 게시글) 메시지에 대한 참조가 필요하므로, 존재해서는 안 됩니다.

그리고 주제(즉 topic/#{topic_id})와 답변하는 대상 게시글(있다면)을 참조하도록 하는 것이 좋겠습니다.

“주제” 메시지 ID를 참조하려면 해당 메시지 ID를 가진 게시글이 이메일로 발송되었어야 합니다. 만약 그렇게 하려면, OP의 메시지 ID를 ...../1 대신 “주제” 메시지 ID로 특별 처리해야 합니다.

3개의 좋아요

여기는 "OP 이전"이어야 합니다. Cameron Simpson, 죄송합니다.

말씀하신 대로, 이것이 바로 우리를 좌절시키는 문제의 핵심입니다:

이것은 변경되어야 한다고 동의합니다. OP 메시지 ID는 (이메일을 통해 넘어오는 ID 대신에) (단순화하여) topic/1이어야 하며 다른 메시지를 참조하지 않아야 합니다.

메시지 ID는 이메일이 아니더라도 단지 Discourse 게시물로만 존재했던 경우에도 변경되지 않아야 합니다.

후속 메시지는 그 메시지를 참조할 수 있습니다.

왜 이메일이 존재해야 하나요? 의미론적으로, 게시물만 있는 상태도 기준에 부합합니다. 응답하는 대상 메시지는 존재하며, 다만 그 사람의 이메일 폴더에 없을 뿐입니다. 우리는 메시지가 중요하다는 결론에 도달했습니다. 그것은 게시물 본문이든 이메일 본문이든 마찬가지입니다. 따라서 topic/#{topic_id}/1@site는 이메일 메시지에 포함되었든 아니든, 해당 게시물을 참조하는 고유한 메시지 ID입니다.

이것은 수신함에 없는 이메일을 참조하는 이메일 답신을 받는 것과 다르지 않습니다. 여전히 답신이므로 References는 적법하고 정확합니다.

근본적으로 저는 당신과 동의합니다. 저 안의 순수주의자는 이것이 정확하기를 원합니다. 하지만 이메일을 사람들의 수신함에 전달해야 하는 실용성이 이 결과를 초래했습니다. 더 나쁜 것은, 수많은 사람들이 gmail을 사용하면서도 그 필터를 훈련시키지 않고, 올바르게 사용하지 않으며, “구독 해제” 대신 스팸으로 신고하는 경향이 있다는 점입니다[1].

동의합니다. 우리는 다음 문장을 너무 문자 그대로 읽은 것 같습니다.

메시지 식별자는 특정 메시지의 정확한 하나의 인스턴스에 해당합니다.

잠시 고민해 본 결과, 이전으로 돌아가야 한다고 생각합니다(무작위화 제거) 그리고 게시물당 단일 메시지 ID를 고정해야 하며, 그것은 다음과 같아야 합니다:

  • message_id_from_incoming_email || topic/#{topic_id}/#{post_num}@site (OP의 post_num은 1)

그리고 우리가 이메일을 발송할 때, 부모 메시지를 OP까지 모두 References에 추가하고 In-Reply-To를 적절한 안정적인 게시물 메시지 ID(토픽에 답하는 경우 OP)로 설정하는 것이 옳다고 생각합니다. 메시지는 게시물이기 때문입니다. 하지만 OP의 해당 필드는 비워두어야 합니다, 맞죠.


  1. 우리가 피드백 루프를 구현했음에도 불구하고 gmail이 이를 우리에게 보고하지는 않습니다. ↩︎

5개의 좋아요

@supermathie님과 @cameron-simpson님의 답변에 감사드립니다. 우리는 합의에 도달했다고 생각합니다. TODO 항목들을 단일 게시글로 정리하고, 이 작업에 곧 착수할 수 있기를 바랍니다:

  1. 생성된 Message-ID 형식을 항상 <discourse/post/:post_id@:hostname>으로 변경합니다. 이는 충분히 고유하며, 기본적으로 우리가 과거에 하던 방식으로 되돌아가는 것입니다. OP를 참조할 때는 이제 더 이상 주제 ID만 사용하는 대신 첫 번째 게시글 ID를 사용합니다.
  2. 게시글에 관련 IncomingEmail 레코드가 있으면 이메일을 보낼 때 항상 해당 Message-ID를 사용하고, 그렇지 않으면 위의 형식을 사용하여 생성합니다.
  3. 주제의 OP에 대한 이메일을 보낼 때 References를 사용하지 않습니다. 스레드의 첫 번째 이메일이므로 아직 Reference할 것이 없기 때문입니다.
  4. PostReply 레코드에 기반하여 올바른 In-Reply-To 및 References 헤더가 생성되도록 보장합니다.

이것은 이미 전송된 이메일의 경우 스레드 측면에서 다소 혼란스러운 상태를 남길 가능성이 있지만, 전환 기간 동안 우리가 벗어나고 있는 형식을 허용하기 위해 최선을 다할 것입니다. 인내해 주셔서 감사합니다!

3개의 좋아요

정확히 짚어보자면 … 이것은 이 요청이 발생한 서버의 hostname이 아니라, 해당 사이트의 URL을 의미하는 것인가요? hostname이라면, 서로 다른 3개의 호스트가 동일한 사이트를 서비스할 때 모든 안정성이 사라지게 됩니다.

1개의 좋아요

아니요, Email::Sender.host_for(Discourse.base_url)에서 가져오는 meta.discourse.org 같은 사이트 도메인을 말하는 거예요. 이미 사용 중인 내용입니다.

2개의 좋아요

좋은 제안입니다. 게시물 이동에 대해서는 생각하지 못했네요. :post_id는 게시물의 ID(post.id)를 의미하는 건가요, 아니면 주제 내의 번호를 의미하는 건가요?

만약 게시물 ID라면, 이 값은 절대 변하지 않으므로 <post/:post_id@:hostname> 형식을 그대로 사용하면 됩니다. 이렇게 하면 기본값에서 벗어나는 경우를 제외하고는 Message-ID를 별도로 저장할 필요가 없게 됩니다.

아니라면… 여기서는 게시물 ID를 사용하는 것이 나을 것 같습니다. 이 부분은 길이가 길 필요가 없으며, 고유하기만 하면 되므로요.

2개의 좋아요

실제 ID이며, 게시물 번호가 아닙니다.

좋은 지적입니다. <post/:post_id@:hostname>은 아마도 잘 작동할 것이며, 추가적인 컬럼 요구 사항을 피할 수 있습니다. 더 Discourse 특화적으로 만들려면 앞쪽에 discourse를 추가할 수 있습니다. 예를 들어 <discourse/post/543563@meta.discourse.org>처럼요 (호스트네임에 Discourse라는 언급이 없는 사이트가 많다는 점을 염두에 두고). 다만 이 시점에서 이는 사소한 차이입니다.

이것이 어떻게 잘못될 수 있을지 생각해 보겠습니다. 게시물을 다른 주제로 이동한 후 누군가가 이메일로 해당 게시물에 답장을 보내면, 답장이 원래 주제 대신 새 주제에 게시될 것입니다. 이것이 괜찮을 수도 있습니다. 또 다른 위험은 게시물이 비공개 카테고리로 이동되는 경우인데, 이 위험은 이미 존재하며 우리는 이를 처리하고 있다고 생각합니다.

그냥 소리 내어 생각해보는 중인데, 괜찮을 것입니다. 어쨌든 변경 사항을 테스트할 때 이 사항들을 다룰 것입니다. :+1:

2개의 좋아요

topic_id를 포함해야 한다는 논리는, 사용자가 특정 게시물을 하나의 주제에서 다른 주제로 분리할 때 스레딩을 의도적으로 끊을 수 있기 때문이다.

나는 이 문제에 대해 의견이 갈린다. 어느 쪽으로든 갈 수 있다. 하지만 그 것이 핵심 아이디어다.

1개의 좋아요

게시물 ID만 사용해야 한다는 주장의 근거는, 이것이 더 정적(static)이라는 점이며, 우리가 원하는 바로 그 특성입니다. 게시물을 다른 토픽으로 이동하더라도 Message-ID의 게시물 ID는 동일하지만 토픽은 달라지기 때문입니다.

만약 게시물을 이동한 후 새 토픽에서 이메일을 발송하게 되면, References와 In-Reply-To 헤더 체인이 달라지므로 메일 클라이언트에서 새 스레드가 올바르게 생성될 것이라 _생각_합니다. 어쨌든 이 시나리오를 테스트하여 우리가 기대하는 대로 동작하는지 확인하겠습니다. 다양한 시나리오가 기대대로 작동할 때까지 코어에 병합되지 않을 것입니다.

1개의 좋아요

이러한 추가 논의에 따라 @cameron-simpson 에게 TODO 항목을 다음과 같이 업데이트했습니다. Discourse 편집은 이메일로 전달되지 않으므로, 업데이트 사항을 알리기 위해 여기에 게시합니다:

  1. 생성되는 Message-ID 형식을 항상 <discourse/post/:post_id@:hostname>으로 변경합니다. 이는 충분히 고유하며, 기본적으로 이전 방식(첫 번째 게시글 ID를 참조 ID로 사용하는 방식)으로 복귀하는 것입니다.
  2. 게시글에 연결된 IncomingEmail 레코드가 있으면, 이메일을 전송할 때 항상 해당 Message-ID를 사용합니다. 그렇지 않으면 위의 형식을 사용하여 생성합니다.
  3. Post 레코드에 새로운 outbound_message_id 컬럼을 추가합니다. 이 컬럼은 a) 게시글을 생성한 수신 이메일의 Message-ID 또는 b) Discourse 웹 UI를 통해 생성된 게시글의 경우 우리가 생성하는 발신 Message-ID로 채워집니다.
  4. 토픽의 첫 번째 게시글(OP)에 대한 이메일을 전송할 때는 References 또는 In-Reply-To 헤더를 사용하지 않습니다. 이는 스레드의 첫 번째 이메일이므로 참조하거나 답장할 대상이 없기 때문입니다.
  5. PostReply 레코드를 기반으로 올바른 In-Reply-To 및 References 헤더가 생성되도록 보장합니다.
1개의 좋아요

이것이 인용도 포함합니까? (예: 10개의 다른 게시물을 인용한 게시물인 경우, 해당 게시물들을 참조하는 경우 등)

1개의 좋아요

Sam Saffron이 Discourse Meta를 통해 2022년 7월 29일 02:31에 작성:

이것이 인용(quote)도 포함하는 것인가요? (예: 10개의 다른 게시물을 인용한 게시물이 그들을 참조하는 경우)

In-Reply-To는 하나의 이전 메시지(antecedent)만 인용할 수 있으므로 하나를 선택해야 합니다. References는 하나 이상을 참조할 수 있지만, RFC에서는 이 게시물을 원본 게시물(OP)까지 되돌아가는 선형 체인(linear chain) 이외의 구조를 모든 클라이언트 앱이 기대하지 않을 수 있기 때문에 이를 명시적으로 권장하지 않습니다.

References의 경우 두 방식 모두 괜찮지만, 보수적인 방식을 선호할 것입니다. 계산이 쉬운 방법은 다음과 같습니다:

  • In-Reply-To: 첫 번째 인용된 메시지의 message-id를 사용(또는 어떤 정책에 기반하든 선택한 단일 인용에 해당)
  • References: 위의 단일 선택된 이전 게시물(antecedant post)의 References에 해당 게시물의 message-id를 추가

이러한 방식은 안정적이고 예측 가능하며 정확합니다.

감사합니다,
Cameron Simpson cs@cskk.id.au

2개의 좋아요

References 헤더는 다음과 같은 용도로 사용하는 것이 권장되지 않습니다:

참고: 일부 구현에서는 “References:” 필드를 파싱하여 "토론 스레드"를 표시합니다. 이러한 구현은 각 새 메시지가 단일 부모 메시지에 대한 답장이라고 가정하며, 따라서 “References:” 필드를 거꾸로 탐색하여 거기에 나열된 각 메시지의 부모를 찾는다고 가정합니다. 따라서 여러 부모를 가진 답장에 대해 “References:” 필드를 구성하려는 시도는 권장되지 않으며, 이를 수행하는 방법은 이 문서에서 정의되어 있지 않습니다.

2개의 좋아요

Martin Brennan가 Discourse Meta에서 2022년 7월 29일 01:57에 작성:

이 추가 논의들을 바탕으로 @cameron-simpson에게 TODO를 다음과 같이 업데이트했습니다.
Discourse의 수정 내역은 이메일로 전송되지 않으므로 업데이트 내용을 공유합니다:

  1. 생성되는 Message-ID 형식을 항상 <discourse/post/:post_id@:hostname>으로 변경합니다. 이는 충분히 고유하며, 기본적으로 기존에 하던 방식으로 되돌아가는 것입니다. 원본 게시물(OP)에 대한 참조는 이제 단순한 주제 ID 대신 첫 번째 게시물 ID를 사용하게 됩니다.
  2. 게시물에 연관된 IncomingEmail 레코드가 있으면 이메일 전송 시 항상 해당 Message-ID를 사용하고, 그렇지 않으면 위의 형식을 사용하여 생성합니다.
  3. 주제의 원본 게시물(OP)에 대한 이메일을 보낼 때 References 헤더를 사용하지 않습니다. 스레드의 첫 번째 이메일이므로 아직 참조할 대상이 없기 때문입니다.

원본 게시물(OP) 이메일의 In-Reply-To 헤더도 생략하겠습니다.

  1. PostReply 레코드를 기반으로 올바른 In-Reply-To 및 References 헤더가 생성되도록 보장합니다.

네, 맞습니다.

개인적으로는 이메일 측의 message-id를 저장할 컬럼을 추가하는 한 단계 더 나아간 방식을 선호합니다. 이렇게 하면 게시물에 message-id를 할당할 때(이메일에서 온 경우 소스 이메일의 ID를 사용하거나, 웹 인터페이스에서 온 경우 생성된 ID를 사용) 이를 저장해 두면, 현재나 나중에 코드가 어떻게 변하든 관계없이 해당 ID가 안정적으로 유지됩니다. 즉, IncomingEmail이 없더라도 message-id 생성은 한 번만 이루어지며, 재계산(변경될 수 있는)되지 않습니다.

즉, 생성된 후 저장함으로써 안정성을 확보하는 것입니다.

현재 IncomingEmail 관계가 있는 것으로 보입니다. 이메일로 게시물이 전달될 때 생성되는 추가 상태(outbound email messages)를 위해 OutgoingEmail 관계를 사용하거나 추가할 수 있을 것입니다.

게시물이 생성될 때 이러한 처리가 이루어지는 기본 흐름은 알고 있지만, 나중에 다음과 같은 사용자 기능이 추가될 수 있다고 상상해 볼 수 있습니다:

  • 이제 관심이 생겼으니 이 주제 전체에 대한 이메일을 전달해 주세요.
  • 게시물이 수정되면, 동일한 message-id를 사용하여 업데이트된 메시지를 전송하는 것을 고려해 주세요.

두 번째 예시가 떠오른 이유는 보고할 사항이 더 있기 때문입니다. 하나는 Discourse가 게시물을 간결하게 유지하기 위해 상단 배치된 답변의 인용 부분을 제거하려는 노력을 하고 있는 것 같습니다. 몇 주 전 Python 관련 포럼에서 긴 게시물을 작성했는데, 심각하게 잘려서 전송되었습니다. 저는 개인적으로 보관해 둔 원문으로 포럼에서 직접 게시물을 수정했습니다. 그러나 수신자 중 한 명은 전체 내용을 받았다고 하였고, Discourse가 수정 업데이트를 동일한 ID를 가진 대체 메시지로 전송하는지 궁금해졌습니다. 만약 그렇다면, 최종 사용자 클라이언트의 처리 방식에 따라 꽤 유용할 것입니다.

1개의 좋아요

Martin Brennan 님이 Discourse Meta에서 2022년 7월 29일 00:36에 작성:

  1. Post 테이블에 새로운 outbound_message_id를 추가하여, 게시물이 토픽을 이동하거나 유사한 일이 발생하더라도 스레드가 유지되도록 보장합니다. 위 두 가지 경우 모두 Message-ID를 여기에 저장합니다.

네, 이것이 중요하다고 생각합니다. 구현 방식(관계나 컬럼 등)은 무엇이든 상관없습니다. 제가 수정된 TODO 목록에서 언급했던 내용이라고 생각합니다.

  1. 토픽의 최초 게시자(OP)에게 이메일을 보낼 때는 References 헤더를 사용하지 마세요. 스레드의 첫 번째 이메일이므로 아직 Reference할 내용이 없기 때문입니다.
  2. PostReply 레코드와 Post 테이블의 새로운 outbound_message_id 컬럼을 기반으로 올바른 In-Reply-To 및 References 헤더가 생성되도록 확인하세요.

이미 발송된 이메일들의 경우 스레드 관점에서 다소 불분명한 상태가 될 수 있지만, 이전으로 돌아가고 있는 형식을 전환 기간 동안 허용할 수 있도록 최선을 다하겠습니다. 양해 부탁드립니다!

기존 이메일에 대해서는 우리가 할 수 있는 일이 없습니다. 앞으로 잘 스레딩되도록 하면 됩니다!

감사합니다!
Cameron Simpson cs@cskk.id.au

1개의 좋아요

우리는 EmailLog를 가지고 있지만, 해당 기록은 90일마다 정리되므로 이 용도에 적합하지 않다고 생각합니다. 다음과 같이 처리하겠습니다:

1개의 좋아요

완전히 저장하지 않는 것에 대한 이야기였지만… 다시 생각해보니 포스트 ID는 절대 변하지 않지만 호스트네임은 변할 수 있겠네요. 따라서 모든 경우에서 저장 직후 즉시 저장해야 합니다.

messageid를 모든 Post의 속성으로 영원히 불변(immutable)하게 두는 것도 나쁘지 않을 것 같습니다…

이것은 메시지의 다른 버전이 되는 것 아닌가요? 스펙에 따르면:

“Message-ID:” 필드는 특정 메시지의 특정 버전을 가리키는 고유한 메시지 식별자를 제공합니다. … 메시지 식별자는 특정 메시지의 정확히 하나의 버전에만 해당되며, 메시지의 후속 개정판에는 각각 새로운 메시지 식별자가 부여됩니다.

따라서 우리가 생성하는 message-id는 <discourse/post/:post_id/rev/:revision_num>여야 할 것입니다(첫 번째 개정판의 경우 /rev/:revision_num을 생략할 수도 있음). 이렇게 하면 최종 사용자의 수신 측에서 중복 제거를 허용한다는 점을 고려할 때, 수신자가 편집 업데이트를 수신할 수 있게 됩니다.

1개의 좋아요

네, 그렇게 하겠습니다. 수정과 리비전 관련해서 다른 논의들이 있지만, 그건 지금 우리가 깊이 들어갈 문제는 아닌 것 같습니다… 일단 스레딩(threading) 문제를 먼저 해결합시다 :slight_smile:

1개의 좋아요