diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 13:00:54 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-20 13:00:54 +0300 |
commit | 3cccd102ba543e02725d247893729e5c73b38295 (patch) | |
tree | f36a04ec38517f5deaaacb5acc7d949688d1e187 /app/workers/container_registry | |
parent | 205943281328046ef7b4528031b90fbda70c75ac (diff) |
Add latest changes from gitlab-org/gitlab@14-10-stable-eev14.10.0-rc42
Diffstat (limited to 'app/workers/container_registry')
-rw-r--r-- | app/workers/container_registry/migration/enqueuer_worker.rb | 107 | ||||
-rw-r--r-- | app/workers/container_registry/migration/guard_worker.rb | 52 |
2 files changed, 115 insertions, 44 deletions
diff --git a/app/workers/container_registry/migration/enqueuer_worker.rb b/app/workers/container_registry/migration/enqueuer_worker.rb index 5feaba870e6..8705deb0cb2 100644 --- a/app/workers/container_registry/migration/enqueuer_worker.rb +++ b/app/workers/container_registry/migration/enqueuer_worker.rb @@ -6,6 +6,9 @@ module ContainerRegistry include ApplicationWorker include CronjobQueue # rubocop:disable Scalability/CronWorkerContext include Gitlab::Utils::StrongMemoize + include ExclusiveLeaseGuard + + DEFAULT_LEASE_TIMEOUT = 30.minutes.to_i.freeze data_consistency :always feature_category :container_registry @@ -14,70 +17,103 @@ module ContainerRegistry idempotent! def perform - return unless migration.enabled? - return unless below_capacity? - return unless waiting_time_passed? + re_enqueue = false + try_obtain_lease do + break unless runnable? - re_enqueue_if_capacity if handle_aborted_migration || handle_next_migration - rescue StandardError => e - Gitlab::ErrorTracking.log_exception( - e, - next_repository_id: next_repository&.id, - next_aborted_repository_id: next_aborted_repository&.id - ) - - next_repository&.abort_import + re_enqueue = handle_aborted_migration || handle_next_migration + end + re_enqueue_if_capacity if re_enqueue end private def handle_aborted_migration - return unless next_aborted_repository&.retry_aborted_migration + return unless next_aborted_repository - log_extra_metadata_on_done(:container_repository_id, next_aborted_repository.id) log_extra_metadata_on_done(:import_type, 'retry') + log_repository(next_aborted_repository) + + next_aborted_repository.retry_aborted_migration + + true + rescue StandardError => e + Gitlab::ErrorTracking.log_exception(e, next_aborted_repository_id: next_aborted_repository&.id) true + ensure + log_repository_migration_state(next_aborted_repository) end def handle_next_migration return unless next_repository + + log_extra_metadata_on_done(:import_type, 'next') + log_repository(next_repository) + # We return true because the repository was successfully processed (migration_state is changed) return true if tag_count_too_high? return unless next_repository.start_pre_import - log_extra_metadata_on_done(:container_repository_id, next_repository.id) - log_extra_metadata_on_done(:import_type, 'next') - true + rescue StandardError => e + Gitlab::ErrorTracking.log_exception(e, next_repository_id: next_repository&.id) + next_repository&.abort_import + + false + ensure + log_repository_migration_state(next_repository) end def tag_count_too_high? return false unless next_repository.tags_count > migration.max_tags_count next_repository.skip_import(reason: :too_many_tags) + log_extra_metadata_on_done(:tags_count_too_high, true) + log_extra_metadata_on_done(:max_tags_count_setting, migration.max_tags_count) true end def below_capacity? - current_capacity <= maximum_capacity + current_capacity < maximum_capacity end def waiting_time_passed? delay = migration.enqueue_waiting_time return true if delay == 0 - return true unless last_step_completed_repository + return true unless last_step_completed_repository&.last_import_step_done_at last_step_completed_repository.last_import_step_done_at < Time.zone.now - delay end - def current_capacity - strong_memoize(:current_capacity) do - ContainerRepository.with_migration_states( - %w[pre_importing pre_import_done importing] - ).count + def runnable? + unless migration.enabled? + log_extra_metadata_on_done(:migration_enabled, false) + return false + end + + unless below_capacity? + log_extra_metadata_on_done(:max_capacity_setting, maximum_capacity) + log_extra_metadata_on_done(:below_capacity, false) + + return false + end + + unless waiting_time_passed? + log_extra_metadata_on_done(:waiting_time_passed, false) + log_extra_metadata_on_done(:current_waiting_time_setting, migration.enqueue_waiting_time) + + return false end + + true + end + + def current_capacity + ContainerRepository.with_migration_states( + %w[pre_importing pre_import_done importing] + ).count end def maximum_capacity @@ -107,10 +143,31 @@ module ContainerRegistry end def re_enqueue_if_capacity - return unless current_capacity < maximum_capacity + return unless below_capacity? self.class.perform_async end + + def log_repository(repository) + log_extra_metadata_on_done(:container_repository_id, repository&.id) + log_extra_metadata_on_done(:container_repository_path, repository&.path) + end + + def log_repository_migration_state(repository) + return unless repository + + log_extra_metadata_on_done(:container_repository_migration_state, repository.migration_state) + end + + # used by ExclusiveLeaseGuard + def lease_key + 'container_registry:migration:enqueuer_worker' + end + + # used by ExclusiveLeaseGuard + def lease_timeout + DEFAULT_LEASE_TIMEOUT + end end end end diff --git a/app/workers/container_registry/migration/guard_worker.rb b/app/workers/container_registry/migration/guard_worker.rb index 77ae111c1cb..bab6b8c2a72 100644 --- a/app/workers/container_registry/migration/guard_worker.rb +++ b/app/workers/container_registry/migration/guard_worker.rb @@ -29,46 +29,45 @@ module ContainerRegistry log_extra_metadata_on_done(:stale_migrations_count, repositories.to_a.size) repositories.each do |repository| - if abortable?(repository) + if actively_importing?(repository) + # if a repository is actively importing but not yet long_running, do nothing + if long_running_migration?(repository) + long_running_migration_ids << repository.id + cancel_long_running_migration(repository) + aborts_count += 1 + end + else repository.abort_import aborts_count += 1 - else - long_running_migration_ids << repository.id if long_running_migration?(repository) end end log_extra_metadata_on_done(:aborted_stale_migrations_count, aborts_count) if long_running_migration_ids.any? - log_extra_metadata_on_done(:long_running_stale_migration_container_repository_ids, long_running_migration_ids) + log_extra_metadata_on_done(:aborted_long_running_migration_ids, long_running_migration_ids) end end private - # This can ping the Container Registry API. - # We loop on a set of repositories to calls this function (see #perform) - # In the worst case scenario, we have a n+1 API calls situation here. - # - # This is reasonable because the maximum amount of repositories looped - # on is `25`. See ::ContainerRegistry::Migration.capacity. - # - # TODO We can remove this n+1 situation by having a Container Registry API - # endpoint that accepts multiple repository paths at once. This is issue + # A repository is actively_importing if it has an importing migration state + # and that state matches the state in the registry + # TODO We can have an API call n+1 situation here. It can be solved when the + # endpoint accepts multiple repository paths at once. This is issue # https://gitlab.com/gitlab-org/container-registry/-/issues/582 - def abortable?(repository) - # early return to save one Container Registry API request - return true unless repository.importing? || repository.pre_importing? - return true unless external_migration_in_progress?(repository) + def actively_importing?(repository) + return false unless repository.importing? || repository.pre_importing? + return false unless external_state_matches_migration_state?(repository) - false + true end def long_running_migration?(repository) migration_start_timestamp(repository).before?(long_running_migration_threshold) end - def external_migration_in_progress?(repository) + def external_state_matches_migration_state?(repository) status = repository.external_import_status (status == 'pre_import_in_progress' && repository.pre_importing?) || @@ -96,6 +95,21 @@ module ContainerRegistry def long_running_migration_threshold @threshold ||= 30.minutes.ago end + + def cancel_long_running_migration(repository) + result = repository.migration_cancel + + case result[:status] + when :ok + repository.skip_import(reason: :migration_canceled) + when :bad_request + repository.reconcile_import_status(result[:state]) do + repository.abort_import + end + else + repository.abort_import + end + end end end end |