@RGJ 님도 마찬가지인데, 모든 이미지를 다시 빌드하는 번거로움을 피하는 확실한 방법을 아는 사람이 있다면 저는 전적으로 찬성합니다. 만약 제가 숙련된 Discourse 개발자였고 쉽게 할 수 있는 방법을 알았다면, 처음부터 그렇게 했을 가능성이 높았을 것입니다.
많은 이미지를 가진 사용자들에게 S3에서 마이그레이션하는 것은 흔한 경우가 아니라고 추측합니다. 따라서 Discourse에 보다 일반적으로 유용한 기능을 추가하는 것과 비교할 때, 이 작업에 시간을 들일 가치가 있는지 판단하기 어렵습니다. Google+에서 MakerForums로 가져오면서 약 100GB의 업로드가 발생했고, 많은 사람들이 해당 커뮤니티에서 MakerForums로 이동할 수 있다는 기대감으로 S3를 선택했습니다. 하지만 결국 활발하게 이동한 커뮤니티는 소수에 불과하여, 지속적인 성장은 S3에 머무는 것을 뒷받침하지 못했습니다. 저는 이것이 상당히 극단적인 예외라고 생각하며, 이미지 재처리 비용은 일회성 비용입니다.
마지막으로, 이 주제를 처음 열어서 정말 감사합니다. 그렇지 않았다면 게시물이 아닌(non-post) 업로드를 놓칠 수도 있었을 것입니다.
…
@RGJ 님, 삭제 후 재생성(destroy-and-recreate) 방식이 좋지 않다는 지적은 명확히 옳습니다. url != new_upload.short_url일 경우 raise "Error: upload url #{url} changed to #{new_upload.short_url}, should be unchanged."라는 체크를 추가하여, 손상된 소스에 대한 문제를 보고받게 했습니다. 오늘 Digital Ocean Spaces 장애로 인해 손상된 데이터가 전송되었고, 이 오류가 많은 업로드에서 발생했습니다 — 몇 건인지 정확히 알 수 없습니다. 하지만 기존 업로드가 이미 삭제된 상태였기 때문에, 일부 페이지가 손상되었고, 어떤 페이지가 손상되었는지 알려주는 로그가 포함된 스크롤백을 잃어버린 후에야 이를 인지하게 되었습니다. 따라서 백업에서 수동으로 수정할 수도 없는 상황입니다.
따라서 제 작업은 최소한 일부 오류를 보고한다는 점에서 기존 방식보다 개선되었지만, 파일을 다운로드하고 다운로드된 파일의 SHA1을 확인한 후 로컬 파일로 쓰는 방식이 훨씬 낫습니다. 그 사이, 저는 데이터 손실을 제한하기 위해 처리되지 않은 오류가 발생하면 마이그레이션을 중단하도록 PR을 수정했습니다.
저는 여전히 제 작업이 파레토 개선(pareto improvement)으로 병합되어야 한다고 생각합니다. 상황이 나빠지지 않고 더 좋아지므로, 하지만 올바른 방식으로 처리하는 것이 훨씬 나을 것입니다. 저는 lib/file_store/from_s3_migration.rb를 작성하여 lib/file_store/to_s3_migration.rb와 대칭을 이루게 하는 것이 그 방법이라고 생각하지만, 제가 감당할 수준은 아닙니다.
제 PR이 아직 검토되지 않았기 때문에, 더 많은 내용을 추가했습니다. raw 필드에서 업로드 객체가 존재하지 않는 upload://... 인스턴스를 찾는 uploads:report_missing_uploads 태스크를 추가했습니다. 적어도 제 경우처럼 백업이 있다면, 백업을 통해 고아 파일(orphaned files)을 찾아 사이트로 복원한 후, 해당 게시물을 다시 베이킹(rebake)하여 사라진 이미지를 복원할 수 있습니다. 백업에서 찾을 파일 678개 중 10개를 제외한 나머지를 모두 찾았으므로, 이 테스트를 작성해幸好했습니다. 나머지 게시물들을 다시 베이킹하는 단계로 넘어가기 전에 이 문제를 해결해야 합니다.
…
공유 업로드가 있는 게시물의 다시 베이킹과 게시물 외 업로드를 포함하지 않은 마이그레이션의 첫 번째 단계를 완료했습니다. 원격 백업을 한 번 더 완료한 후, 해당 기능들도 테스트하고 PR에 추가할 계획입니다.
…
이제 마이그레이션의 다음 단계인 다시 베이킹과 게시물 외 업로드 마이그레이션을 테스트하기 시작했습니다. 첫 번째 교훈은 다음 단계로 다시 베이킹을 수행하면 인용된 게시물 내의 게시물 외 업로드 아바타로 인해 실패한다는 것이었습니다. 따라서 게시물 다시 베이킹은 마지막 단계여야 합니다.
다음 발견: 외래 키 제약 조건에 감사드립니다! 게시물 외 업로드를 마이그레이션하는 동안, 게시물 외 업로드를 일반적으로 마이그레이션하기 전에 최소한 프로필을 먼저 마이그레이션해야 한다는 것을 발견했습니다.
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: update or delete on table "uploads" violates foreign key constraint "fk_rails_1d362f2e97" on table "user_profiles"
프로필 마이그레이션은 까다로웠습니다. 프로필에 연결된 두 개의 업로드를 마이그레이션해야 했는데, 일부 사용자는 두 곳에 동일한 이미지를 사용했기 때문에 이 작업을 세 단계로 나누어 수행해야 했습니다. 제 사이트의 S3 URL을 가진 모든 프로필을 성공적으로 마이그레이션했습니다.
모든 게시물 외, 프로필 외 업로드를 마이그레이션했습니다. @RGJ 님, 제가 올린 브랜치를 시도해보고 싶으시다면, 제 작업과 동기화되어 있습니다. 여기 포함된 모든 내용은 이제 완료된 사이트 마이그레이션에 사용되었습니다.
…
@cvx 님, 언제 검토하실지 모르겠지만, 제 PR이 없다면 migrate_from_s3에는 분명히 침묵 데이터 파괴(silent data destruction) 버그가 가득합니다. 제가 한 작업은 완벽하지는 않지만, 실무에서 겪은 많은 버그로부터 보호합니다.
현재까지 하기로 한 모든 작업을 완료했습니다. 제 사이트를 마이그레이션했기 때문에, 요청된 변경 사항조차 효과적으로 평가할 수 없는 상태입니다. 이 PR을 거부하신다면, 기존 마이그레이션을 삭제하는 것을 강력히 권장합니다. 왜냐하면 그것은 사용자의 데이터를 침묵적으로 파괴할 것이기 때문입니다.
PR과 관련하여, CI(현재 버전)에서 다음과 같은 테스트 실패가 가끔 발생합니다:
7867 examples, 0 failures, 11 pending, 1 error occurred outside of examples
##[error]Process completed with exit code 1.
이것은 제 로컬에서는(지금까지) 재현되지 않고, CI 로그에서 CI에서 무엇이 잘못되었는지를 알려주는 정보를 찾지 못했기 때문에, 숨겨진 정보를 찾기 위해 CI 출력을 파고들며 시간을 더 낭비하지 않을 것입니다.
마지막으로 한 가지: 마이그레이션을 완료한 후, 일부 사용자의 프로필 사진이 마이그레이션 중 손실되었음을 발견했고, 그들은 수동으로 복원하고 있습니다. 그걸 성공적으로 하려면 추가 코드가 필요했을 것이라고 추측하지만, 너무 늦게 그 사실을 깨달았기 때문에 이를 검증할 테스트 케이스가 없습니다. 따라서 일부 설정에서 문제를 일으킬 수 있는 남은 버그이지만, 저는 이제 그 추가 수정을 작성할 위치에 있지 않습니다.