データベースから欠落したアップロードの再追加

データベースのアップロードテーブルから多数のアップロードが削除されたようですが、ファイルシステムにはまだ残っているサイトがあります。その結果、これらのファイルへのリンクが壊れています。いくつかは、新しいトピックにファイルを再アップロードしてDBにレコードを作成することで修正しました(当時はファイルも欠落していると思っていましたが、@angus さんがなぜ見つけられなかったのか丁寧に指摘してくれました。いつか質問したいのですが、なぜこれらのアセットに sha1 base62 の両方の名前が必要なのでしょう?)その後、それらのアップロードを含む投稿を以下のように再ベイクしました:

def rebake_posts_with_uploads(topic_id, post_number)
  p = Post.find_by(topic_id: topic_id, post_number: post_number)
  exit unless p
  re = /upload:\/\/(.+?)\)/
  shas= p.raw.scan(re)

  shas.each do |sha|
    posts = Post.where("raw like '%#{sha[0]}%'")
    next unless posts
    puts "Found #{posts.count - 1} #{sha[0]}"
    posts.each do |post|
      next if post.id == p.id
      puts "rebake #{post.id}--#{BASE_URL}/t/-/#{post.topic_id}/#{post.post_number}"
      post.rebake!
    end
  end
end

ファイルシステムにはファイルが存在するがデータベースには存在しないという状況なので、新しい計画として以下のような処理を検討しています:

/shared/uploads/default/original/1x 内のすべてのファイルに対して do |file|
  unless file が uploads テーブルにある
     アップロードレコードを作成
     そのアップロードレコードを含む各投稿に対して
       rebake

これで合っていますか?uploads.rake を見ていますが、これを行う既存のものは見当たりません。これは以下の処理と逆の操作です:

FileUtils.rm(file_path) の代わりに Upload.create を実行するつもりです。

もしこのアプローチが明らかに愚かであるか、もっと良い解決策がある場合は、この小さなラビットホールに飛び込む前に教えていただけると幸いです。

ありがとうございます。

これがどうして起きたのか分かりません。カスタムプラグインのせいにしたいところでしたが、残念ながらそうではないようです。別の議論と関連しているかもしれません。そこで誰かが以下のように述べていました:

「いいね!」 3

はい、当社のアップロードに関するこれまでの経緯はまばらで、これは私たちの責任です。@sam さん、簡単なアドバイスを提供してくれる人をご提案いただけますか?

「いいね!」 2

これで動きますが、ローカルでのテストには注意が必要です。また、2 倍/3 倍のディレクトリも確認してください。アップロードが至るところにあります。

「いいね!」 3

こんな感じでやってみようとしています:

def add_missing_files_to_uploads
  public_directory = Rails.root.join("public").to_s
  db = RailsMultisite::ConnectionManagement.current_db
  uploads_directory = File.join(public_directory, 'uploads', db).to_s
  # アップロードファイルと最適化された画像
  missing = 0
  matched = 0
  Dir.glob("#{uploads_directory}/**/*.pdf").each do |file_path|
    sha1 = Upload.generate_digest(file_path)
    url = file_path.split(public_directory, 2)[1]
    if (Upload.where(sha1: sha1).empty? &&
        Upload.where(url: url).empty?)
      puts "MISSING #{file_path}" if DEBUG
      missing += 1
    else
      matched += 1
    end
  end
  puts "MISS: #{missing}. Match #{matched}"
end

これでもう少し近いですか?最初のテストは失敗したようですが、何か間違えてしまったかもしれません。

@pfaffman さん、こんにちは。このトピックを読んでみました。こちらの私の問題 に対する有望な解決策のように見えます。最終的に良い結果が得られましたか?

申し訳ありませんが、記憶にありません。

サムと私の見解では、これは機能するようです。

私は以前、透明な GIF 画像へのリンクを含む投稿を作成し、その中にすべての画像へのリンクを挿入した後、transparent.gif を含むすべての投稿を再構築することで、シミュレーターの問題を解決しました。

これは、少しより洗練された解決策のように思えます。

データベースのみのバックアップを作成して試してみることをお勧めします。私は現在、少し休暇中ですが、予算があれば、何らかの形でサポートできるかもしれません。

私も ams3 に重要なクライアントを持っています。まだ AWS への移行は行っていませんが、近々移行する予定だと思います。かつて DigitalOcean で働いていた友人が、ams3 を推奨してくれました。当時は同社にとって最高のデータセンターであり、ほとんどが未活用だったからです(これはかなり昔の話です)。しかし、期待したようにはうまくいきませんでした。

「いいね!」 1

これが今のところ最善の方法なのか疑問です。

ひどい S3 移行(古い rake:s3_migrate を使用)の後、ファイルシステム上にあるがデータベースにないアップロードを復元しようとしています。

良い方法というものはあまりありませんね。:crying_cat:

でも、ええ、それがまだ考え方です。それ以来、Discourseで何か変更があったとは思えません。通常、期待通りに動作するかどうかを確認するために、手動でいくつか実行します。また、まずバックアップを取り、サイトを読み取り専用モードに設定すると、何か問題が発生した場合に、作業中に作成された投稿を破棄する必要がなくなります。

フォーラムで簡単に伝えることができる以上のことを知らなければ、それが本当に最善の方法なのか、あるいはもっと簡単な方法があるのかどうかはわかりません。例えば、URLのパスをgsubできるかもしれません。予算がある場合は、私に連絡するか、Marketplace で尋ねることができます。

「いいね!」 1

OK、Claudeとたくさんの賞賛で解決しました。同様または同じ問題を持つ他の誰かを助けるために、私が行ったことを共有します。

これが最も賢明で最適な方法であるかどうかはわかりませんが、私にとっては機能した方法です。

私は専門家ではなく、常に学んでいる初心者であることを念頭に置いて、注意してください。

問題点(S3 → ローカルファイルシステム)

AWS S3からローカルFSに移行した後、多くの画像がtransparent.pngとして表示されました。ファイルは常にディスク上にありましたが、Discourseはそれらを解決できませんでした。

根本原因は壊れた連鎖でした。

  1. upload://の短いURL(base62でエンコードされたSHA1)を持つ投稿
  2. SHA1 → ローカルファイルパスへのデータベースuploadsマッピング。
  3. SHA1ハッシュで名前が付けられたファイルを保存するファイルシステム

移行によりファイルは正しくディスクに移動しましたが、uploads DBレコードは存在しませんでした。一致するレコードがないため、Discourseはtransparent.pngにフォールバックします。

解決策(レコードの作成とリベイク)

孤立したファイルから失われたアップロードレコードの作成:

dir = Rails.root.join("public", "uploads", "default", "original")
created = 0

Dir.glob(dir.join("**", "*")).select { |f| File.file?(f) }.each do |path|
  sha = File.basename(path, File.extname(path))
  next if Upload.find_by(sha1: sha)

  ext = File.extname(path).delete(".")
  relative = path.sub("#{Rails.root}/public", "")

  u = Upload.new
  u.sha1 = sha
  u.url = relative
  u.original_filename = File.basename(path)
  u.filesize = File.size(path)
  u.extension = ext
  u.user_id = -1
  u.save!(validate: false)

  created += 1
  puts "Created upload #{u.id}: #{sha}"
end

puts "Total created: #{created}"

復元されたアップロードを参照する投稿のリベイク:

fixed_posts = 0

Upload.where(user_id: -1).find_each do |u|
  short = u.short_url
  next unless short

  Post.where("raw LIKE '%upload://%'").find_each do |p|
    urls = p.raw.scan(/upload:\/\/^[^\s\]\)]+/)
    urls.each do |url|
      decoded = Upload.sha1_from_short_url(url)
      if decoded == u.sha1
        p.rebake!
        fixed_posts += 1
        puts "Rebaked post #{p.id}"
        break
      end
    end
  end
end

puts "Total rebaked: #{fixed_posts}"

失われた最適化の再生成:

元のファイルを修正した後、最適化されたファイル(1X、2Xなど)を移入する必要があります。

rake uploads:regenerate_missing_optimized

安全なロールバック(念のため)

作成されたすべてのレコードは user_id: -1 を使用します。元に戻すには:

Upload.where(user_id: -1).delete_all

delete_allはコールバックをスキップするため、ファイルシステム上のファイルは変更されません。

以前は誤ってdestroy_allを使用したため、コールバックがトリガーされ、ファイルが墓石(tombstone)に移動されました。

テストに使用した個別のものを回復し、アプローチを再構築しました。

「いいね!」 3