Commit e7a06032 authored by Gabriel Mazetto's avatar Gabriel Mazetto
Browse files

Moving away from the "extend" based factory to a more traditional one.

Using `extend` dynamically can lead to bad performance as it
invalidates the method's cache.
parent 642da64440be
...@@ -32,6 +32,8 @@ class Project < ActiveRecord::Base ...@@ -32,6 +32,8 @@ class Project < ActiveRecord::Base
:merge_requests_enabled?, :issues_enabled?, to: :project_feature, :merge_requests_enabled?, :issues_enabled?, to: :project_feature,
allow_nil: true allow_nil: true
delegate :base_dir, :disk_path, :ensure_storage_path_exist, :rename_repo, to: :storage
default_value_for :archived, false default_value_for :archived, false
default_value_for :visibility_level, gitlab_config_features.visibility_level default_value_for :visibility_level, gitlab_config_features.visibility_level
default_value_for :container_registry_enabled, gitlab_config_features.container_registry default_value_for :container_registry_enabled, gitlab_config_features.container_registry
...@@ -59,7 +61,7 @@ class Project < ActiveRecord::Base ...@@ -59,7 +61,7 @@ class Project < ActiveRecord::Base
after_validation :check_pending_delete after_validation :check_pending_delete
# Storage specific hooks # Storage specific hooks
after_initialize :load_storage after_initialize :use_hashed_storage
after_create :ensure_storage_path_exist after_create :ensure_storage_path_exist
after_save :ensure_storage_path_exist, if: :namespace_id_changed? after_save :ensure_storage_path_exist, if: :namespace_id_changed?
...@@ -1424,16 +1426,20 @@ def forks_count ...@@ -1424,16 +1426,20 @@ def forks_count
private private
def load_storage def storage
return unless has_attribute?(:storage_version) @storage ||=
if !has_attribute?(:storage_version) # during migration
Storage::LegacyProject.new(self)
elsif self.storage_version && self.storage_version >= 1
Storage::HashedProject.new(self)
else
Storage::LegacyProject.new(self)
end
end
if self.storage_version && self.storage_version >= 1 def use_hashed_storage
self.extend Storage::HashedProject if !self.persisted? && current_application_settings.hashed_storage_enabled
elsif !self.persisted? && current_application_settings.hashed_storage_enabled
self.storage_version = LATEST_STORAGE_VERSION self.storage_version = LATEST_STORAGE_VERSION
self.extend Storage::HashedProject
else
self.extend Storage::LegacyProject
end end
end end
......
module Storage module Storage
module HashedProject class HashedProject
extend ActiveSupport::Concern attr_accessor :project
delegate :namespace, :gitlab_shell, :repository_storage_path, to: :project
def initialize(project)
@project = project
end
# Base directory # Base directory
# #
...@@ -22,13 +27,13 @@ def ensure_storage_path_exist ...@@ -22,13 +27,13 @@ def ensure_storage_path_exist
def rename_repo def rename_repo
# TODO: We cannot wipe most of this method until we provide migration path for Container Registries # TODO: We cannot wipe most of this method until we provide migration path for Container Registries
path_was = previous_changes['path'].first path_was = project.previous_changes['path'].first
old_path_with_namespace = File.join(namespace.full_path, path_was) old_path_with_namespace = File.join(namespace.full_path, path_was)
new_path_with_namespace = File.join(namespace.full_path, path) new_path_with_namespace = File.join(namespace.full_path, project.path)
Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}" Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"
if has_container_registry_tags? if project.has_container_registry_tags?
Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!" Rails.logger.error "Project #{old_path_with_namespace} 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 doesn't support renaming repository if it contains images in container registry
...@@ -37,14 +42,14 @@ def rename_repo ...@@ -37,14 +42,14 @@ def rename_repo
begin begin
# TODO: we can avoid cache expiration if cache is based on UUID or just project_id # TODO: we can avoid cache expiration if cache is based on UUID or just project_id
expire_caches_before_rename(old_path_with_namespace) project.expire_caches_before_rename(old_path_with_namespace)
expires_full_path_cache project.expires_full_path_cache
send_move_instructions(old_path_with_namespace) project.send_move_instructions(old_path_with_namespace)
@old_path_with_namespace = old_path_with_namespace @old_path_with_namespace = old_path_with_namespace
SystemHooksService.new.execute_hooks_for(self, :rename) SystemHooksService.new.execute_hooks_for(project, :rename)
@repository = nil @repository = nil
rescue => e rescue => e
...@@ -57,8 +62,8 @@ def rename_repo ...@@ -57,8 +62,8 @@ def rename_repo
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
# TODO: When we move Uploads and Pages to use UUID we can disable this transfers as well # TODO: When we move Uploads and Pages to use UUID we can disable this transfers as well
Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path) Gitlab::UploadsTransfer.new.rename_project(path_was, project.path, namespace.full_path)
Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path) Gitlab::PagesTransfer.new.rename_project(path_was, project.path, namespace.full_path)
end end
private private
...@@ -66,7 +71,7 @@ def rename_repo ...@@ -66,7 +71,7 @@ def rename_repo
# Generates the hash for the project path and name on disk # Generates the hash for the project path and name on disk
# If you need to refer to the repository on disk, use the `#disk_path` # If you need to refer to the repository on disk, use the `#disk_path`
def disk_hash def disk_hash
@disk_hash ||= Digest::SHA2.hexdigest(self.id.to_s) if self.id @disk_hash ||= Digest::SHA2.hexdigest(project.id.to_s) if project.id
end end
end end
end end
module Storage module Storage
module LegacyProject class LegacyProject
extend ActiveSupport::Concern attr_accessor :project
delegate :namespace, :gitlab_shell, :repository_storage_path, to: :project
def initialize(project)
@project = project
end
# Base directory # Base directory
# #
...@@ -13,28 +18,30 @@ def base_dir ...@@ -13,28 +18,30 @@ def base_dir
# #
# @return [String] combination of base_dir and the repository own name without `.git` or `.wiki.git` extensions # @return [String] combination of base_dir and the repository own name without `.git` or `.wiki.git` extensions
def disk_path def disk_path
full_path project.full_path
end end
def ensure_storage_path_exist def ensure_storage_path_exist
return unless namespace
gitlab_shell.add_namespace(repository_storage_path, base_dir) gitlab_shell.add_namespace(repository_storage_path, base_dir)
end end
def rename_repo def rename_repo
path_was = previous_changes['path'].first path_was = project.previous_changes['path'].first
old_path_with_namespace = File.join(namespace.full_path, path_was) old_path_with_namespace = File.join(base_dir, path_was)
new_path_with_namespace = File.join(namespace.full_path, path) new_path_with_namespace = File.join(base_dir, project.path)
Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}" Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"
if has_container_registry_tags? if project.has_container_registry_tags?
Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!" Rails.logger.error "Project #{old_path_with_namespace} 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 doesn'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
expire_caches_before_rename(old_path_with_namespace) project.expire_caches_before_rename(old_path_with_namespace)
if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace) if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
# If repository moved successfully we need to send update instructions to users. # If repository moved successfully we need to send update instructions to users.
...@@ -42,12 +49,12 @@ def rename_repo ...@@ -42,12 +49,12 @@ def rename_repo
# So we basically we mute exceptions in next actions # So we basically we mute exceptions in next actions
begin begin
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
send_move_instructions(old_path_with_namespace) project.send_move_instructions(old_path_with_namespace)
expires_full_path_cache project.expires_full_path_cache
@old_path_with_namespace = old_path_with_namespace @old_path_with_namespace = old_path_with_namespace
SystemHooksService.new.execute_hooks_for(self, :rename) SystemHooksService.new.execute_hooks_for(project, :rename)
@repository = nil @repository = nil
rescue => e rescue => e
...@@ -66,8 +73,8 @@ def rename_repo ...@@ -66,8 +73,8 @@ def rename_repo
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path) Gitlab::UploadsTransfer.new.rename_project(path_was, project.path, base_dir)
Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path) Gitlab::PagesTransfer.new.rename_project(path_was, project.path, base_dir)
end end
end end
end end
...@@ -8,7 +8,6 @@ class AddStorageFieldsToProject < ActiveRecord::Migration ...@@ -8,7 +8,6 @@ class AddStorageFieldsToProject < ActiveRecord::Migration
disable_ddl_transaction! disable_ddl_transaction!
def up def up
# rubocop:disable Migration/AddColumnWithDefaultToLargeTable
add_column :projects, :storage_version, :integer, limit: 2 add_column :projects, :storage_version, :integer, limit: 2
add_concurrent_index :projects, :storage_version add_concurrent_index :projects, :storage_version
end end
......
...@@ -2395,11 +2395,12 @@ def enable_lfs ...@@ -2395,11 +2395,12 @@ def enable_lfs
end end
context 'hashed storage' do context 'hashed storage' do
let(:project) { create(:project, :repository, :hashed) } let(:project) { create(:project, :repository) }
let(:gitlab_shell) { Gitlab::Shell.new } let(:gitlab_shell) { Gitlab::Shell.new }
let(:hash) { '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' } let(:hash) { '6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b' }
before do before do
stub_application_setting(hashed_storage_enabled: true)
allow(Digest::SHA2).to receive(:hexdigest) { hash } allow(Digest::SHA2).to receive(:hexdigest) { hash }
end end
......
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