]> cat aescling's git repositories - mastodon.git/commitdiff
Add cache buster feature for media files (#15155)
authorEugen Rochko <eugen@zeonfederated.com>
Thu, 19 Nov 2020 16:38:06 +0000 (17:38 +0100)
committerGitHub <noreply@github.com>
Thu, 19 Nov 2020 16:38:06 +0000 (17:38 +0100)
Nginx can be configured to bypass proxy cache when a special header
is in the request. If the response is cacheable, it will replace
the cache for that request. Proxy caching of media files is
desirable when using object storage as a way of minimizing bandwidth
costs, but has the drawback of leaving deleted media files for
a configured amount of cache time. A cache buster can make those
media files immediately unavailable. This especially makes sense
when suspending and unsuspending an account.

app/lib/cache_buster.rb [new file with mode: 0644]
app/services/suspend_account_service.rb
app/services/unsuspend_account_service.rb
app/workers/cache_buster_worker.rb [new file with mode: 0644]
config/initializers/cache_buster.rb [new file with mode: 0644]
config/initializers/paperclip.rb

diff --git a/app/lib/cache_buster.rb b/app/lib/cache_buster.rb
new file mode 100644 (file)
index 0000000..0356115
--- /dev/null
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class CacheBuster
+  def initialize(options = {})
+    @secret_header = options[:secret_header] || 'Secret-Header'
+    @secret        = options[:secret] || 'True'
+  end
+
+  def bust(url)
+    site = Addressable::URI.parse(url).normalized_site
+
+    request_pool.with(site) do |http_client|
+      build_request(url, http_client).perform
+    end
+  end
+
+  private
+
+  def request_pool
+    RequestPool.current
+  end
+
+  def build_request(url, http_client)
+    Request.new(:get, url, http_client: http_client).tap do |request|
+      request.add_headers(@secret_header => @secret)
+    end
+  end
+end
index 7c70a6021b88cdf33fc01aa73596daadc472a7bf..19d65280d97b7be52f6d10dcb6f3122c3464410e 100644 (file)
@@ -78,6 +78,8 @@ class SuspendAccountService < BaseService
               Rails.logger.warn "Tried to change permission on non-existent file #{attachment.path(style)}"
             end
           end
+
+          CacheBusterWorker.perform_async(attachment.path(style)) if Rails.configuration.x.cache_buster_enabled
         end
       end
     end
index a81d1ac4f70a0e42d95eb9fe20cd58f46c2a9958..f07a3f053b4c07fdbae2899f4a80dbd30e048a5f 100644 (file)
@@ -69,6 +69,8 @@ class UnsuspendAccountService < BaseService
               Rails.logger.warn "Tried to change permission on non-existent file #{attachment.path(style)}"
             end
           end
+
+          CacheBusterWorker.perform_async(attachment.path(style)) if Rails.configuration.x.cache_buster_enabled
         end
       end
     end
diff --git a/app/workers/cache_buster_worker.rb b/app/workers/cache_buster_worker.rb
new file mode 100644 (file)
index 0000000..5ad0a44
--- /dev/null
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class CacheBusterWorker
+  include Sidekiq::Worker
+  include RoutingHelper
+
+  sidekiq_options queue: 'pull'
+
+  def perform(path)
+    cache_buster.bust(full_asset_url(path))
+  end
+
+  private
+
+  def cache_buster
+    CacheBuster.new(Rails.configuration.x.cache_buster)
+  end
+end
diff --git a/config/initializers/cache_buster.rb b/config/initializers/cache_buster.rb
new file mode 100644 (file)
index 0000000..227e450
--- /dev/null
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+Rails.application.configure do
+  config.x.cache_buster_enabled = ENV['CACHE_BUSTER_ENABLED'] == 'true'
+
+  config.x.cache_buster = {
+    secret_header: ENV['CACHE_BUSTER_SECRET_HEADER'],
+    secret: ENV['CACHE_BUSTER_SECRET'],
+  }
+end
index b841d52203ca664893587d3ad4a0c3b39b57ec95..25adcd8d634c6f452bc6e005532b922a34476871 100644 (file)
@@ -107,7 +107,6 @@ elsif ENV['SWIFT_ENABLED'] == 'true'
 else
   Paperclip::Attachment.default_options.merge!(
     storage: :filesystem,
-    use_timestamp: true,
     path: File.join(ENV.fetch('PAPERCLIP_ROOT_PATH', File.join(':rails_root', 'public', 'system')), ':prefix_path:class', ':attachment', ':id_partition', ':style', ':filename'),
     url: ENV.fetch('PAPERCLIP_ROOT_URL', '/system') + '/:prefix_url:class/:attachment/:id_partition/:style/:filename',
   )