마이그레이션 후 이전 스레드 매핑을 돕는 플러그인

안녕하세요, 제와 제 미친 친구들은 비드볼린3(Vbulletin3) 포럼을 디스코르스(Discourse)로 이관하는 작업을 거의 마무리했습니다. 마침내 원본 데이터베이스의 2,100만 개의 답변을 디스코르스로 이관하는 즉흥 스크립트를 작성했거든요.

현재 우리는 답변 본문에 포함된 주제/답변 링크 문제를 해결해야 합니다.

우리가 작성한 이관 스크립트에서는 “구” 주제와 게시글 ID가 디스코르스에서 어떤 ID로 매핑되는지에 대한 매핑을 기록합니다.

예를 들어:

   id   | topic_id |   name    | value  |         created_at         |         updated_at
--------+----------+-----------+--------+----------------------------+----------------------------
 581727 |   581736 | import_id | 599137 | 2023-02-08 16:30:01.600759 | 2023-02-08 16:30:01.600759

지금 제가 생각 중인 것은 구 포럼 형식의 링크를 가로채서 새로운 스레드/답변으로 변환하는 플러그인입니다.

예를 들어, 다음과 같은 경우:

https://oldforum.something.com/showthread.php?t=123456

123456 값을 기준으로 topics_custom_field를 조회하여 디스코르스의 topic_id를 찾고, 그 ID로 topic_links 테이블을 조회하여 url을 찾습니다. 마지막으로 클라이언트 측에서 게시글 내용을 조작한다고 가정하고 해당 URL로 교체합니다.

게시글(post)도 비슷한 방식으로 처리할 것입니다.

하지만 디스코르스에서 이런 기능을 만들기 위해 어디서부터 시작해야 하는지 좋은 예시를 찾지 못하고 있습니다.
누군가 힌트, 예시, 또는 비슷한 기능을 수행하는 플러그인(답변 내 특정 부분 문자열을 확인하고 교체하거나, API? DB?에서 하나의 값을 조회하여 다른 값을 가져오는 등)을 알려주실 수 있을까요?

감사합니다

이 기능은 이미 코어에 있으며 Permalinks라고 불립니다. 기존 VB4 가져오기 스크립트에도 이에 대한 코드가 있습니다.

/showthread\.php\?(\d*)/thread/\1과 같은 값을 permalink_normalizations 설정에 입력해야 합니다.

확인 차 여쭤보는데, 마이그레이션이 완료된 후에 이 로직을 실행하면 되는 거죠? 다시 모든 답글을 순회하면서 영구 링크를 변경하는 거잖아요.

변경 사항이라고요? 이미 영구 링크(permalinks)를 가지고 계신 건가요?

예를 들어 https://oldforum.something.com/showthread.php?t=123456 같은 링크가 포함된 답변의 내용을 이관할 때, 해당 토픽이 Discourse에서 어떤 id를 가질지 알 수 없지 않나요?

위 코드를 사용하여 영구 링크를 만들면 그렇게 됩니다.

참고로 showthread.php?t=는 답변이 아니라 주제/스레드를 가리킵니다.

그 링크는 그냥 예시로 사용한 것뿐입니다 :slight_smile:

불행히도 그 코드를 사용할 수 없습니다. 2,000만 개의 게시물을 가져오는 데 시간이 너무 오래 걸리고, 일괄 가져오기(bulk import)가 단순히 작동하지 않기 때문입니다. 누락된 부분이 있습니다.

그래서 저희는 자체 마이그레이션 스크립트를 작성해야 했습니다. 이 스크립트는 4코어, 8GB RAM 환경에서 약 6시간 만에 모든 것(pm, 사용자, 사용자 그룹, 카테고리, 토픽, 답장)을 처리합니다. 하지만 영구 링크(permalinks)가 누락되어 있다는 사실을 알게 되었습니다 :slight_smile:

nginx 맵을 활용한 사전 영구 링크(pre-permalinks) 해결책을 고려해 보시는 건 어떨까요? Redirect vBulletin URLs to Discourse URLs

내부적으로 논의한 결과, 모든 응답이 마이그레이션된 후에 두 번째 패스를 실행하기로 했습니다.

아이디어를 함께 나누어 주셔서 감사합니다, Richard :heart:

스크립트가 import_ids를 생성했나요? 그렇다면 영구 링크(permalinks)를 직접 생성하지 않았더라도, 이를 통해 비교적 빠르게 영구 링크를 생성할 수 있습니다.

야, 그래 제이, 우리도 그렇게 생각해.

2천만 개가 넘는 답글을 다시 한 번 순환 처리하는 것을 피하려고 했었지만, 대안적인 솔루션(플러그인, nginx 리다이렉트 등)은 상당히 복잡하거나 외부 요인에 의존하여 엉성하게 될 것 같아서, 결국 답글을 다시 순환 처리하고 영구 링크(permaliinks)를 처리하기로 했습니다. 마이그레이션에 시간이 좀 더 걸리겠지만, 그렇게까지는 아닐 것으로 기대합니다.

나머지 모든 것은 우리가 ‘raw’ 데이터가 html로 변환되어야 할 것을 알고 있으므로 실시간으로 ‘조리’(처리)됩니다.

영구 링크의 경우 그렇게 할 수 없는데, 편집을 통해 영구 링크가 추가되면 아직 처리되지 않은 토픽(더 높은 토픽 ID)을 참조할 수 있고, 처리 시점에 topics_custom_field 테이블에서 해당 토픽을 찾을 수 없기 때문입니다.

토픽을 먼저 생성하지 않고 topic_custom_fields를 어떻게 생성할 수 있었는지 모르겠습니다. 아마 다음과 같은 방식으로

TopicCustomField.each do |tcf|

영구 링크(permalinks)를 생성할 수 있을 것 같지만, 여러분의 코드에 대해 모르는 부분이 많습니다.

설명드리겠습니다:

vbulletin 데이터베이스에서 주제 ID가 작은 것부터 큰 것 순서대로 주제와 모든 답변이 가져오기됩니다. 이는 시간 순서대로 가져오는 것을 의미합니다.

하지만 이는 다른 주제에 대한 참조를 발견할 경우, 해당 주제가 이미 존재했어야 한다는 것을 시사합니다.

그러나 이것이 사실이 아닌 경우가 있습니다. 예를 들어 다음과 같은 경우입니다:

  • 분할된 주제와 분할을 유발한 댓글. 분할된 주제는 더 큰 ID를 가지지만, 더 작은 ID를 가진 주제에 존재합니다.
  • 향후 독자를 위한 편집으로 인해 오래된 주제의 게시물이 더 최근의 주제에 대한 참조를 포함하는 경우

따라서, 첫 번째 주제에서 설명한 대로 topic_custom_field는 가져오기가 진행됨에 따라 생성되고 채워지지만, ID 간의 올바른 대응 관계를 항상 확인할 수 없기 때문에 "즉석(on the fly)"으로 수행하는 것은 신뢰할 수 없습니다.

전체 가져오기가 완료된 후 추가 패스가 필요합니다.

TopicCustomField.each do |tcf|에 대해서는, tcf 부분이 무엇을 하는지 확실하지 않습니다. Ruby는 제가 배운 언어가 아닙니다. 이 작업을 위해 도움을 주신 대부분의 사람들이 이미 업무용으로 사용하므로, 우리의 스크립트는 C#으로 작성되었습니다.