gitlab-ce/lib/click_house/migration_support/migrator.rb

149 lines
4.1 KiB
Ruby

# frozen_string_literal: true
module ClickHouse
module MigrationSupport
class Migrator
attr_accessor :logger
def self.migrations_paths(database_name)
paths = [File.join("db/click_house/migrate", database_name.to_s)]
unless ::Gitlab::Utils.to_boolean(ENV['SKIP_POST_DEPLOYMENT_MIGRATIONS'])
paths << File.join("db/click_house/post_migrate",
database_name.to_s)
end
paths
end
def initialize(
direction, migrations, schema_migration, target_version = nil, step = nil,
logger = Gitlab::AppLogger
)
@direction = direction
@target_version = target_version
@step = step
@migrations = migrations
@schema_migration = schema_migration
@logger = logger
validate(@migrations)
end
def current_version
migrated.max || 0
end
def current_migration
migrations.detect { |m| m.version == current_version }
end
alias_method :current, :current_migration
def migrate
ClickHouse::MigrationSupport::ExclusiveLock.execute_migration do
migrate_without_lock
end
end
def runnable
runnable = migrations[start..finish]
if up?
runnable = runnable.reject { |m| ran?(m) }
else
# skip the last migration if we're headed down, but not ALL the way down
runnable.pop if target
runnable = runnable.find_all { |m| ran?(m) }
end
runnable = runnable.take(@step) if @step && !@target_version
runnable
end
def migrations
down? ? @migrations.reverse : @migrations.sort_by(&:version)
end
def migrated
@migrated_versions || load_migrated
end
def load_migrated
@migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
end
private
# Used for running multiple migrations up to or down to a certain value.
def migrate_without_lock
raise ClickHouse::MigrationSupport::Errors::UnknownMigrationVersionError, @target_version if invalid_target?
runnable.each(&method(:execute_migration)) # rubocop: disable Performance/MethodObjectAsBlock -- Execute through proxy
end
def ran?(migration)
migrated.include?(migration.version.to_i)
end
# Return true if a valid version is not provided.
def invalid_target?
return unless @target_version
return if @target_version == 0
!target
end
def execute_migration(migration)
return if down? && migrated.exclude?(migration.version.to_i)
return if up? && migrated.include?(migration.version.to_i)
logger.info "Migrating to #{migration.name} (#{migration.version})" if logger
migration.migrate(@direction)
record_version_state_after_migrating(migration.version)
rescue StandardError => e
msg = "An error has occurred, all later migrations canceled:\n\n#{e}"
raise StandardError, msg, e.backtrace
end
def target
migrations.detect { |m| m.version == @target_version }
end
def finish
migrations.index(target) || (migrations.size - 1)
end
def start
up? ? 0 : (migrations.index(current) || 0)
end
def validate(migrations)
name, = migrations.group_by(&:name).find { |_, v| v.length > 1 }
raise ClickHouse::MigrationSupport::Errors::DuplicateMigrationNameError, name if name
version, = migrations.group_by(&:version).find { |_, v| v.length > 1 }
raise ClickHouse::MigrationSupport::Errors::DuplicateMigrationVersionError, version if version
end
def record_version_state_after_migrating(version)
if down?
migrated.delete(version)
@schema_migration.create!(version: version.to_s, active: 0)
else
migrated << version
@schema_migration.create!(version: version.to_s)
end
end
def up?
@direction == :up
end
def down?
@direction == :down
end
end
end
end