次に扱うべきことが 2 つあります。まず 1 つ目は、制限は有用ですが、あくまで検索空間に対する制限である点です。次に、変更する投稿の最大数を指定できるようにしたいと考えています。そのため、クエリの制限に加え、変更する投稿の最大数も追加したいと考えています。
また、max を指定する際は、デバッグ目的で処理対象を明確に示すことが理にかなっています。したがって、max が nil でない場合は出力を詳細(verbose)にしたいと考えています。これにより、この作業の主なユースケースである「続行する前にプロセスを検証する」ことが可能になります。
私の考えでは、最初の引数に max を、2 番目の引数にオプションの limit を指定するようにします。なぜなら、max が最も重要であり、limit は「1 回のリクエストのみ」のコストを下げるためのものだからです。
2 つ目は、非アップロード(non-upload)関連のアップロードです。1 年以上前、Google+ から Discourse への移行方法を模索している最中に、参加していた Discourse サイトに動画をアップロードしようとしました。その際、生成された URL は https://#{SiteSettings.absolute_base_url}/original/3X/b/a/ba9e06ebc2f4397f26793bb5cd4e169308dd371d.mp4 となっていました。
現在、動画をアップロードすると、 のような形式になります。
直近のテストでは、migrate_from_s3 がこれらの URL を完全に破損させ、もはや URL として機能しない状態にしてしまいました。これは確実に修正する必要があります。その後、このタスクが実際には  形式の URL に遭遇することは少ないと予想されるため、まずは正規表現で両方のケースを処理して可読性を低下させるのではなく、マジックな upload プロトコルの周りに Markdown リンクを追加するアプローチを取りたいと考えています。ただし、この方針を変更する可能性もあります。
video または audio タグは、JavaScript 内の正規表現マッチによって追加されているようです。そのため、それらを正しく識別するために、app/assets/javascripts/discourse/app/lib/uploads.js 内の正規表現をタスクにコピーする必要があります。次回このコードに触れる人が更新元がわかるように、正規表現のソースも記載します。![]()
…
今夜、この作業に取り組む時間を確保し、ドラフトの PR を作成しました。まだ完成していません。バグが残っていることは承知しています。現時点では、upload: 疑似プロトコル URL(画像の標準的なもの)の動作は変更していないつもりですが、健全性チェック(sanity check)を追加しました。
この PR の変更により、通常の upload: 疑似プロトコルによるアップロードと、S3(私の場合は DigitalOcean Spaces)への参照で明示的に参照されている動画を、どちらも正常に移行できました。このコマンドを使用して、1 つの投稿ずつ変更しています。
bin/rake uploads:batch_migrate_from_s3[1,1000]
なお、これはデータベースクエリからある程度ランダムに取得された最初の 1000 件の投稿を超えて実行されません。この低い制限は、1 つの投稿ずつ移行しながらクエリを高速に実行し、その投稿の動作を確認してから、次のものを見つけるために再度開始するためのものです。このコマンドがこのように動作するのは、現在私が作業中の PR がある場合に限られます!
…
作業を進めるにつれて診断出力を追加しており、これが開発以上の重要性を持っていると考えるようになっています。DigitalOcean Spaces からの一時的なダウンロード失敗が多数発生しており、投稿内の一部のファイルのみが移行され、残りが移行されないという状況が見られます。元のコードでは、これは . を表示して処理を続行し、最後に Done と表示するだけですが、実際にはタスクは完了していません。ある投稿を完全に移行するまでに、5〜6 回の実行が必要でした。(最初はローカルバグのデバッグだと思っていたため、回数を数えていませんでした。)診断出力がクリーンになるまで、この移行を同じ制限値で繰り返し実行する必要があると考えています。そのため、max が設定されている場合のみ詳細な進行状況を表示し、どちらの場合でも有用な警告メッセージを表示するようにしています。
現在、Discourse Spaces での断続的なダウンロード失敗に対処するために、以下のハックを使用しています。これにより、実用上の成功率が劇的に向上しています(これまでに数百件の投稿を移行しましたが、3 回のリトライで完全に十分でした)。
https://github.com/johnsonm/discourse/commit/7dfac12a2ea6ec04ba4e0616b4e0dbd1d806cff7
また、Google+ からインポートした際に、設定した制限を超えるサイズの動画が何らかの理由で存在していることを発見しました。いくつかの超大容量動画のワンショット移行を行う間、SiteSettings.max_image_size_kb と SiteSettings.max_attachment_size_kb の両方を一時的に増やす必要がありました。これらの動画がどのようにしてサイト上に存在することになったのかは不明ですが、今はそれらを壊したくありません。超大容量アップロードを許可したバグが、私のインポートスクリプトにあったのか、Discourse 側にあったのか、あるいは単に私が時間経過とともに設定を変更した記憶が間違っていたのかはわかりません。![]()
移行対象の多くが G+ からのインポートだったため、現在のバリデーションに失敗する投稿がいくつかありました。Unhandled failure: Validation failed: Sorry, new users can only put one image in a post というエラーがいくつか発生しましたが、当初はなぜそれが再発しないのか理解できませんでした。実は、アップロードは正常にローカルへ移動され、すべて upload: 疑似プロトコルを使用していたため、生データ(raw)は変更されませんでした。しかし、post.save! はバリデーションエラーで失敗し、post.rebake! が発火しない状態でした。そのため、3 万件の投稿のうちいくつかの投稿に、再焼き直し(rebake)が必要な画像が残っています。残念ながら、どの投稿が該当するかは記録していません。現在、別の修正として post.save!(validate: false) に切り替えたため、この特定の問題は再発しないはずです。未処理エラーで移行を中断するようにしたことは非常に幸いでした。そうでなければ、いくつかの投稿以上の被害が出ていた可能性があります。
…
移行実行中にサイトを利用可能(通知の配信を含む)に保つため、sidekiq キューへのスパムを避けたいと考えています。名前付けは、キャッシュの無効化やオフ・バイ・ワン・エラーと並んで、コンピュータサイエンスにおける最も難しい問題の 2 つのうちの 1 つだとされていますが、移行中の rebake 後に次のアイテムに移行する際に、キューに詰められることができる合計キュースロット数(ジョブスロット数ではありません)を制御するための環境変数 DISCOURSE_MIGRATION_MAX_ENQUEUED を提案します。これにより、キューへのスパムを避け、サイトが機能し続けるようにします。lib/tasks/uploads.rake 内のすべての投稿ごとの rebake に対して、デフォルト値を 0 とするパッチを作成しました。これは、本番環境での移行に使用しています。