解决 GitLab 无法清理孤儿上传文件的问题
后知后觉 暂无评论

搭建 GitLab Geo 后,在同步状态页中可以查看到大量的 Uploads 文件同步失败,查看日志,报错类似于 Error during verification: undefined method `underscore' for NilClass:ClassThe model which owns this Upload is missing.

是因为原实例中存在大量的孤儿上传文件,孤儿文件的意思就是其引用或所属的资源已经被删除,但文件本身未被删除。

官方解决方案

官方文档中提供了一个用于清理的脚本,如下:

def delete_orphaned_uploads(dry_run: true)
  if dry_run
    p "This is a dry run. Upload rows will only be printed."
  else
    p "This is NOT A DRY RUN! Upload rows will be deleted from the DB!"
  end

  subquery = Geo::UploadState.where("(verification_failure LIKE 'Error during verification: The model which owns this Upload is missing.%' OR verification_failure = 'Error during verification: undefined method `underscore'' for NilClass:Class') AND verification_checksum IS NULL")
  uploads = Upload.where(upload_state: subquery)
  p "Found #{uploads.count} uploads with a model that does not exist"

  uploads_deleted = 0
  begin
    uploads.each do |upload|

      if dry_run
        p upload
      else
        uploads_deleted=uploads_deleted + 1
        p upload.destroy!
      end
    rescue => e
      puts "checking upload #{upload.id} failed with #{e.message}"
    end
  end

  p "#{uploads_deleted} remote objects were destroyed." unless dry_run
end

但实际执行后发现并未清理任何文件。参考其 changelog ,发现其脚本中存在错误的语法,使用下方的正确脚本可以进行清理。

清理方案

原脚本中的 SQL 语句拼接的有问题,无法正确筛选以 Error during verification: undefined method `underscore'' 开头的项导致无法清理。

def delete_orphaned_uploads(dry_run: true)
  if dry_run
    p "This is a dry run. Upload rows will only be printed."
  else
    p "This is NOT A DRY RUN! Upload rows will be deleted from the DB!"
  end

  subquery = Geo::UploadState.where("(verification_failure LIKE 'Error during verification: The model which owns this Upload is missing.%' OR verification_failure LIKE 'Error during verification: undefined method `underscore'' for NilClass:Class%') AND verification_checksum IS NULL")
  uploads = Upload.where(upload_state: subquery)
  p "Found #{uploads.count} uploads with a model that does not exist"

  uploads_deleted = 0
  begin
    uploads.each do |upload|

      if dry_run
        p upload
      else
        uploads_deleted=uploads_deleted + 1
        p upload.destroy!
      end
    rescue => e
      puts "checking upload #{upload.id} failed with #{e.message}"
    end
  end

  p "#{uploads_deleted} remote objects were destroyed." unless dry_run
end

粘贴进 Rails 里执行,然后执行模拟运行:

delete_orphaned_uploads(dry_run: true)

然后实际执行清理:

delete_orphaned_uploads(dry_run: false)

附录

参考链接

如果遇到问题或者对文章内容存疑,请在下方留言,博主看到后将及时回复,谢谢!
回复 / 查看「历史评论
回答28+19=