Commit 520b70a8 authored by Reuben Pereira's avatar Reuben Pereira Committed by Alper Akgun

Remove duplicate members before joining with users table

If there are duplicate members when joining with the users
table, it results in a lot of unnecessary memory usage.

The members table can contain the same user_id multiple times
if the user_id is part of multiple groups/projects. Currently, the
SQL query generated by Group#billed_project_members joins the
members and users tables. The duplicate user_ids in the members
table can result in unnecessary memory usage.
This commit modifies Group#billed_project_members to filter out
duplicate members rows first in a subquery, and the subquery is then
used as an IN condition to query the users table.

It also renames Group#billed_project_members to
Group#billed_project_users.
parent 75173a79
......@@ -108,10 +108,14 @@ class Member < ApplicationRecord
scope :active_without_invites_and_requests, -> do
left_join_users
.where(users: { state: 'active' })
.non_request
.without_invites_and_requests
.reorder(nil)
end
scope :without_invites_and_requests, -> do
non_request
.non_invite
.non_minimal_access
.reorder(nil)
end
scope :invite, -> { where.not(invite_token: nil) }
......
......@@ -513,34 +513,34 @@ module EE
def billed_user_ids_excluding_guests
strong_memoize(:billed_user_ids_excluding_guests) do
group_member_ids = billed_group_members.non_guests.distinct.pluck(:user_id)
project_member_ids = billed_project_members.non_guests.distinct.pluck(:user_id)
shared_group_ids = billed_shared_non_guests_group_members.non_guests.distinct.pluck(:user_id)
shared_project_ids = billed_invited_non_guests_group_to_project_members.non_guests.distinct.pluck(:user_id)
group_member_user_ids = billed_group_members.non_guests.distinct.pluck(:user_id)
project_member_user_ids = billed_project_users(non_guests: true).distinct.pluck(:id)
shared_group_user_ids = billed_shared_non_guests_group_members.non_guests.distinct.pluck(:user_id)
shared_project_user_ids = billed_invited_non_guests_group_to_project_members.non_guests.distinct.pluck(:user_id)
{
user_ids: (group_member_ids + project_member_ids + shared_group_ids + shared_project_ids).to_set,
group_member_user_ids: group_member_ids.to_set,
project_member_user_ids: project_member_ids.to_set,
shared_group_user_ids: shared_group_ids.to_set,
shared_project_user_ids: shared_project_ids.to_set
user_ids: (group_member_user_ids + project_member_user_ids + shared_group_user_ids + shared_project_user_ids).to_set,
group_member_user_ids: group_member_user_ids.to_set,
project_member_user_ids: project_member_user_ids.to_set,
shared_group_user_ids: shared_group_user_ids.to_set,
shared_project_user_ids: shared_project_user_ids.to_set
}
end
end
def billed_user_ids_including_guests
strong_memoize(:billed_user_ids_including_guests) do
group_member_ids = billed_group_members.distinct.pluck(:user_id)
project_member_ids = billed_project_members.distinct.pluck(:user_id)
shared_group_ids = billed_shared_group_members.distinct.pluck(:user_id)
shared_project_ids = billed_invited_group_to_project_members.distinct.pluck(:user_id)
group_member_user_ids = billed_group_members.distinct.pluck(:user_id)
project_member_user_ids = billed_project_users.distinct.pluck(:id)
shared_group_user_ids = billed_shared_group_members.distinct.pluck(:user_id)
shared_project_user_ids = billed_invited_group_to_project_members.distinct.pluck(:user_id)
{
user_ids: (group_member_ids + project_member_ids + shared_group_ids + shared_project_ids).to_set,
group_member_user_ids: group_member_ids.to_set,
project_member_user_ids: project_member_ids.to_set,
shared_group_user_ids: shared_group_ids.to_set,
shared_project_user_ids: shared_project_ids.to_set
user_ids: (group_member_user_ids + project_member_user_ids + shared_group_user_ids + shared_project_user_ids).to_set,
group_member_user_ids: group_member_user_ids.to_set,
project_member_user_ids: project_member_user_ids.to_set,
shared_group_user_ids: shared_group_user_ids.to_set,
shared_project_user_ids: shared_project_user_ids.to_set
}
end
end
......@@ -553,10 +553,18 @@ module EE
end
# Members belonging directly to Projects within Group or Projects within subgroups
def billed_project_members
::ProjectMember.active_without_invites_and_requests.without_project_bots.where(
def billed_project_users(non_guests: false)
members = ::ProjectMember.without_invites_and_requests
members = members.non_guests if non_guests
user_ids = members.where(
source_id: ::Project.joins(:group).where(namespace: self_and_descendants)
)
.distinct
.select(:user_id)
::User.with_state(:active).without_project_bot.where(id: user_ids)
end
# Members belonging to Groups invited to collaborate with Projects
......
......@@ -408,6 +408,20 @@ RSpec.describe Member do
it { is_expected.not_to include @member_with_minimal_access }
end
describe '.without_invites_and_requests' do
subject { described_class.without_invites_and_requests.to_a }
it { is_expected.to include @owner }
it { is_expected.to include @maintainer }
it { is_expected.not_to include @invited_member }
it { is_expected.to include @accepted_invite_member }
it { is_expected.not_to include @requested_member }
it { is_expected.to include @accepted_request_member }
it { is_expected.to include @blocked_maintainer }
it { is_expected.to include @blocked_developer }
it { is_expected.not_to include @member_with_minimal_access }
end
describe '.connected_to_user' do
subject { described_class.connected_to_user.to_a }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment