diff options
Diffstat (limited to 'app/services/users/destroy_service.rb')
-rw-r--r-- | app/services/users/destroy_service.rb | 65 |
1 files changed, 42 insertions, 23 deletions
diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb index a378cb09854..d4c00a4dcec 100644 --- a/app/services/users/destroy_service.rb +++ b/app/services/users/destroy_service.rb @@ -8,9 +8,20 @@ module Users def initialize(current_user) @current_user = current_user + + @scheduled_records_gauge = Gitlab::Metrics.gauge( + :gitlab_ghost_user_migration_scheduled_records_total, + 'The total number of scheduled ghost user migrations' + ) + @lag_gauge = Gitlab::Metrics.gauge( + :gitlab_ghost_user_migration_lag_seconds, + 'The waiting time in seconds of the oldest scheduled record for ghost user migration' + ) end - # Synchronously destroys +user+ + # Asynchronously destroys +user+ + # Migrating the associated user records, and post-migration cleanup is + # handled by the Users::MigrateRecordsToGhostUserWorker cron worker. # # The operation will fail if the user is the sole owner of any groups. To # force the groups to be destroyed, pass `delete_solo_owned_groups: true` in @@ -24,10 +35,7 @@ module Users # a hard deletion without destroying solo-owned groups, pass # `delete_solo_owned_groups: false, hard_delete: true` in +options+. # - # To make the service asynchronous, a new behaviour is being introduced - # behind the user_destroy_with_limited_execution_time_worker feature flag. - # Migrating the associated user records, and post-migration cleanup is - # handled by the Users::MigrateRecordsToGhostUserWorker cron worker. + def execute(user, options = {}) delete_solo_owned_groups = options.fetch(:delete_solo_owned_groups, options[:hard_delete]) @@ -62,32 +70,43 @@ module Users yield(user) if block_given? hard_delete = options.fetch(:hard_delete, false) + Users::GhostUserMigration.create!(user: user, + initiator_user: current_user, + hard_delete: hard_delete) - if Feature.enabled?(:user_destroy_with_limited_execution_time_worker) - Users::GhostUserMigration.create!(user: user, - initiator_user: current_user, - hard_delete: hard_delete) + update_metrics + end - else - MigrateToGhostUserService.new(user).execute(hard_delete: options[:hard_delete]) + private + + attr_reader :scheduled_records_gauge, :lag_gauge - response = Snippets::BulkDestroyService.new(current_user, user.snippets) - .execute(skip_authorization: hard_delete) - raise DestroyError, response.message if response.error? + def update_metrics + update_scheduled_records_gauge + update_lag_gauge + end - # Rails attempts to load all related records into memory before - # destroying: https://github.com/rails/rails/issues/22510 - # This ensures we delete records in batches. - user.destroy_dependent_associations_in_batches(exclude: [:snippets]) - user.nullify_dependent_associations_in_batches + def update_scheduled_records_gauge + # We do not want to issue unbounded COUNT() queries, hence we limit the + # query to count 1001 records and then approximate the result. + count = Users::GhostUserMigration.limit(1001).count - # Destroy the namespace after destroying the user since certain methods may depend on the namespace existing - user_data = user.destroy - namespace.destroy + if count == 1001 + # more than 1000 records, approximate count + min = Users::GhostUserMigration.minimum(:id) || 0 + max = Users::GhostUserMigration.maximum(:id) || 0 - user_data + scheduled_records_gauge.set({}, max - min) + else + # less than 1000 records, count is accurate + scheduled_records_gauge.set({}, count) end end + + def update_lag_gauge + oldest_job = Users::GhostUserMigration.first + lag_gauge.set({}, Time.current - oldest_job.created_at) + end end end |