summaryrefslogtreecommitdiff
path: root/app/models/project_team.rb
diff options
context:
space:
mode:
authorStan Hu <stanhu@gmail.com>2016-07-19 21:52:31 -0700
committerStan Hu <stanhu@gmail.com>2016-07-26 15:33:05 -0700
commitd1ea2bca61dff21948024d897e1d4475123a10e8 (patch)
treedb9a7093c70ece66eb66b09bf7d6288246ae25cd /app/models/project_team.rb
parent95efb6f1163b7c2c40d03ddd834016905fc45b50 (diff)
downloadgitlab-ce-d1ea2bca61dff21948024d897e1d4475123a10e8.tar.gz
Optimize maximum user access level lookup in loading of notes
NotesHelper#note_editable? and ProjectTeam#human_max_access currently take about 16% of the load time of an issue page. This MR preloads the maximum access level of users for all notes in issues and merge requests with several queries instead of one per user and caches the result in RequestStore.
Diffstat (limited to 'app/models/project_team.rb')
-rw-r--r--app/models/project_team.rb46
1 files changed, 35 insertions, 11 deletions
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index 9d312a53790..67faea1f9f3 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -132,22 +132,41 @@ class ProjectTeam
Gitlab::Access.options_with_owner.key(max_member_access(user_id))
end
- # This method assumes project and group members are eager loaded for optimal
- # performance.
- def max_member_access(user_id)
- access = []
+ # Determine the maximum access level for a group of users in bulk.
+ #
+ # Returns a Hash mapping user ID -> maxmum access level.
+ def max_member_access_for_user_ids(user_ids)
+ user_ids = user_ids.uniq
+ key = "max_member_access:#{project.id}"
+ RequestStore.store[key] ||= Hash.new
+ access = RequestStore.store[key]
- access += project.members.where(user_id: user_id).has_access.pluck(:access_level)
+ # Lookup only the IDs we need
+ user_ids = user_ids - access.keys
- if group
- access += group.members.where(user_id: user_id).has_access.pluck(:access_level)
- end
+ if user_ids.present?
+ user_ids.map { |id| access[id] = Gitlab::Access::NO_ACCESS }
- if project.invited_groups.any? && project.allowed_to_share_with_group?
- access << max_invited_level(user_id)
+ member_access = project.members.where(user_id: user_ids).has_access.pluck(:user_id, :access_level).to_h
+ merge_max!(access, member_access)
+
+ if group
+ group_access = group.members.where(user_id: user_ids).has_access.pluck(:user_id, :access_level).to_h
+ merge_max!(access, group_access)
+ end
+
+ if project.invited_groups.any? && project.allowed_to_share_with_group?
+ # Not optimized
+ invited_levels = user_ids.map { |id| [id, max_invited_level(id)] }.to_h
+ merge_max!(access, invited_levels)
+ end
end
- access.compact.max
+ access
+ end
+
+ def max_member_access(user_id)
+ max_member_access_for_user_ids([user_id])[user_id]
end
private
@@ -156,6 +175,7 @@ class ProjectTeam
project.project_group_links.map do |group_link|
invited_group = group_link.group
access = invited_group.group_members.find_by(user_id: user_id).try(:access_field)
+ access = Gitlab::Access::NO_ACCESS unless access.present?
# If group member has higher access level we should restrict it
# to max allowed access level
@@ -215,4 +235,8 @@ class ProjectTeam
def group
project.group
end
+
+ def merge_max!(first_hash, second_hash)
+ first_hash.merge!(second_hash) { |_key, old, new| old > new ? old : new }
+ end
end