active_record_shards 3.15.1 → 3.18.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65d92b02ab6446ce1f4faeaf82a4ec5a0b064474c232db1210887c07aa046584
4
- data.tar.gz: 95846224823c39b0abb7afe59b2ee0b52507147391a549581428330274cf60fb
3
+ metadata.gz: 1f573041b49f55115d94db028525da537bbbadffc4526d8a4f220f3d4ab1d4fd
4
+ data.tar.gz: f29528996589d7cef3f14671df47d10a8d4cf4a3b0ad81b022aaddf7b229178d
5
5
  SHA512:
6
- metadata.gz: d20e7ac275197777749f69c2c31c09b5e1ae26f47c212107afd464d74977b36f348fa6e7a92566139240089c4b9b559b790ab4be0d34d975a59ab3e2e40ee933
7
- data.tar.gz: '09f0100020a3539e297d480f8ced91577bdbdc4cbe4f563df735cac3a8a03bcd2540c41a3ecddde09756649ffdeac03ca16b7ec96c7869d8264b53452b706517'
6
+ metadata.gz: 782a638a2d4ce88a06ac0f6d93d277de20831d918ee1014fec1b2a01f95631b5857fb2df1d83b15ced1e52b1749283ec497c43ccfc0d85ffd6e3bd271d4d9f28
7
+ data.tar.gz: f28464784b171c3dd8a888574f1bb362e645f3949dcf3b07a70a9c195e73ae00a1c5a7319c107666f4060e37c04c3daacd8415187124dbda891aae5cb19a7ecf
data/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  # ActiveRecord Shards
4
4
 
5
- ActiveRecord Shards is an extension for ActiveRecord that provides support for sharded database and slaves. Basically it is just a nice way to
5
+ ActiveRecord Shards is an extension for ActiveRecord that provides support for sharded database and replicas. Basically it is just a nice way to
6
6
  switch between database connections. We've made the implementation very small, and have tried not to reinvent any wheels already present in ActiveRecord.
7
7
 
8
- ActiveRecord Shards has been used and tested on Rails 4.2 and 5.0 and has in some form or another been used in production on a large Rails app for several years.
8
+ ActiveRecord Shards has been used and tested on Rails 4.2, 5.x and 6.0, and has in some form or another been used in production on large Rails apps for several years.
9
9
 
10
10
  - [Installation](#installation)
11
11
  - [Configuration](#configuration)
@@ -24,7 +24,7 @@ and make sure to require 'active\_record\_shards' in some way.
24
24
 
25
25
  ## Configuration
26
26
 
27
- Add the slave and shard configuration to config/database.yml:
27
+ Add the replica and shard configuration to config/database.yml:
28
28
 
29
29
  ```yaml
30
30
  production:
@@ -35,19 +35,19 @@ production:
35
35
  host: db1
36
36
  username: root
37
37
  password:
38
- slave:
39
- host: db1_slave
38
+ replica:
39
+ host: db1_replica
40
40
  shards:
41
41
  1:
42
42
  host: db_shard1
43
43
  database: my_app_shard
44
- slave:
45
- host: db_shard1_slave
44
+ replica:
45
+ host: db_shard1_replica
46
46
  2:
47
47
  host: db_shard2
48
48
  database: my_app_shard
49
- slave:
50
- host: db_shard2_slave
49
+ replica:
50
+ host: db_shard2_replica
51
51
  ```
52
52
 
53
53
  basically connections inherit configuration from the parent configuration file.
@@ -140,23 +140,23 @@ class AccountMiddleware
140
140
  end
141
141
  ```
142
142
 
143
- You can switch to the slave databases at any point by wrapping your code in an on\_slave block:
143
+ You can switch to the replica databases at any point by wrapping your code in an on\_replica block:
144
144
 
145
145
  ```ruby
146
- ActiveRecord::Base.on_slave do
146
+ ActiveRecord::Base.on_replica do
147
147
  Account.find_by_big_expensive_query
148
148
  end
149
149
  ```
150
150
 
151
- This will perform the query on the slave, and mark the returned instances as read only. There is also a shortcut for this:
151
+ This will perform the query on the replica, and mark the returned instances as read only. There is also a shortcut for this:
152
152
 
153
153
  ```ruby
154
- Account.on_slave.find_by_big_expensive_query
154
+ Account.on_replica.find_by_big_expensive_query
155
155
  ```
156
156
 
157
157
  ## Debugging
158
158
 
159
- Show if a query went to master or slave in the logs:
159
+ Show if a query went to primary or replica in the logs:
160
160
 
161
161
  ```Ruby
162
162
  require 'active_record_shards/sql_comments'
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'active_record'
3
4
  require 'active_record/base'
4
5
  require 'active_record_shards/configuration_parser'
@@ -7,7 +8,7 @@ require 'active_record_shards/shard_selection'
7
8
  require 'active_record_shards/connection_switcher'
8
9
  require 'active_record_shards/association_collection_connection_selection'
9
10
  require 'active_record_shards/migration'
10
- require 'active_record_shards/default_slave_patches'
11
+ require 'active_record_shards/default_replica_patches'
11
12
  require 'active_record_shards/schema_dumper_extension'
12
13
 
13
14
  module ActiveRecordShards
@@ -22,17 +23,61 @@ end
22
23
  ActiveRecord::Base.extend(ActiveRecordShards::ConfigurationParser)
23
24
  ActiveRecord::Base.extend(ActiveRecordShards::Model)
24
25
  ActiveRecord::Base.extend(ActiveRecordShards::ConnectionSwitcher)
25
- ActiveRecord::Base.extend(ActiveRecordShards::DefaultSlavePatches)
26
- ActiveRecord::Relation.include(ActiveRecordShards::DefaultSlavePatches::ActiveRelationPatches)
26
+ ActiveRecord::Base.extend(ActiveRecordShards::DefaultReplicaPatches)
27
+ ActiveRecord::Relation.include(ActiveRecordShards::DefaultReplicaPatches::ActiveRelationPatches)
27
28
  ActiveRecord::Associations::CollectionProxy.include(ActiveRecordShards::AssociationCollectionConnectionSelection)
28
- ActiveRecord::Associations::Builder::HasAndBelongsToMany.include(ActiveRecordShards::DefaultSlavePatches::Rails41HasAndBelongsToManyBuilderExtension)
29
+ ActiveRecord::Associations::Builder::HasAndBelongsToMany.include(ActiveRecordShards::DefaultReplicaPatches::Rails41HasAndBelongsToManyBuilderExtension)
29
30
  ActiveRecord::SchemaDumper.prepend(ActiveRecordShards::SchemaDumperExtension)
30
31
 
31
32
  case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
32
33
  when '4.2'
33
34
  require 'active_record_shards/patches-4-2'
34
- when '5.0', '5.1', '5.2'
35
+ when '5.0', '5.1', '5.2', '6.0'
35
36
  :ok
36
37
  else
37
38
  raise "ActiveRecordShards is not compatible with #{ActiveRecord::VERSION::STRING}"
38
39
  end
40
+
41
+ require 'active_record_shards/deprecation'
42
+
43
+ ActiveRecordShards::Deprecation.deprecate_methods(
44
+ ActiveRecordShards::AssociationCollectionConnectionSelection,
45
+ on_slave_if: :on_replica_if,
46
+ on_slave_unless: :on_replica_unless,
47
+ on_slave: :on_replica,
48
+ on_master: :on_primary,
49
+ on_master_if: :on_primary_if,
50
+ on_master_unless: :on_primary_unless
51
+ )
52
+
53
+ ActiveRecordShards::Deprecation.deprecate_methods(
54
+ ActiveRecordShards::ConnectionSwitcher,
55
+ on_slave_if: :on_replica_if,
56
+ on_slave_unless: :on_replica_unless,
57
+ on_master_or_slave: :on_primary_or_replica,
58
+ on_slave: :on_replica,
59
+ on_master: :on_primary,
60
+ on_master_if: :on_primary_if,
61
+ on_master_unless: :on_primary_unless,
62
+ on_slave?: :on_replica?
63
+ )
64
+
65
+ ActiveRecordShards::Deprecation.deprecate_methods(
66
+ ActiveRecordShards::DefaultReplicaPatches,
67
+ columns_with_force_slave: :columns_with_force_replica,
68
+ table_exists_with_force_slave?: :table_exists_with_force_replica?,
69
+ transaction_with_slave_off: :transaction_with_replica_off,
70
+ on_slave_unless_tx: :on_replica_unless_tx
71
+ )
72
+
73
+ ActiveRecordShards::Deprecation.deprecate_methods(
74
+ ActiveRecordShards::Model,
75
+ on_slave_by_default?: :on_replica_by_default?,
76
+ :on_slave_by_default= => :on_replica_by_default=
77
+ )
78
+
79
+ ActiveRecordShards::Deprecation.deprecate_methods(
80
+ ActiveRecordShards::ShardSelection,
81
+ on_slave?: :on_replica?,
82
+ :on_slave= => :on_replica=
83
+ )
@@ -1,40 +1,49 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ActiveRecordShards
3
4
  module AssociationCollectionConnectionSelection
4
- def on_slave_if(condition)
5
- condition ? on_slave : self
5
+ def on_replica_if(condition)
6
+ condition ? on_replica : self
6
7
  end
8
+ alias_method :on_slave_if, :on_replica_if
7
9
 
8
- def on_slave_unless(condition)
9
- on_slave_if(!condition)
10
+ def on_replica_unless(condition)
11
+ on_replica_if(!condition)
10
12
  end
13
+ alias_method :on_slave_unless, :on_replica_unless
11
14
 
12
- def on_master_if(condition)
13
- condition ? on_master : self
15
+ def on_primary_if(condition)
16
+ condition ? on_primary : self
14
17
  end
18
+ alias_method :on_master_if, :on_primary_if
15
19
 
16
- def on_master_unless(condition)
17
- on_master_if(!condition)
20
+ def on_primary_unless(condition)
21
+ on_primary_if(!condition)
18
22
  end
23
+ alias_method :on_master_unless, :on_primary_unless
19
24
 
20
- def on_slave
21
- MasterSlaveProxy.new(self, :slave)
25
+ def on_replica
26
+ PrimaryReplicaProxy.new(self, :replica)
22
27
  end
28
+ alias_method :on_slave, :on_replica
23
29
 
24
- def on_master
25
- MasterSlaveProxy.new(self, :master)
30
+ def on_primary
31
+ PrimaryReplicaProxy.new(self, :primary)
26
32
  end
33
+ alias_method :on_master, :on_primary
27
34
 
28
- class MasterSlaveProxy
35
+ class PrimaryReplicaProxy
29
36
  def initialize(association_collection, which)
30
37
  @association_collection = association_collection
31
38
  @which = which
32
39
  end
33
40
 
34
- def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
41
+ def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
35
42
  reflection = @association_collection.proxy_association.reflection
36
43
  reflection.klass.on_cx_switch_block(@which) { @association_collection.send(method, *args, &block) }
37
44
  end
38
45
  end
46
+
47
+ MasterSlaveProxy = PrimaryReplicaProxy
39
48
  end
40
49
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'active_support/core_ext'
3
4
 
4
5
  module ActiveRecordShards
@@ -6,7 +7,7 @@ module ActiveRecordShards
6
7
  module_function
7
8
 
8
9
  def explode(conf)
9
- conf = conf.deep_dup
10
+ conf = conf.to_h.deep_dup
10
11
 
11
12
  conf.to_a.each do |env_name, env_config|
12
13
  next unless shards = env_config.delete('shards')
@@ -23,10 +24,18 @@ module ActiveRecordShards
23
24
  end
24
25
 
25
26
  conf.to_a.each do |env_name, env_config|
26
- if slave_conf = env_config.delete('slave')
27
- expand_child!(env_config, slave_conf)
28
- conf["#{env_name}_slave"] = slave_conf
27
+ if replica_conf = env_config.delete('replica')
28
+ expand_child!(env_config, replica_conf)
29
+ conf["#{env_name}_replica"] = replica_conf
30
+ end
31
+
32
+ # rubocop:disable Style/Next
33
+ if legacy_replica_conf = env_config.delete('slave')
34
+ ActiveRecordShards::Deprecation.warn('`slave` configuration keys should be replaced with `replica` keys!')
35
+ expand_child!(env_config, legacy_replica_conf)
36
+ conf["#{env_name}_replica"] = legacy_replica_conf
29
37
  end
38
+ # rubocop:enable Style/Next
30
39
  end
31
40
 
32
41
  conf
@@ -34,7 +43,7 @@ module ActiveRecordShards
34
43
 
35
44
  def expand_child!(parent, child)
36
45
  parent.each do |key, value|
37
- unless ['slave', 'shards'].include?(key) || value.is_a?(Hash)
46
+ unless ['slave', 'replica', 'shards'].include?(key) || value.is_a?(Hash)
38
47
  child[key] ||= value
39
48
  end
40
49
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  ActiveRecord::ConnectionAdapters::ConnectionHandler.class_eval do
3
4
  remove_method :retrieve_connection_pool
4
5
  def retrieve_connection_pool(klass)
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module ActiveRecordShards
3
4
  ConnectionPoolNameDecorator = Struct.new(:name)
4
5
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  class << ActiveRecord::Base
3
4
  remove_method :establish_connection if ActiveRecord::VERSION::MAJOR >= 5
4
5
  def establish_connection(spec = ENV["DATABASE_URL"])
@@ -4,8 +4,8 @@ module ActiveRecordShards
4
4
  def connection_pool_name # :nodoc:
5
5
  name = current_shard_selection.shard_name(self)
6
6
 
7
- # e.g. if "production_slave" is not defined in `Configuration`, fall back to "production"
8
- if configurations[name].nil? && on_slave?
7
+ # e.g. if "production_replica" is not defined in `Configuration`, fall back to "production"
8
+ if configurations[name].nil? && on_replica?
9
9
  current_shard_selection.shard_name(self, false)
10
10
  else
11
11
  name
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'active_record_shards/shard_support'
3
4
 
4
5
  module ActiveRecordShards
5
6
  module ConnectionSwitcher
6
- SHARD_NAMES_CONFIG_KEY = 'shard_names'.freeze
7
+ SHARD_NAMES_CONFIG_KEY = 'shard_names'
7
8
 
8
9
  def self.extended(base)
9
10
  if ActiveRecord::VERSION::MAJOR >= 5
@@ -23,6 +24,10 @@ module ActiveRecordShards
23
24
  switch_connection(shard: new_default_shard)
24
25
  end
25
26
 
27
+ def on_primary_db(&block)
28
+ on_shard(nil, &block)
29
+ end
30
+
26
31
  def on_shard(shard)
27
32
  old_options = current_shard_selection.options
28
33
  switch_connection(shard: shard) if supports_sharding?
@@ -31,9 +36,9 @@ module ActiveRecordShards
31
36
  switch_connection(old_options)
32
37
  end
33
38
 
34
- def on_first_shard
39
+ def on_first_shard(&block)
35
40
  shard_name = shard_names.first
36
- on_shard(shard_name) { yield }
41
+ on_shard(shard_name, &block)
37
42
  end
38
43
 
39
44
  def shards
@@ -54,70 +59,79 @@ module ActiveRecordShards
54
59
  switch_connection(old_options)
55
60
  end
56
61
 
57
- def on_slave_if(condition, &block)
58
- condition ? on_slave(&block) : yield
62
+ def on_replica_if(condition, &block)
63
+ condition ? on_replica(&block) : yield
59
64
  end
65
+ alias_method :on_slave_if, :on_replica_if
60
66
 
61
- def on_slave_unless(condition, &block)
62
- on_slave_if(!condition, &block)
67
+ def on_replica_unless(condition, &block)
68
+ on_replica_if(!condition, &block)
63
69
  end
70
+ alias_method :on_slave_unless, :on_replica_unless
64
71
 
65
- def on_master_if(condition, &block)
66
- condition ? on_master(&block) : yield
72
+ def on_primary_if(condition, &block)
73
+ condition ? on_primary(&block) : yield
67
74
  end
75
+ alias_method :on_master_if, :on_primary_if
68
76
 
69
- def on_master_unless(condition, &block)
70
- on_master_if(!condition, &block)
77
+ def on_primary_unless(condition, &block)
78
+ on_primary_if(!condition, &block)
71
79
  end
80
+ alias_method :on_master_unless, :on_primary_unless
72
81
 
73
- def on_master_or_slave(which, &block)
82
+ def on_primary_or_replica(which, &block)
74
83
  if block_given?
75
84
  on_cx_switch_block(which, &block)
76
85
  else
77
- MasterSlaveProxy.new(self, which)
86
+ PrimaryReplicaProxy.new(self, which)
78
87
  end
79
88
  end
89
+ alias_method :on_master_or_slave, :on_primary_or_replica
80
90
 
81
- # Executes queries using the slave database. Fails over to master if no slave is found.
82
- # if you want to execute a block of code on the slave you can go:
83
- # Account.on_slave do
91
+ # Executes queries using the replica database. Fails over to primary if no replica is found.
92
+ # if you want to execute a block of code on the replica you can go:
93
+ # Account.on_replica do
84
94
  # Account.first
85
95
  # end
86
- # the first account will be found on the slave DB
96
+ # the first account will be found on the replica DB
87
97
  #
88
98
  # For one-liners you can simply do
89
- # Account.on_slave.first
90
- def on_slave(&block)
91
- on_master_or_slave(:slave, &block)
99
+ # Account.on_replica.first
100
+ def on_replica(&block)
101
+ on_primary_or_replica(:replica, &block)
92
102
  end
103
+ alias_method :on_slave, :on_replica
93
104
 
94
- def on_master(&block)
95
- on_master_or_slave(:master, &block)
105
+ def on_primary(&block)
106
+ on_primary_or_replica(:primary, &block)
96
107
  end
108
+ alias_method :on_master, :on_primary
97
109
 
98
110
  # just to ease the transition from replica to active_record_shards
99
- alias_method :with_slave, :on_slave
100
- alias_method :with_slave_if, :on_slave_if
101
- alias_method :with_slave_unless, :on_slave_unless
111
+ alias_method :with_slave, :on_replica
112
+ alias_method :with_slave_if, :on_replica_if
113
+ alias_method :with_slave_unless, :on_replica_unless
102
114
 
103
115
  def on_cx_switch_block(which, force: false, construct_ro_scope: nil, &block)
104
- @disallow_slave ||= 0
105
- @disallow_slave += 1 if which == :master
116
+ @disallow_replica ||= 0
117
+ @disallow_replica += 1 if [:primary, :master].include?(which)
118
+
119
+ ActiveRecordShards::Deprecation.warn('the `:master` option should be replaced with `:primary`!') if which == :master
106
120
 
107
- switch_to_slave = force || @disallow_slave.zero?
121
+ switch_to_replica = force || @disallow_replica.zero?
108
122
  old_options = current_shard_selection.options
109
123
 
110
- switch_connection(slave: switch_to_slave)
124
+ switch_connection(replica: switch_to_replica)
111
125
 
112
126
  # we avoid_readonly_scope to prevent some stack overflow problems, like when
113
127
  # .columns calls .with_scope which calls .columns and onward, endlessly.
114
- if self == ActiveRecord::Base || !switch_to_slave || construct_ro_scope == false
128
+ if self == ActiveRecord::Base || !switch_to_replica || construct_ro_scope == false
115
129
  yield
116
130
  else
117
131
  readonly.scoping(&block)
118
132
  end
119
133
  ensure
120
- @disallow_slave -= 1 if which == :master
134
+ @disallow_replica -= 1 if [:primary, :master].include?(which)
121
135
  switch_connection(old_options) if old_options
122
136
  end
123
137
 
@@ -125,9 +139,10 @@ module ActiveRecordShards
125
139
  shard_names.any?
126
140
  end
127
141
 
128
- def on_slave?
129
- current_shard_selection.on_slave?
142
+ def on_replica?
143
+ current_shard_selection.on_replica?
130
144
  end
145
+ alias_method :on_slave?, :on_replica?
131
146
 
132
147
  def current_shard_selection
133
148
  Thread.current[:shard_selection] ||= ShardSelection.new
@@ -144,6 +159,7 @@ module ActiveRecordShards
144
159
  unless config.fetch(SHARD_NAMES_CONFIG_KEY, []).all? { |shard_name| shard_name.is_a?(Integer) }
145
160
  raise "All shard names must be integers: #{config[SHARD_NAMES_CONFIG_KEY].inspect}."
146
161
  end
162
+
147
163
  config[SHARD_NAMES_CONFIG_KEY] || []
148
164
  end
149
165
 
@@ -151,14 +167,15 @@ module ActiveRecordShards
151
167
 
152
168
  def switch_connection(options)
153
169
  if options.any?
154
- if options.key?(:slave)
155
- current_shard_selection.on_slave = options[:slave]
170
+ if options.key?(:replica)
171
+ current_shard_selection.on_replica = options[:replica]
156
172
  end
157
173
 
158
174
  if options.key?(:shard)
159
175
  unless configurations[shard_env]
160
176
  raise "Did not find #{shard_env} in configurations, did you forget to add it to your database config? (configurations: #{configurations.keys.inspect})"
161
177
  end
178
+
162
179
  current_shard_selection.shard = options[:shard]
163
180
  end
164
181
 
@@ -170,9 +187,9 @@ module ActiveRecordShards
170
187
  ActiveRecordShards.rails_env
171
188
  end
172
189
 
173
- def with_default_shard
190
+ def with_default_shard(&block)
174
191
  if is_sharded? && current_shard_id.nil? && table_name != ActiveRecord::SchemaMigration.table_name
175
- on_first_shard { yield }
192
+ on_first_shard(&block)
176
193
  else
177
194
  yield
178
195
  end
@@ -192,25 +209,27 @@ module ActiveRecordShards
192
209
  with_default_shard { table_exists_without_default_shard? }
193
210
  end
194
211
 
195
- class MasterSlaveProxy
212
+ class PrimaryReplicaProxy
196
213
  def initialize(target, which)
197
214
  @target = target
198
215
  @which = which
199
216
  end
200
217
 
201
- def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
202
- @target.on_master_or_slave(@which) { @target.send(method, *args, &block) }
218
+ def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
219
+ @target.on_primary_or_replica(@which) { @target.send(method, *args, &block) }
203
220
  end
204
221
  end
222
+
223
+ MasterSlaveProxy = PrimaryReplicaProxy
205
224
  end
206
225
  end
207
226
 
208
227
  case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
209
228
  when '4.2'
210
- require 'active_record_shards/connection_switcher-4-0'
229
+ require 'active_record_shards/connection_switcher-4-2'
211
230
  when '5.0'
212
231
  require 'active_record_shards/connection_switcher-5-0'
213
- when '5.1', '5.2'
232
+ when '5.1', '5.2', '6.0'
214
233
  require 'active_record_shards/connection_switcher-5-1'
215
234
  else
216
235
  raise "ActiveRecordShards is not compatible with #{ActiveRecord::VERSION::STRING}"