active_record_shards 3.11.0 → 3.19.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,150 +1,5 @@
1
- # frozen_string_literal: true
2
- module ActiveRecordShards
3
- module DefaultSlavePatches
4
- def self.wrap_method_in_on_slave(class_method, base, method)
5
- base_methods =
6
- if class_method
7
- base.methods + base.private_methods
8
- else
9
- base.instance_methods + base.private_instance_methods
10
- end
11
-
12
- return unless base_methods.include?(method)
13
- _, method, punctuation = method.to_s.match(/^(.*?)([\?\!]?)$/).to_a
14
- base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
15
- #{class_method ? 'class << self' : ''}
16
- def #{method}_with_default_slave#{punctuation}(*args, &block)
17
- on_slave_unless_tx do
18
- #{method}_without_default_slave#{punctuation}(*args, &block)
19
- end
20
- end
21
-
22
- alias_method :#{method}_without_default_slave#{punctuation}, :#{method}#{punctuation}
23
- alias_method :#{method}#{punctuation}, :#{method}_with_default_slave#{punctuation}
24
- #{class_method ? 'end' : ''}
25
- RUBY
26
- end
27
-
28
- def columns_with_force_slave(*args, &block)
29
- on_cx_switch_block(:slave, construct_ro_scope: false, force: true) do
30
- columns_without_force_slave(*args, &block)
31
- end
32
- end
33
-
34
- def table_exists_with_force_slave?(*args, &block)
35
- on_cx_switch_block(:slave, construct_ro_scope: false, force: true) do
36
- table_exists_without_force_slave?(*args, &block)
37
- end
38
- end
39
-
40
- def transaction_with_slave_off(*args, &block)
41
- if on_slave_by_default?
42
- begin
43
- old_val = Thread.current[:_active_record_shards_slave_off]
44
- Thread.current[:_active_record_shards_slave_off] = true
45
- transaction_without_slave_off(*args, &block)
46
- ensure
47
- Thread.current[:_active_record_shards_slave_off] = old_val
48
- end
49
- else
50
- transaction_without_slave_off(*args, &block)
51
- end
52
- end
53
-
54
- module InstanceMethods
55
- # fix ActiveRecord to do the right thing, and use our aliased quote_value
56
- def quote_value(*args, &block)
57
- self.class.quote_value(*args, &block)
58
- end
59
-
60
- def reload_with_slave_off(*args, &block)
61
- self.class.on_master { reload_without_slave_off(*args, &block) }
62
- end
63
- end
64
-
65
- CLASS_SLAVE_METHODS = [:find_by_sql, :count_by_sql, :calculate, :find_one, :find_some, :find_every, :exists?].freeze
66
-
67
- def self.extended(base)
68
- CLASS_SLAVE_METHODS.each { |m| ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(true, base, m) }
69
-
70
- base.class_eval do
71
- include InstanceMethods
72
-
73
- alias_method :reload_without_slave_off, :reload
74
- alias_method :reload, :reload_with_slave_off
1
+ ActiveRecordShards::Deprecation.warn('`DefaultSlavePatches` is deprecated, please use `DefaultReplicaPatches`.')
75
2
 
76
- class << self
77
- alias_method :columns_without_force_slave, :columns
78
- alias_method :columns, :columns_with_force_slave
79
-
80
- alias_method :table_exists_without_force_slave?, :table_exists?
81
- alias_method :table_exists?, :table_exists_with_force_slave?
82
-
83
- alias_method :transaction_without_slave_off, :transaction
84
- alias_method :transaction, :transaction_with_slave_off
85
- end
86
- end
87
- if ActiveRecord::Associations.const_defined?(:HasAndBelongsToManyAssociation)
88
- ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(false, ActiveRecord::Associations::HasAndBelongsToManyAssociation, :construct_sql)
89
- ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(false, ActiveRecord::Associations::HasAndBelongsToManyAssociation, :construct_find_options!)
90
- end
91
- end
92
-
93
- def on_slave_unless_tx
94
- if on_slave_by_default? && !Thread.current[:_active_record_shards_slave_off]
95
- on_slave { yield }
96
- else
97
- yield
98
- end
99
- end
100
-
101
- module ActiveRelationPatches
102
- def self.included(base)
103
- [:calculate, :exists?, :pluck, :find_with_associations].each do |m|
104
- ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(false, base, m)
105
- end
106
- end
107
-
108
- def on_slave_unless_tx
109
- @klass.on_slave_unless_tx { yield }
110
- end
111
- end
112
-
113
- module HasAndBelongsToManyPreloaderPatches
114
- def self.included(base)
115
- ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(false, base, :records_for) rescue nil # rubocop:disable Style/RescueModifier
116
- end
117
-
118
- def on_slave_unless_tx
119
- klass.on_slave_unless_tx { yield }
120
- end
121
-
122
- def exists_with_default_slave?(*args, &block)
123
- on_slave_unless_tx { exists_without_default_slave?(*args, &block) }
124
- end
125
- end
126
-
127
- # in rails 4.1+, they create a join class that's used to pull in records for HABTM.
128
- # this simplifies the hell out of our existence, because all we have to do is inerit on-slave-by-default
129
- # down from the parent now.
130
- module Rails41HasAndBelongsToManyBuilderExtension
131
- def self.included(base)
132
- base.class_eval do
133
- alias_method :through_model_without_inherit_default_slave_from_lhs, :through_model
134
- alias_method :through_model, :through_model_with_inherit_default_slave_from_lhs
135
- end
136
- end
137
-
138
- def through_model_with_inherit_default_slave_from_lhs
139
- model = through_model_without_inherit_default_slave_from_lhs
140
- def model.on_slave_by_default?
141
- left_reflection.klass.on_slave_by_default?
142
- end
143
-
144
- # also transfer the sharded-ness of the left table to the join model
145
- model.not_sharded unless model.left_reflection.klass.is_sharded?
146
- model
147
- end
148
- end
149
- end
3
+ module ActiveRecordShards
4
+ DefaultSlavePatches = DefaultReplicaPatches
150
5
  end
@@ -0,0 +1,12 @@
1
+ module ActiveRecordShards
2
+ class Deprecation < ActiveSupport::Deprecation
3
+ # This allows us to define separate deprecation behavior for ActiveRecordShards, but defaults to
4
+ # the same behavior globally configured with ActiveSupport.
5
+ #
6
+ # For example, this allows us to silence our own deprecation warnings in test while still being
7
+ # able to fail tests for upstream deprecation warnings.
8
+ def behavior
9
+ @behavior ||= ActiveSupport::Deprecation.behavior
10
+ end
11
+ end
12
+ end
@@ -1,43 +1,50 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ActiveRecord
3
4
  class Migrator
4
- class << self
5
- [:up, :down, :run].each do |m|
6
- define_method("#{m}_with_sharding") do |*args|
7
- ActiveRecord::Base.on_shard(nil) do
8
- send("#{m}_without_sharding", *args)
9
- end
10
- ActiveRecord::Base.on_all_shards do
11
- send("#{m}_without_sharding", *args)
12
- end
13
- end
14
- alias_method :"#{m}_without_sharding", m.to_sym
15
- alias_method m.to_sym, :"#{m}_with_sharding"
5
+ def self.shards_migration_context
6
+ if ActiveRecord::VERSION::MAJOR >= 6
7
+ ActiveRecord::MigrationContext.new(ActiveRecord::Migrator.migrations_paths, ActiveRecord::SchemaMigration)
8
+ elsif ActiveRecord::VERSION::STRING >= '5.2.0'
9
+ ActiveRecord::MigrationContext.new(ActiveRecord::Migrator.migrations_paths)
10
+ else
11
+ self
16
12
  end
13
+ end
17
14
 
18
- def bootstrap_migrations_from_nil_shard(migrations_path, this_migration = nil)
19
- migrations = nil
20
- ActiveRecord::Base.on_shard(nil) do
21
- migrations = ActiveRecord::Migrator.new(:up, migrations_path).migrated
22
- end
15
+ def initialize_with_sharding(*args)
16
+ initialize_without_sharding(*args)
23
17
 
24
- puts "inserting #{migrations.size} migrations on all shards..."
25
- ActiveRecord::Base.on_all_shards do
26
- migrator = ActiveRecord::Migrator.new(:up, migrations_path)
27
- migrations.each do |m|
28
- migrator.__send__(:record_version_state_after_migrating, m)
29
- end
30
- if this_migration
31
- migrator.__send__(:record_version_state_after_migrating, this_migration)
32
- end
18
+ # Rails creates the internal tables on the unsharded DB. We make them
19
+ # manually on the sharded DBs.
20
+ ActiveRecord::Base.on_all_shards do
21
+ ActiveRecord::SchemaMigration.create_table
22
+ if ActiveRecord::VERSION::MAJOR >= 5
23
+ ActiveRecord::InternalMetadata.create_table
33
24
  end
34
25
  end
35
26
  end
27
+ alias_method :initialize_without_sharding, :initialize
28
+ alias_method :initialize, :initialize_with_sharding
29
+
30
+ def run_with_sharding
31
+ ActiveRecord::Base.on_shard(nil) { run_without_sharding }
32
+ ActiveRecord::Base.on_all_shards { run_without_sharding }
33
+ end
34
+ alias_method :run_without_sharding, :run
35
+ alias_method :run, :run_with_sharding
36
+
37
+ def migrate_with_sharding
38
+ ActiveRecord::Base.on_shard(nil) { migrate_without_sharding }
39
+ ActiveRecord::Base.on_all_shards { migrate_without_sharding }
40
+ end
41
+ alias_method :migrate_without_sharding, :migrate
42
+ alias_method :migrate, :migrate_with_sharding
36
43
 
37
44
  # don't allow Migrator class to cache versions
38
45
  undef migrated
39
46
  def migrated
40
- self.class.get_all_versions
47
+ self.class.shards_migration_context.get_all_versions
41
48
  end
42
49
 
43
50
  # list of pending migrations is any migrations that haven't run on all shards.
@@ -56,7 +63,7 @@ module ActiveRecord
56
63
  missing = {}
57
64
 
58
65
  collect = lambda do |shard|
59
- migrated = get_all_versions
66
+ migrated = shards_migration_context.get_all_versions
60
67
 
61
68
  p = versions - migrated
62
69
  pending[shard] = p if p.any?
@@ -82,9 +89,6 @@ module ActiveRecordShards
82
89
  end
83
90
  end
84
91
 
85
- # ok, so some 'splaining to do. Rails 3.1 puts the migrate() method on the instance of the
86
- # migration, where it should have been. But this makes our monkey patch incompatible.
87
- # So we're forced to *either* include or extend this.
88
92
  module ActualMigrationExtension
89
93
  def migrate_with_forced_shard(direction)
90
94
  if migration_shard.blank?
@@ -6,10 +6,11 @@ module ActiveRecordShards
6
6
  if self != ActiveRecord::Base && self != base_class
7
7
  raise "You should only call not_sharded on direct descendants of ActiveRecord::Base"
8
8
  end
9
+
9
10
  self.sharded = false
10
11
  end
11
12
 
12
- def is_sharded? # rubocop:disable Style/PredicateName
13
+ def is_sharded? # rubocop:disable Naming/PredicateName
13
14
  if self == ActiveRecord::Base
14
15
  sharded != false && supports_sharding?
15
16
  elsif self == base_class
@@ -23,23 +24,38 @@ module ActiveRecordShards
23
24
  end
24
25
  end
25
26
 
26
- def on_slave_by_default?
27
- base_class.instance_variable_get(:@on_slave_by_default)
27
+ def on_replica_by_default?
28
+ if self == ActiveRecord::Base
29
+ false
30
+ else
31
+ base = base_class
32
+ if base.instance_variable_defined?(:@on_replica_by_default)
33
+ base.instance_variable_get(:@on_replica_by_default)
34
+ end
35
+ end
28
36
  end
37
+ alias_method :on_slave_by_default?, :on_replica_by_default?
29
38
 
30
- def on_slave_by_default=(value)
31
- base_class.instance_variable_set(:@on_slave_by_default, value)
39
+ def on_replica_by_default=(value)
40
+ if self == ActiveRecord::Base
41
+ raise ArgumentError, "Cannot set on_replica_by_default on ActiveRecord::Base"
42
+ else
43
+ base_class.instance_variable_set(:@on_replica_by_default, value)
44
+ end
32
45
  end
46
+ alias_method :on_slave_by_default=, :on_replica_by_default=
33
47
 
34
48
  module InstanceMethods
35
- def initialize_shard_and_slave
36
- @from_slave = !!self.class.current_shard_selection.options[:slave]
49
+ def initialize_shard_and_replica
50
+ @from_replica = !!self.class.current_shard_selection.options[:replica]
37
51
  @from_shard = self.class.current_shard_selection.options[:shard]
38
52
  end
53
+ alias_method :initialize_shard_and_slave, :initialize_shard_and_replica
39
54
 
40
- def from_slave?
41
- @from_slave
55
+ def from_replica?
56
+ @from_replica
42
57
  end
58
+ alias_method :from_slave?, :from_replica?
43
59
 
44
60
  def from_shard
45
61
  @from_shard
@@ -48,7 +64,7 @@ module ActiveRecordShards
48
64
 
49
65
  def self.extended(base)
50
66
  base.send(:include, InstanceMethods)
51
- base.after_initialize :initialize_shard_and_slave
67
+ base.after_initialize :initialize_shard_and_replica
52
68
  end
53
69
 
54
70
  private
@@ -1,13 +1,9 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'active_record_shards/connection_pool'
3
4
  require 'active_record_shards/connection_handler'
4
5
  require 'active_record_shards/connection_specification'
5
- require 'active_record_shards/schema_dumper_extension'
6
6
 
7
7
  ActiveRecordShards::ConnectionSpecification = ActiveRecord::ConnectionAdapters::ConnectionSpecification
8
8
  methods_to_override = [:establish_connection, :remove_connection, :pool_for, :pool_from_any_process_for]
9
9
  ActiveRecordShards.override_connection_handler_methods(methods_to_override)
10
-
11
- ActiveRecord::Associations::Builder::HasAndBelongsToMany.include(ActiveRecordShards::DefaultSlavePatches::Rails41HasAndBelongsToManyBuilderExtension)
12
-
13
- ActiveRecord::SchemaDumper.prepend(ActiveRecordShards::SchemaDumperExtension)
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ActiveRecordShards
3
4
  module SchemaDumperExtension
4
5
  def dump(stream)
@@ -23,15 +24,15 @@ module ActiveRecordShards
23
24
  def shard_header(stream)
24
25
  define_params = @version ? "version: #{@version}" : ""
25
26
 
26
- stream.puts <<HEADER
27
+ stream.puts <<~HEADER
27
28
 
28
29
 
29
- # This section generated by active_record_shards
30
+ # This section generated by active_record_shards
30
31
 
31
- ActiveRecord::Base.on_all_shards do
32
- ActiveRecord::Schema.define(#{define_params}) do
32
+ ActiveRecord::Base.on_all_shards do
33
+ ActiveRecord::Schema.define(#{define_params}) do
33
34
 
34
- HEADER
35
+ HEADER
35
36
  end
36
37
 
37
38
  def shard_trailer(stream)
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ActiveRecordShards
3
4
  class ShardSelection
4
5
  NO_SHARD = :_no_shard
5
6
  cattr_accessor :default_shard
6
7
 
7
8
  def initialize
8
- @on_slave = false
9
+ @on_replica = false
9
10
  @shard = nil
10
11
  end
11
12
 
@@ -21,17 +22,17 @@ module ActiveRecordShards
21
22
  end
22
23
  end
23
24
 
24
- def shard_name(klass = nil, try_slave = true)
25
+ def shard_name(klass = nil, try_replica = true)
25
26
  the_shard = shard(klass)
26
27
 
27
28
  @shard_names ||= {}
28
29
  @shard_names[ActiveRecordShards.rails_env] ||= {}
29
30
  @shard_names[ActiveRecordShards.rails_env][the_shard] ||= {}
30
- @shard_names[ActiveRecordShards.rails_env][the_shard][try_slave] ||= {}
31
- @shard_names[ActiveRecordShards.rails_env][the_shard][try_slave][@on_slave] ||= begin
31
+ @shard_names[ActiveRecordShards.rails_env][the_shard][try_replica] ||= {}
32
+ @shard_names[ActiveRecordShards.rails_env][the_shard][try_replica][@on_replica] ||= begin
32
33
  s = ActiveRecordShards.rails_env.dup
33
34
  s << "_shard_#{the_shard}" if the_shard
34
- s << "_slave" if @on_slave && try_slave
35
+ s << "_replica" if @on_replica && try_replica
35
36
  s
36
37
  end
37
38
  end
@@ -46,25 +47,24 @@ module ActiveRecordShards
46
47
  end
47
48
  end
48
49
 
49
- PRIMARY = "primary".freeze
50
+ PRIMARY = "primary"
50
51
  def resolve_connection_name(sharded:, configurations:)
51
52
  resolved_shard = sharded ? shard : nil
52
-
53
- if !resolved_shard && !@on_slave
54
- return PRIMARY
55
- end
56
-
57
- @shard_names ||= {}
58
- @shard_names[ActiveRecordShards.rails_env] ||= {}
59
- @shard_names[ActiveRecordShards.rails_env][resolved_shard] ||= {}
60
- @shard_names[ActiveRecordShards.rails_env][resolved_shard][@on_slave] ||= begin
61
- s = ActiveRecordShards.rails_env.dup
62
- s << "_shard_#{resolved_shard}" if resolved_shard
63
-
64
- if @on_slave && configurations["#{s}_slave"] # fall back to master connection
65
- s << "_slave"
53
+ env = ActiveRecordShards.rails_env
54
+
55
+ @connection_names ||= {}
56
+ @connection_names[env] ||= {}
57
+ @connection_names[env][resolved_shard] ||= {}
58
+ @connection_names[env][resolved_shard][@on_replica] ||= begin
59
+ name = env.dup
60
+ name << "_shard_#{resolved_shard}" if resolved_shard
61
+ if @on_replica && configurations["#{name}_replica"]
62
+ "#{name}_replica"
63
+ else
64
+ # ActiveRecord always names its default connection pool 'primary'
65
+ # while everything else is named by the configuration name
66
+ resolved_shard ? name : PRIMARY
66
67
  end
67
- s
68
68
  end
69
69
  end
70
70
 
@@ -74,16 +74,18 @@ module ActiveRecordShards
74
74
  @shard = (new_shard || NO_SHARD)
75
75
  end
76
76
 
77
- def on_slave?
78
- @on_slave
77
+ def on_replica?
78
+ @on_replica
79
79
  end
80
+ alias_method :on_slave?, :on_replica?
80
81
 
81
- def on_slave=(new_slave)
82
- @on_slave = (new_slave == true)
82
+ def on_replica=(new_replica)
83
+ @on_replica = (new_replica == true)
83
84
  end
85
+ alias_method :on_slave=, :on_replica=
84
86
 
85
87
  def options
86
- { shard: @shard, slave: @on_slave }
88
+ { shard: @shard, replica: @on_replica }
87
89
  end
88
90
  end
89
91
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ActiveRecordShards
3
4
  class ShardSupport
4
5
  class ShardEnumerator
@@ -1,16 +1,23 @@
1
- # show which connection was picked to debug master/slave slowness when both servers are the same
1
+ # show which connection was picked to debug primary/replica slowness when both servers are the same
2
2
  module ActiveRecordShards
3
3
  module SqlComments
4
4
  module Methods
5
5
  def execute(query, name = nil)
6
- slave = ActiveRecord::Base.current_shard_selection.on_slave?
7
- query += " /* #{slave ? 'slave' : 'master'} */"
6
+ shard = ActiveRecord::Base.current_shard_selection.shard
7
+ shard_text = shard ? "shard #{shard}" : 'unsharded'
8
+ replica = ActiveRecord::Base.current_shard_selection.on_replica?
9
+ replica_text = replica ? 'replica' : 'primary'
10
+ query = "/* #{shard_text} #{replica_text} */ " + query
8
11
  super(query, name)
9
12
  end
10
13
  end
11
14
 
12
15
  def self.enable
13
- ActiveRecord::Base.connection.class.prepend(Methods)
16
+ ActiveRecord::Base.on_replica do
17
+ ActiveRecord::Base.on_shard(nil) do
18
+ ActiveRecord::Base.connection.class.prepend(Methods)
19
+ end
20
+ end
14
21
  end
15
22
  end
16
23
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'active_record_shards'
3
4
 
4
5
  %w[db:drop db:create db:abort_if_pending_migrations db:reset db:test:purge].each do |name|
@@ -8,15 +9,16 @@ end
8
9
  namespace :db do
9
10
  desc 'Drops the database for the current RAILS_ENV including shards'
10
11
  task drop: :load_config do
11
- ActiveRecord::Base.configurations.each do |key, conf|
12
- next if !key.starts_with?(ActiveRecordShards.rails_env) || key.ends_with?("_slave")
12
+ ActiveRecord::Base.configurations.to_h.each do |key, conf|
13
+ next if !key.start_with?(ActiveRecordShards.rails_env) || key.end_with?("_replica", "_slave")
14
+
13
15
  begin
14
16
  ActiveRecordShards::Tasks.root_connection(conf).drop_database(conf['database'])
15
17
  # rescue ActiveRecord::NoDatabaseError # TODO: exists in AR but never is raised here ...
16
18
  # $stderr.puts "Database '#{conf['database']}' does not exist"
17
- rescue StandardError => error
18
- $stderr.puts error, *error.backtrace
19
- $stderr.puts "Couldn't drop #{conf['database']}"
19
+ rescue StandardError => e
20
+ warn e, *e.backtrace
21
+ warn "Couldn't drop #{conf['database']}"
20
22
  end
21
23
  end
22
24
  end
@@ -28,25 +30,22 @@ namespace :db do
28
30
 
29
31
  desc "Create the database defined in config/database.yml for the current RAILS_ENV including shards"
30
32
  task create: :load_config do
31
- ActiveRecord::Base.configurations.each do |key, conf|
32
- next if !key.starts_with?(ActiveRecordShards.rails_env) || key.ends_with?("_slave")
33
- if ActiveRecord::VERSION::MAJOR >= 4
34
- begin
35
- # MysqlAdapter takes charset instead of encoding in Rails 4
36
- # https://github.com/rails/rails/commit/78b30fed9336336694fb2cb5d2825f95800b541c
37
- symbolized_configuration = conf.symbolize_keys
38
- symbolized_configuration[:charset] = symbolized_configuration[:encoding]
33
+ ActiveRecord::Base.configurations.to_h.each do |key, conf|
34
+ next if !key.start_with?(ActiveRecordShards.rails_env) || key.end_with?("_replica", "_slave")
35
+
36
+ begin
37
+ # MysqlAdapter takes charset instead of encoding in Rails 4.2 or greater
38
+ # https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/tasks/mysql_database_tasks.rb#L85-L96
39
+ symbolized_configuration = conf.symbolize_keys
40
+ symbolized_configuration[:charset] = symbolized_configuration[:encoding]
39
41
 
40
- ActiveRecordShards::Tasks.root_connection(conf).create_database(conf['database'], symbolized_configuration)
41
- rescue ActiveRecord::StatementInvalid => ex
42
- if ex.message.include?('database exists')
43
- puts "#{conf['database']} already exists"
44
- else
45
- raise ex
46
- end
42
+ ActiveRecordShards::Tasks.root_connection(conf).create_database(conf['database'], symbolized_configuration)
43
+ rescue ActiveRecord::StatementInvalid => e
44
+ if e.message.include?('database exists')
45
+ puts "#{conf['database']} already exists"
46
+ else
47
+ raise e
47
48
  end
48
- else
49
- create_database(conf)
50
49
  end
51
50
  end
52
51
  ActiveRecord::Base.establish_connection(ActiveRecordShards.rails_env.to_sym)
@@ -56,16 +55,20 @@ namespace :db do
56
55
  task abort_if_pending_migrations: :environment do
57
56
  if defined? ActiveRecord
58
57
  pending_migrations =
59
- if Rails::VERSION::MAJOR >= 4
60
- ActiveRecord::Base.on_shard(nil) { ActiveRecord::Migrator.open(ActiveRecord::Migrator.migrations_paths).pending_migrations }
58
+ if ActiveRecord::VERSION::MAJOR >= 6
59
+ migrations = ActiveRecord::MigrationContext.new(ActiveRecord::Migrator.migrations_paths, ActiveRecord::SchemaMigration).migrations
60
+ ActiveRecord::Migrator.new(:up, migrations, ActiveRecord::SchemaMigration).pending_migrations
61
+ elsif ActiveRecord::VERSION::STRING >= "5.2.0"
62
+ migrations = ActiveRecord::MigrationContext.new(ActiveRecord::Migrator.migrations_paths).migrations
63
+ ActiveRecord::Migrator.new(:up, migrations).pending_migrations
61
64
  else
62
- ActiveRecord::Base.on_shard(nil) { ActiveRecord::Migrator.new(:up, 'db/migrate').pending_migrations }
65
+ ActiveRecord::Base.on_shard(nil) { ActiveRecord::Migrator.open(ActiveRecord::Migrator.migrations_paths).pending_migrations }
63
66
  end
64
67
 
65
68
  if pending_migrations.any?
66
- puts "You have #{pending_migrations.size} pending migrations:"
69
+ warn "You have #{pending_migrations.size} pending migrations:"
67
70
  pending_migrations.each do |pending_migration|
68
- puts ' %4d %s' % [pending_migration.version, pending_migration.name]
71
+ warn ' %4d %s' % [pending_migration.version, pending_migration.name]
69
72
  end
70
73
  abort %(Run "rake db:migrate" to update your database then try again.)
71
74
  end
@@ -89,17 +92,19 @@ end
89
92
 
90
93
  module ActiveRecordShards
91
94
  module Tasks
92
- if ActiveRecord::VERSION::MAJOR < 5
93
- def self.root_connection(conf)
94
- ActiveRecord::Base.send("#{conf['adapter']}_connection", conf.merge('database' => nil))
95
- end
96
- else
97
- def self.root_connection(conf)
98
- # this will trigger rails to load the adapter
95
+ class << self
96
+ def root_connection(conf)
99
97
  conf = conf.merge('database' => nil)
98
+ spec = spec_for(conf)
99
+
100
+ ActiveRecord::Base.send("#{conf['adapter']}_connection", spec.config)
101
+ end
102
+
103
+ private
104
+
105
+ def spec_for(conf)
100
106
  resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(ActiveRecord::Base.configurations)
101
107
  resolver.spec(conf)
102
- ActiveRecord::Base.send("#{conf['adapter']}_connection", conf)
103
108
  end
104
109
  end
105
110
  end