From c009a6ae36d4129f77537bdd9897068d91a404d1 Mon Sep 17 00:00:00 2001
From: Sam Saffron <sam.saffron@gmail.com>
Date: Fri, 26 Apr 2019 18:11:39 +1000
Subject: [PATCH] PERF: speed up user deletion logic

Previously any user deletion would scan a very large number of tables

This avoids scans on 6 tables/indexes on delete
---
 app/models/user.rb                                     | 10 +++++++++-
 ...0190426074404_add_missing_user_destroyer_indexes.rb | 10 ++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)
 create mode 100644 db/migrate/20190426074404_add_missing_user_destroyer_indexes.rb

diff --git a/app/models/user.rb b/app/models/user.rb
index 095061d8123..f26ac53661f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -49,7 +49,9 @@ class User < ActiveRecord::Base
   has_many :user_warnings
   has_many :user_archived_messages, dependent: :destroy
   has_many :email_change_requests, dependent: :destroy
-  has_many :directory_items, dependent: :delete_all
+
+  # see before_destroy
+  has_many :directory_items
   has_many :user_auth_tokens, dependent: :destroy
   has_many :user_auth_token_logs, dependent: :destroy
 
@@ -141,6 +143,12 @@ class User < ActiveRecord::Base
     # we need to bypass the default scope here, which appears not bypassed for :delete_all
     # however :destroy it is bypassed
     PostAction.with_deleted.where(user_id: self.id).delete_all
+
+    # This is a perf optimisation to ensure we hit the index
+    # without this we need to scan a much larger number of rows
+    DirectoryItem.where(user_id: self.id)
+      .where('period_type in (?)', DirectoryItem.period_types.values)
+      .delete_all
   end
 
   # Skip validating email, for example from a particular auth provider plugin
diff --git a/db/migrate/20190426074404_add_missing_user_destroyer_indexes.rb b/db/migrate/20190426074404_add_missing_user_destroyer_indexes.rb
new file mode 100644
index 00000000000..3d41773e5db
--- /dev/null
+++ b/db/migrate/20190426074404_add_missing_user_destroyer_indexes.rb
@@ -0,0 +1,10 @@
+class AddMissingUserDestroyerIndexes < ActiveRecord::Migration[5.2]
+  def change
+    # these indexes are required to make deletions of users fast
+    add_index :user_actions, [:target_user_id], where: 'target_user_id IS NOT NULL'
+    add_index :post_actions, [:user_id]
+    add_index :user_uploads, [:user_id, :upload_id]
+    add_index :user_auth_token_logs, [:user_id]
+    add_index :topic_link, [:user_id]
+  end
+end