Skip to content

Instantly share code, notes, and snippets.

@jrochkind
Created August 25, 2020 20:12
Show Gist options
  • Save jrochkind/b3789d5ee7414c2fc64d45b25e9ffb65 to your computer and use it in GitHub Desktop.
Save jrochkind/b3789d5ee7414c2fc64d45b25e9ffb65 to your computer and use it in GitHub Desktop.
Benchmarking different ways of creating presigned S3 URLs
# A local domain ActiveRecord, Asset#file is a shrine attachment
asset = Asset.last
# load outside of benchmarking
asset.file
s3_object = asset.file.storage.object(asset.file.id)
ITERATIONS = (ENV['ITERATIONS'] || "1200").to_i
$stderr.puts "#{ITERATIONS} iterations\n\n"
def naive_public_url(shrine_file)
"https://#{["#{shrine_file.storage.bucket.name}.s3.amazonaws.com", *shrine_file.storage.prefix, shrine_file.id].join('/')}"
end
if naive_public_url(asset.file) != asset.file.url(public: true)
raise "naive_public_url implementation does not pass:\n\n#{naive_public_url(asset.file)}\n#{asset.file.url(public: true)}"
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
if naive_with_uri_escape_escaping(asset.file) != asset.file.url(public: true)
raise "naive_with_uri_escape_escaping implementation does not pass:\n\n#{naive_with_uri_escape_escaping(asset.file)}\n#{asset.file.url(public: true)}"
end
# Re-use a Presigner to see if it gets us any speed -- hope it's thread-safe?
AWS_CLIENT = asset.file.storage.client
S3_PRESIGNER = Aws::S3::Presigner.new(client: AWS_CLIENT)
def reused_sdk_presigner(shrine_file)
bucket_name = shrine_file.storage.bucket.name
s3_key = [*shrine_file.storage.prefix, shrine_file.id].join("/")
S3_PRESIGNER.presigned_url(
:get_object,
bucket: bucket_name,
key: s3_key
)
end
if reused_sdk_presigner(asset.file) != asset.file.url(public: false)
raise "reused_sdk_presigner implementation does not pass:\n\n#{reused_sdk_presigner(asset.file)}\n#{asset.file.url(public: false)}"
end
AWS_SIG4_SIGNER = Aws::Sigv4::Signer.new(
service: 's3',
region: AWS_CLIENT.config.region,
credentials_provider: AWS_CLIENT.config.credentials,
unsigned_headers: Aws::S3::Presigner::BLACKLISTED_HEADERS,
uri_escape_path: false
)
def direct_aws_sig4_signer(url)
AWS_SIG4_SIGNER.presign_url(
http_method: "GET",
url: url,
headers: {},
body_digest: 'UNSIGNED-PAYLOAD',
expires_in: 900, # seconds
time: nil
).to_s
end
if direct_aws_sig4_signer(naive_public_url(asset.file)) != asset.file.url(public: false)
raise "direct_aws_sig4_signer implementation does not pass:\n\n#{direct_aws_sig4_signer(asset.file)}\n#{asset.file.url(public: false)}"
end
def inline_direct_aws_sig4_signer(url)
signer = Aws::Sigv4::Signer.new(
service: 's3',
region: AWS_CLIENT.config.region,
credentials_provider: AWS_CLIENT.config.credentials,
unsigned_headers: Aws::S3::Presigner::BLACKLISTED_HEADERS,
uri_escape_path: false
)
signer.presign_url(
http_method: "GET",
url: url,
headers: {},
body_digest: 'UNSIGNED-PAYLOAD',
expires_in: 900, # seconds
time: nil
).to_s
end
Benchmark.bmbm do |x|
x.report("sdk public_url") do
ITERATIONS.times do
s3_object.public_url
end
end
x.report("naive S3 public url") do
# May not always properly escape chars that need to be escaped in uris? Not sure, may be fine.
ITERATIONS.times do
naive_public_url(asset.file)
end
end
x.report("naive S3 public url with URI.escape") do
ITERATIONS.times do
naive_with_uri_escape_escaping(asset.file)
end
end
x.report("sdk presigned_url") do
ITERATIONS.times do
s3_object.presigned_url(:get)
end
end
x.report("re-use instantiated SDK Presigner") do
ITERATIONS.times do
reused_sdk_presigner(asset.file)
end
end
x.report("use inline instantiated Aws::Sigv4::Signer directly for presigned url (with escaping)") do
ITERATIONS.times do
inline_direct_aws_sig4_signer(naive_with_uri_escape_escaping(asset.file))
end
end
x.report("Re-use Aws::Sigv4::Signer for presigned url (with escaping)") do
ITERATIONS.times do
direct_aws_sig4_signer(naive_with_uri_escape_escaping(asset.file))
end
end
x.report("Re-use Aws::Sigv4::Signer for presigned url (without escaping)") do
ITERATIONS.times do
direct_aws_sig4_signer(naive_public_url(asset.file))
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment