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 ...@@ -1559,7 +1559,7 @@ class Project < ActiveRecord::Base
if has_container_registry_tags? if has_container_registry_tags?
Rails.logger.error "Project #{full_path_was} cannot be renamed because container registry tags are present!" 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') raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
end end
...@@ -1939,6 +1939,16 @@ class Project < ActiveRecord::Base ...@@ -1939,6 +1939,16 @@ class Project < ActiveRecord::Base
end end
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) def storage_version=(value)
super super
...@@ -2023,6 +2033,10 @@ class Project < ActiveRecord::Base ...@@ -2023,6 +2033,10 @@ class Project < ActiveRecord::Base
auto_cancel_pending_pipelines == 'enabled' auto_cancel_pending_pipelines == 'enabled'
end end
def repository_in_use?
repo_reference_count > 0 || wiki_reference_count > 0
end
private private
def storage def storage
......
...@@ -3,15 +3,16 @@ module Projects ...@@ -3,15 +3,16 @@ module Projects
AttachmentMigrationError = Class.new(StandardError) AttachmentMigrationError = Class.new(StandardError)
class MigrateAttachmentsService < BaseService 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 @project = project
@logger = logger || Rails.logger @logger = options.delete(:logger) || Rails.logger
@options = options
end end
def execute def execute
@old_path = project.full_path @old_path = options[:old_path] || project.full_path
@new_path = project.disk_path @new_path = project.disk_path
origin = FileUploader.absolute_base_dir(project) origin = FileUploader.absolute_base_dir(project)
......
...@@ -3,16 +3,19 @@ module Projects ...@@ -3,16 +3,19 @@ module Projects
class MigrateRepositoryService < BaseService class MigrateRepositoryService < BaseService
include Gitlab::ShellAdapter 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 @project = project
@logger = logger || Rails.logger @logger = options.delete(:logger) || Rails.logger
@options = options
end end
def execute def execute
@old_disk_path = project.disk_path @old_disk_path = options[:old_path] || project.disk_path
has_wiki = project.wiki.repository_exists? @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 @old_storage_version = project.storage_version
project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository] project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository]
...@@ -23,7 +26,6 @@ module Projects ...@@ -23,7 +26,6 @@ module Projects
result = move_repository(@old_disk_path, @new_disk_path) result = move_repository(@old_disk_path, @new_disk_path)
if has_wiki if has_wiki
@old_wiki_disk_path = "#{@old_disk_path}.wiki"
result &&= move_repository("#{@old_wiki_disk_path}", "#{@new_disk_path}.wiki") result &&= move_repository("#{@old_wiki_disk_path}", "#{@new_disk_path}.wiki")
end end
......
module Projects module Projects
class HashedStorageMigrationService < BaseService class HashedStorageMigrationService < BaseService
attr_reader :logger attr_reader :options
def initialize(project, logger = nil) def initialize(project, options = {})
@project = project @project = project
@logger = logger || Rails.logger @options = options
@options[:logger] ||= Rails.logger
end end
def execute def execute
# Migrate repository from Legacy to Hashed Storage # Migrate repository from Legacy to Hashed Storage
unless project.hashed_storage?(:repository) unless project.hashed_storage?(:repository)
return unless HashedStorage::MigrateRepositoryService.new(project, logger).execute return unless HashedStorage::MigrateRepositoryService.new(project, options).execute
end end
# Migrate attachments from Legacy to Hashed Storage # Migrate attachments from Legacy to Hashed Storage
unless project.hashed_storage?(:attachments) unless project.hashed_storage?(:attachments)
HashedStorage::MigrateAttachmentsService.new(project, logger).execute HashedStorage::MigrateAttachmentsService.new(project, options).execute
end end
end end
end end
......
...@@ -22,9 +22,19 @@ module Projects ...@@ -22,9 +22,19 @@ module Projects
# If the block added errors, don't try to save the project # If the block added errors, don't try to save the project
return validation_failed! if project.errors.any? 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') 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 else
system_hook_service.execute_hooks_for(project, :update) system_hook_service.execute_hooks_for(project, :update)
end end
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
.form-check .form-check
= f.check_box :hashed_storage_enabled, class: 'form-check-input' = f.check_box :hashed_storage_enabled, class: 'form-check-input'
= f.label :hashed_storage_enabled, class: 'form-check-label' do = 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 .form-text.text-muted
Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents 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. 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 ...@@ -5,13 +5,13 @@ class ProjectMigrateHashedStorageWorker
LEASE_TIMEOUT = 30.seconds.to_i LEASE_TIMEOUT = 30.seconds.to_i
def perform(project_id) def perform(project_id, options = {})
project = Project.find_by(id: project_id) project = Project.find_by(id: project_id)
return if project.nil? || project.pending_delete? return if project.nil? || project.pending_delete?
uuid = lease_for(project_id).try_obtain uuid = lease_for(project_id).try_obtain
if uuid if uuid
::Projects::HashedStorageMigrationService.new(project, logger).execute ::Projects::HashedStorageMigrationService.new(project, options.merge(logger: logger)).execute
else else
false false
end end
......
---
title: Enable hashed storage for all newly created or renamed projects
merge_request: 19747
author:
type: changed
...@@ -30,7 +30,7 @@ module Gitlab ...@@ -30,7 +30,7 @@ module Gitlab
end end
end end
# Flag a project to me migrated # Flag a project to be migrated
# #
# @param [Object] project that will be migrated # @param [Object] project that will be migrated
def migrate(project) def migrate(project)
......
require 'spec_helper' require 'spec_helper'
describe Projects::HashedStorage::MigrateAttachmentsService do describe Projects::HashedStorage::MigrateAttachmentsService do
subject(:service) { described_class.new(project) } subject(:service) { described_class.new(project, {}) }
let(:project) { create(:project, :legacy_storage) } let(:project) { create(:project, :legacy_storage) }
let(:legacy_storage) { Storage::LegacyProject.new(project) } let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::HashedProject.new(project) } let(:hashed_storage) { Storage::HashedProject.new(project) }
......
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe Projects::HashedStorage::MigrateRepositoryService do describe Projects::HashedStorage::MigrateRepositoryService do
let(:gitlab_shell) { Gitlab::Shell.new } let(:gitlab_shell) { Gitlab::Shell.new }
let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo) } 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(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::HashedProject.new(project) } let(:hashed_storage) { Storage::HashedProject.new(project) }
......
...@@ -2,14 +2,15 @@ require 'spec_helper' ...@@ -2,14 +2,15 @@ require 'spec_helper'
describe Projects::HashedStorageMigrationService do describe Projects::HashedStorageMigrationService do
let(:project) { create(:project, :empty_repo, :wiki_repo, :legacy_storage) } 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 describe '#execute' do
context 'repository migration' 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 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) expect(repository_service).to receive(:execute)
service.execute service.execute
...@@ -24,10 +25,10 @@ describe Projects::HashedStorageMigrationService do ...@@ -24,10 +25,10 @@ describe Projects::HashedStorageMigrationService do
end end
context 'attachments migration' do 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 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) expect(attachments_service).to receive(:execute)
service.execute service.execute
......
...@@ -212,6 +212,24 @@ describe Projects::UpdateService do ...@@ -212,6 +212,24 @@ describe Projects::UpdateService do
expect(project.errors.messages).to have_key(:base) 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') expect(project.errors.messages[:base]).to include('There is already a repository with that name on disk')
end 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 end
context 'with hashed storage' do 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