diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-02 21:08:01 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-02 21:08:01 +0000 |
commit | 561e1b470f0a99fe6304c8f197348c47a637d594 (patch) | |
tree | 6b361b6b0b412b70450aca167079c50a13bd88d8 /app/services/projects/update_repository_storage_service.rb | |
parent | 7b52c7cb634ef7047d30b0337fe477bcdcedf41d (diff) | |
download | gitlab-ce-561e1b470f0a99fe6304c8f197348c47a637d594.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/services/projects/update_repository_storage_service.rb')
-rw-r--r-- | app/services/projects/update_repository_storage_service.rb | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/app/services/projects/update_repository_storage_service.rb b/app/services/projects/update_repository_storage_service.rb new file mode 100644 index 00000000000..30b99e85304 --- /dev/null +++ b/app/services/projects/update_repository_storage_service.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true + +module Projects + class UpdateRepositoryStorageService < BaseService + include Gitlab::ShellAdapter + + RepositoryAlreadyMoved = Class.new(StandardError) + + def initialize(project) + @project = project + end + + def execute(new_repository_storage_key) + # Raising an exception is a little heavy handed but this behavior (doing + # nothing if the repo is already on the right storage) prevents data + # loss, so it is valuable for us to be able to observe it via the + # exception. + raise RepositoryAlreadyMoved if project.repository_storage == new_repository_storage_key + + if mirror_repositories(new_repository_storage_key) + mark_old_paths_for_archive + + project.update(repository_storage: new_repository_storage_key, repository_read_only: false) + project.leave_pool_repository + project.track_project_repository + + enqueue_housekeeping + else + project.update(repository_read_only: false) + end + end + + private + + def mirror_repositories(new_repository_storage_key) + result = mirror_repository(new_repository_storage_key) + + if project.wiki.repository_exists? + result &&= mirror_repository(new_repository_storage_key, type: Gitlab::GlRepository::WIKI) + end + + result + end + + def mirror_repository(new_storage_key, type: Gitlab::GlRepository::PROJECT) + return false unless wait_for_pushes(type) + + repository = type.repository_for(project) + full_path = repository.full_path + raw_repository = repository.raw + + # Initialize a git repository on the target path + gitlab_shell.create_repository(new_storage_key, raw_repository.relative_path, full_path) + new_repository = Gitlab::Git::Repository.new(new_storage_key, + raw_repository.relative_path, + raw_repository.gl_repository, + full_path) + + new_repository.fetch_repository_as_mirror(raw_repository) + end + + def mark_old_paths_for_archive + old_repository_storage = project.repository_storage + new_project_path = moved_path(project.disk_path) + + # Notice that the block passed to `run_after_commit` will run with `project` + # as its context + project.run_after_commit do + GitlabShellWorker.perform_async(:mv_repository, + old_repository_storage, + disk_path, + new_project_path) + + if wiki.repository_exists? + GitlabShellWorker.perform_async(:mv_repository, + old_repository_storage, + wiki.disk_path, + "#{new_project_path}.wiki") + end + end + end + + def moved_path(path) + "#{path}+#{project.id}+moved+#{Time.now.to_i}" + 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 + +Projects::UpdateRepositoryStorageService.prepend_if_ee('EE::Projects::UpdateRepositoryStorageService') |