Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/services/authorized_project_update')
-rw-r--r--app/services/authorized_project_update/periodic_recalculate_service.rb7
-rw-r--r--app/services/authorized_project_update/project_group_link_create_service.rb2
-rw-r--r--app/services/authorized_project_update/project_recalculate_service.rb80
-rw-r--r--app/services/authorized_project_update/recalculate_for_user_range_service.rb20
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