commit: cba2897108dffe69d5a16befe6c6220f6eaae59f
parent: 9b8a4484778fb55bcb2526992807a51270229b72
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Sun, 18 Feb 2018 03:14:46 +0100
Cache relationships in API (#6482)
* Cache relationships in API
* Fetch relationships for search results in UI
* Only save one account's maps in each cache item
Diffstat:
8 files changed, 92 insertions(+), 12 deletions(-)
diff --git a/app/javascript/mastodon/actions/search.js b/app/javascript/mastodon/actions/search.js
@@ -1,4 +1,5 @@
import api from '../api';
+import { fetchRelationships } from './accounts';
export const SEARCH_CHANGE = 'SEARCH_CHANGE';
export const SEARCH_CLEAR = 'SEARCH_CLEAR';
@@ -38,6 +39,7 @@ export function submitSearch() {
},
}).then(response => {
dispatch(fetchSearchSuccess(response.data));
+ dispatch(fetchRelationships(response.data.accounts.map(item => item.id)));
}).catch(error => {
dispatch(fetchSearchFail(error));
});
diff --git a/app/models/account_domain_block.rb b/app/models/account_domain_block.rb
@@ -16,12 +16,16 @@ class AccountDomainBlock < ApplicationRecord
belongs_to :account
validates :domain, presence: true, uniqueness: { scope: :account_id }
- after_create :remove_blocking_cache
- after_destroy :remove_blocking_cache
+ after_commit :remove_blocking_cache
+ after_commit :remove_relationship_cache
private
def remove_blocking_cache
Rails.cache.delete("exclude_domains_for:#{account_id}")
end
+
+ def remove_relationship_cache
+ Rails.cache.delete_matched("relationship:#{account_id}:*")
+ end
end
diff --git a/app/models/block.rb b/app/models/block.rb
@@ -12,14 +12,14 @@
class Block < ApplicationRecord
include Paginable
+ include RelationshipCacheable
belongs_to :account
belongs_to :target_account, class_name: 'Account'
validates :account_id, uniqueness: { scope: :target_account_id }
- after_create :remove_blocking_cache
- after_destroy :remove_blocking_cache
+ after_commit :remove_blocking_cache
private
diff --git a/app/models/concerns/relationship_cacheable.rb b/app/models/concerns/relationship_cacheable.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module RelationshipCacheable
+ extend ActiveSupport::Concern
+
+ included do
+ after_commit :remove_relationship_cache
+ end
+
+ private
+
+ def remove_relationship_cache
+ Rails.cache.delete("relationship:#{account_id}:#{target_account_id}")
+ Rails.cache.delete("relationship:#{target_account_id}:#{account_id}")
+ end
+end
diff --git a/app/models/follow.rb b/app/models/follow.rb
@@ -13,6 +13,7 @@
class Follow < ApplicationRecord
include Paginable
+ include RelationshipCacheable
belongs_to :account, counter_cache: :following_count
diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb
@@ -13,6 +13,7 @@
class FollowRequest < ApplicationRecord
include Paginable
+ include RelationshipCacheable
belongs_to :account
belongs_to :target_account, class_name: 'Account'
diff --git a/app/models/mute.rb b/app/models/mute.rb
@@ -13,14 +13,14 @@
class Mute < ApplicationRecord
include Paginable
+ include RelationshipCacheable
belongs_to :account
belongs_to :target_account, class_name: 'Account'
validates :account_id, uniqueness: { scope: :target_account_id }
- after_create :remove_blocking_cache
- after_destroy :remove_blocking_cache
+ after_commit :remove_blocking_cache
private
diff --git a/app/presenters/account_relationships_presenter.rb b/app/presenters/account_relationships_presenter.rb
@@ -5,11 +5,67 @@ class AccountRelationshipsPresenter
:muting, :requested, :domain_blocking
def initialize(account_ids, current_account_id, **options)
- @following = Account.following_map(account_ids, current_account_id).merge(options[:following_map] || {})
- @followed_by = Account.followed_by_map(account_ids, current_account_id).merge(options[:followed_by_map] || {})
- @blocking = Account.blocking_map(account_ids, current_account_id).merge(options[:blocking_map] || {})
- @muting = Account.muting_map(account_ids, current_account_id).merge(options[:muting_map] || {})
- @requested = Account.requested_map(account_ids, current_account_id).merge(options[:requested_map] || {})
- @domain_blocking = Account.domain_blocking_map(account_ids, current_account_id).merge(options[:domain_blocking_map] || {})
+ @account_ids = account_ids.map { |a| a.is_a?(Account) ? a.id : a }
+ @current_account_id = current_account_id
+
+ @following = cached[:following].merge(Account.following_map(@uncached_account_ids, @current_account_id))
+ @followed_by = cached[:followed_by].merge(Account.followed_by_map(@uncached_account_ids, @current_account_id))
+ @blocking = cached[:blocking].merge(Account.blocking_map(@uncached_account_ids, @current_account_id))
+ @muting = cached[:muting].merge(Account.muting_map(@uncached_account_ids, @current_account_id))
+ @requested = cached[:requested].merge(Account.requested_map(@uncached_account_ids, @current_account_id))
+ @domain_blocking = cached[:domain_blocking].merge(Account.domain_blocking_map(@uncached_account_ids, @current_account_id))
+
+ cache_uncached!
+
+ @following.merge!(options[:following_map] || {})
+ @followed_by.merge!(options[:followed_by_map] || {})
+ @blocking.merge!(options[:blocking_map] || {})
+ @muting.merge!(options[:muting_map] || {})
+ @requested.merge!(options[:requested_map] || {})
+ @domain_blocking.merge!(options[:domain_blocking_map] || {})
+ end
+
+ private
+
+ def cached
+ return @cached if defined?(@cached)
+
+ @cached = {
+ following: {},
+ followed_by: {},
+ blocking: {},
+ muting: {},
+ requested: {},
+ domain_blocking: {},
+ }
+
+ @uncached_account_ids = []
+
+ @account_ids.each do |account_id|
+ maps_for_account = Rails.cache.read("relationship:#{@current_account_id}:#{account_id}")
+
+ if maps_for_account.is_a?(Hash)
+ @cached.merge!(maps_for_account)
+ else
+ @uncached_account_ids << account_id
+ end
+ end
+
+ @cached
+ end
+
+ def cache_uncached!
+ @uncached_account_ids.each do |account_id|
+ maps_for_account = {
+ following: { account_id => following[account_id] },
+ followed_by: { account_id => followed_by[account_id] },
+ blocking: { account_id => blocking[account_id] },
+ muting: { account_id => muting[account_id] },
+ requested: { account_id => requested[account_id] },
+ domain_blocking: { account_id => domain_blocking[account_id] },
+ }
+
+ Rails.cache.write("relationship:#{@current_account_id}:#{account_id}", maps_for_account, expires_in: 1.day)
+ end
end
end