diff options
Diffstat (limited to 'app/services/authorized_project_update')
4 files changed, 87 insertions, 22 deletions
diff --git a/app/services/authorized_project_update/periodic_recalculate_service.rb b/app/services/authorized_project_update/periodic_recalculate_service.rb index 662d10c648b..16dc76eb4cf 100644 --- a/app/services/authorized_project_update/periodic_recalculate_service.rb +++ b/app/services/authorized_project_update/periodic_recalculate_service.rb @@ -9,7 +9,12 @@ module AuthorizedProjectUpdate # Using this approach (instead of eg. User.each_batch) keeps the arguments # the same for AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker # even if the user list changes, so we can deduplicate these jobs. - (1..User.maximum(:id)).each_slice(BATCH_SIZE).with_index do |batch, index| + + # Since UserRefreshOverUserRangeWorker has set data_consistency to delayed, + # a job enqueued without a delay could fail because the replica could not catch up with the primary. + # To prevent this, we start the index from `1` instead of `0` so as to ensure that + # no UserRefreshOverUserRangeWorker job is enqueued without a delay. + (1..User.maximum(:id)).each_slice(BATCH_SIZE).with_index(1) do |batch, index| delay = DELAY_INTERVAL * index AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker.perform_in(delay, *batch.minmax) end diff --git a/app/services/authorized_project_update/project_group_link_create_service.rb b/app/services/authorized_project_update/project_group_link_create_service.rb index 090b22a7820..e9e7c56d7c7 100644 --- a/app/services/authorized_project_update/project_group_link_create_service.rb +++ b/app/services/authorized_project_update/project_group_link_create_service.rb @@ -49,7 +49,7 @@ module AuthorizedProjectUpdate def access_level(membership_access_level) return membership_access_level unless group_access - # access level must not be higher than the max access level set when + # access level (role) must not be higher than the max access level (role) set when # creating the project share [membership_access_level, group_access].min end diff --git a/app/services/authorized_project_update/project_recalculate_service.rb b/app/services/authorized_project_update/project_recalculate_service.rb new file mode 100644 index 00000000000..cbb8efaf99f --- /dev/null +++ b/app/services/authorized_project_update/project_recalculate_service.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +module AuthorizedProjectUpdate + class ProjectRecalculateService + # Service for refreshing all the authorizations to a particular project. + include Gitlab::Utils::StrongMemoize + BATCH_SIZE = 1000 + + def initialize(project) + @project = project + end + + def execute + refresh_authorizations if needs_refresh? + ServiceResponse.success + end + + private + + attr_reader :project + + def needs_refresh? + user_ids_to_remove.any? || + authorizations_to_create.any? + end + + def current_authorizations + strong_memoize(:current_authorizations) do + project.project_authorizations + .pluck(:user_id, :access_level) # rubocop: disable CodeReuse/ActiveRecord + end + end + + def fresh_authorizations + strong_memoize(:fresh_authorizations) do + result = [] + + Projects::Members::EffectiveAccessLevelFinder.new(project) + .execute + .each_batch(of: BATCH_SIZE, column: :user_id) do |member_batch| + result += member_batch.pluck(:user_id, 'MAX(access_level)') # rubocop: disable CodeReuse/ActiveRecord + end + + result + end + end + + def user_ids_to_remove + strong_memoize(:user_ids_to_remove) do + (current_authorizations - fresh_authorizations) + .map {|user_id, _| user_id } + end + end + + def authorizations_to_create + strong_memoize(:authorizations_to_create) do + (fresh_authorizations - current_authorizations).map do |user_id, access_level| + { + user_id: user_id, + access_level: access_level, + project_id: project.id + } + end + end + end + + def refresh_authorizations + ProjectAuthorization.transaction do + if user_ids_to_remove.any? + ProjectAuthorization.where(project_id: project.id, user_id: user_ids_to_remove) # rubocop: disable CodeReuse/ActiveRecord + .delete_all + end + + if authorizations_to_create.any? + ProjectAuthorization.insert_all(authorizations_to_create) + end + end + end + end +end diff --git a/app/services/authorized_project_update/recalculate_for_user_range_service.rb b/app/services/authorized_project_update/recalculate_for_user_range_service.rb deleted file mode 100644 index f300c45f019..00000000000 --- a/app/services/authorized_project_update/recalculate_for_user_range_service.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -module AuthorizedProjectUpdate - class RecalculateForUserRangeService - def initialize(start_user_id, end_user_id) - @start_user_id = start_user_id - @end_user_id = end_user_id - end - - def execute - User.where(id: start_user_id..end_user_id).select(:id).find_each do |user| # rubocop: disable CodeReuse/ActiveRecord - Users::RefreshAuthorizedProjectsService.new(user, source: self.class.name).execute - end - end - - private - - attr_reader :start_user_id, :end_user_id - end -end |