active_record_shards 3.11.0 → 3.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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