トピック内の破損したアップロードへのリンクが2.5.0で500エラーを引き起こす

Discourse サイトを運営しており、まずはこのソフトウェアを愛していることを伝えたいと思います。チームの皆様の素晴らしい仕事に感謝します。これは素晴らしいツールです。

2.5.0 へのアップグレード後、大きな問題に直面しました。以下にその概要と私が行った回避策をまとめます。これが今後のバージョンでより良い修正につながることを願っています。

当社のデータベースにあるトピックの一部には、壊れたアップロードが含まれていました。"S3 アップロード"機能を使用していたところ、誤ってバケットを削除してしまいました。はい、それは非常に愚かな行為でしたが、やってしまいました!その結果、すべてのトピックの画像が失われました。

多くの画像を失ったという事実を除けば、すべては正常でした。Discourse は存在しない画像への壊れたリンクを表示していましたが、問題はありませんでした。

しかし、2.5.0 へのアップグレードを試みたところ、状況は荒れ狂いました。ログインしているすべてのユーザーが 500 エラーを取得する一方、匿名の訪問者はウェブサイトを閲覧できました。

調査した結果、Discourse が「ファイルが見つかりません」というエラーで停止していることに気づきました。どうやらファイルをダウンロードしようとしていたようです。/var/www/discourse/app/models/upload.rb 内の local? メソッドを強制的に true に設定する必要があり、それが機能しました。しかし、次のアップグレードについて少し心配しています。

これは明らかに新しい問題です。以前のアップグレードではこのような問題は発生しませんでした。

ところで、トピックに含まれているすべての無効な画像リンクを削除するための方法はあるでしょうか?

ありがとうございます。

逆の動作が発生しました。ログイン済みのユーザーは正常ですが、未ログインのユーザーは接続できず、エラー 500 が表示されます。

そのようなエラーの完全なバックトレースを共有してもらえますか?
/logs ページを確認してください。

これは、互換性のないプラグインが原因である可能性が非常に高いです。どのような非公式プラグインを実行していますか?それらなしで再構築を試みましたか?

はい、私の log/production.log には、Discourse が空の S3 バケット(問題を解決するはずだと期待して再作成したもの)にアクセスした際に、以下の例外が発生していました。

Started GET “/” for 86.246.127.170 at 2020-05-16 14:29:06 +0000
Processing by ListController#latest as HTML
Creating scope :open. Overwriting existing method Poll.open.
Completed 500 Internal Server Error in 3638ms (ActiveRecord: 0.0ms | Allocations: 135090)
NoMethodError (undefined method path' for nil:NilClass) /var/www/discourse/lib/file_store/base_store.rb:150:in cache_file’

これが役立つことを願っています。

ありがとうございます、その可能性がありそうです。実際に Discourse Adsense と discourse-chat-integration の 2 つのプラグインがインストールされています。これらなしで再構築を試してみます。

これはプラグインが原因ではないことを確認しました。最新のバックアップを復元し、docker_manager を除いてプラグインを一切インストールしていない新規インストールでも同じバグが再現しました。

最初に取得したスタックトレースは以下の通りです:

Completed 500 Internal Server Error in 4169ms (ActiveRecord: 0.0ms | Allocations: 72058)
NoMethodError (undefined method path' for nil:NilClass) /var/www/discourse/lib/file_store/base_store.rb:150:in cache_file’

私はこれを以下のように修正しました:

def cache_file(file, filename)
path = get_cache_path_for(filename)
dir = File.dirname(path)
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)

  if file.nil?
    return
  end

  FileUtils.cp(file.path, path)

これにより、2 番目のエラー(初期投稿で言及したエラー)が発生しました:

Started GET “/” for 86.246.127.170 at 2020-05-18 07:37:40 +0000
Processing by CategoriesController#index as HTML
Completed 500 Internal Server Error in 4342ms (ActiveRecord: 0.0ms | Allocations: 60478)
NoMethodError (undefined method path' for nil:NilClass) /var/www/discourse/app/models/upload.rb:193:in fix_dimensions!’

これは local? メソッドを強制的に true にすることで修正しました:

def local?
return true
!(url =~ /^(https?:)?///)
end

過去に、ファイルを S3 に移動させたり、S3 から移動させたりしたことはありますか?

はい、そうしました。しかしその後、バケットが削除されてしまいました。そのため、ローカルストレージに戻しました(もちろん、以前のトピックにはリンク切れの画像リンクが残っています)。

ここでの最善策は、データベースから悪い Upload 行をすべてハード削除することだと思います。

アップロードがないなら、そこに残さないでください。

はい、同意しますが、DBに対して手動でそのような大量削除を実行するのは不安です。適切な方法で実行するアイデアはありますか?それとも本当に「手動」で行うべきでしょうか?

また、Discourseのこの挙動の変化は少し懸念されます。私の理解では、どこかに死んだ画像リンクが存在すると、アプリがクラッシュする可能性があります。

以前はそうではありませんでした。このような状況に対して、より堅牢にするよう提案します。

私たちの環境と非常に似ています:

ウィザードセットアップを再実行することで「解決」しました!

はい、これは同じ問題のようです。@sukria さん、テストに合格した最新バージョンに更新してください。壊れたアップロードに対してより堅牢になるよう修正を追加しました(ただし、壊れたアップロードの参照は引き続き整理してください)。

壊れたリンクを簡単に整理する方法にも興味がありますよ :wink:

@david さん、ありがとうございます。tests-passed に対して新しいビルドを実行したところ、問題なく動作することを確認できました。ありがとうございます!トピック内の無効なリンクを削除するための適切な簡単な方法について、何かご提案はありますか?