mirror of
https://github.com/discourse/discourse.git
synced 2024-11-21 16:38:15 -06:00
PERF: Add GlobalSetting to redirect avatars instead of proxying (#19281)
When uploads are stored on S3, by default Discourse will fetch the avatars and proxy them through to the requesting client. This is simple, but it can lead to significant inbound/outbound network load in the hosting environment. This commit adds an optional redirect_avatar_requests GlobalSetting. When enabled, requests for user avatars will be redirected to the S3 asset instead of being proxied. This adds an extra round-trip for clients, but it should significantly reduce server load. To mitigate that extra round-trip for clients, a CDN with 'follow redirect' capability could be used.
This commit is contained in:
parent
cc5b4cd49a
commit
badd64ceee
@ -125,6 +125,8 @@ class UserAvatarsController < ApplicationController
|
||||
if optimized.local?
|
||||
optimized_path = Discourse.store.path_for(optimized)
|
||||
image = optimized_path if File.exist?(optimized_path)
|
||||
elsif GlobalSetting.redirect_avatar_requests
|
||||
return redirect_s3_avatar(Discourse.store.cdn_url(optimized.url))
|
||||
else
|
||||
return proxy_avatar(Discourse.store.cdn_url(optimized.url), upload.created_at)
|
||||
end
|
||||
@ -179,6 +181,11 @@ class UserAvatarsController < ApplicationController
|
||||
send_file path, disposition: nil
|
||||
end
|
||||
|
||||
def redirect_s3_avatar(url)
|
||||
immutable_for 1.hour
|
||||
redirect_to url, allow_other_host: true
|
||||
end
|
||||
|
||||
# this protects us from a DoS
|
||||
def render_blank
|
||||
path = Rails.root + "public/images/avatar.png"
|
||||
|
@ -359,3 +359,6 @@ long_polling_interval =
|
||||
|
||||
# Moves asset preloading from tags in the response document head to response headers
|
||||
preload_link_header = false
|
||||
|
||||
# When using an external upload store, redirect `user_avatar` requests instead of proxying
|
||||
redirect_avatar_requests = false
|
||||
|
@ -100,6 +100,33 @@ RSpec.describe UserAvatarsController do
|
||||
expect(response).to redirect_to("http://awesome.com/boom/user_avatar/default/#{user.encoded_username(lower: true)}/98/#{upload.id}_#{OptimizedImage::VERSION}.png")
|
||||
end
|
||||
|
||||
it "redirects to external store when enabled" do
|
||||
global_setting :redirect_avatar_requests, true
|
||||
setup_s3
|
||||
SiteSetting.avatar_sizes = "100|49"
|
||||
SiteSetting.s3_cdn_url = "https://s3-cdn.example.com"
|
||||
set_cdn_url("https://app-cdn.example.com")
|
||||
|
||||
upload = Fabricate(:upload, url: "//#{SiteSetting.s3_upload_bucket}.s3.dualstack.us-west-1.amazonaws.com/upload/path")
|
||||
|
||||
optimized_image = Fabricate(:optimized_image,
|
||||
sha1: SecureRandom.hex << "A" * 8,
|
||||
upload: upload,
|
||||
width: 98,
|
||||
height: 98,
|
||||
url: "//#{SiteSetting.s3_upload_bucket}.s3.dualstack.us-west-1.amazonaws.com/optimized/path",
|
||||
version: OptimizedImage::VERSION
|
||||
)
|
||||
|
||||
user = Fabricate(:user, uploaded_avatar_id: upload.id)
|
||||
|
||||
get "/user_avatar/default/#{user.username}/98/#{upload.id}.png"
|
||||
|
||||
expect(response.status).to eq(302)
|
||||
expect(response.location).to eq("https://s3-cdn.example.com/optimized/path")
|
||||
expect(response.headers["Cache-Control"]).to eq('max-age=31556952, public, immutable')
|
||||
end
|
||||
|
||||
it 'serves new version for old urls' do
|
||||
user = Fabricate(:user)
|
||||
SiteSetting.avatar_sizes = "45"
|
||||
|
Loading…
Reference in New Issue
Block a user