Skip to content

Instantly share code, notes, and snippets.

@jrochkind
Created August 24, 2020 16:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jrochkind/764fb0985b7af3c8d9252e33d47d713e to your computer and use it in GitHub Desktop.
Save jrochkind/764fb0985b7af3c8d9252e33d47d713e to your computer and use it in GitHub Desktop.
Comparing S3 public_url generation implementations for performance
# A local domain ActiveRecord, Asset#file is a shrine attachment
asset = Asset.last
asset.file # load outside of benchmarking
s3_object = asset.file.storage.object(asset.file.id) # Aws::S3::Object
require 'erb'
require 'escape_utils'
def naive_public_url(shrine_file)
"https://#{["#{shrine_file.storage.bucket.name}.s3.amazonaws.com", *shrine_file.storage.prefix, shrine_file.id].join('/')}"
end
def naive_with_escaping(shrine_file)
path = shrine_file.id.gsub(/[^\/]+/) { |s| ERB::Util.url_encode(s) }
"https://#{["#{shrine_file.storage.bucket.name}.s3.amazonaws.com", *shrine_file.storage.prefix, shrine_file.id].join('/')}"
end
# escape_utils looks unmaintained, but has a faster C implementation...
#
# https://github.com/brianmario/escape_utils
# https://rubygems.org/gems/escape_utils/
#
# Note this doesn't escape the same as original implementation... but original implementation over-escapes!
def naive_with_escape_utils_escaping(shrine_file)
# because EscapeUtils.escape_uri does NOT escape `/`, we don't need to split it, which is what
# actually saves us the time.
path = EscapeUtils.escape_uri(shrine_file.id)
"https://#{["#{shrine_file.storage.bucket.name}.s3.amazonaws.com", *shrine_file.storage.prefix, shrine_file.id].join('/')}"
end
# URI.escape may do exactly what we need without requiring splitting on "/", but
# it's sadly deprecated when it really ought not to be
def naive_with_uri_escape_escaping(shrine_file)
# because URI.escape does NOT escape `/`, we don't need to split it, which is what
# actually saves us the time.
path = URI.escape(shrine_file.id)
"https://#{["#{shrine_file.storage.bucket.name}.s3.amazonaws.com", *shrine_file.storage.prefix, shrine_file.id].join('/')}"
end
class Aws::S3::Object
def faster_public_url(options = {})
url = bucket.url(options)
url << "/" unless url.end_with?('/')
url << key.gsub(/[^\/]+/) { |s| ERB::Util.url_encode(s) }
end
end
ITERATIONS = (ENV['ITERATIONS'] || "1200").to_i
puts "Iterations: #{ITERATIONS}"
Benchmark.bmbm do |x|
x.report("original AWS SDK public_url implementation") do
ITERATIONS.times do
s3_object.public_url
end
end
x.report("optimized implementation") do
ITERATIONS.times do
s3_object.faster_public_url
end
end
x.report("naive implementation") do
ITERATIONS.times do
naive_public_url(asset.file)
end
end
x.report("naive with escaping") do
ITERATIONS.times do
naive_with_escaping(asset.file)
end
end
x.report("naive with escape_utils escaping") do
ITERATIONS.times do
naive_with_escape_utils_escaping(asset.file)
end
end
x.report("naive with URI.escape escaping") do
ITERATIONS.times do
naive_with_uri_escape_escaping(asset.file)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment