Migrate a phpBB3 forum to Discourse

phpBB 포럼을 기존 Discourse 포럼에 가져와 병합하는 것이 가능한가요? 카테고리 매핑 설정을 보면 가능할 것 같습니다.

주의해야 할 문제가 있을까요?

이메일 주소가 동일한 사용자는 병합되나요?

1개의 좋아요

네, 가능합니다. 설정 파일에서 직접 카테고리 매핑을 하지 않으면 가져오기 과정에서 필요한 새 카테고리가 자동으로 생성됩니다. Discourse에 이미 존재하는 카테고리가 phpBB의 카테고리 이름과 일치하면 두 카테고리가 병합됩니다.

오, 네. 제가 겪은 내용을 자세히 다룬 스레드가 있습니다. 몇 가지 팁과 함께 가져오기 도구에 제가 적용한 통합 업데이트가 포함된 저장소도 있습니다.

정확한 조건은 기억나지 않지만, 네, 가져오기 과정에서 사용자가 자동으로 병합됩니다.

2개의 좋아요

phpbb3 포럼을 Discourse로 통합할 때, 가져온 모든 주제를 읽기 전용으로 유지할 수 있는지 궁금합니다. 관련 스레드를 검색해 보았지만 이 부분에 대한 언급은 찾지 못했습니다. 감사합니다.

수입 스크립트를 수정하여 생성된 모든 주제를 아카이브하도록 설정할 수도 있고, 스크립트가 실행된 후 일괄 처리하는 것이 더 쉽습니다. 예제는 Administrative Bulk Operations 를 참고하세요.

2개의 좋아요

안녕하세요,

importer를 시작할 때 문제가 발생하는 것 같습니다. 사용자 가져오기 중에나 직후에 발생하는 것 같습니다. 스택 트레이스와 출력을 담은 20초짜리 짧은 영상을 첨부했습니다.

조언을 부탁드립니다.

0x00007fe87a88bb10 /var/www/discourse/script/import_scripts/phpbb3/importers/user_importer.rb:87>}
/var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/validations.rb:80:in `raise_validation_error': Validation failed: Name can't be blank (ActiveRecord::RecordInvalid)
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/validations.rb:53:in `save!'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/transactions.rb:302:in `block in save!'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/transactions.rb:354:in `block in with_transaction_returning_status'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/connection_adapters/abstract/database_statements.rb:314:in `transaction'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/transactions.rb:350:in `with_transaction_returning_status'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/transactions.rb:302:in `save!'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/suppressor.rb:54:in `save!'
        from /var/www/discourse/script/import_scripts/base.rb:361:in `block in create_user'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/connection_adapters/abstract/transaction.rb:319:in `block in within_new_transaction'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activesupport-7.0.7/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activesupport-7.0.7/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activesupport-7.0.7/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activesupport-7.0.7/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/connection_adapters/abstract/transaction.rb:317:in `within_new_transaction'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/connection_adapters/abstract/database_statements.rb:316:in `transaction'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/activerecord-7.0.7/lib/active_record/transactions.rb:209:in `transaction'
        from /var/www/discourse/script/import_scripts/base.rb:360:in `create_user'
        from /var/www/discourse/script/import_scripts/base.rb:278:in `block in create_users'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/rack-mini-profiler-3.1.1/lib/patches/db/mysql2/alias_method.rb:8:in `each'
        from /var/www/discourse/vendor/bundle/ruby/3.2.0/gems/rack-mini-profiler-3.1.1/lib/patches/db/mysql2/alias_method.rb:8:in `each'
        from /var/www/discourse/script/import_scripts/base.rb:266:in `create_users'
        from /var/www/discourse/script/import_scripts/phpbb3/importer.rb:106:in `block in import_anonymous_users'
        from /var/www/discourse/script/import_scripts/base.rb:948:in `block in batches'
        from /var/www/discourse/script/import_scripts/base.rb:947:in `loop'
        from /var/www/discourse/script/import_scripts/base.rb:947:in `batches'
        from /var/www/discourse/script/import_scripts/phpbb3/importer.rb:293:in `batches'
        from /var/www/discourse/script/import_scripts/phpbb3/importer.rb:102:in `import_anonymous_users'
        from /var/www/discourse/script/import_scripts/phpbb3/importer.rb:33:in `execute'
        from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
        from /var/www/discourse/script/import_scripts/phpbb3/importer.rb:22:in `perform'
        from script/import_scripts/phpbb3.rb:35:in `<module:PhpBB3>'
        from script/import_scripts/phpbb3.rb:16:in `<module:ImportScripts>'
        from script/import_scripts/phpbb3.rb:15:in `<main>'

수정:

익명 사용자와 관련이 있는 것 같습니다. 데이터베이스를 살펴볼 예정입니다.

 def map_anonymous_user(row)
      username = row[:post_username]

      {
        id: @settings.prefix(username),
        email: "anonymous_#{SecureRandom.hex}@no-email.invalid",
        username: username,
        name: @settings.username_as_name ? username : "",
        created_at: Time.zone.at(row[:first_post_time]),
        active: true,
        trust_level: TrustLevel[0],
        approved: true,
        approved_by_id: Discourse.system_user.id,
        approved_at: Time.now,
        post_create_action:
          proc do |user|
            row[:user_inactive_reason] = Constants::INACTIVE_MANUAL
            row[:ban_reason] = "Anonymous user from phpBB3" # TODO i18n
            suspend_user(user, row, true)
          end,
      }
    end

수정

map_anonymous_user 메서드의 이 줄을 변경하여 에러를 넘길 수 있었습니다. 기본적으로 username이 빈 문자열로 설정되어 있었는데, 단순히 단어 하나를 넣었습니다.

이 부분을

name: @settings.username_as_name ? username : "",

다음과 같이 변경했습니다.

name: @settings.username_as_name ? username : "Anonymous ",
3개의 좋아요

증분 가져오기(incremental import)를 실행하기 전에 기존 게시물을 삭제해도 될까요? 개발 환경에서 로컬 설치로 가져오기를 성공적으로 실행한 뒤, Docker로 설치된 서버로 복원했습니다. 그리고 이제 서버에서 새 게시물에 대한 가져오기를 실행할 계획입니다. 하지만 이미 가져온 게시물을 무시하더라도, 서버에서 가져오기를 실행하는 데 몇 시간이나 걸립니다. 제 포럼이 꽤 규모가 크고(게시물 130만 개) 서버 디스크 속도가 느려서, .sql 파일만 로딩해도 몇 시간이 걸립니다.

로컬에서 DELETE FROM phpbb_posts WHERE post_id < 2926807과 같은 작업을 수행한 후, 이를 덤프(dump)해서 서버에 업로드하고 정상적으로 가져오기를 실행하면 문제가 생길까요?

일부 스크립트에 IMPORT_AFTER 설정을 추가하여 특정 날짜 이후의 데이터만 가져오도록 했습니다. 따라서 포스트를 가져오는 SQL을 수정하여 마지막 데이터 덤프 하루 전 이후의 항목만 가져오도록 했습니다.

하지만 디스크 속도가 매우 느리면 Discourse 호스팅에 문제가 발생할 수 있습니다. 현재 가지고 있는 데이터 기준으로 성능이 허용 가능한 수준인가요?

1개의 좋아요

네, 일반적인 사용에는 더 빠른 디스크를 사용할 예정이지만, 여러 데이터베이스 사본을 동시에 호스팅하기에는 용량이 부족합니다. 그래서 import.yml의 volumes 섹션에서 /shared/import를 더 크지만 느린 디스크로 라우팅하고 있습니다. 이 방법은 초기 백업 복원 시에는 잘 작동했습니다. 18G의 여유 공간이 있고 데이터베이스 크기가 6G인데도 공간 부족으로 복원이 실패하자, /shared/tmp를 느린 디스크로 다시 라우팅했습니다(복원 중 데이터베이스가 3개의 사본으로 생성된다는 내용을 다른 곳에서 언급하신 것으로 기억합니다). 하지만 그 시점에는 가동 중단 시간이 큰 문제가 아니었습니다.

따라서 일상적인 사용에서는 용량이나 속도가 문제가 되지 않으며, 제 포럼은 오래되었을 뿐 비교적 조용합니다.

스크립트의 SQL을 수정하는 방법을 살펴보겠습니다. 감사합니다!

2개의 좋아요

settings.yml에서 MySQL에 연결하기 위한 내용은 다음과 같습니다:


데이터베이스 이름을 어디에 입력해야 하나요?

데이터베이스 이름은 schema 파라미터에 입력해야 합니다. 해당 파라미터의 이름이 다소 혼란스럽긴 합니다. 여기의 코드를 보면, 데이터베이스 이름으로 전달되는 것을 확인할 수 있습니다.

2개의 좋아요

https://www.loom.com/share/1f66315779af4cf7b286c8541d4f3f09

안녕하세요! 가져오기(import) 과정에서 겪고 있는 문제에 대해 간단히 설명하는 영상을 만들었습니다. 약 650,000개의 게시글을 이전하고 있습니다.

감사합니다!

https://www.loom.com/share/1f66315779af4cf7b286c8541d4f3f09?sid=11a46d3c-8510-43a1-82e9-1a3524cbb365

안녕하세요. 임포트를 행(row) 단위로 실행할 수 있는 방법이 있을까요? 스크립트가 완료되지 않는 문제가 발생하고 있습니다. 사용자나 아바타는 정상적으로 작동하지만, 다른 항목들은 그렇지 않습니다. 임포트가 오류로 중단된 후에는 클린 설치가 필요하다는 내용을 어딘가에서 읽어서 서버를 이미 약 4회 재이미징했습니다.

기타 자세한 내용은 제가 올린 영상에서 확인하실 수 있습니다. 도움이 주시면 감사하겠습니다.

안녕하세요. 마이그레이션을 진행한 지 약 18시간이 지났는데, 여전히 마이그레이션이 정상 작동하도록 만드는 데 진전이 없습니다. 제 영상을 확인해 주시고, 팁이나 요령이 있거나 설정에서 빠진 부분이 있는지 조언해 주실 수 있을까요? 저희가 필요한 것은 게시물, 주제, 개인 메시지, 카테고리뿐입니다. 아바타와 사용자는 이미 정상 작동하고 있습니다. 서버는 깨끗한 UBUNTU 22.04 환경입니다.

더 쉬운 해결 방법은 사이트를 만든 직후 백업을 만들어 두는 것입니다. 이렇게 하면 그 백업으로 복원할 수 있습니다. 깨끗한 데이터베이스를 만드는 또 다른 방법은 rake db:drop db:create db:migrate를 실행하는 것입니다. 하지만 이를 가능하게 하려면 몇 가지 작업을 수행해야 합니다. 오류 메시지에서 그 작업들을 추측할 수 없다면, 백업/복원 솔루션이 최선의 방법이며, 오히려 더 빠를 수도 있습니다.

디버깅용 puts 문구를 추가할 수는 있지만, Ruby에 익숙하지 않은 사람들에게 phpbb3 스크립트의 구조는 다소 혼란스럽습니다.

또한, 스크립트를 여러 번 실행할 수 있으며, 이미 처리된 항목은 건너뛰므로, 새로 시작해야 하는 변경 사항(즉, 이미 가져온 데이터에 대해 다른 작업을 수행해야 하는 경우)이 있을 때까지 데이터베이스를 지울 필요가 없습니다.

그것은 제가 무료로 도와드릴 수 있는 범위를 벗어납니다. 예산이 있으시다면 직접 연락을 주시거나 Marketplace 채널에 게시물을 올려 주세요.

가져오기 스크립트가 작동하지 않는 것 같습니다. 문제는 영상에 있습니다. Discourse 마이그레이션 팀에서 확인해 주시면 감사하겠습니다.

지원 서비스의 과금이 유료라면, 마이그레이션 비용을 목록으로 제시해 주실 수 있을까요? 제 이해로는 이 스크립트를 작동시키는 방법에 대해 질문하기 위해 이 곳으로 오게 된 것입니다. 수수료는 괜찮습니다. 다만, 이 프로젝트가 오픈소스 프로젝트라고 생각했고, 질문을 하기 위해 이 곳을 방문한다고 알고 있었을 뿐입니다.

저는 여기에서 자원봉사를 하고 있으며, 주로 비합리적인 영상 혐오 때문에 무료 영상을 보고 싶지 않다는 점을 말씀드리는 것입니다. 하지만 저만이 그런 것은 아닙니다. 여기에는 영상을 좋아하는 사람들이 많습니다.

즉, 실행될 때마다 이전 실행 시 생성했던 사용자의 중복 항목을 생성한다는 말씀이신가요? 그렇게 될 수 있는 방법을 상상할 수 없습니다. 정말로 그러신가요? 그것은 스크립트가 작동하도록 설계된 방식이 아닙니다.

다시 가져오는 것처럼 보일 수 있지만, 실제로는 아무것도 수행하지 않으므로 훨씬 빠르게 처리됩니다. 이미 가져온 항목에는 import_id 커스텀 필드가 설정되어 있어 다시 가져오는 것을 방지하기 때문입니다. 만약 매 실행마다 데이터가 중복된다면 매우 이상한 문제가 있는 것입니다.

그렇습니다. 때로는 무료로는 받을 수 없는 더 많은 도움이 필요하거나 원하는 경우가 있습니다.

2개의 좋아요

알겠습니다. 마이그레이션 비용을 게시해 주실 수 있을까요? 스크립트가 작동하지 않는 것 같고 유료 지원이 필요하다는 점은 이해하고 있습니다. 전혀 문제가 되지 않습니다.

1개의 좋아요

마이그레이션은 상당히 복잡한 작업입니다. 모든 포럼의 데이터가 고유하기 때문에, 각 마이그레이션마다 다른 도전 과제가 있습니다. 마이그레이션 문제를 디버깅하는 데는 수 시간이 걸리며, 로그, 데이터베이스, 코드 등에 대한 심층적인 조사가 필요합니다. 단순히 이 게시물들만으로는 해결하기가 어려워 죄송합니다. 더 많은 도움을 드리지 못해 죄송합니다! 마이그레이션 FAQ는 여기 있습니다: Migrating to Discourse | Discourse - Civilized Discussion

링크를 보내주셔서 감사합니다. 문제를 파악했고, 이제 가져오기 스크립트가 실행되고 있습니다. 현재 약 100,000/666,357개 게시물을 처리 중입니다. Chat GPT가 권한 기반 오류 등을 식별하는 데 도움이 되었습니다.

2개의 좋아요