-
- Downloads
There was an error fetching the commit references. Please try again later.
Merge branch 'rework-authorizations-performance' into 'master'
Rework project authorizations and nested groups for better performance See merge request !10885
parent
6f2e590e
No related branches found
No related tags found
Pipeline #
Showing
- app/controllers/groups_controller.rb 2 additions, 0 deletionsapp/controllers/groups_controller.rb
- app/models/concerns/routable.rb 0 additions, 83 deletionsapp/models/concerns/routable.rb
- app/models/concerns/select_for_project_authorization.rb 5 additions, 1 deletionapp/models/concerns/select_for_project_authorization.rb
- app/models/group.rb 5 additions, 1 deletionapp/models/group.rb
- app/models/namespace.rb 9 additions, 15 deletionsapp/models/namespace.rb
- app/models/project_authorization.rb 6 additions, 0 deletionsapp/models/project_authorization.rb
- app/models/user.rb 13 additions, 21 deletionsapp/models/user.rb
- app/services/users/refresh_authorized_projects_service.rb 7 additions, 33 deletionsapp/services/users/refresh_authorized_projects_service.rb
- app/views/groups/_show_nav.html.haml 4 additions, 3 deletionsapp/views/groups/_show_nav.html.haml
- changelogs/unreleased/rework-authorizations-performance.yml 6 additions, 0 deletionschangelogs/unreleased/rework-authorizations-performance.yml
- config/initializers/postgresql_cte.rb 132 additions, 0 deletionsconfig/initializers/postgresql_cte.rb
- db/migrate/20170503140201_reschedule_project_authorizations.rb 44 additions, 0 deletions...grate/20170503140201_reschedule_project_authorizations.rb
- db/migrate/20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb 123 additions, 0 deletions...40202_turn_nested_groups_into_regular_groups_for_mysql.rb
- db/migrate/20170504182103_add_index_project_group_links_group_id.rb 19 additions, 0 deletions.../20170504182103_add_index_project_group_links_group_id.rb
- db/post_migrate/20170503120310_remove_users_authorized_projects_populated.rb 15 additions, 0 deletions...70503120310_remove_users_authorized_projects_populated.rb
- db/schema.rb 2 additions, 1 deletiondb/schema.rb
- doc/user/group/subgroups/index.md 9 additions, 0 deletionsdoc/user/group/subgroups/index.md
- lib/api/entities.rb 4 additions, 1 deletionlib/api/entities.rb
- lib/api/groups.rb 5 additions, 1 deletionlib/api/groups.rb
- lib/api/v3/entities.rb 4 additions, 1 deletionlib/api/v3/entities.rb
config/initializers/postgresql_cte.rb
0 → 100644
# See http://doc.gitlab.com/ce/development/migration_style_guide.html | ||
# for more information on how to write migrations for GitLab. | ||
# This migration depends on code external to it. For example, it relies on | ||
# updating a namespace to also rename directories (uploads, GitLab pages, etc). | ||
# The alternative is to copy hundreds of lines of code into this migration, | ||
# adjust them where needed, etc; something which doesn't work well at all. | ||
class TurnNestedGroupsIntoRegularGroupsForMysql < ActiveRecord::Migration | ||
|
||
include Gitlab::Database::MigrationHelpers | ||
# Set this constant to true if this migration requires downtime. | ||
DOWNTIME = false | ||
def run_migration? | ||
Gitlab::Database.mysql? | ||
end | ||
def up | ||
return unless run_migration? | ||
# For all sub-groups we need to give the right people access. We do this as | ||
# follows: | ||
# | ||
# 1. Get all the ancestors for the current namespace | ||
# 2. Get all the members of these namespaces, along with their higher access | ||
# level | ||
# 3. Give these members access to the current namespace | ||
Namespace.unscoped.where('parent_id IS NOT NULL').find_each do |namespace| | ||
rows = [] | ||
existing = namespace.members.pluck(:user_id) | ||
all_members_for(namespace).each do |member| | ||
next if existing.include?(member[:user_id]) | ||
rows << { | ||
access_level: member[:access_level], | ||
source_id: namespace.id, | ||
source_type: 'Namespace', | ||
user_id: member[:user_id], | ||
notification_level: 3, # global | ||
type: 'GroupMember', | ||
created_at: Time.current, | ||
updated_at: Time.current | ||
} | ||
end | ||
bulk_insert_members(rows) | ||
# This method relies on the parent to determine the proper path. | ||
# Because we reset "parent_id" this method will not return the right path | ||
# when moving namespaces. | ||
full_path_was = namespace.send(:full_path_was) | ||
namespace.define_singleton_method(:full_path_was) { full_path_was } | ||
namespace.update!(parent_id: nil, path: new_path_for(namespace)) | ||
end | ||
end | ||
def down | ||
# There is no way to go back from regular groups to nested groups. | ||
end | ||
# Generates a new (unique) path for a namespace. | ||
def new_path_for(namespace) | ||
counter = 1 | ||
base = namespace.full_path.tr('/', '-') | ||
new_path = base | ||
while Namespace.unscoped.where(path: new_path).exists? | ||
new_path = base + "-#{counter}" | ||
counter += 1 | ||
end | ||
new_path | ||
end | ||
# Returns an Array containing all the ancestors of the current namespace. | ||
# | ||
# This method is not particularly efficient, but it's probably still faster | ||
# than using the "routes" table. Most importantly of all, it _only_ depends | ||
# on the namespaces table and the "parent_id" column. | ||
def ancestors_for(namespace) | ||
ancestors = [] | ||
current = namespace | ||
while current&.parent_id | ||
# We're using find_by(id: ...) here to deal with cases where the | ||
# parent_id may point to a missing row. | ||
current = Namespace.unscoped.select([:id, :parent_id]). | ||
find_by(id: current.parent_id) | ||
ancestors << current.id if current | ||
end | ||
ancestors | ||
end | ||
# Returns a relation containing all the members that have access to any of | ||
# the current namespace's parent namespaces. | ||
def all_members_for(namespace) | ||
Member. | ||
unscoped. | ||
select(['user_id', 'MAX(access_level) AS access_level']). | ||
where(source_type: 'Namespace', source_id: ancestors_for(namespace)). | ||
group(:user_id) | ||
end | ||
def bulk_insert_members(rows) | ||
return if rows.empty? | ||
keys = rows.first.keys | ||
tuples = rows.map do |row| | ||
row.map { |(_, value)| connection.quote(value) } | ||
end | ||
execute <<-EOF.strip_heredoc | ||
INSERT INTO members (#{keys.join(', ')}) | ||
VALUES #{tuples.map { |tuple| "(#{tuple.join(', ')})" }.join(', ')} | ||
EOF | ||
end | ||
end |