]> cat aescling's git repositories - mastodon.git/commitdiff
Performance improvement for notifications API
authorEugen Rochko <eugen@zeonfederated.com>
Mon, 21 Nov 2016 15:10:42 +0000 (16:10 +0100)
committerEugen Rochko <eugen@zeonfederated.com>
Mon, 21 Nov 2016 15:10:42 +0000 (16:10 +0100)
app/controllers/api/v1/apps_controller.rb
app/controllers/api/v1/notifications_controller.rb
app/controllers/api_controller.rb
app/views/api/v1/accounts/show.rabl
app/views/api/v1/statuses/_show.rabl

index fb95928a8b9fbb4b53fa6241eaeea0735054a2f2..1b33770f4e32a08de49eb06d3f43115d5786802a 100644 (file)
@@ -4,6 +4,6 @@ class Api::V1::AppsController < ApiController
   respond_to :json
 
   def create
-    @app = Doorkeeper::Application.create!(name: params[:client_name], redirect_uri: params[:redirect_uris], scopes: params[:scopes])
+    @app = Doorkeeper::Application.create!(name: params[:client_name], redirect_uri: params[:redirect_uris], scopes: (params[:scopes] || Doorkeeper.configuration.default_scopes))
   end
 end
index 63abee6b526762358afc9ec30dfbff13b7628a9d..c76189e875c39627dd661908fccbaab63b56e553 100644 (file)
@@ -8,8 +8,11 @@ class Api::V1::NotificationsController < ApiController
 
   def index
     @notifications = Notification.where(account: current_account).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
+    statuses       = @notifications.select { |n| !n.target_status.nil? }.map(&:target_status)
 
-    set_maps(@notifications.select { |n| !n.target_status.nil? }.map(&:target_status))
+    set_maps(statuses)
+    set_counters_maps(statuses)
+    set_account_counters_maps(@notifications.map(&:from_account))
 
     next_path = api_v1_notifications_url(max_id: @notifications.last.id)    if @notifications.size == 20
     prev_path = api_v1_notifications_url(since_id: @notifications.first.id) unless @notifications.empty?
index aafaf843cbdf077af5ccc737468b0f66150b7943..d880400a8f7547573ab6cb024742a41f1fcac002 100644 (file)
@@ -89,4 +89,17 @@ class ApiController < ApplicationController
     @reblogs_map    = Status.reblogs_map(status_ids, current_account)
     @favourites_map = Status.favourites_map(status_ids, current_account)
   end
+
+  def set_counters_maps(statuses) # rubocop:disable Style/AccessorMethodName
+    status_ids             = statuses.map { |s| s.reblog? ? s.reblog_of_id : s.id }.uniq
+    @favourites_counts_map = Favourite.select('status_id, COUNT(id) AS favourites_count').group('status_id').where(status_id: status_ids).map { |f| [f.status_id, f.favourites_count] }.to_h
+    @reblogs_counts_map    = Status.select('statuses.id, COUNT(reblogs.id) AS reblogs_count').joins('LEFT OUTER JOIN statuses AS reblogs ON statuses.id = reblogs.reblog_of_id').where(id: status_ids).group('statuses.id').map { |r| [r.id, r.reblogs_count] }.to_h
+  end
+
+  def set_account_counters_maps(accounts) # rubocop:disable Style/AccessorMethodName
+    account_ids = accounts.map(&:id)
+    @followers_counts_map = Follow.unscoped.select('target_account_id, COUNT(account_id) AS followers_count').group('target_account_id').where(target_account_id: account_ids).map { |f| [f.target_account_id, f.followers_count] }.to_h
+    @following_counts_map = Follow.unscoped.select('account_id, COUNT(target_account_id) AS following_count').group('account_id').where(account_id: account_ids).map { |f| [f.account_id, f.following_count] }.to_h
+    @statuses_counts_map  = Status.unscoped.select('account_id, COUNT(id) AS statuses_count').group('account_id').where(account_id: account_ids).map { |s| [s.account_id, s.statuses_count] }.to_h
+  end
 end
index 623329059087a8c1df7c7e8f646ca59166507f62..c01349ef23f5a29e72cd8ab18907b0d6d53e6bb1 100644 (file)
@@ -6,6 +6,6 @@ node(:note)            { |account| Formatter.instance.simplified_format(account)
 node(:url)             { |account| TagManager.instance.url_for(account) }
 node(:avatar)          { |account| full_asset_url(account.avatar.url(:large, false)) }
 node(:header)          { |account| full_asset_url(account.header.url(:medium, false)) }
-node(:followers_count) { |account| account.try(:followers_count) || account.followers.count }
-node(:following_count) { |account| account.try(:following_count) || account.following.count }
-node(:statuses_count)  { |account| account.try(:statuses_count)  || account.statuses.count  }
+node(:followers_count) { |account| defined?(@followers_counts_map) ? (@followers_counts_map[account.id] || 0) : (account.try(:followers_count) || account.followers.count) }
+node(:following_count) { |account| defined?(@following_counts_map) ? (@following_counts_map[account.id] || 0) : (account.try(:following_count) || account.following.count) }
+node(:statuses_count)  { |account| defined?(@statuses_counts_map)  ? (@statuses_counts_map[account.id]  || 0) : (account.try(:statuses_count)  || account.statuses.count) }
index 3435d1039c32c3fb0ad2196bbb4799991ac55bb0..90457eca9375de2326fa6db1eb383abc80b059ad 100644 (file)
@@ -3,8 +3,8 @@ attributes :id, :created_at, :in_reply_to_id
 node(:uri)              { |status| TagManager.instance.uri_for(status) }
 node(:content)          { |status| Formatter.instance.format(status) }
 node(:url)              { |status| TagManager.instance.url_for(status) }
-node(:reblogs_count)    { |status| status.reblogs_count }
-node(:favourites_count) { |status| status.favourites_count }
+node(:reblogs_count)    { |status| defined?(@reblogs_counts_map)    ? (@reblogs_counts_map[status.id]    || 0) : status.reblogs_count }
+node(:favourites_count) { |status| defined?(@favourites_counts_map) ? (@favourites_counts_map[status.id] || 0) : status.favourites_count }
 
 child :account do
   extends 'api/v1/accounts/show'