diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/account_stat.rb | 42 | ||||
-rw-r--r-- | app/models/concerns/account_counters.rb | 60 | ||||
-rw-r--r-- | app/models/notification.rb | 12 |
3 files changed, 64 insertions, 50 deletions
diff --git a/app/models/account_stat.rb b/app/models/account_stat.rb index e70b54d79..a826a9af3 100644 --- a/app/models/account_stat.rb +++ b/app/models/account_stat.rb @@ -18,46 +18,4 @@ class AccountStat < ApplicationRecord belongs_to :account, inverse_of: :account_stat update_index('accounts#account', :account) - - def increment_count!(key) - update(attributes_for_increment(key)) - rescue ActiveRecord::StaleObjectError, ActiveRecord::RecordNotUnique - begin - reload_with_id - rescue ActiveRecord::RecordNotFound - return - end - - retry - end - - def decrement_count!(key) - update(attributes_for_decrement(key)) - rescue ActiveRecord::StaleObjectError, ActiveRecord::RecordNotUnique - begin - reload_with_id - rescue ActiveRecord::RecordNotFound - return - end - - retry - end - - private - - def attributes_for_increment(key) - attrs = { key => public_send(key) + 1 } - attrs[:last_status_at] = Time.now.utc if key == :statuses_count - attrs - end - - def attributes_for_decrement(key) - attrs = { key => [public_send(key) - 1, 0].max } - attrs - end - - def reload_with_id - self.id = self.class.find_by!(account: account).id if new_record? - reload - end end diff --git a/app/models/concerns/account_counters.rb b/app/models/concerns/account_counters.rb index 6e25e1905..fd3f161ad 100644 --- a/app/models/concerns/account_counters.rb +++ b/app/models/concerns/account_counters.rb @@ -3,6 +3,8 @@ module AccountCounters extend ActiveSupport::Concern + ALLOWED_COUNTER_KEYS = %i(statuses_count following_count followers_count).freeze + included do has_one :account_stat, inverse_of: :account after_save :save_account_stat @@ -14,11 +16,65 @@ module AccountCounters :following_count=, :followers_count, :followers_count=, - :increment_count!, - :decrement_count!, :last_status_at, to: :account_stat + # @param [Symbol] key + def increment_count!(key) + update_count!(key, 1) + end + + # @param [Symbol] key + def decrement_count!(key) + update_count!(key, -1) + end + + # @param [Symbol] key + # @param [Integer] value + def update_count!(key, value) + raise ArgumentError, "Invalid key #{key}" unless ALLOWED_COUNTER_KEYS.include?(key) + raise ArgumentError, 'Do not call update_count! on dirty objects' if association(:account_stat).loaded? && account_stat&.changed? && account_stat.changed_attribute_names_to_save == %w(id) + + value = value.to_i + default_value = value.positive? ? value : 0 + + # We do an upsert using manually written SQL, as Rails' upsert method does + # not seem to support writing expressions in the UPDATE clause, but only + # re-insert the provided values instead. + # Even ARel seem to be missing proper handling of upserts. + sql = if value.positive? && key == :statuses_count + <<-SQL.squish + INSERT INTO account_stats(account_id, #{key}, created_at, updated_at, last_status_at) + VALUES (:account_id, :default_value, now(), now(), now()) + ON CONFLICT (account_id) DO UPDATE + SET #{key} = account_stats.#{key} + :value, + last_status_at = now(), + lock_version = account_stats.lock_version + 1, + updated_at = now() + RETURNING id; + SQL + else + <<-SQL.squish + INSERT INTO account_stats(account_id, #{key}, created_at, updated_at) + VALUES (:account_id, :default_value, now(), now()) + ON CONFLICT (account_id) DO UPDATE + SET #{key} = account_stats.#{key} + :value, + lock_version = account_stats.lock_version + 1, + updated_at = now() + RETURNING id; + SQL + end + + sql = AccountStat.sanitize_sql([sql, account_id: id, default_value: default_value, value: value]) + account_stat_id = AccountStat.connection.exec_query(sql)[0]['id'] + + # Reload account_stat if it was loaded, taking into account newly-created unsaved records + if association(:account_stat).loaded? + account_stat.id = account_stat_id if account_stat.new_record? + account_stat.reload + end + end + def account_stat super || build_account_stat end diff --git a/app/models/notification.rb b/app/models/notification.rb index 98a6a618f..3bf9dd483 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -49,12 +49,12 @@ class Notification < ApplicationRecord belongs_to :from_account, class_name: 'Account', optional: true belongs_to :activity, polymorphic: true, optional: true - belongs_to :mention, foreign_type: 'Mention', foreign_key: 'activity_id', optional: true - belongs_to :status, foreign_type: 'Status', foreign_key: 'activity_id', optional: true - belongs_to :follow, foreign_type: 'Follow', foreign_key: 'activity_id', optional: true - belongs_to :follow_request, foreign_type: 'FollowRequest', foreign_key: 'activity_id', optional: true - belongs_to :favourite, foreign_type: 'Favourite', foreign_key: 'activity_id', optional: true - belongs_to :poll, foreign_type: 'Poll', foreign_key: 'activity_id', optional: true + belongs_to :mention, foreign_key: 'activity_id', optional: true + belongs_to :status, foreign_key: 'activity_id', optional: true + belongs_to :follow, foreign_key: 'activity_id', optional: true + belongs_to :follow_request, foreign_key: 'activity_id', optional: true + belongs_to :favourite, foreign_key: 'activity_id', optional: true + belongs_to :poll, foreign_key: 'activity_id', optional: true validates :type, inclusion: { in: TYPES } |