1# frozen_string_literal: true 2 3module UpdateRepositoryStorageMethods 4 include Gitlab::Utils::StrongMemoize 5 6 Error = Class.new(StandardError) 7 8 attr_reader :repository_storage_move 9 delegate :container, :source_storage_name, :destination_storage_name, to: :repository_storage_move 10 11 def initialize(repository_storage_move) 12 @repository_storage_move = repository_storage_move 13 end 14 15 def execute 16 repository_storage_move.with_lock do 17 return ServiceResponse.success unless repository_storage_move.scheduled? # rubocop:disable Cop/AvoidReturnFromBlocks 18 19 repository_storage_move.start! 20 end 21 22 mirror_repositories unless same_filesystem? 23 24 repository_storage_move.transaction do 25 repository_storage_move.finish_replication! 26 27 track_repository(destination_storage_name) 28 end 29 30 unless same_filesystem? 31 remove_old_paths 32 enqueue_housekeeping 33 end 34 35 repository_storage_move.finish_cleanup! 36 37 ServiceResponse.success 38 rescue StandardError => e 39 repository_storage_move.do_fail! 40 41 Gitlab::ErrorTracking.track_and_raise_exception(e, container_klass: container.class.to_s, container_path: container.full_path) 42 end 43 44 private 45 46 def track_repository(destination_shard) 47 raise NotImplementedError 48 end 49 50 def mirror_repositories 51 raise NotImplementedError 52 end 53 54 def mirror_repository(type:) 55 unless wait_for_pushes(type) 56 raise Error, s_('UpdateRepositoryStorage|Timeout waiting for %{type} repository pushes') % { type: type.name } 57 end 58 59 repository = type.repository_for(container) 60 full_path = repository.full_path 61 raw_repository = repository.raw 62 checksum = repository.checksum 63 64 # Initialize a git repository on the target path 65 new_repository = Gitlab::Git::Repository.new( 66 destination_storage_name, 67 raw_repository.relative_path, 68 raw_repository.gl_repository, 69 full_path 70 ) 71 72 new_repository.replicate(raw_repository) 73 new_checksum = new_repository.checksum 74 75 if checksum != new_checksum 76 raise Error, s_('UpdateRepositoryStorage|Failed to verify %{type} repository checksum from %{old} to %{new}') % { type: type.name, old: checksum, new: new_checksum } 77 end 78 end 79 80 def same_filesystem? 81 strong_memoize(:same_filesystem) do 82 Gitlab::GitalyClient.filesystem_id(source_storage_name) == Gitlab::GitalyClient.filesystem_id(destination_storage_name) 83 end 84 end 85 86 def remove_old_paths 87 if container.repository_exists? 88 Gitlab::Git::Repository.new( 89 source_storage_name, 90 "#{container.disk_path}.git", 91 nil, 92 nil 93 ).remove 94 end 95 end 96 97 def enqueue_housekeeping 98 # no-op 99 end 100 101 def wait_for_pushes(type) 102 reference_counter = container.reference_counter(type: type) 103 104 # Try for 30 seconds, polling every 10 105 3.times do 106 return true if reference_counter.value == 0 107 108 sleep 10 109 end 110 111 false 112 end 113end 114