DEV: Put a mutex around `Upload.migrate_to_new_scheme`.

This ensures that only one migration is running at any given point in
time across the instances.
This commit is contained in:
Guo Xiang Tan 2019-04-24 17:07:10 +08:00
parent 55f406bb79
commit 3c9495b989
1 changed files with 85 additions and 77 deletions

View File

@ -205,97 +205,105 @@ class Upload < ActiveRecord::Base
def self.migrate_to_new_scheme(limit: nil)
problems = []
if SiteSetting.migrate_to_new_scheme
max_file_size_kb = [SiteSetting.max_image_size_kb, SiteSetting.max_attachment_size_kb].max.kilobytes
local_store = FileStore::LocalStore.new
db = RailsMultisite::ConnectionManagement.current_db
DistributedMutex.synchronize("migrate_upload_to_new_scheme") do
if SiteSetting.migrate_to_new_scheme
max_file_size_kb = [
SiteSetting.max_image_size_kb,
SiteSetting.max_attachment_size_kb
].max.kilobytes
scope = Upload.by_users.where("url NOT LIKE '%/original/_X/%' AND url LIKE '%/uploads/#{db}%'").order(id: :desc)
local_store = FileStore::LocalStore.new
db = RailsMultisite::ConnectionManagement.current_db
scope = scope.limit(limit) if limit
scope = Upload.by_users
.where("url NOT LIKE '%/original/_X/%' AND url LIKE '%/uploads/#{db}%'")
.order(id: :desc)
if scope.count == 0
SiteSetting.migrate_to_new_scheme = false
return problems
end
scope = scope.limit(limit) if limit
remap_scope = nil
if scope.count == 0
SiteSetting.migrate_to_new_scheme = false
return problems
end
scope.each do |upload|
begin
# keep track of the url
previous_url = upload.url.dup
# where is the file currently stored?
external = previous_url =~ /^\/\//
# download if external
if external
url = SiteSetting.scheme + ":" + previous_url
remap_scope = nil
begin
retries ||= 0
scope.each do |upload|
begin
# keep track of the url
previous_url = upload.url.dup
# where is the file currently stored?
external = previous_url =~ /^\/\//
# download if external
if external
url = SiteSetting.scheme + ":" + previous_url
file = FileHelper.download(
url,
max_file_size: max_file_size_kb,
tmp_file_name: "discourse",
follow_redirect: true
)
rescue OpenURI::HTTPError
retry if (retires += 1) < 1
next
begin
retries ||= 0
file = FileHelper.download(
url,
max_file_size: max_file_size_kb,
tmp_file_name: "discourse",
follow_redirect: true
)
rescue OpenURI::HTTPError
retry if (retires += 1) < 1
next
end
path = file.path
else
path = local_store.path_for(upload)
end
# compute SHA if missing
if upload.sha1.blank?
upload.sha1 = Upload.generate_digest(path)
end
path = file.path
else
path = local_store.path_for(upload)
end
# compute SHA if missing
if upload.sha1.blank?
upload.sha1 = Upload.generate_digest(path)
end
# store to new location & update the filesize
File.open(path) do |f|
upload.url = Discourse.store.store_upload(f, upload)
upload.filesize = f.size
upload.save!(validate: false)
end
# remap the URLs
DbHelper.remap(UrlHelper.absolute(previous_url), upload.url) unless external
# store to new location & update the filesize
File.open(path) do |f|
upload.url = Discourse.store.store_upload(f, upload)
upload.filesize = f.size
upload.save!(validate: false)
end
# remap the URLs
DbHelper.remap(UrlHelper.absolute(previous_url), upload.url) unless external
DbHelper.remap(
previous_url,
upload.url,
excluded_tables: %w{
posts
post_search_data
}
)
DbHelper.remap(
previous_url,
upload.url,
excluded_tables: %w{
posts
post_search_data
}
)
remap_scope ||= begin
Post.with_deleted
.where("raw ~ '/uploads/#{db}/\\d+/' OR raw ~ '/uploads/#{db}/original/\\d{1}/'")
.select(:raw, :cooked)
.all
end
remap_scope ||= begin
Post.with_deleted
.where("raw ~ '/uploads/#{db}/\\d+/' OR raw ~ '/uploads/#{db}/original/\\d{1}/'")
.select(:raw, :cooked)
.all
end
remap_scope.each do |post|
post.raw.gsub!(previous_url, upload.url)
post.cooked.gsub!(previous_url, upload.url)
post.save! if post.changed?
end
remap_scope.each do |post|
post.raw.gsub!(previous_url, upload.url)
post.cooked.gsub!(previous_url, upload.url)
post.save! if post.changed?
upload.optimized_images.find_each(&:destroy!)
upload.rebake_posts_on_old_scheme
# remove the old file (when local)
unless external
FileUtils.rm(path, force: true)
end
rescue => e
problems << { upload: upload, ex: e }
ensure
file&.unlink
file&.close
end
upload.optimized_images.find_each(&:destroy!)
upload.rebake_posts_on_old_scheme
# remove the old file (when local)
unless external
FileUtils.rm(path, force: true)
end
rescue => e
problems << { upload: upload, ex: e }
ensure
file&.unlink
file&.close
end
end
end