안녕하세요, 이 주제에는 제가 천천히 계획하고 테스트 중인 마이그레이션에 대한 배경 정보가 담겨 있습니다. 마침 지난 금요일, 이 방법과 이 방법을 조합하여 테스트용 VPS에서 Drupal 가져오기 스크립트를 처음 시도해 보았습니다. 이 글을 작성하는 지금도 가져오기 스크립트가 실행 중이라 테스트 사이트의 기능을 실제로 확인하지는 못했지만, 곧 완료될 예정입니다.
제가 직면한 가장 큰 문제는 약 80,000개의 전체 노드 중 8개의 노드(Discourse의 주제에 해당)에서 발생하는 “중복된 키 값” 오류입니다. 혹시 Y2K와 유사한 수학 버그가 원인이 아닐까 싶어 해당 nid 번호들을 여기에 나열합니다:
42081, 53125, 57807, 63932, 66756, 76561, 78250, 82707
가져오기 스크립트를 다시 실행할 때마다 항상 동일한 nid에서 이 오류가 발생합니다:
Traceback (most recent call last):
19: from script/import_scripts/drupal.rb:537:in `<main>'
18: from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
17: from script/import_scripts/drupal.rb:39:in `execute'
16: from script/import_scripts/drupal.rb:169:in `import_forum_topics'
15: from /var/www/discourse/script/import_scripts/base.rb:916:in `batches'
14: from /var/www/discourse/script/import_scripts/base.rb:916:in `loop'
13: from /var/www/discourse/script/import_scripts/base.rb:917:in `block in batches'
12: from script/import_scripts/drupal.rb:195:in `block in import_forum_topics'
11: from /var/www/discourse/script/import_scripts/base.rb:224:in `all_records_exist?'
10: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-7.0.3.1/lib/active_record/transactions.rb:209:in `transaction'
9: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-7.0.3.1/lib/active_record/connection_adapters/abstract/database_statements.rb:316:in `transaction'
8: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-7.0.3.1/lib/active_record/connection_adapters/abstract/transaction.rb:317:in `within_new_transaction'
7: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.3.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
6: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.3.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
5: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.3.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
4: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.3.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
3: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-7.0.3.1/lib/active_record/connection_adapters/abstract/transaction.rb:319:in `block in within_new_transaction'
2: from /var/www/discourse/script/import_scripts/base.rb:231:in `block in all_records_exist?'
1: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/pg.rb:56:in `exec'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/pg.rb:56:in `exec': ERROR: duplicate key value violates unique constraint "import_ids_pkey" (PG::UniqueViolation)
DETAIL: Key (val)=(nid:42081) already exists.
20: from script/import_scripts/drupal.rb:537:in `<main>'
19: from /var/www/discourse/script/import_scripts/base.rb:47:in `perform'
18: from script/import_scripts/drupal.rb:39:in `execute'
17: from script/import_scripts/drupal.rb:169:in `import_forum_topics'
16: from /var/www/discourse/script/import_scripts/base.rb:916:in `batches'
15: from /var/www/discourse/script/import_scripts/base.rb:916:in `loop'
14: from /var/www/discourse/script/import_scripts/base.rb:917:in `block in batches'
13: from script/import_scripts/drupal.rb:195:in `block in import_forum_topics'
12: from /var/www/discourse/script/import_scripts/base.rb:224:in `all_records_exist?'
11: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-7.0.3.1/lib/active_record/transactions.rb:209:in `transaction'
10: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-7.0.3.1/lib/active_record/connection_adapters/abstract/database_statements.rb:316:in `transaction'
9: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-7.0.3.1/lib/active_record/connection_adapters/abstract/transaction.rb:317:in `within_new_transaction'
8: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.3.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'
7: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.3.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'
6: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.3.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'
5: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activesupport-7.0.3.1/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'
4: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/activerecord-7.0.3.1/lib/active_record/connection_adapters/abstract/transaction.rb:319:in `block in within_new_transaction'
3: from /var/www/discourse/script/import_scripts/base.rb:243:in `block in all_records_exist?'
2: from /var/www/discourse/script/import_scripts/base.rb:243:in `ensure in block in all_records_exist?'
1: from /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/pg.rb:56:in `exec'
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/pg.rb:56:in `exec': ERROR: current transaction is aborted, commands ignored until end of transaction block (PG::InFailedSqlTransaction)
진행을 계속할 수 있었던 유일한 방법은 SQL 조건문을 해킹하는 것이었습니다:
...
LEFT JOIN node_counter nc ON nc.nid = n.nid
WHERE n.type = 'forum'
AND n.status = 1
AND n.nid != 42081
AND n.nid != 53125
AND n.nid != 57807
AND n.nid != 63932
AND n.nid != 66756
AND n.nid != 76561
AND n.nid != 78250
AND n.nid != 82707
LIMIT #{BATCH_SIZE}
OFFSET #{offset};
...
소스 Drupal 데이터베이스에서 실패한 첫 번째 노드와 그 양쪽의 이전 및 다음 nid를 검사해 보았지만, 아무런 문제가 보이지 않습니다. nid는 기본 키로 설정되어 있고 AUTO_INCREMENT가 적용되어 있으며, 원래 Drupal 사이트는 정상적으로 작동하므로 소스 데이터베이스 무결성에 근본적인 문제가 있을 수는 없습니다.
위 버그 외에도 스크립트 사용 시 겪고 있는 제한 사항들은 다음과 같습니다:
-
영구 링크(Permalinks): 가져오기 스크립트는 이전 노드 URL
example.com/node/XXXXXXX에 대한 영구 링크를 생성하는 것으로 보입니다. 하지만 해당 노드 내 특정 댓글에 대한 링크도 유지해야 하며, 이 링크의 형식은example.com/comment/YYYYYYY#comment-YYYYYYY입니다 (YYYYYYY는 두 곳에서 동일합니다). Drupal URL 체계에는 댓글이 연결된 노드 ID가 포함되지 않지만, Discourse에는 포함됩니다(example.com/t/topic-keywords/XXXXXXX/YY). 따라서 이는 상당한 복잡성을 초래하는 것으로 보입니다. -
사용자 이름 제한: Drupal은 사용자 이름에 공백을 허용합니다. Discourse는 이를 허용하지 않는 것으로 이해하고 있으며, 적어도 신규 사용자는 그렇게 생성할 수 없습니다. 이 게시물에 따르면 가져오기 스크립트가 문제 있는 사용자 이름을 자동으로 "변환"한다고 하지만,업데이트: 실제로는 Discourse가 이를 올바른 방식으로 자동으로 처리한 것으로 보입니다./import_scripts/drupal.rb에서 해당 코드를 찾을 수 없습니다. -
차단된 사용자: 스크립트가 차단된 계정 포함 모든 사용자를 가져오는 것으로 보입니다. 활성 사용자 계정만 가져오기 위해 SQL 선택 조건에
WHERE status = 1을 쉽게 추가할 수 있을 것 같지만, 이것이 레코드 직렬화에 문제를 일으키지는 않을지 확신이 서지 않습니다. 무엇보다도 이전에 차단된 계정 이름과 관련 이메일 주소를 영구적으로 차단하여 같은 문제 사용자들이 Discourse에서 다시 가입하지 못하게 하는 것이 가장 좋습니다. -
사용자 프로필 필드: 사용자 계정 프로필의 개인 정보 필드를 가져오는 데 사용할 예제 코드가 다른 가져오기 스크립트에 있는지 아는 분이 있을까요? 가져와야 할 프로필 필드는 하나(“위치”)뿐입니다.
-
아바타 (Gravatar가 아닌 경우): Drupal 가져오기 스크립트에는 Gravatar를 가져오는 코드는 있지만, 훨씬 더 일반적으로 사용되는 로컬 계정 아바타 사진을 가져오는 코드는 없는 것이 다소 이상하게 느껴집니다.
-
개인 메시지: 거의 모든 Drupal 7 포럼은 서드파티 privatemsg 모듈(공식 Drupal 개인 메시지 기능이 없음)을 사용할 가능성이 높습니다. 가져오기 스크립트는 개인 메시지 가져오기를 지원하지 않습니다. 제 경우 약 150만 개의 개인 메시지를 가져와야 합니다.
도움 주셔서 미리 감사드립니다. 또한 Drupal 가져오기 스크립트를 제공해 주셔서 감사합니다.




