forked from treehouse/mastodon
Add (back) rails-level JSON caching (#11333)
parent
59fd622adc
commit
c669bb42ba
|
@ -41,7 +41,7 @@ class AccountsController < ApplicationController
|
||||||
|
|
||||||
format.json do
|
format.json do
|
||||||
expires_in 3.minutes, public: !(authorized_fetch_mode? && signed_request_account.present?)
|
expires_in 3.minutes, public: !(authorized_fetch_mode? && signed_request_account.present?)
|
||||||
render json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, fields: restrict_fields_to
|
render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, fields: restrict_fields_to
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
||||||
|
|
||||||
def show
|
def show
|
||||||
expires_in 3.minutes, public: public_fetch_mode?
|
expires_in 3.minutes, public: public_fetch_mode?
|
||||||
render json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true
|
render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -6,8 +6,7 @@ class Api::V1::CustomEmojisController < Api::BaseController
|
||||||
skip_before_action :set_cache_headers
|
skip_before_action :set_cache_headers
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render_cached_json('api:v1:custom_emojis', expires_in: 1.minute) do
|
expires_in 3.minutes, public: true
|
||||||
ActiveModelSerializers::SerializableResource.new(CustomEmoji.local.where(disabled: false).includes(:category), each_serializer: REST::CustomEmojiSerializer)
|
render_with_cache(each_serializer: REST::CustomEmojiSerializer) { CustomEmoji.local.where(disabled: false).includes(:category) }
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,8 @@ class Api::V1::Instances::ActivityController < Api::BaseController
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render_cached_json('api:v1:instances:activity:show', expires_in: 1.day) { activity }
|
expires_in 1.day, public: true
|
||||||
|
render_with_cache json: :activity, expires_in: 1.day
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -7,7 +7,8 @@ class Api::V1::Instances::PeersController < Api::BaseController
|
||||||
respond_to :json
|
respond_to :json
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render_cached_json('api:v1:instances:peers:index', expires_in: 1.day) { Account.remote.domains }
|
expires_in 1.day, public: true
|
||||||
|
render_with_cache(expires_in: 1.day) { Account.remote.domains }
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -5,8 +5,7 @@ class Api::V1::InstancesController < Api::BaseController
|
||||||
skip_before_action :set_cache_headers
|
skip_before_action :set_cache_headers
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render_cached_json('api:v1:instances', expires_in: 5.minutes) do
|
expires_in 3.minutes, public: true
|
||||||
ActiveModelSerializers::SerializableResource.new({}, serializer: REST::InstanceSerializer)
|
render_with_cache json: {}, serializer: REST::InstanceSerializer
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,7 @@ class ApplicationController < ActionController::Base
|
||||||
include Localized
|
include Localized
|
||||||
include UserTrackingConcern
|
include UserTrackingConcern
|
||||||
include SessionTrackingConcern
|
include SessionTrackingConcern
|
||||||
|
include CacheConcern
|
||||||
|
|
||||||
helper_method :current_account
|
helper_method :current_account
|
||||||
helper_method :current_session
|
helper_method :current_session
|
||||||
|
@ -115,47 +116,10 @@ class ApplicationController < ActionController::Base
|
||||||
current_user.setting_theme
|
current_user.setting_theme
|
||||||
end
|
end
|
||||||
|
|
||||||
def cache_collection(raw, klass)
|
|
||||||
return raw unless klass.respond_to?(:with_includes)
|
|
||||||
|
|
||||||
raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation)
|
|
||||||
cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id)
|
|
||||||
uncached_ids = raw.map(&:id) - cached_keys_with_value.keys
|
|
||||||
|
|
||||||
klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!)
|
|
||||||
|
|
||||||
unless uncached_ids.empty?
|
|
||||||
uncached = klass.where(id: uncached_ids).with_includes.each_with_object({}) { |item, h| h[item.id] = item }
|
|
||||||
|
|
||||||
uncached.each_value do |item|
|
|
||||||
Rails.cache.write(item, item)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact
|
|
||||||
end
|
|
||||||
|
|
||||||
def respond_with_error(code)
|
def respond_with_error(code)
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.any { head code }
|
format.any { head code }
|
||||||
format.html { render "errors/#{code}", layout: 'error', status: code }
|
format.html { render "errors/#{code}", layout: 'error', status: code }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_cached_json(cache_key, **options)
|
|
||||||
options[:expires_in] ||= 3.minutes
|
|
||||||
cache_public = options.key?(:public) ? options.delete(:public) : true
|
|
||||||
content_type = options.delete(:content_type) || 'application/json'
|
|
||||||
|
|
||||||
data = Rails.cache.fetch(cache_key, { raw: true }.merge(options)) do
|
|
||||||
yield.to_json
|
|
||||||
end
|
|
||||||
|
|
||||||
expires_in options[:expires_in], public: cache_public
|
|
||||||
render json: data, content_type: content_type
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_cache_headers
|
|
||||||
response.headers['Vary'] = public_fetch_mode? ? 'Accept' : 'Accept, Signature'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module CacheConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
def render_with_cache(**options)
|
||||||
|
raise ArgumentError, 'only JSON render calls are supported' unless options.key?(:json) || block_given?
|
||||||
|
|
||||||
|
key = options.delete(:key) || [[params[:controller], params[:action]].join('/'), options[:json].respond_to?(:cache_key) ? options[:json].cache_key : nil, options[:fields].nil? ? nil : options[:fields].join(',')].compact.join(':')
|
||||||
|
expires_in = options.delete(:expires_in) || 3.minutes
|
||||||
|
body = Rails.cache.read(key, raw: true)
|
||||||
|
|
||||||
|
if body
|
||||||
|
render(options.except(:json, :serializer, :each_serializer, :adapter, :fields).merge(json: body))
|
||||||
|
else
|
||||||
|
if block_given?
|
||||||
|
options[:json] = yield
|
||||||
|
elsif options[:json].is_a?(Symbol)
|
||||||
|
options[:json] = send(options[:json])
|
||||||
|
end
|
||||||
|
|
||||||
|
render(options)
|
||||||
|
Rails.cache.write(key, response.body, expires_in: expires_in, raw: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.headers['Vary'] = public_fetch_mode? ? 'Accept' : 'Accept, Signature'
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_collection(raw, klass)
|
||||||
|
return raw unless klass.respond_to?(:with_includes)
|
||||||
|
|
||||||
|
raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation)
|
||||||
|
cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id)
|
||||||
|
uncached_ids = raw.map(&:id) - cached_keys_with_value.keys
|
||||||
|
|
||||||
|
klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!)
|
||||||
|
|
||||||
|
unless uncached_ids.empty?
|
||||||
|
uncached = klass.where(id: uncached_ids).with_includes.each_with_object({}) { |item, h| h[item.id] = item }
|
||||||
|
|
||||||
|
uncached.each_value do |item|
|
||||||
|
Rails.cache.write(item, item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,7 +8,7 @@ class EmojisController < ApplicationController
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.json do
|
format.json do
|
||||||
expires_in 3.minutes, public: true
|
expires_in 3.minutes, public: true
|
||||||
render json: @emoji, content_type: 'application/activity+json', serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter
|
render_with_cache json: @emoji, content_type: 'application/activity+json', serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,14 +32,14 @@ class StatusesController < ApplicationController
|
||||||
|
|
||||||
format.json do
|
format.json do
|
||||||
expires_in 3.minutes, public: @status.distributable? && public_fetch_mode?
|
expires_in 3.minutes, public: @status.distributable? && public_fetch_mode?
|
||||||
render json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter
|
render_with_cache json: @status, content_type: 'application/activity+json', serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def activity
|
def activity
|
||||||
expires_in 3.minutes, public: @status.distributable? && public_fetch_mode?
|
expires_in 3.minutes, public: @status.distributable? && public_fetch_mode?
|
||||||
render json: @status, content_type: 'application/activity+json', serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter
|
render_with_cache json: @status, content_type: 'application/activity+json', serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter
|
||||||
end
|
end
|
||||||
|
|
||||||
def embed
|
def embed
|
||||||
|
|
Loading…
Reference in New Issue