Commit c0e18247 authored by Valery Sizov's avatar Valery Sizov

[Hashed storage] If hashed storage is enbaled we should migrate just

renamed repositpries as well
parent 5588ea1e
......@@ -1559,7 +1559,7 @@ class Project < ActiveRecord::Base
if has_container_registry_tags?
Rails.logger.error "Project #{full_path_was} cannot be renamed because container registry tags are present!"
# we currently doesn't support renaming repository if it contains images in container registry
# we currently don't support renaming repository if it contains images in container registry
raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
end
......@@ -1939,6 +1939,16 @@ class Project < ActiveRecord::Base
end
end
# To call this method we have to make sure that there are no references
# (repo_reference_count = 0 AND wiki_reference_count = 0)
def migrate_to_hashed_storage_synchronously!
options = { old_path: full_path_was }
update!(repository_read_only: true)
ProjectMigrateHashedStorageWorker.new.perform(id, options)
end
def storage_version=(value)
super
......@@ -2023,6 +2033,10 @@ class Project < ActiveRecord::Base
auto_cancel_pending_pipelines == 'enabled'
end
def repository_in_use?
repo_reference_count > 0 || wiki_reference_count > 0
end
private
def storage
......
......@@ -3,15 +3,16 @@ module Projects
AttachmentMigrationError = Class.new(StandardError)
class MigrateAttachmentsService < BaseService
attr_reader :logger, :old_path, :new_path
attr_reader :logger, :old_path, :new_path, :options
def initialize(project, logger = nil)
def initialize(project, options)
@project = project
@logger = logger || Rails.logger
@logger = options.delete(:logger) || Rails.logger
@options = options
end
def execute
@old_path = project.full_path
@old_path = options[:old_path] || project.full_path
@new_path = project.disk_path
origin = FileUploader.absolute_base_dir(project)
......
......@@ -3,16 +3,19 @@ module Projects
class MigrateRepositoryService < BaseService
include Gitlab::ShellAdapter
attr_reader :old_disk_path, :new_disk_path, :old_wiki_disk_path, :old_storage_version, :logger
attr_reader :old_disk_path, :new_disk_path, :old_wiki_disk_path, :old_storage_version, :logger, :options
def initialize(project, logger = nil)
def initialize(project, options)
@project = project
@logger = logger || Rails.logger
@logger = options.delete(:logger) || Rails.logger
@options = options
end
def execute
@old_disk_path = project.disk_path
has_wiki = project.wiki.repository_exists?
@old_disk_path = options[:old_path] || project.disk_path
@old_wiki_disk_path = "#{@old_disk_path}.wiki"
has_wiki = gitlab_shell.exists?(project.repository_storage, "#{@old_wiki_disk_path}.git")
@old_storage_version = project.storage_version
project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository]
......@@ -23,7 +26,6 @@ module Projects
result = move_repository(@old_disk_path, @new_disk_path)
if has_wiki
@old_wiki_disk_path = "#{@old_disk_path}.wiki"
result &&= move_repository("#{@old_wiki_disk_path}", "#{@new_disk_path}.wiki")
end
......
module Projects
class HashedStorageMigrationService < BaseService
attr_reader :logger
attr_reader :options
def initialize(project, logger = nil)
def initialize(project, options = {})
@project = project
@logger = logger || Rails.logger
@options = options
@options[:logger] ||= Rails.logger
end
def execute
# Migrate repository from Legacy to Hashed Storage
unless project.hashed_storage?(:repository)
return unless HashedStorage::MigrateRepositoryService.new(project, logger).execute
return unless HashedStorage::MigrateRepositoryService.new(project, options).execute
end
# Migrate attachments from Legacy to Hashed Storage
unless project.hashed_storage?(:attachments)
HashedStorage::MigrateAttachmentsService.new(project, logger).execute
HashedStorage::MigrateAttachmentsService.new(project, options).execute
end
end
end
......
......@@ -22,9 +22,19 @@ module Projects
# If the block added errors, don't try to save the project
return validation_failed! if project.errors.any?
if project.update_attributes(params.except(:default_branch))
if params[:path] && (params[:path] != project.path)
if project.repository_in_use?
return error("Repository currently in use and can not be moved. Try later")
end
end
if project.update(params.except(:default_branch))
if project.previous_changes.include?('path')
project.rename_repo
if Gitlab::CurrentSettings.hashed_storage_enabled && (project.storage_version != Project::LATEST_STORAGE_VERSION)
project.migrate_to_hashed_storage_synchronously!
else
project.rename_repo
end
else
system_hook_service.execute_hooks_for(project, :update)
end
......
......@@ -7,7 +7,7 @@
.form-check
= f.check_box :hashed_storage_enabled, class: 'form-check-input'
= f.label :hashed_storage_enabled, class: 'form-check-label' do
Create new projects using hashed storage paths
Use hashed storage paths for newly created and renamed projects
.form-text.text-muted
Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents
repositories from having to be moved or renamed when the Project URL changes and may improve disk I/O performance.
......
......@@ -5,13 +5,13 @@ class ProjectMigrateHashedStorageWorker
LEASE_TIMEOUT = 30.seconds.to_i
def perform(project_id)
def perform(project_id, options = {})
project = Project.find_by(id: project_id)
return if project.nil? || project.pending_delete?
uuid = lease_for(project_id).try_obtain
if uuid
::Projects::HashedStorageMigrationService.new(project, logger).execute
::Projects::HashedStorageMigrationService.new(project, options.merge(logger: logger)).execute
else
false
end
......
---
title: Enable hashed storage for all newly created or renamed projects
merge_request: 19747
author:
type: changed
......@@ -30,7 +30,7 @@ module Gitlab
end
end
# Flag a project to me migrated
# Flag a project to be migrated
#
# @param [Object] project that will be migrated
def migrate(project)
......
require 'spec_helper'
describe Projects::HashedStorage::MigrateAttachmentsService do
subject(:service) { described_class.new(project) }
subject(:service) { described_class.new(project, {}) }
let(:project) { create(:project, :legacy_storage) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::HashedProject.new(project) }
......
......@@ -3,7 +3,7 @@ require 'spec_helper'
describe Projects::HashedStorage::MigrateRepositoryService do
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo) }
let(:service) { described_class.new(project) }
let(:service) { described_class.new(project, {}) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::HashedProject.new(project) }
......
......@@ -2,14 +2,15 @@ require 'spec_helper'
describe Projects::HashedStorageMigrationService do
let(:project) { create(:project, :empty_repo, :wiki_repo, :legacy_storage) }
subject(:service) { described_class.new(project) }
let(:options) { { logger: Rails.logger } }
subject(:service) { described_class.new(project, options) }
describe '#execute' do
context 'repository migration' do
let(:repository_service) { Projects::HashedStorage::MigrateRepositoryService.new(project, subject.logger) }
let(:repository_service) { Projects::HashedStorage::MigrateRepositoryService.new(project, options) }
it 'delegates migration to Projects::HashedStorage::MigrateRepositoryService' do
expect(Projects::HashedStorage::MigrateRepositoryService).to receive(:new).with(project, subject.logger).and_return(repository_service)
expect(Projects::HashedStorage::MigrateRepositoryService).to receive(:new).with(project, options).and_return(repository_service)
expect(repository_service).to receive(:execute)
service.execute
......@@ -24,10 +25,10 @@ describe Projects::HashedStorageMigrationService do
end
context 'attachments migration' do
let(:attachments_service) { Projects::HashedStorage::MigrateAttachmentsService.new(project, subject.logger) }
let(:attachments_service) { Projects::HashedStorage::MigrateAttachmentsService.new(project, options) }
it 'delegates migration to Projects::HashedStorage::MigrateRepositoryService' do
expect(Projects::HashedStorage::MigrateAttachmentsService).to receive(:new).with(project, subject.logger).and_return(attachments_service)
expect(Projects::HashedStorage::MigrateAttachmentsService).to receive(:new).with(project, options).and_return(attachments_service)
expect(attachments_service).to receive(:execute)
service.execute
......
......@@ -212,6 +212,24 @@ describe Projects::UpdateService do
expect(project.errors.messages).to have_key(:base)
expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk')
end
context 'when hashed storage enabled' do
before do
stub_application_setting(hashed_storage_enabled: true)
end
it 'migrates project to a hashed storage instead of renaming the repo to another legacy name' do
Sidekiq::Testing.inline! do
result = update_project(project, admin, path: 'new-path')
# stub_application_setting(hashed_storage_enabled: true)
expect(result).not_to include(status: :error)
expect(project).to be_valid
expect(project.errors).to be_empty
expect(project.reload.hashed_storage?(:repository)).to be_truthy
end
end
end
end
context 'with hashed storage' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment