가져오기 스크립트 높은 메모리 사용량

기존 보드에서 가져오기(import) 테스트를 진행 중입니다. 가져와야 할 게시물이 약 2,500만 개(일반 게시물 + 비공개 대화) 정도 되는데, 처리 속도를 높이기 위해 가져오기 스크립트를 여러 개 복제해 동시에 실행하고 토픽 부하를 분산했습니다. 며칠 동안은 문제가 없었지만, 시간이 지날수록 각 프로세스의 메모리 사용량이 서서히 증가해 각각 약 2GB까지 올라가는 것을 확인했습니다. 그러다 결국 서버 메모리가 고갈되어 MySQL 소스 데이터베이스가 약 1,600만 게시물 지점에서 강제 종료되었습니다.

시스템 메모리를 24GB에서 32GB로 늘렸지만, 이제 가져오기 프로세스를 1개만 다시 시작하여 중단된 위치부터 이어서 실행하려고 해도, 게시물을 실제로 가져오기 시작하기도 전에 해당 프로세스가 약 10GB의 메모리를 차지하고 있습니다. 이전에 8개의 가져오기 프로세스를 동시에 실행할 수 있었는데, 이제는 더 큰 메모리 풀에서도 2개만 실행할 수 있습니다. 클린 설치 시의 메모리 사용량과 실패 후 가져오기를 재개할 때의 메모리 사용량 사이에 이렇게 큰 차이가 나는 이유는 무엇일까요? 가져오기 프로세스를 다시 빠르게 진행할 수 있도록 이 메모리 사용량을 줄이는 방법이 있을까요? 128GB~256GB 메모리를 갖춘 서버는 비용이 prohibitively 비싸고(그리고 가져오기 이후에는 필요하지 않으므로) 적합하지 않으며, 가져오기 프로세스를 2개만 실행하게 되면 완료까지 몇 주가 걸릴 것입니다.

무한 루프에 갇힌 정규식 같은 것 같습니다. 디버그 메시지를 출력하고, 가져오기 시 문제 있는 행을 건너뛰세요.

메모리 사용량은 실제 게시글 처리 단계가 아니라, 가져오기 스크립트 시작 시의 “기존 게시글 불러오는 중…”(또는 주제) 섹션에서 모두 발생하는 것으로 보입니다. 제가 확인한 바로는, 해당 섹션은 데이터베이스에서 게시글 및 주제 정보를 가져오기 때문에 정규식(regex)이 관여될 이유가 없습니다.

@posts와 @topics 변수는 “topic_lookup_from_imported_post_id” 메서드 등에 사용되는 것으로 보입니다. 이는 이해가 되지만, 스크립트가 처음 실행되었을 때 메모리 사용량은 현재 확인되는 수준에 훨씬 못 미쳤음에도 불구하고 해당 메서드들은 여전히 정상적으로 작동하고 있습니다.

벌크 가져오기 스크립트를 확인해 보셨나요? 메모리 사용량이 더 적을 수도 있습니다.

하지만 가져오기 스크립트는 기존 사용자, 주제, 게시물의 ID를 새 ID로 매핑하는 맵을 메모리에 유지하기 때문에, 특히 여러 복사본을 원한다면 상당한 RAM을 차지합니다.

최초 가져오기를 실행한 후 새 데이터만 가져오기 위해 다시 실행하게 되며, 이때는 훨씬 빠르게 실행된다는 점을 이해하고 계시죠? 따라서 최초 가져오기를 한 달 정도 기다린 후 실행하면 최종 가져오기에 걸리는 시간이 더 짧아집니다.

벌크 임포트 스크립트에 대해 알고 있는 것이 없습니다. import_scripts 디렉터리 어딘가에 있는 건가요?

네, 바로 그 부분에서 어려움을 겪고 있습니다. 8개의 임포트 프로세스를 실행하던 초기 임포트 단계에서는 비교적 문제가 없었으나, 시스템이 메모리를 모두 소진할 때까지 그랬습니다. 이제 임포트 프로세스를 다시 시작하여 중단된 부분부터 계속 진행하려고 하면, 각 프로세스가 처음 충돌했을 때보다 약 5배 많은 메모리를 사용하게 됩니다.

적절한 테스트를 수행하고 이 마이그레이션이 실제로 언제 수행될지에 대한 기대치를 설정하려면 전체 임포트를 완료해야 합니다. 현재로서는 성능과 같은 사항에 대해 무엇을 기대해야 할지 명확한 이해가 없습니다. 또한 1,600만 건의 게시물을 처리한 시점에서도 데이터베이스 크기가 이미 현재 데이터베이스보다 50% 이상 큰 것을 발견했는데, 이는 다소 놀라운 부분입니다. 긴 임포트 시간은 이를 불가능하게 만들지는 않지만, 기대치가 주 단위가 아니라 일 단위로 설정된다면 훨씬 편리할 것입니다.

https://github.com/discourse/discourse/tree/master/script/bulk_import를 확인해 보세요.

토픽과 게시글의 경우, 토픽과 그 이전의 모든 게시글이 이미 가져와지지 않은 상태에서는 해당 토픽의 게시글을 가져올 수 없으므로 병렬 가져오기를 실행하는 것은 사실상 불가능합니다. 사용자와 토픽에 대해서는 병렬 프로세스를 사용할 수 있다고 생각하지만, 게시글에 대해서는 병렬 가져오기를 허용하도록 스크립트를 다시 작성하여 특정 토픽의 모든 게시글을 한꺼번에 가져오도록 해야 합니다. 이는 분명히 가능하지만, 제가 사용해 온 스크립트 중 어느 것도 그렇게 동작하지는 않습니다. 하지만 여전히 RAM에 구식에서 최신으로의 ID 매핑을 유지해야 하는 문제가 남게 됩니다.

2,500만 개의 게시글을 가져오는 것은 쉽지 않습니다. :slight_smile:

제가 지금 하고 있는 작업입니다. 각 주제가 하나의 프로세스에서만 처리되도록 주제를 분리했습니다. 완벽한 분리는 아니지만, 단일 선형 프로세스보다 여러 번 더 빠릅니다.

이 부분이 가장 혼란스럽습니다. 처리하면서 동시에 이 작업을 수행한다고 가정하고, 그래서 3일 동안 각 프로세스의 메모리 사용량이 증가하는 것을 보았습니다. 데이터베이스 연결이 끊기기 전에 각 프로세스의 메모리 사용량이 약 2~2.5GB까지 올라갔습니다.

각 프로세스는 가져온 게시글에 대한 매핑만 유지합니까? 그렇다면 재시작 후 메모리 사용량이 급증한 이유가 설명될 수 있을 것 같습니다.

그렇다고 생각합니다. 다른 프로세스들은 다른 프로세스들이 가져온 날짜들이 없기 때문에 올바르게 작동하지 않을 것입니다. 현재 하고 계신 작업이 제대로 작동할 것이라고 생각하지 않습니다.

벌크 가져오기 스크립트를 확인하거나, base.rb를 다시 작성하여 가져오기 ID에 대한 링크를 다른 방식으로 유지해야 합니다.

기다리는 것보다 코드를 디버깅하는 데 더 많은 주소를 보내게 될 가능성이 높습니다. 단일 프로세스 CPU 속도를 높이는 것이 작업 속도를 높이는 가장 좋은 방법입니다.

이 접근 방식에 대해 지금까지는 문제가 없는 것 같습니다. 다만, 가져오기(import) 이후에 실행되는 일부 메서드에서 문제가 발생할 수 있을 것 같습니다. 그래서 그 시점 이후에는 모든 처리를 중단하고, 나머지 작업을 깔끔하게 처리할 수 있도록 단일 스레드 실행을 수행하는 것이 좋을 것 같습니다.

그럼에도 불구하고, 가져오기 ID 링크를 다른 방식으로 처리하라는 제안은 어떤 경우에도 좋은 아이디어일 것입니다. 스크립트 실행이 끝날 때까지 변수에 임의로 큰 양의 데이터를 유지하는 것은 매우 비효율적이기 때문입니다.