123 lines
3.5 KiB
Ruby
123 lines
3.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module RepositoryStorageMovable
|
|
extend ActiveSupport::Concern
|
|
include AfterCommitQueue
|
|
|
|
included do
|
|
scope :order_created_at_desc, -> { order(created_at: :desc) }
|
|
|
|
validates :container, presence: true
|
|
validates :state, presence: true
|
|
validates :source_storage_name,
|
|
on: :create,
|
|
presence: true,
|
|
inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
|
|
validates :destination_storage_name,
|
|
on: :create,
|
|
presence: true,
|
|
inclusion: { in: ->(_) { Gitlab.config.repositories.storages.keys } }
|
|
validate :container_repository_writable, on: :create
|
|
|
|
default_value_for(:destination_storage_name, allows_nil: false) do
|
|
Repository.pick_storage_shard
|
|
end
|
|
|
|
state_machine initial: :initial do
|
|
event :schedule do
|
|
transition initial: :scheduled
|
|
end
|
|
|
|
event :start do
|
|
transition scheduled: :started
|
|
end
|
|
|
|
event :finish_replication do
|
|
transition started: :replicated
|
|
end
|
|
|
|
event :finish_cleanup do
|
|
transition replicated: :finished
|
|
end
|
|
|
|
event :do_fail do
|
|
transition [:initial, :scheduled, :started] => :failed
|
|
transition replicated: :cleanup_failed
|
|
end
|
|
|
|
around_transition initial: :scheduled do |storage_move, block|
|
|
block.call
|
|
|
|
begin
|
|
storage_move.container.set_repository_read_only!(skip_git_transfer_check: true)
|
|
rescue => err
|
|
storage_move.add_error(err.message)
|
|
next false
|
|
end
|
|
|
|
storage_move.run_after_commit do
|
|
storage_move.schedule_repository_storage_update_worker
|
|
end
|
|
|
|
true
|
|
end
|
|
|
|
before_transition started: :replicated do |storage_move|
|
|
storage_move.container.set_repository_writable!
|
|
|
|
storage_move.update_repository_storage(storage_move.destination_storage_name)
|
|
end
|
|
|
|
after_transition started: :replicated do |storage_move|
|
|
# We have several scripts in place that replicate some statistics information
|
|
# to other databases. Some of them depend on the updated_at column
|
|
# to identify the models they need to extract.
|
|
#
|
|
# If we don't update the `updated_at` of the container after a repository storage move,
|
|
# the scripts won't know that they need to sync them.
|
|
#
|
|
# See https://gitlab.com/gitlab-data/analytics/-/issues/7868
|
|
storage_move.container.touch
|
|
end
|
|
|
|
before_transition started: :failed do |storage_move|
|
|
storage_move.container.set_repository_writable!
|
|
end
|
|
|
|
state :initial, value: 1
|
|
state :scheduled, value: 2
|
|
state :started, value: 3
|
|
state :finished, value: 4
|
|
state :failed, value: 5
|
|
state :replicated, value: 6
|
|
state :cleanup_failed, value: 7
|
|
end
|
|
end
|
|
|
|
# Projects, snippets, and group wikis has different db structure. In projects,
|
|
# we need to update some columns in this step, but we don't with the other resources.
|
|
#
|
|
# Therefore, we create this No-op method for snippets and wikis and let project
|
|
# overwrite it in their implementation.
|
|
def update_repository_storage(new_storage)
|
|
# No-op
|
|
end
|
|
|
|
def schedule_repository_storage_update_worker
|
|
raise NotImplementedError
|
|
end
|
|
|
|
def add_error(message)
|
|
errors.add(error_key, message)
|
|
end
|
|
|
|
private
|
|
|
|
def container_repository_writable
|
|
add_error(_('is read only')) if container&.repository_read_only?
|
|
end
|
|
|
|
def error_key
|
|
raise NotImplementedError
|
|
end
|
|
end
|