Browse Source

Add `tootctl cache recount` command (#11597)

Eugen Rochko 4 years ago
parent
commit
96702e7f67
3 changed files with 52 additions and 3 deletions
  1. 2 1
      app/models/concerns/account_counters.rb
  2. 5 2
      app/models/status.rb
  3. 45 0
      lib/mastodon/cache_cli.rb

+ 2 - 1
app/models/concerns/account_counters.rb

@@ -26,7 +26,8 @@ module AccountCounters
   private
 
   def save_account_stat
-    return unless account_stat&.changed?
+    return unless association(:account_stat).loaded? && account_stat&.changed?
+
     account_stat.save
   end
 end

+ 5 - 2
app/models/status.rb

@@ -388,13 +388,16 @@ class Status < ApplicationRecord
     end
   end
 
+  def status_stat
+    super || build_status_stat
+  end
+
   private
 
   def update_status_stat!(attrs)
     return if marked_for_destruction? || destroyed?
 
-    record = status_stat || build_status_stat
-    record.update(attrs)
+    status_stat.update(attrs)
   end
 
   def store_uri

+ 45 - 0
lib/mastodon/cache_cli.rb

@@ -15,5 +15,50 @@ module Mastodon
       Rails.cache.clear
       say('OK', :green)
     end
+
+    desc 'recount TYPE', 'Update hard-cached counters'
+    long_desc <<~LONG_DESC
+      Update hard-cached counters of TYPE by counting referenced
+      records from scratch. TYPE can be "accounts" or "statuses".
+
+      It may take a very long time to finish, depending on the
+      size of the database.
+    LONG_DESC
+    def recount(type)
+      processed = 0
+
+      case type
+      when 'accounts'
+        Account.local.includes(:account_stat).find_each do |account|
+          account_stat                 = account.account_stat
+          account_stat.following_count = account.active_relationships.count
+          account_stat.followers_count = account.passive_relationships.count
+          account_stat.statuses_count  = account.statuses.where.not(visibility: :direct).count
+
+          account_stat.save if account_stat.changed?
+
+          processed += 1
+          say('.', :green, false)
+        end
+      when 'statuses'
+        Status.includes(:status_stat).find_each do |status|
+          status_stat                  = status.status_stat
+          status_stat.replies_count    = status.replies.where.not(visibility: :direct).count
+          status_stat.reblogs_count    = status.reblogs.count
+          status_stat.favourites_count = status.favourites.count
+
+          status_stat.save if status_stat.changed?
+
+          processed += 1
+          say('.', :green, false)
+        end
+      else
+        say("Unknown type: #{type}", :red)
+        exit(1)
+      end
+
+      say
+      say("OK, recounted #{processed} records", :green)
+    end
   end
 end