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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
# frozen_string_literal: true
module Projects
class UpdateRepositoryStorageService
Error = Class.new(StandardError)
SameFilesystemError = Class.new(Error)
attr_reader :repository_storage_move
delegate :project, :source_storage_name, :destination_storage_name, to: :repository_storage_move
def initialize(repository_storage_move)
@repository_storage_move = repository_storage_move
end
def execute
repository_storage_move.with_lock do
return ServiceResponse.success unless repository_storage_move.scheduled? # rubocop:disable Cop/AvoidReturnFromBlocks
repository_storage_move.start!
end
raise SameFilesystemError if same_filesystem?(source_storage_name, destination_storage_name)
mirror_repositories
repository_storage_move.transaction do
repository_storage_move.finish_replication!
project.leave_pool_repository
project.track_project_repository
end
remove_old_paths
enqueue_housekeeping
repository_storage_move.finish_cleanup!
ServiceResponse.success
rescue StandardError => e
repository_storage_move.do_fail!
Gitlab::ErrorTracking.track_exception(e, project_path: project.full_path)
ServiceResponse.error(
message: s_("UpdateRepositoryStorage|Error moving repository storage for %{project_full_path} - %{message}") % { project_full_path: project.full_path, message: e.message }
)
end
private
def same_filesystem?(old_storage, new_storage)
Gitlab::GitalyClient.filesystem_id(old_storage) == Gitlab::GitalyClient.filesystem_id(new_storage)
end
def mirror_repositories
mirror_repository
if project.wiki.repository_exists?
mirror_repository(type: Gitlab::GlRepository::WIKI)
end
if project.design_repository.exists?
mirror_repository(type: ::Gitlab::GlRepository::DESIGN)
end
end
def mirror_repository(type: Gitlab::GlRepository::PROJECT)
unless wait_for_pushes(type)
raise Error, s_('UpdateRepositoryStorage|Timeout waiting for %{type} repository pushes') % { type: type.name }
end
repository = type.repository_for(project)
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 remove_old_paths
Gitlab::Git::Repository.new(
source_storage_name,
"#{project.disk_path}.git",
nil,
nil
).remove
if project.wiki.repository_exists?
Gitlab::Git::Repository.new(
source_storage_name,
"#{project.wiki.disk_path}.git",
nil,
nil
).remove
end
if project.design_repository.exists?
Gitlab::Git::Repository.new(
source_storage_name,
"#{project.design_repository.disk_path}.git",
nil,
nil
).remove
end
end
# The underlying FetchInternalRemote call uses a `git fetch` to move data
# to the new repository, which leaves it in a less-well-packed state,
# lacking bitmaps and commit graphs. Housekeeping will boost performance
# significantly.
def enqueue_housekeeping
return unless Gitlab::CurrentSettings.housekeeping_enabled?
return unless Feature.enabled?(:repack_after_shard_migration, project)
Projects::HousekeepingService.new(project, :gc).execute
rescue Projects::HousekeepingService::LeaseTaken
# No action required
end
def wait_for_pushes(type)
reference_counter = project.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
end
|