PERF: Update Group#user_count counter cache outside DB transaction (#19256)

While load testing our user creation code path in production, we
identified that executing the DB statement to update the `Group#user_count` column within a
transaction is creating a bottleneck for us. This is because the
creation of a user and addition of the user to the relevant groups are
done in a transaction. When we execute the DB statement to update
`Group#user_count` for the relevant group, a row level lock is held
until the transaction completes. This row level lock acts like a global
lock when the server is creating users that will be added to the same
group in quick succession.

Instead of updating the counter cache within a transaction which the
default ActiveRecord `counter_cache` option does, we simply update the
counter cache outside of the committing transaction.

Co-authored-by: Rafael dos Santos Silva <xfalcox@gmail.com>

Co-authored-by: Rafael dos Santos Silva <xfalcox@gmail.com>
This commit is contained in:
Alan Guo Xiang Tan
2022-11-30 22:52:08 +08:00
committed by GitHub
parent 9f022112e3
commit 7c321d3aad
7 changed files with 52 additions and 11 deletions

View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true
class GroupUser < ActiveRecord::Base
belongs_to :group, counter_cache: "user_count"
belongs_to :group
belongs_to :user
after_save :update_title
@@ -15,6 +15,9 @@ class GroupUser < ActiveRecord::Base
after_save :set_category_notifications
after_save :set_tag_notifications
after_commit :increase_group_user_count, on: [:create]
after_commit :decrease_group_user_count, on: [:destroy]
def self.notification_levels
NotificationLevels.all
end
@@ -186,6 +189,14 @@ class GroupUser < ActiveRecord::Base
end
end
end
def increase_group_user_count
Group.increment_counter(:user_count, self.group_id)
end
def decrease_group_user_count
Group.decrement_counter(:user_count, self.group_id)
end
end
# == Schema Information