1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# frozen_string_literal: true
module UpdateRepositoryStorageMethods
include Gitlab::Utils::StrongMemoize
Error = Class.new(StandardError)
attr_reader :repository_storage_move
delegate :container, :source_storage_name, :destination_storage_name, to: :repository_storage_move
def initialize(repository_storage_move)
@repository_storage_move = repository_storage_move
end
def execute
response = repository_storage_move.with_lock do
next ServiceResponse.success unless repository_storage_move.scheduled?
repository_storage_move.start!
nil
end
return response if response
unless same_filesystem?
mirror_repositories
repository_storage_move.transaction do
mirror_object_pool(destination_storage_name)
end
end
repository_storage_move.transaction do
repository_storage_move.finish_replication!
track_repository(destination_storage_name)
end
remove_old_paths unless same_filesystem?
repository_storage_move.finish_cleanup!
ServiceResponse.success
rescue StandardError => e
repository_storage_move.do_fail!
Gitlab::ErrorTracking.track_and_raise_exception(e, container_klass: container.class.to_s, container_path: container.full_path)
end
private
def track_repository(destination_shard)
raise NotImplementedError
end
def mirror_repositories
raise NotImplementedError
end
def mirror_object_pool(_destination_shard)
# no-op, redefined for Projects::UpdateRepositoryStorageService
nil
end
def mirror_repository(type:)
unless wait_for_pushes(type)
raise Error, s_('UpdateRepositoryStorage|Timeout waiting for %{type} repository pushes') % { type: type.name }
end
# `Projects::UpdateRepositoryStorageService`` expects the repository it is
# moving to have a `Project` as a container.
# This hack allows design repos to also be moved as part of a project move
# as before.
# The alternative to this hack is to setup a service like
# `Snippets::UpdateRepositoryStorageService' and a corresponding worker like
# `Snippets::UpdateRepositoryStorageWorker` for snippets.
#
# Gitlab issue: https://gitlab.com/gitlab-org/gitlab/-/issues/423429
repository = type.repository_for(type.design? ? container.design_management_repository : container)
full_path = repository.full_path
raw_repository = repository.raw
checksum = repository.checksum
# Initialize a git repository on the target path
new_repository = Gitlab::Git::Repository.new(
destination_storage_name,
raw_repository.relative_path,
raw_repository.gl_repository,
full_path
)
new_repository.replicate(raw_repository)
new_checksum = new_repository.checksum
if checksum != new_checksum
raise Error, s_('UpdateRepositoryStorage|Failed to verify %{type} repository checksum from %{old} to %{new}') % { type: type.name, old: checksum, new: new_checksum }
end
end
def same_filesystem?
strong_memoize(:same_filesystem) do
Gitlab::GitalyClient.filesystem_id(source_storage_name) == Gitlab::GitalyClient.filesystem_id(destination_storage_name)
end
end
def remove_old_paths
if container.repository_exists?
Gitlab::Git::Repository.new(
source_storage_name,
"#{container.disk_path}.git",
nil,
nil
).remove
end
end
def wait_for_pushes(type)
reference_counter = container.reference_counter(type: type)
# Try for 30 seconds, polling every 10
3.times do
return true if reference_counter.value == 0
sleep 10
end
false
end
end
|