kanbanworkflowstimelinescrumrubyroadmapproject-planningproject-managementopenprojectangularissue-trackerifcgantt-chartganttbug-trackerboardsbcf
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
104 lines
4.0 KiB
104 lines
4.0 KiB
module Group::Destroy
|
|
##
|
|
# Instead of firing of separate queries for each and every Member and MemberRole
|
|
# instance upon group deletion this implementation does most of the deletion
|
|
# in a hand full of aggregate queries.
|
|
#
|
|
# Instead of doing
|
|
#
|
|
# Member:
|
|
# before_destroy :remove_from_category_assignments
|
|
# after_destroy :unwatch_from_permission_change
|
|
# after_destroy :destroy_notification
|
|
#
|
|
# MemberRole:
|
|
# after_destroy :remove_role_from_group_users
|
|
#
|
|
# for every row all relevant roles are deleted within 5 mass delete queries
|
|
# + 1 query for each member instance for each group itself + number of watchers
|
|
# among the users in the deleted group.
|
|
#
|
|
# Example:
|
|
#
|
|
# Given: 150 projects and 1 group with 20 users which is member in every project
|
|
#
|
|
# That makes 150 * 20 3000 Member rows and also 3000 MemberRole rows.
|
|
#
|
|
# Without this patch this would result in at least 4 queries for each member
|
|
# (the callbacks mentioned above + the deletion of the member) and 2 queries
|
|
# for each MemberRole (callback mentioned above + deletion of the actual MemberRole).
|
|
# Altogether that makes:
|
|
#
|
|
# num_queries_pre_patch = 3000 * 4 + 3000 * 2 + W = 18000 + W
|
|
#
|
|
# Where W is the number of watchers among the users in the destroyed group.
|
|
# The actual number is actually even higher as for the callbacks a bunch of read queries
|
|
# (loading the project, the user, etc.) are triggered, too.
|
|
#
|
|
# With this patch the number of queries is reduced to the 5 + 1 for each group member
|
|
# as explained above, making it:
|
|
#
|
|
# num_queries_post_patch = 5 + 150 + W = 155 + W
|
|
#
|
|
def destroy
|
|
MemberRole.transaction do
|
|
members = Member.table_name
|
|
member_roles = MemberRole.table_name
|
|
|
|
# Store all project/user combinations for later watcher pruning
|
|
# See: Member#unwatch_from_permission_change
|
|
user_id_and_project_id = Member
|
|
.joins(
|
|
"INNER JOIN #{member_roles} umr
|
|
ON #{members}.id = umr.member_id
|
|
INNER JOIN #{member_roles} gmr
|
|
ON umr.inherited_from = gmr.id
|
|
INNER JOIN #{members} gm
|
|
ON gm.id = gmr.member_id AND gm.user_id = #{id}"
|
|
)
|
|
.distinct
|
|
.pluck(:user_id, :project_id)
|
|
|
|
user_ids, project_ids = user_id_and_project_id.each_with_object([[], []]) do |element, array|
|
|
array.first << element.first
|
|
array.last << element.last
|
|
end
|
|
|
|
users = User.find(user_ids)
|
|
|
|
# Delete all MemberRoles created through this group for each user within it.
|
|
MemberRole
|
|
.joins("INNER JOIN #{member_roles} b on #{member_roles}.inherited_from = b.id")
|
|
.joins("INNER JOIN #{members} on #{members}.id = b.member_id")
|
|
.where("#{members}.user_id" => self.id) # group ID
|
|
.delete_all
|
|
|
|
# Delete all MemberRoles associating this group itself with a project.
|
|
MemberRole
|
|
.joins("INNER JOIN #{members} on #{members}.id = #{member_roles}.member_id")
|
|
.where("#{members}.user_id" => self.id)
|
|
.delete_all
|
|
|
|
Watcher.prune(user: users, project_id: project_ids)
|
|
|
|
# Destroy member instances for this group itself to trigger
|
|
# member destroyed notifications.
|
|
Member
|
|
.where(user_id: self.id)
|
|
.destroy_all
|
|
|
|
# Remove category based auto assignments for this member.
|
|
# See: Member#remove_from_category_assignments
|
|
Category
|
|
.joins("INNER JOIN #{members}
|
|
ON #{members}.project_id = categories.project_id
|
|
AND #{members}.user_id = categories.assigned_to_id")
|
|
.update_all "assigned_to_id = NULL"
|
|
|
|
self.users.delete_all # remove all users from this group
|
|
self.reload # so associated member instances are not destroyed again
|
|
|
|
super # destroy the actual, now empty group
|
|
end
|
|
end
|
|
end
|
|
|