122 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			122 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| module Gitlab
 | |
|   module HashedStorage
 | |
|     # Hashed Storage Migrator
 | |
|     #
 | |
|     # This is responsible for scheduling and flagging projects
 | |
|     # to be migrated from Legacy to Hashed storage, either one by one or in bulk.
 | |
|     class Migrator
 | |
|       BATCH_SIZE = 100
 | |
| 
 | |
|       # Schedule a range of projects to be bulk migrated with #bulk_migrate asynchronously
 | |
|       #
 | |
|       # @param [Integer] start first project id for the range
 | |
|       # @param [Integer] finish last project id for the range
 | |
|       def bulk_schedule_migration(start:, finish:)
 | |
|         ::HashedStorage::MigratorWorker.perform_async(start, finish)
 | |
|       end
 | |
| 
 | |
|       # Schedule a range of projects to be bulk rolledback with #bulk_rollback asynchronously
 | |
|       #
 | |
|       # @param [Integer] start first project id for the range
 | |
|       # @param [Integer] finish last project id for the range
 | |
|       def bulk_schedule_rollback(start:, finish:)
 | |
|         ::HashedStorage::RollbackerWorker.perform_async(start, finish)
 | |
|       end
 | |
| 
 | |
|       # Start migration of projects from specified range
 | |
|       #
 | |
|       # Flagging a project to be migrated is a synchronous action
 | |
|       # but the migration runs through async jobs
 | |
|       #
 | |
|       # @param [Integer] start first project id for the range
 | |
|       # @param [Integer] finish last project id for the range
 | |
|       # rubocop: disable CodeReuse/ActiveRecord
 | |
|       def bulk_migrate(start:, finish:)
 | |
|         projects = build_relation(start, finish)
 | |
| 
 | |
|         projects.with_route.find_each(batch_size: BATCH_SIZE) do |project|
 | |
|           migrate(project)
 | |
|         end
 | |
|       end
 | |
|       # rubocop: enable CodeReuse/ActiveRecord
 | |
| 
 | |
|       # Start rollback of projects from specified range
 | |
|       #
 | |
|       # Flagging a project to be rolled back is a synchronous action
 | |
|       # but the rollback runs through async jobs
 | |
|       #
 | |
|       # @param [Integer] start first project id for the range
 | |
|       # @param [Integer] finish last project id for the range
 | |
|       # rubocop: disable CodeReuse/ActiveRecord
 | |
|       def bulk_rollback(start:, finish:)
 | |
|         projects = build_relation(start, finish)
 | |
| 
 | |
|         projects.with_route.find_each(batch_size: BATCH_SIZE) do |project|
 | |
|           rollback(project)
 | |
|         end
 | |
|       end
 | |
|       # rubocop: enable CodeReuse/ActiveRecord
 | |
| 
 | |
|       # Flag a project to be migrated to Hashed Storage
 | |
|       #
 | |
|       # @param [Project] project that will be migrated
 | |
|       # rubocop:disable Gitlab/RailsLogger
 | |
|       def migrate(project)
 | |
|         Rails.logger.info "Starting storage migration of #{project.full_path} (ID=#{project.id})..."
 | |
| 
 | |
|         project.migrate_to_hashed_storage!
 | |
|       rescue => err
 | |
|         Rails.logger.error("#{err.message} migrating storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}")
 | |
|       end
 | |
|       # rubocop:enable Gitlab/RailsLogger
 | |
| 
 | |
|       # Flag a project to be rolled-back to Legacy Storage
 | |
|       #
 | |
|       # @param [Project] project that will be rolled-back
 | |
|       # rubocop:disable Gitlab/RailsLogger
 | |
|       def rollback(project)
 | |
|         Rails.logger.info "Starting storage rollback of #{project.full_path} (ID=#{project.id})..."
 | |
| 
 | |
|         project.rollback_to_legacy_storage!
 | |
|       rescue => err
 | |
|         Rails.logger.error("#{err.message} rolling-back storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}")
 | |
|       end
 | |
|       # rubocop:enable Gitlab/RailsLogger
 | |
| 
 | |
|       # Returns whether we have any pending storage migration
 | |
|       #
 | |
|       def migration_pending?
 | |
|         any_non_empty_queue?(::HashedStorage::MigratorWorker, ::HashedStorage::ProjectMigrateWorker)
 | |
|       end
 | |
| 
 | |
|       # Returns whether we have any pending storage rollback
 | |
|       #
 | |
|       def rollback_pending?
 | |
|         any_non_empty_queue?(::HashedStorage::RollbackerWorker, ::HashedStorage::ProjectRollbackWorker)
 | |
|       end
 | |
| 
 | |
|       private
 | |
| 
 | |
|       def any_non_empty_queue?(*workers)
 | |
|         workers.any? do |worker|
 | |
|           Sidekiq::Queue.new(worker.queue).size != 0 # rubocop:disable Style/ZeroLengthPredicate
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       # rubocop: disable CodeReuse/ActiveRecord
 | |
|       def build_relation(start, finish)
 | |
|         relation = Project
 | |
|         table = Project.arel_table
 | |
| 
 | |
|         relation = relation.where(table[:id].gteq(start)) if start
 | |
|         relation = relation.where(table[:id].lteq(finish)) if finish
 | |
| 
 | |
|         relation
 | |
|       end
 | |
|       # rubocop: enable CodeReuse/ActiveRecord
 | |
|     end
 | |
|   end
 | |
| end
 |