module ActiveRecord::Turntable::Migration extend ActiveSupport::Concern included do extend ShardDefinition prepend OverrideMethods class_attribute :target_shards, :current_shard ::ActiveRecord::ConnectionAdapters::AbstractAdapter.include(SchemaStatementsExt) ::ActiveRecord::Migration::CommandRecorder.include(CommandRecorder) if ActiveRecord::Turntable::Util.ar52_or_later? ::ActiveRecord::MigrationContext.prepend(MigrationContext) else ::ActiveRecord::Migrator.prepend(Migrator) end end module ShardDefinition def clusters(*cluster_names) config = ActiveRecord::Base.turntable_configuration (self.target_shards ||= []).concat( if cluster_names.first == :all config.clusters.map(&:shards).flatten else cluster_names.map do |cluster_name| config.cluster(cluster_name).shards end.flatten end ) end def shards(*connection_names) (self.target_shards ||= []).concat connection_names end end module OverrideMethods def announce(message) super("#{message} - Shard: #{current_shard}") end def exec_migration(*args) super(*args) if target_shard?(current_shard) end def target_shard?(shard_name) target_shards.blank? or target_shards.include?(shard_name) end end module SchemaStatementsExt def create_sequence_for(table_name, options = {}) options = options.merge(id: false) # TODO: pkname should be pulled from table definitions sequence_table_name = ActiveRecord::Turntable::Sequencer.sequence_name(table_name, "id") create_table(sequence_table_name, options) do |t| t.integer :id, limit: 8 end execute "INSERT INTO #{quote_table_name(sequence_table_name)} (`id`) VALUES (0)" end def drop_sequence_for(table_name, options = {}) # TODO: pkname should be pulled from table definitions sequence_table_name = ActiveRecord::Turntable::Sequencer.sequence_name(table_name, "id") drop_table(sequence_table_name) end def rename_sequence_for(table_name, new_name) # TODO: pkname should pulled from table definitions seq_table_name = ActiveRecord::Turntable::Sequencer.sequence_name(table_name, "id") new_seq_name = ActiveRecord::Turntable::Sequencer.sequence_name(new_name, "id") rename_table(seq_table_name, new_seq_name) end end module CommandRecorder def create_sequence_for(*args) record(:create_sequence_for, args) end def rename_sequence_for(*args) record(:rename_sequence_for, args) end private def invert_create_sequence_for(args) [:drop_sequence_for, args] end def invert_rename_sequence_for(args) [:rename_sequence_for, args.reverse] end end module MigrationContext extend ActiveSupport::Concern def up(target_version = nil) result = super ActiveRecord::Tasks::DatabaseTasks.each_current_turntable_cluster_connected(current_environment) do |name, configuration| puts "[turntable] *** Migrating database: #{configuration['database']}(Shard: #{name})" super(target_version) end result end def down(target_version = nil) result = super ActiveRecord::Tasks::DatabaseTasks.each_current_turntable_cluster_connected(current_environment) do |name, configuration| puts "[turntable] *** Migrating database: #{configuration['database']}(Shard: #{name})" super(target_version) end result end def run(direction, target_version) result = super ActiveRecord::Tasks::DatabaseTasks.each_current_turntable_cluster_connected(current_environment) do |name, configuration| puts "[turntable] *** Migrating database: #{configuration['database']}(Shard: #{name})" super(target_version) end result end end module Migrator extend ActiveSupport::Concern def self.prepended(base) class << base prepend ClassMethods end end module ClassMethods def up(migrations_paths, target_version = nil) result = super ActiveRecord::Tasks::DatabaseTasks.each_current_turntable_cluster_connected(current_environment) do |name, configuration| puts "[turntable] *** Migrating database: #{configuration['database']}(Shard: #{name})" super(migrations_paths, target_version) end result end def down(migrations_paths, target_version = nil, &block) result = super ActiveRecord::Tasks::DatabaseTasks.each_current_turntable_cluster_connected(current_environment) do |name, configuration| puts "[turntable] *** Migrating database: #{configuration['database']}(Shard: #{name})" super(migrations_paths, target_version, &block) end result end def run(*args) result = super ActiveRecord::Tasks::DatabaseTasks.each_current_turntable_cluster_connected(current_environment) do |name, configuration| puts "[turntable] *** Migrating database: #{configuration['database']}(Shard: #{name})" super(*args) end result end end end end