uploads: generate thumbnails in parallel.
Make uploads faster by generating and saving thumbnails in parallel. We generate each thumbnail in parallel, then send each thumbnail to the backend image servers in parallel. Most images have 5 variants: 'preview' (150x150), 180x180, 360x360, 720x720, and 'sample' (850px width). Plus the original file, that's 6 files we have to save. In production we have 2 image servers, so we have to save each file twice, to 2 remote servers. Doing all this in parallel should make uploads significantly faster.
This commit is contained in:
@@ -30,7 +30,7 @@ class StorageManager
|
||||
# written, or an error is raised and the original file is left unchanged. The
|
||||
# file should never be in a partially written state.
|
||||
#
|
||||
# @param io [IO] a file (or a readable IO object)
|
||||
# @param src_file [File] the file to store
|
||||
# @param path [String] the remote path where the file should be stored
|
||||
def store(io, path)
|
||||
raise NotImplementedError, "store not implemented"
|
||||
|
||||
@@ -12,13 +12,12 @@ class StorageManager::Local < StorageManager
|
||||
super(**options)
|
||||
end
|
||||
|
||||
def store(io, dest_path)
|
||||
def store(src_file, dest_path)
|
||||
temp_path = full_path(dest_path) + "-" + SecureRandom.uuid + ".tmp"
|
||||
|
||||
FileUtils.mkdir_p(File.dirname(temp_path))
|
||||
io.rewind
|
||||
bytes_copied = IO.copy_stream(io, temp_path)
|
||||
raise Error, "store failed: #{bytes_copied}/#{io.size} bytes copied" if bytes_copied != io.size
|
||||
bytes_copied = IO.copy_stream(src_file.path, temp_path)
|
||||
raise Error, "store failed: #{bytes_copied}/#{src_file.size} bytes copied" if bytes_copied != src_file.size
|
||||
|
||||
FileUtils.chmod(DEFAULT_PERMISSIONS, temp_path)
|
||||
File.rename(temp_path, full_path(dest_path))
|
||||
|
||||
@@ -9,14 +9,14 @@ class StorageManager::Mirror < StorageManager
|
||||
super(**options)
|
||||
end
|
||||
|
||||
def store(io, dest_path)
|
||||
services.each do |service|
|
||||
service.store(io, dest_path)
|
||||
def store(src_file, dest_path)
|
||||
Parallel.each(services, in_threads: Etc.nprocessors) do |service|
|
||||
service.store(src_file, dest_path)
|
||||
end
|
||||
end
|
||||
|
||||
def delete(path)
|
||||
services.each do |service|
|
||||
Parallel.each(services, in_threads: Etc.nprocessors) do |service|
|
||||
service.delete(path)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,7 +7,7 @@ class StorageManager::Null < StorageManager
|
||||
super(base_url: nil)
|
||||
end
|
||||
|
||||
def store(io, path)
|
||||
def store(src_file, path)
|
||||
# no-op
|
||||
end
|
||||
|
||||
|
||||
@@ -271,7 +271,7 @@ class MediaAsset < ApplicationRecord
|
||||
end
|
||||
|
||||
def distribute_files!(media_file)
|
||||
variants.each do |variant|
|
||||
Parallel.each(variants, in_threads: Etc.nprocessors) do |variant|
|
||||
variant.store_file!(media_file)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user