active_record_shards 3.21.0 → 5.3.1
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.
- checksums.yaml +4 -4
- data/README.md +19 -1
- data/lib/active_record_shards/association_collection_connection_selection.rb +1 -8
- data/lib/active_record_shards/configuration_parser.rb +2 -25
- data/lib/active_record_shards/connection_switcher-6-1.rb +31 -0
- data/lib/active_record_shards/{connection_switcher-5-0.rb → connection_switcher-7-0.rb} +9 -7
- data/lib/active_record_shards/connection_switcher.rb +55 -47
- data/lib/active_record_shards/default_replica_patches.rb +3 -44
- data/lib/active_record_shards/default_shard.rb +27 -0
- data/lib/active_record_shards/migration.rb +2 -3
- data/lib/active_record_shards/model.rb +0 -4
- data/lib/active_record_shards/shard_selection.rb +34 -53
- data/lib/active_record_shards/shard_support.rb +5 -6
- data/lib/active_record_shards/tasks.rb +52 -16
- data/lib/active_record_shards.rb +2 -69
- metadata +18 -37
- data/lib/active_record_shards/connection_handler.rb +0 -8
- data/lib/active_record_shards/connection_pool.rb +0 -36
- data/lib/active_record_shards/connection_specification.rb +0 -19
- data/lib/active_record_shards/connection_switcher-4-2.rb +0 -56
- data/lib/active_record_shards/default_slave_patches.rb +0 -5
- data/lib/active_record_shards/deprecation.rb +0 -12
- data/lib/active_record_shards/patches-4-2.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 032e2279ef2f82d2a1a30a171429a7b34b625766fe3a6060ad21141d551c0ca4
|
4
|
+
data.tar.gz: 169c3f8e0bf7d4f036a23106d8523b5e09756535f289be4da5ec295ec6c1605d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c35a082c7b33926009467f10871177ecb02a2c5dc3b009a1f5d1a889fb49f039ea88bb931cdd80c7cc67763120158748610925ece6d53ee463020388a4690cc5
|
7
|
+
data.tar.gz: 4acc42ae93f573d60d12384f6f7454b4a85d1a5a13239491cb6b00a10d722dfb1534026119f845b3f8c39c5e0114500099eac6806cc2c8b6864c7c9f74835fde
|
data/README.md
CHANGED
@@ -5,7 +5,9 @@
|
|
5
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
|
8
|
+
ActiveRecord Shards has been used and tested on Rails 5.x and 6.0, and has in some form or another been used in production on large Rails apps for several years.
|
9
|
+
|
10
|
+
Rails 6.1 introduced new connection handling and support for sharding. Apps are encouraged to migrate to the native sharding logic but ActiveRecord Shards supports Rails 6.1 when `legacy_connection_handling` is set to `true`. For more information see [Rails 6.1 installation](#rails-61-installation) and Rails' [multiple databases guide](https://guides.rubyonrails.org/active_record_multiple_databases.html).
|
9
11
|
|
10
12
|
- [Installation](#installation)
|
11
13
|
- [Configuration](#configuration)
|
@@ -22,6 +24,22 @@ ActiveRecord Shards has been used and tested on Rails 4.2, 5.x and 6.0, and has
|
|
22
24
|
|
23
25
|
and make sure to require 'active\_record\_shards' in some way.
|
24
26
|
|
27
|
+
### Rails 6.1 & 7.0 installation
|
28
|
+
|
29
|
+
Rails 6.1 & 7.0 are **only** supported with `legacy_connection_handling` set to `true`.
|
30
|
+
|
31
|
+
Enable the legacy handling in your configuration files e.g. `config/application.rb` by setting:
|
32
|
+
|
33
|
+
``` Ruby
|
34
|
+
config.active_record.legacy_connection_handling = true
|
35
|
+
```
|
36
|
+
|
37
|
+
or
|
38
|
+
|
39
|
+
``` Ruby
|
40
|
+
ActiveRecord::Base.legacy_connection_handling = true
|
41
|
+
```
|
42
|
+
|
25
43
|
## Configuration
|
26
44
|
|
27
45
|
Add the replica and shard configuration to config/database.yml:
|
@@ -5,32 +5,26 @@ module ActiveRecordShards
|
|
5
5
|
def on_replica_if(condition)
|
6
6
|
condition ? on_replica : self
|
7
7
|
end
|
8
|
-
alias_method :on_slave_if, :on_replica_if
|
9
8
|
|
10
9
|
def on_replica_unless(condition)
|
11
10
|
on_replica_if(!condition)
|
12
11
|
end
|
13
|
-
alias_method :on_slave_unless, :on_replica_unless
|
14
12
|
|
15
13
|
def on_primary_if(condition)
|
16
14
|
condition ? on_primary : self
|
17
15
|
end
|
18
|
-
alias_method :on_master_if, :on_primary_if
|
19
16
|
|
20
17
|
def on_primary_unless(condition)
|
21
18
|
on_primary_if(!condition)
|
22
19
|
end
|
23
|
-
alias_method :on_master_unless, :on_primary_unless
|
24
20
|
|
25
21
|
def on_replica
|
26
22
|
PrimaryReplicaProxy.new(self, :replica)
|
27
23
|
end
|
28
|
-
alias_method :on_slave, :on_replica
|
29
24
|
|
30
25
|
def on_primary
|
31
26
|
PrimaryReplicaProxy.new(self, :primary)
|
32
27
|
end
|
33
|
-
alias_method :on_master, :on_primary
|
34
28
|
|
35
29
|
class PrimaryReplicaProxy
|
36
30
|
def initialize(association_collection, which)
|
@@ -42,8 +36,7 @@ module ActiveRecordShards
|
|
42
36
|
reflection = @association_collection.proxy_association.reflection
|
43
37
|
reflection.klass.on_cx_switch_block(@which) { @association_collection.send(method, *args, &block) }
|
44
38
|
end
|
39
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
45
40
|
end
|
46
|
-
|
47
|
-
MasterSlaveProxy = PrimaryReplicaProxy
|
48
41
|
end
|
49
42
|
end
|
@@ -28,27 +28,6 @@ module ActiveRecordShards
|
|
28
28
|
expand_child!(env_config, replica_conf)
|
29
29
|
conf["#{env_name}_replica"] = replica_conf
|
30
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
|
37
|
-
end
|
38
|
-
# rubocop:enable Style/Next
|
39
|
-
end
|
40
|
-
|
41
|
-
conf
|
42
|
-
end
|
43
|
-
|
44
|
-
def replace_slave_keys(conf)
|
45
|
-
conf.to_a.each do |env_name, env_config|
|
46
|
-
next unless env_name.end_with?("_slave")
|
47
|
-
|
48
|
-
replica_key = env_name.sub(/_slave$/, "_replica")
|
49
|
-
next if conf.key?(replica_key)
|
50
|
-
|
51
|
-
conf[replica_key] = env_config.deep_dup
|
52
31
|
end
|
53
32
|
|
54
33
|
conf
|
@@ -56,16 +35,14 @@ module ActiveRecordShards
|
|
56
35
|
|
57
36
|
def expand_child!(parent, child)
|
58
37
|
parent.each do |key, value|
|
59
|
-
unless ['
|
38
|
+
unless ['replica', 'shards'].include?(key) || value.is_a?(Hash)
|
60
39
|
child[key] ||= value
|
61
40
|
end
|
62
41
|
end
|
63
42
|
end
|
64
43
|
|
65
44
|
def configurations_with_shard_explosion=(conf)
|
66
|
-
|
67
|
-
configuration_with_slave_keys_replaced = replace_slave_keys(exploded_configuration)
|
68
|
-
self.configurations_without_shard_explosion = configuration_with_slave_keys_replaced
|
45
|
+
self.configurations_without_shard_explosion = explode(conf)
|
69
46
|
end
|
70
47
|
|
71
48
|
def self.extended(base)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ActiveRecordShards
|
2
|
+
module ConnectionSwitcher
|
3
|
+
def connection_specification_name
|
4
|
+
name = current_shard_selection.resolve_connection_name(sharded: is_sharded?, configurations: configurations)
|
5
|
+
|
6
|
+
@_ars_connection_specification_names ||= {}
|
7
|
+
unless @_ars_connection_specification_names.include?(name)
|
8
|
+
unless configurations.configs_for(env_name: name, include_replicas: true).any? || name == "ActiveRecord::Base"
|
9
|
+
raise ActiveRecord::AdapterNotSpecified, "No database defined by #{name} in your database config. (configurations: #{configurations.configurations.map(&:env_name).inspect})"
|
10
|
+
end
|
11
|
+
|
12
|
+
@_ars_connection_specification_names[name] = true
|
13
|
+
end
|
14
|
+
|
15
|
+
name
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def ensure_shard_connection
|
21
|
+
# See if we've connected before. If not, call `#establish_connection`
|
22
|
+
# so that ActiveRecord can resolve connection_specification_name to an
|
23
|
+
# ARS connection.
|
24
|
+
spec_name = connection_specification_name
|
25
|
+
|
26
|
+
pool = connection_handler.retrieve_connection_pool(spec_name)
|
27
|
+
|
28
|
+
connection_handler.establish_connection(spec_name.to_sym) if pool.nil?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -3,8 +3,13 @@ module ActiveRecordShards
|
|
3
3
|
def connection_specification_name
|
4
4
|
name = current_shard_selection.resolve_connection_name(sharded: is_sharded?, configurations: configurations)
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
@_ars_connection_specification_names ||= {}
|
7
|
+
unless @_ars_connection_specification_names.include?(name)
|
8
|
+
unless configurations.configs_for(env_name: name, include_hidden: true).any? || name == "ActiveRecord::Base"
|
9
|
+
raise ActiveRecord::AdapterNotSpecified, "No database defined by #{name} in your database config. (configurations: #{configurations.configurations.map(&:env_name).inspect})"
|
10
|
+
end
|
11
|
+
|
12
|
+
@_ars_connection_specification_names[name] = true
|
8
13
|
end
|
9
14
|
|
10
15
|
name
|
@@ -19,11 +24,8 @@ module ActiveRecordShards
|
|
19
24
|
spec_name = connection_specification_name
|
20
25
|
|
21
26
|
pool = connection_handler.retrieve_connection_pool(spec_name)
|
22
|
-
|
23
|
-
|
24
|
-
spec = resolver.spec(spec_name.to_sym, spec_name)
|
25
|
-
connection_handler.establish_connection(spec)
|
26
|
-
end
|
27
|
+
|
28
|
+
connection_handler.establish_connection(spec_name.to_sym) if pool.nil?
|
27
29
|
end
|
28
30
|
end
|
29
31
|
end
|
@@ -4,26 +4,23 @@ require 'active_record_shards/shard_support'
|
|
4
4
|
|
5
5
|
module ActiveRecordShards
|
6
6
|
module ConnectionSwitcher
|
7
|
-
|
7
|
+
class LegacyConnectionHandlingError < StandardError; end
|
8
|
+
|
9
|
+
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
10
|
+
when '6.1', '7.0'
|
11
|
+
SHARD_NAMES_CONFIG_KEY = :shard_names
|
12
|
+
else
|
13
|
+
SHARD_NAMES_CONFIG_KEY = 'shard_names'
|
14
|
+
end
|
8
15
|
|
9
16
|
def self.extended(base)
|
10
|
-
|
11
|
-
|
12
|
-
base.singleton_class.send(:alias_method, :load_schema!, :load_schema_with_default_shard!)
|
13
|
-
else
|
14
|
-
base.singleton_class.send(:alias_method, :columns_without_default_shard, :columns)
|
15
|
-
base.singleton_class.send(:alias_method, :columns, :columns_with_default_shard)
|
16
|
-
end
|
17
|
+
base.singleton_class.send(:alias_method, :load_schema_without_default_shard!, :load_schema!)
|
18
|
+
base.singleton_class.send(:alias_method, :load_schema!, :load_schema_with_default_shard!)
|
17
19
|
|
18
20
|
base.singleton_class.send(:alias_method, :table_exists_without_default_shard?, :table_exists?)
|
19
21
|
base.singleton_class.send(:alias_method, :table_exists?, :table_exists_with_default_shard?)
|
20
22
|
end
|
21
23
|
|
22
|
-
def default_shard=(new_default_shard)
|
23
|
-
ActiveRecordShards::ShardSelection.default_shard = new_default_shard
|
24
|
-
switch_connection(shard: new_default_shard)
|
25
|
-
end
|
26
|
-
|
27
24
|
def on_primary_db(&block)
|
28
25
|
on_shard(nil, &block)
|
29
26
|
end
|
@@ -62,22 +59,18 @@ module ActiveRecordShards
|
|
62
59
|
def on_replica_if(condition, &block)
|
63
60
|
condition ? on_replica(&block) : yield
|
64
61
|
end
|
65
|
-
alias_method :on_slave_if, :on_replica_if
|
66
62
|
|
67
63
|
def on_replica_unless(condition, &block)
|
68
64
|
on_replica_if(!condition, &block)
|
69
65
|
end
|
70
|
-
alias_method :on_slave_unless, :on_replica_unless
|
71
66
|
|
72
67
|
def on_primary_if(condition, &block)
|
73
68
|
condition ? on_primary(&block) : yield
|
74
69
|
end
|
75
|
-
alias_method :on_master_if, :on_primary_if
|
76
70
|
|
77
71
|
def on_primary_unless(condition, &block)
|
78
72
|
on_primary_if(!condition, &block)
|
79
73
|
end
|
80
|
-
alias_method :on_master_unless, :on_primary_unless
|
81
74
|
|
82
75
|
def on_primary_or_replica(which, &block)
|
83
76
|
if block_given?
|
@@ -86,7 +79,6 @@ module ActiveRecordShards
|
|
86
79
|
PrimaryReplicaProxy.new(self, which)
|
87
80
|
end
|
88
81
|
end
|
89
|
-
alias_method :on_master_or_slave, :on_primary_or_replica
|
90
82
|
|
91
83
|
# Executes queries using the replica database. Fails over to primary if no replica is found.
|
92
84
|
# if you want to execute a block of code on the replica you can go:
|
@@ -100,25 +92,15 @@ module ActiveRecordShards
|
|
100
92
|
def on_replica(&block)
|
101
93
|
on_primary_or_replica(:replica, &block)
|
102
94
|
end
|
103
|
-
alias_method :on_slave, :on_replica
|
104
95
|
|
105
96
|
def on_primary(&block)
|
106
97
|
on_primary_or_replica(:primary, &block)
|
107
98
|
end
|
108
|
-
alias_method :on_master, :on_primary
|
109
|
-
|
110
|
-
# just to ease the transition from replica to active_record_shards
|
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
|
114
99
|
|
115
100
|
def on_cx_switch_block(which, force: false, construct_ro_scope: nil, &block)
|
116
|
-
|
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
|
101
|
+
self.disallow_replica += 1 if which == :primary
|
120
102
|
|
121
|
-
switch_to_replica = force ||
|
103
|
+
switch_to_replica = force || disallow_replica.zero?
|
122
104
|
old_options = current_shard_selection.options
|
123
105
|
|
124
106
|
switch_connection(replica: switch_to_replica)
|
@@ -131,10 +113,18 @@ module ActiveRecordShards
|
|
131
113
|
readonly.scoping(&block)
|
132
114
|
end
|
133
115
|
ensure
|
134
|
-
|
116
|
+
self.disallow_replica -= 1 if which == :primary
|
135
117
|
switch_connection(old_options) if old_options
|
136
118
|
end
|
137
119
|
|
120
|
+
def disallow_replica=(value)
|
121
|
+
Thread.current[:__active_record_shards__disallow_replica_by_thread] = value
|
122
|
+
end
|
123
|
+
|
124
|
+
def disallow_replica
|
125
|
+
Thread.current[:__active_record_shards__disallow_replica_by_thread] ||= 0
|
126
|
+
end
|
127
|
+
|
138
128
|
def supports_sharding?
|
139
129
|
shard_names.any?
|
140
130
|
end
|
@@ -142,7 +132,6 @@ module ActiveRecordShards
|
|
142
132
|
def on_replica?
|
143
133
|
current_shard_selection.on_replica?
|
144
134
|
end
|
145
|
-
alias_method :on_slave?, :on_replica?
|
146
135
|
|
147
136
|
def current_shard_selection
|
148
137
|
Thread.current[:shard_selection] ||= ShardSelection.new
|
@@ -165,7 +154,15 @@ module ActiveRecordShards
|
|
165
154
|
def config_for_env
|
166
155
|
@_ars_config_for_env ||= {}
|
167
156
|
@_ars_config_for_env[shard_env] ||= begin
|
168
|
-
|
157
|
+
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
158
|
+
when '7.0'
|
159
|
+
config = configurations.configs_for(env_name: shard_env, include_hidden: true).first.configuration_hash
|
160
|
+
when '6.1'
|
161
|
+
config = configurations.configs_for(env_name: shard_env, include_replicas: true).first.configuration_hash
|
162
|
+
else
|
163
|
+
config = configurations[shard_env]
|
164
|
+
end
|
165
|
+
unless config
|
169
166
|
raise "Did not find #{shard_env} in configurations, did you forget to add it to your database config? (configurations: #{configurations.to_h.keys.inspect})"
|
170
167
|
end
|
171
168
|
|
@@ -175,6 +172,8 @@ module ActiveRecordShards
|
|
175
172
|
alias_method :check_config_for_env, :config_for_env
|
176
173
|
|
177
174
|
def switch_connection(options)
|
175
|
+
ensure_legacy_connection_handling if ActiveRecord.version >= Gem::Version.new('6.1')
|
176
|
+
|
178
177
|
if options.any?
|
179
178
|
if options.key?(:replica)
|
180
179
|
current_shard_selection.on_replica = options[:replica]
|
@@ -190,6 +189,22 @@ module ActiveRecordShards
|
|
190
189
|
end
|
191
190
|
end
|
192
191
|
|
192
|
+
def ensure_legacy_connection_handling
|
193
|
+
unless legacy_connection_handling_owner.legacy_connection_handling
|
194
|
+
raise LegacyConnectionHandlingError, "ActiveRecordShards is _only_ compatible with ActiveRecord `legacy_connection_handling` set to `true`."
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def legacy_connection_handling_owner
|
199
|
+
@legacy_connection_handling_owner ||=
|
200
|
+
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
201
|
+
when '7.0'
|
202
|
+
ActiveRecord
|
203
|
+
when '6.1'
|
204
|
+
ActiveRecord::Base
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
193
208
|
def shard_env
|
194
209
|
ActiveRecordShards.app_env
|
195
210
|
end
|
@@ -204,14 +219,8 @@ module ActiveRecordShards
|
|
204
219
|
end
|
205
220
|
end
|
206
221
|
|
207
|
-
|
208
|
-
|
209
|
-
with_default_shard { load_schema_without_default_shard! }
|
210
|
-
end
|
211
|
-
else
|
212
|
-
def columns_with_default_shard
|
213
|
-
with_default_shard { columns_without_default_shard }
|
214
|
-
end
|
222
|
+
def load_schema_with_default_shard!
|
223
|
+
with_default_shard { load_schema_without_default_shard! }
|
215
224
|
end
|
216
225
|
|
217
226
|
def table_exists_with_default_shard?
|
@@ -227,21 +236,20 @@ module ActiveRecordShards
|
|
227
236
|
def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
|
228
237
|
@target.on_primary_or_replica(@which) { @target.send(method, *args, &block) }
|
229
238
|
end
|
239
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
230
240
|
end
|
231
|
-
|
232
|
-
MasterSlaveProxy = PrimaryReplicaProxy
|
233
241
|
end
|
234
242
|
end
|
235
243
|
|
236
244
|
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
237
|
-
when '4.2'
|
238
|
-
require 'active_record_shards/connection_switcher-4-2'
|
239
|
-
when '5.0'
|
240
|
-
require 'active_record_shards/connection_switcher-5-0'
|
241
245
|
when '5.1', '5.2'
|
242
246
|
require 'active_record_shards/connection_switcher-5-1'
|
243
247
|
when '6.0'
|
244
248
|
require 'active_record_shards/connection_switcher-6-0'
|
249
|
+
when '6.1'
|
250
|
+
require 'active_record_shards/connection_switcher-6-1'
|
251
|
+
when '7.0'
|
252
|
+
require 'active_record_shards/connection_switcher-7-0'
|
245
253
|
else
|
246
254
|
raise "ActiveRecordShards is not compatible with #{ActiveRecord::VERSION::STRING}"
|
247
255
|
end
|
@@ -22,21 +22,13 @@ module ActiveRecordShards
|
|
22
22
|
#{method}_without_default_replica#{punctuation}(*args, &block)
|
23
23
|
end
|
24
24
|
end
|
25
|
-
|
25
|
+
ruby2_keywords(:#{method}_with_default_replica#{punctuation}) if respond_to?(:ruby2_keywords, true)
|
26
26
|
alias_method :#{method}_without_default_replica#{punctuation}, :#{method}#{punctuation}
|
27
27
|
alias_method :#{method}#{punctuation}, :#{method}_with_default_replica#{punctuation}
|
28
28
|
#{class_method ? 'end' : ''}
|
29
29
|
RUBY
|
30
30
|
end
|
31
31
|
|
32
|
-
def self.wrap_method_in_on_slave(*args)
|
33
|
-
ActiveRecordShards::Deprecation.deprecation_warning(
|
34
|
-
:'self.wrap_method_in_on_slave',
|
35
|
-
:'self.wrap_method_in_on_replica'
|
36
|
-
)
|
37
|
-
wrap_method_in_on_replica(*args)
|
38
|
-
end
|
39
|
-
|
40
32
|
def transaction_with_replica_off(*args, &block)
|
41
33
|
if on_replica_by_default?
|
42
34
|
begin
|
@@ -50,14 +42,9 @@ module ActiveRecordShards
|
|
50
42
|
transaction_without_replica_off(*args, &block)
|
51
43
|
end
|
52
44
|
end
|
53
|
-
|
45
|
+
ruby2_keywords(:transaction_with_replica_off) if respond_to?(:ruby2_keywords, true)
|
54
46
|
|
55
47
|
module InstanceMethods
|
56
|
-
# fix ActiveRecord to do the right thing, and use our aliased quote_value
|
57
|
-
def quote_value(*args, &block)
|
58
|
-
self.class.quote_value(*args, &block)
|
59
|
-
end
|
60
|
-
|
61
48
|
def on_replica_unless_tx
|
62
49
|
self.class.on_replica_unless_tx { yield }
|
63
50
|
end
|
@@ -84,19 +71,11 @@ module ActiveRecordShards
|
|
84
71
|
:table_exists?
|
85
72
|
].freeze
|
86
73
|
|
87
|
-
CLASS_SLAVE_METHODS = CLASS_REPLICA_METHODS
|
88
|
-
CLASS_FORCE_SLAVE_METHODS = CLASS_FORCE_REPLICA_METHODS
|
89
|
-
|
90
74
|
def self.extended(base)
|
91
75
|
CLASS_REPLICA_METHODS.each { |m| ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(true, base, m) }
|
92
76
|
CLASS_FORCE_REPLICA_METHODS.each { |m| ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(true, base, m, force_on_replica: true) }
|
93
77
|
|
94
|
-
|
95
|
-
ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(true, base, :load_schema!, force_on_replica: true)
|
96
|
-
else
|
97
|
-
ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(true, base, :columns, force_on_replica: true)
|
98
|
-
end
|
99
|
-
|
78
|
+
ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(true, base, :load_schema!, force_on_replica: true)
|
100
79
|
ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(false, base, :reload)
|
101
80
|
|
102
81
|
base.class_eval do
|
@@ -119,7 +98,6 @@ module ActiveRecordShards
|
|
119
98
|
yield
|
120
99
|
end
|
121
100
|
end
|
122
|
-
alias_method :on_slave_unless_tx, :on_replica_unless_tx
|
123
101
|
|
124
102
|
def force_on_replica(&block)
|
125
103
|
return yield if Thread.current[:_active_record_shards_in_migration]
|
@@ -133,11 +111,6 @@ module ActiveRecordShards
|
|
133
111
|
ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(false, base, m)
|
134
112
|
end
|
135
113
|
|
136
|
-
if ActiveRecord::VERSION::MAJOR == 4
|
137
|
-
# `where` and `having` clauses call `create_binds`, which will use the primary connection
|
138
|
-
ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(false, base, :create_binds, force_on_replica: true)
|
139
|
-
end
|
140
|
-
|
141
114
|
ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(false, base, :to_sql, force_on_replica: true)
|
142
115
|
end
|
143
116
|
|
@@ -210,20 +183,6 @@ module ActiveRecordShards
|
|
210
183
|
end
|
211
184
|
end
|
212
185
|
|
213
|
-
module AssociationsAssociationGetRecordsPatch
|
214
|
-
def get_records # rubocop:disable Naming/AccessorMethodName
|
215
|
-
if klass
|
216
|
-
on_replica_unless_tx { super }
|
217
|
-
else
|
218
|
-
super
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
def on_replica_unless_tx
|
223
|
-
klass.on_replica_unless_tx { yield }
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
186
|
module AssociationsPreloaderAssociationAssociatedRecordsByOwnerPatch
|
228
187
|
def associated_records_by_owner(preloader)
|
229
188
|
if klass
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecordShards
|
4
|
+
module DefaultShard
|
5
|
+
def default_shard=(new_default_shard)
|
6
|
+
if ars_shard_type?(new_default_shard)
|
7
|
+
ActiveRecordShards::ShardSelection.ars_default_shard = new_default_shard
|
8
|
+
switch_connection(shard: new_default_shard)
|
9
|
+
else
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def ars_shard_type?(shard)
|
17
|
+
return true if ActiveRecord.version < Gem::Version.new('6.1')
|
18
|
+
return true if shard.nil?
|
19
|
+
return true if shard == :_no_shard
|
20
|
+
return true if shard.is_a?(Integer)
|
21
|
+
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
ActiveRecord::Base.singleton_class.prepend(ActiveRecordShards::DefaultShard)
|
@@ -19,11 +19,10 @@ module ActiveRecord
|
|
19
19
|
# manually on the sharded DBs.
|
20
20
|
ActiveRecord::Base.on_all_shards do
|
21
21
|
ActiveRecord::SchemaMigration.create_table
|
22
|
-
|
23
|
-
ActiveRecord::InternalMetadata.create_table
|
24
|
-
end
|
22
|
+
ActiveRecord::InternalMetadata.create_table
|
25
23
|
end
|
26
24
|
end
|
25
|
+
ruby2_keywords(:initialize_with_sharding) if respond_to?(:ruby2_keywords, true)
|
27
26
|
alias_method :initialize_without_sharding, :initialize
|
28
27
|
alias_method :initialize, :initialize_with_sharding
|
29
28
|
|
@@ -34,7 +34,6 @@ module ActiveRecordShards
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
37
|
-
alias_method :on_slave_by_default?, :on_replica_by_default?
|
38
37
|
|
39
38
|
def on_replica_by_default=(value)
|
40
39
|
if self == ActiveRecord::Base
|
@@ -43,19 +42,16 @@ module ActiveRecordShards
|
|
43
42
|
base_class.instance_variable_set(:@on_replica_by_default, value)
|
44
43
|
end
|
45
44
|
end
|
46
|
-
alias_method :on_slave_by_default=, :on_replica_by_default=
|
47
45
|
|
48
46
|
module InstanceMethods
|
49
47
|
def initialize_shard_and_replica
|
50
48
|
@from_replica = !!self.class.current_shard_selection.options[:replica]
|
51
49
|
@from_shard = self.class.current_shard_selection.options[:shard]
|
52
50
|
end
|
53
|
-
alias_method :initialize_shard_and_slave, :initialize_shard_and_replica
|
54
51
|
|
55
52
|
def from_replica?
|
56
53
|
@from_replica
|
57
54
|
end
|
58
|
-
alias_method :from_slave?, :from_replica?
|
59
55
|
|
60
56
|
def from_shard
|
61
57
|
@from_shard
|
@@ -3,71 +3,54 @@
|
|
3
3
|
module ActiveRecordShards
|
4
4
|
class ShardSelection
|
5
5
|
NO_SHARD = :_no_shard
|
6
|
-
cattr_accessor :
|
6
|
+
cattr_accessor :ars_default_shard
|
7
7
|
|
8
8
|
def initialize
|
9
9
|
@on_replica = false
|
10
10
|
@shard = nil
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
nil
|
19
|
-
else
|
20
|
-
@shard || self.class.default_shard
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def shard_name(klass = nil, try_replica = true)
|
26
|
-
the_shard = shard(klass)
|
27
|
-
|
28
|
-
@shard_names ||= {}
|
29
|
-
@shard_names[ActiveRecordShards.app_env] ||= {}
|
30
|
-
@shard_names[ActiveRecordShards.app_env][the_shard] ||= {}
|
31
|
-
@shard_names[ActiveRecordShards.app_env][the_shard][try_replica] ||= {}
|
32
|
-
@shard_names[ActiveRecordShards.app_env][the_shard][try_replica][@on_replica] ||= begin
|
33
|
-
s = ActiveRecordShards.app_env.dup
|
34
|
-
s << "_shard_#{the_shard}" if the_shard
|
35
|
-
s << "_replica" if @on_replica && try_replica
|
36
|
-
s
|
37
|
-
end
|
13
|
+
def shard
|
14
|
+
if @shard.nil? || @shard == NO_SHARD
|
15
|
+
nil
|
16
|
+
else
|
17
|
+
@shard || self.class.ars_default_shard
|
38
18
|
end
|
39
|
-
|
19
|
+
end
|
20
|
+
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
21
|
+
when '6.1', '7.0'
|
22
|
+
PRIMARY = "ActiveRecord::Base"
|
40
23
|
else
|
41
|
-
|
42
|
-
def shard
|
43
|
-
if @shard.nil? || @shard == NO_SHARD
|
44
|
-
nil
|
45
|
-
else
|
46
|
-
@shard || self.class.default_shard
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
24
|
PRIMARY = "primary"
|
51
|
-
|
52
|
-
|
53
|
-
|
25
|
+
end
|
26
|
+
def resolve_connection_name(sharded:, configurations:)
|
27
|
+
resolved_shard = sharded ? shard : nil
|
28
|
+
env = ActiveRecordShards.app_env
|
54
29
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
30
|
+
@connection_names ||= {}
|
31
|
+
@connection_names[env] ||= {}
|
32
|
+
@connection_names[env][resolved_shard] ||= {}
|
33
|
+
@connection_names[env][resolved_shard][@on_replica] ||= begin
|
34
|
+
name = env.dup
|
35
|
+
name << "_shard_#{resolved_shard}" if resolved_shard
|
36
|
+
replica_config = begin
|
37
|
+
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
38
|
+
when '7.0'
|
39
|
+
configurations.configs_for(env_name: "#{name}_replica", include_hidden: true).any?
|
40
|
+
when '6.1'
|
41
|
+
configurations.configs_for(env_name: "#{name}_replica", include_replicas: true).any?
|
63
42
|
else
|
64
|
-
#
|
65
|
-
# while everything else is named by the configuration name
|
66
|
-
resolved_shard ? name : PRIMARY
|
43
|
+
configurations["#{name}_replica"]
|
67
44
|
end
|
68
45
|
end
|
46
|
+
if @on_replica && replica_config
|
47
|
+
"#{name}_replica"
|
48
|
+
else
|
49
|
+
# ActiveRecord always names its default connection pool 'primary'
|
50
|
+
# while everything else is named by the configuration name
|
51
|
+
resolved_shard ? name : PRIMARY
|
52
|
+
end
|
69
53
|
end
|
70
|
-
|
71
54
|
end
|
72
55
|
|
73
56
|
def shard=(new_shard)
|
@@ -77,12 +60,10 @@ module ActiveRecordShards
|
|
77
60
|
def on_replica?
|
78
61
|
@on_replica
|
79
62
|
end
|
80
|
-
alias_method :on_slave?, :on_replica?
|
81
63
|
|
82
64
|
def on_replica=(new_replica)
|
83
65
|
@on_replica = (new_replica == true)
|
84
66
|
end
|
85
|
-
alias_method :on_slave=, :on_replica=
|
86
67
|
|
87
68
|
def options
|
88
69
|
{ shard: @shard, replica: @on_replica }
|
@@ -23,15 +23,14 @@ module ActiveRecordShards
|
|
23
23
|
|
24
24
|
exception = nil
|
25
25
|
enum.each do
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
exception = e
|
31
|
-
end
|
26
|
+
record = @scope.find(*find_args)
|
27
|
+
return record if record
|
28
|
+
rescue ActiveRecord::RecordNotFound => e
|
29
|
+
exception = e
|
32
30
|
end
|
33
31
|
raise exception
|
34
32
|
end
|
33
|
+
ruby2_keywords(:find) if respond_to?(:ruby2_keywords, true)
|
35
34
|
|
36
35
|
def count
|
37
36
|
enum.inject(0) { |accum, _shard| @scope.clone.count + accum }
|
@@ -9,10 +9,24 @@ end
|
|
9
9
|
namespace :db do
|
10
10
|
desc 'Drops the database for the current RAILS_ENV including shards'
|
11
11
|
task drop: :load_config do
|
12
|
-
|
13
|
-
|
12
|
+
configurations = begin
|
13
|
+
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
14
|
+
when '6.1', '7.0'
|
15
|
+
ActiveRecord::Base.configurations.configurations.map { |configuration| [configuration.env_name, configuration.configuration_hash] }
|
16
|
+
else
|
17
|
+
ActiveRecord::Base.configurations.to_h
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
configurations.each do |key, conf|
|
22
|
+
next if !key.start_with?(ActiveRecordShards.app_env) || key.end_with?("_replica")
|
14
23
|
|
15
24
|
begin
|
25
|
+
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
26
|
+
when '6.1', '7.0'
|
27
|
+
conf = conf.stringify_keys
|
28
|
+
end
|
29
|
+
|
16
30
|
ActiveRecordShards::Tasks.root_connection(conf).drop_database(conf['database'])
|
17
31
|
# rescue ActiveRecord::NoDatabaseError # TODO: exists in AR but never is raised here ...
|
18
32
|
# $stderr.puts "Database '#{conf['database']}' does not exist"
|
@@ -30,13 +44,26 @@ namespace :db do
|
|
30
44
|
|
31
45
|
desc "Create the database defined in config/database.yml for the current RAILS_ENV including shards"
|
32
46
|
task create: :load_config do
|
33
|
-
|
34
|
-
|
47
|
+
configurations = begin
|
48
|
+
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
49
|
+
when '6.1', '7.0'
|
50
|
+
ActiveRecord::Base.configurations.configurations.map { |configuration| [configuration.env_name, configuration.configuration_hash] }
|
51
|
+
else
|
52
|
+
ActiveRecord::Base.configurations.to_h
|
53
|
+
end
|
54
|
+
end
|
55
|
+
configurations.each do |key, conf|
|
56
|
+
next if !key.start_with?(ActiveRecordShards.app_env) || key.end_with?("_replica")
|
35
57
|
|
36
58
|
begin
|
37
59
|
# MysqlAdapter takes charset instead of encoding in Rails 4.2 or greater
|
38
60
|
# https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/tasks/mysql_database_tasks.rb#L85-L96
|
39
61
|
symbolized_configuration = conf.symbolize_keys
|
62
|
+
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
63
|
+
when '6.1', '7.0'
|
64
|
+
conf = conf.stringify_keys
|
65
|
+
end
|
66
|
+
|
40
67
|
symbolized_configuration[:charset] = symbolized_configuration[:encoding]
|
41
68
|
|
42
69
|
ActiveRecordShards::Tasks.root_connection(conf).create_database(conf['database'], symbolized_configuration)
|
@@ -78,14 +105,12 @@ namespace :db do
|
|
78
105
|
namespace :test do
|
79
106
|
desc 'Purges the test databases by dropping and creating'
|
80
107
|
task purge: :load_config do |t|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
Rails.env = saved_env
|
88
|
-
end
|
108
|
+
saved_env = Rails.env
|
109
|
+
Rails.env = 'test'
|
110
|
+
Rake.application.lookup('db:drop', t.scope).execute
|
111
|
+
Rake.application.lookup('db:create', t.scope).execute
|
112
|
+
ensure
|
113
|
+
Rails.env = saved_env
|
89
114
|
end
|
90
115
|
end
|
91
116
|
end
|
@@ -96,15 +121,26 @@ module ActiveRecordShards
|
|
96
121
|
def root_connection(conf)
|
97
122
|
conf = conf.merge('database' => nil)
|
98
123
|
spec = spec_for(conf)
|
99
|
-
|
100
|
-
|
124
|
+
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
125
|
+
when '6.1', '7.0'
|
126
|
+
ActiveRecord::Base.send("#{conf['adapter']}_connection", spec.db_config.configuration_hash)
|
127
|
+
else
|
128
|
+
ActiveRecord::Base.send("#{conf['adapter']}_connection", spec.config)
|
129
|
+
end
|
101
130
|
end
|
102
131
|
|
103
132
|
private
|
104
133
|
|
105
134
|
def spec_for(conf)
|
106
|
-
|
107
|
-
|
135
|
+
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
136
|
+
when '7.0'
|
137
|
+
ActiveRecord::Base.connection_handler.send(:resolve_pool_config, conf, ActiveRecord::Base, ActiveRecord::Base.current_role, ActiveRecord::Base.current_shard)
|
138
|
+
when '6.1'
|
139
|
+
ActiveRecord::Base.connection_handler.send(:resolve_pool_config, conf, ActiveRecord::Base)
|
140
|
+
else
|
141
|
+
resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(ActiveRecord::Base.configurations)
|
142
|
+
resolver.spec(conf)
|
143
|
+
end
|
108
144
|
end
|
109
145
|
end
|
110
146
|
end
|
data/lib/active_record_shards.rb
CHANGED
@@ -9,6 +9,7 @@ require 'active_record_shards/connection_switcher'
|
|
9
9
|
require 'active_record_shards/association_collection_connection_selection'
|
10
10
|
require 'active_record_shards/migration'
|
11
11
|
require 'active_record_shards/default_replica_patches'
|
12
|
+
require 'active_record_shards/default_shard'
|
12
13
|
require 'active_record_shards/schema_dumper_extension'
|
13
14
|
|
14
15
|
module ActiveRecordShards
|
@@ -36,32 +37,6 @@ ActiveRecord::Associations::Builder::HasAndBelongsToMany.include(ActiveRecordSha
|
|
36
37
|
ActiveRecord::SchemaDumper.prepend(ActiveRecordShards::SchemaDumperExtension)
|
37
38
|
|
38
39
|
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
39
|
-
when '4.2'
|
40
|
-
require 'active_record_shards/patches-4-2'
|
41
|
-
|
42
|
-
# https://github.com/rails/rails/blob/v4.2.11.3/activerecord/lib/active_record/associations/association.rb#L97
|
43
|
-
ActiveRecord::Associations::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationAssociationScopePatch)
|
44
|
-
|
45
|
-
# https://github.com/rails/rails/blob/v4.2.11.3/activerecord/lib/active_record/associations/singular_association.rb#L44-L53
|
46
|
-
ActiveRecord::Associations::SingularAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationGetRecordsPatch)
|
47
|
-
|
48
|
-
# https://github.com/rails/rails/blob/v4.2.11.3/activerecord/lib/active_record/associations/collection_association.rb#L447-L456
|
49
|
-
ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationGetRecordsPatch)
|
50
|
-
|
51
|
-
# https://github.com/rails/rails/blob/v4.2.11.3/activerecord/lib/active_record/associations/preloader/association.rb#L89
|
52
|
-
ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsPreloaderAssociationAssociatedRecordsByOwnerPatch)
|
53
|
-
when '5.0'
|
54
|
-
# https://github.com/rails/rails/blob/v5.0.7/activerecord/lib/active_record/associations/association.rb#L97
|
55
|
-
ActiveRecord::Associations::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationAssociationScopePatch)
|
56
|
-
|
57
|
-
# https://github.com/rails/rails/blob/v5.0.7/activerecord/lib/active_record/associations/singular_association.rb#L56-L65
|
58
|
-
ActiveRecord::Associations::SingularAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationGetRecordsPatch)
|
59
|
-
|
60
|
-
# https://github.com/rails/rails/blob/v5.0.7/activerecord/lib/active_record/associations/collection_association.rb#L442-L451
|
61
|
-
ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationGetRecordsPatch)
|
62
|
-
|
63
|
-
# https://github.com/rails/rails/blob/v5.0.7/activerecord/lib/active_record/associations/preloader/association.rb#L120
|
64
|
-
ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsPreloaderAssociationLoadRecordsPatch)
|
65
40
|
when '5.1'
|
66
41
|
# https://github.com/rails/rails/blob/v5.1.7/activerecord/lib/active_record/associations/association.rb#L97
|
67
42
|
ActiveRecord::Associations::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationAssociationScopePatch)
|
@@ -87,7 +62,7 @@ when '5.2'
|
|
87
62
|
|
88
63
|
# https://github.com/rails/rails/blob/v5.2.6/activerecord/lib/active_record/associations/preloader/association.rb#L96
|
89
64
|
ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsPreloaderAssociationLoadRecordsPatch)
|
90
|
-
when '6.0'
|
65
|
+
when '6.0', '6.1', '7.0'
|
91
66
|
# https://github.com/rails/rails/blob/v6.0.4/activerecord/lib/active_record/type_caster/connection.rb#L28
|
92
67
|
ActiveRecord::TypeCaster::Connection.prepend(ActiveRecordShards::DefaultReplicaPatches::TypeCasterConnectionConnectionPatch)
|
93
68
|
|
@@ -103,45 +78,3 @@ when '6.0'
|
|
103
78
|
else
|
104
79
|
raise "ActiveRecordShards is not compatible with #{ActiveRecord::VERSION::STRING}"
|
105
80
|
end
|
106
|
-
|
107
|
-
require 'active_record_shards/deprecation'
|
108
|
-
|
109
|
-
ActiveRecordShards::Deprecation.deprecate_methods(
|
110
|
-
ActiveRecordShards::AssociationCollectionConnectionSelection,
|
111
|
-
on_slave_if: :on_replica_if,
|
112
|
-
on_slave_unless: :on_replica_unless,
|
113
|
-
on_slave: :on_replica,
|
114
|
-
on_master: :on_primary,
|
115
|
-
on_master_if: :on_primary_if,
|
116
|
-
on_master_unless: :on_primary_unless
|
117
|
-
)
|
118
|
-
|
119
|
-
ActiveRecordShards::Deprecation.deprecate_methods(
|
120
|
-
ActiveRecordShards::ConnectionSwitcher,
|
121
|
-
on_slave_if: :on_replica_if,
|
122
|
-
on_slave_unless: :on_replica_unless,
|
123
|
-
on_master_or_slave: :on_primary_or_replica,
|
124
|
-
on_slave: :on_replica,
|
125
|
-
on_master: :on_primary,
|
126
|
-
on_master_if: :on_primary_if,
|
127
|
-
on_master_unless: :on_primary_unless,
|
128
|
-
on_slave?: :on_replica?
|
129
|
-
)
|
130
|
-
|
131
|
-
ActiveRecordShards::Deprecation.deprecate_methods(
|
132
|
-
ActiveRecordShards::DefaultReplicaPatches,
|
133
|
-
transaction_with_slave_off: :transaction_with_replica_off,
|
134
|
-
on_slave_unless_tx: :on_replica_unless_tx
|
135
|
-
)
|
136
|
-
|
137
|
-
ActiveRecordShards::Deprecation.deprecate_methods(
|
138
|
-
ActiveRecordShards::Model,
|
139
|
-
on_slave_by_default?: :on_replica_by_default?,
|
140
|
-
:on_slave_by_default= => :on_replica_by_default=
|
141
|
-
)
|
142
|
-
|
143
|
-
ActiveRecordShards::Deprecation.deprecate_methods(
|
144
|
-
ActiveRecordShards::ShardSelection,
|
145
|
-
on_slave?: :on_replica?,
|
146
|
-
:on_slave= => :on_replica=
|
147
|
-
)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_shards
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 5.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Benjamin Quorning
|
@@ -10,10 +10,10 @@ authors:
|
|
10
10
|
- Mick Staugaard
|
11
11
|
- Eric Chapweske
|
12
12
|
- Ben Osheroff
|
13
|
-
autorequire:
|
13
|
+
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date:
|
16
|
+
date: 2023-02-14 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: activerecord
|
@@ -21,40 +21,40 @@ dependencies:
|
|
21
21
|
requirements:
|
22
22
|
- - ">="
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: '
|
24
|
+
version: '5.1'
|
25
25
|
- - "<"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
27
|
+
version: '7.1'
|
28
28
|
type: :runtime
|
29
29
|
prerelease: false
|
30
30
|
version_requirements: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
34
|
+
version: '5.1'
|
35
35
|
- - "<"
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version: '
|
37
|
+
version: '7.1'
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: activesupport
|
40
40
|
requirement: !ruby/object:Gem::Requirement
|
41
41
|
requirements:
|
42
42
|
- - ">="
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version: '
|
44
|
+
version: '5.1'
|
45
45
|
- - "<"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '7.1'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '5.1'
|
55
55
|
- - "<"
|
56
56
|
- !ruby/object:Gem::Version
|
57
|
-
version: '
|
57
|
+
version: '7.1'
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: bump
|
60
60
|
requirement: !ruby/object:Gem::Requirement
|
@@ -97,20 +97,6 @@ dependencies:
|
|
97
97
|
- - ">="
|
98
98
|
- !ruby/object:Gem::Version
|
99
99
|
version: '0'
|
100
|
-
- !ruby/object:Gem::Dependency
|
101
|
-
name: mocha
|
102
|
-
requirement: !ruby/object:Gem::Requirement
|
103
|
-
requirements:
|
104
|
-
- - ">="
|
105
|
-
- !ruby/object:Gem::Version
|
106
|
-
version: 1.4.0
|
107
|
-
type: :development
|
108
|
-
prerelease: false
|
109
|
-
version_requirements: !ruby/object:Gem::Requirement
|
110
|
-
requirements:
|
111
|
-
- - ">="
|
112
|
-
- !ruby/object:Gem::Version
|
113
|
-
version: 1.4.0
|
114
100
|
- !ruby/object:Gem::Dependency
|
115
101
|
name: mysql2
|
116
102
|
requirement: !ruby/object:Gem::Requirement
|
@@ -195,20 +181,15 @@ files:
|
|
195
181
|
- lib/active_record_shards.rb
|
196
182
|
- lib/active_record_shards/association_collection_connection_selection.rb
|
197
183
|
- lib/active_record_shards/configuration_parser.rb
|
198
|
-
- lib/active_record_shards/connection_handler.rb
|
199
|
-
- lib/active_record_shards/connection_pool.rb
|
200
|
-
- lib/active_record_shards/connection_specification.rb
|
201
|
-
- lib/active_record_shards/connection_switcher-4-2.rb
|
202
|
-
- lib/active_record_shards/connection_switcher-5-0.rb
|
203
184
|
- lib/active_record_shards/connection_switcher-5-1.rb
|
204
185
|
- lib/active_record_shards/connection_switcher-6-0.rb
|
186
|
+
- lib/active_record_shards/connection_switcher-6-1.rb
|
187
|
+
- lib/active_record_shards/connection_switcher-7-0.rb
|
205
188
|
- lib/active_record_shards/connection_switcher.rb
|
206
189
|
- lib/active_record_shards/default_replica_patches.rb
|
207
|
-
- lib/active_record_shards/
|
208
|
-
- lib/active_record_shards/deprecation.rb
|
190
|
+
- lib/active_record_shards/default_shard.rb
|
209
191
|
- lib/active_record_shards/migration.rb
|
210
192
|
- lib/active_record_shards/model.rb
|
211
|
-
- lib/active_record_shards/patches-4-2.rb
|
212
193
|
- lib/active_record_shards/schema_dumper_extension.rb
|
213
194
|
- lib/active_record_shards/shard_selection.rb
|
214
195
|
- lib/active_record_shards/shard_support.rb
|
@@ -218,7 +199,7 @@ homepage: https://github.com/zendesk/active_record_shards
|
|
218
199
|
licenses:
|
219
200
|
- MIT
|
220
201
|
metadata: {}
|
221
|
-
post_install_message:
|
202
|
+
post_install_message:
|
222
203
|
rdoc_options: []
|
223
204
|
require_paths:
|
224
205
|
- lib
|
@@ -226,15 +207,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
226
207
|
requirements:
|
227
208
|
- - ">="
|
228
209
|
- !ruby/object:Gem::Version
|
229
|
-
version: '2.
|
210
|
+
version: '2.6'
|
230
211
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
231
212
|
requirements:
|
232
213
|
- - ">="
|
233
214
|
- !ruby/object:Gem::Version
|
234
215
|
version: '0'
|
235
216
|
requirements: []
|
236
|
-
rubygems_version: 3.1
|
237
|
-
signing_key:
|
217
|
+
rubygems_version: 3.0.3.1
|
218
|
+
signing_key:
|
238
219
|
specification_version: 4
|
239
220
|
summary: Simple database switching for ActiveRecord.
|
240
221
|
test_files: []
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActiveRecordShards
|
4
|
-
ConnectionPoolNameDecorator = Struct.new(:name)
|
5
|
-
|
6
|
-
# It overrides given connection handler methods (they differ depend on
|
7
|
-
# Rails version).
|
8
|
-
#
|
9
|
-
# It takes the first argument, ActiveRecord::Base object or
|
10
|
-
# String (connection_pool_name), converts it in Struct object and
|
11
|
-
# passes to the original method.
|
12
|
-
#
|
13
|
-
# Example:
|
14
|
-
# methods_to_override = [:establish_connection, :remove_connection]
|
15
|
-
# ActiveRecordShards.override_connection_handler_methods(methods_to_override)
|
16
|
-
#
|
17
|
-
def self.override_connection_handler_methods(method_names)
|
18
|
-
method_names.each do |method_name|
|
19
|
-
ActiveRecord::ConnectionAdapters::ConnectionHandler.class_eval do
|
20
|
-
define_method("#{method_name}_with_connection_pool_name") do |*args|
|
21
|
-
unless args[0].is_a? ConnectionPoolNameDecorator
|
22
|
-
name = if args[0].is_a? String
|
23
|
-
args[0]
|
24
|
-
else
|
25
|
-
args[0].connection_pool_name
|
26
|
-
end
|
27
|
-
args[0] = ConnectionPoolNameDecorator.new(name)
|
28
|
-
end
|
29
|
-
send("#{method_name}_without_connection_pool_name", *args)
|
30
|
-
end
|
31
|
-
alias_method :"#{method_name}_without_connection_pool_name", method_name
|
32
|
-
alias_method method_name, :"#{method_name}_with_connection_pool_name"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class << ActiveRecord::Base
|
4
|
-
remove_method :establish_connection if ActiveRecord::VERSION::MAJOR >= 5
|
5
|
-
def establish_connection(spec = ENV["DATABASE_URL"])
|
6
|
-
spec ||= ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
|
7
|
-
spec = spec.to_sym if spec.is_a?(String)
|
8
|
-
resolver = ActiveRecordShards::ConnectionSpecification::Resolver.new configurations
|
9
|
-
spec = resolver.spec(spec)
|
10
|
-
|
11
|
-
unless respond_to?(spec.adapter_method)
|
12
|
-
raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
|
13
|
-
end
|
14
|
-
|
15
|
-
remove_connection
|
16
|
-
specification_cache[connection_pool_name] = spec
|
17
|
-
connection_handler.establish_connection self, spec
|
18
|
-
end
|
19
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
module ActiveRecordShards
|
2
|
-
module ConnectionSwitcher
|
3
|
-
# Name of the connection pool. Used by ConnectionHandler to retrieve the current connection pool.
|
4
|
-
def connection_pool_name # :nodoc:
|
5
|
-
name = current_shard_selection.shard_name(self)
|
6
|
-
|
7
|
-
# e.g. if "production_replica" is not defined in `Configuration`, fall back to "production"
|
8
|
-
if configurations[name].nil? && on_replica?
|
9
|
-
current_shard_selection.shard_name(self, false)
|
10
|
-
else
|
11
|
-
name
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def ensure_shard_connection
|
18
|
-
establish_shard_connection unless connected_to_shard?
|
19
|
-
end
|
20
|
-
|
21
|
-
def establish_shard_connection
|
22
|
-
name = connection_pool_name
|
23
|
-
spec = configurations[name]
|
24
|
-
|
25
|
-
if spec.nil?
|
26
|
-
raise ActiveRecord::AdapterNotSpecified, "No database defined by #{name} in your database config. (configurations: #{configurations.keys.inspect})"
|
27
|
-
end
|
28
|
-
|
29
|
-
specification_cache[name] ||= begin
|
30
|
-
resolver = ActiveRecordShards::ConnectionSpecification::Resolver.new configurations
|
31
|
-
resolver.spec(spec)
|
32
|
-
end
|
33
|
-
|
34
|
-
connection_handler.establish_connection(self, specification_cache[name])
|
35
|
-
end
|
36
|
-
|
37
|
-
def specification_cache
|
38
|
-
@@specification_cache ||= {}
|
39
|
-
end
|
40
|
-
|
41
|
-
# Helper method to clear global state when testing.
|
42
|
-
def clear_specification_cache
|
43
|
-
@@specification_cache = {}
|
44
|
-
end
|
45
|
-
|
46
|
-
def connection_pool_key
|
47
|
-
specification_cache[connection_pool_name]
|
48
|
-
end
|
49
|
-
|
50
|
-
def connected_to_shard?
|
51
|
-
specs_to_pools = Hash[connection_handler.connection_pool_list.map { |pool| [pool.spec, pool] }]
|
52
|
-
|
53
|
-
specs_to_pools.key?(connection_pool_key)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,12 +0,0 @@
|
|
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,9 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_record_shards/connection_pool'
|
4
|
-
require 'active_record_shards/connection_handler'
|
5
|
-
require 'active_record_shards/connection_specification'
|
6
|
-
|
7
|
-
ActiveRecordShards::ConnectionSpecification = ActiveRecord::ConnectionAdapters::ConnectionSpecification
|
8
|
-
methods_to_override = [:establish_connection, :remove_connection, :pool_for, :pool_from_any_process_for]
|
9
|
-
ActiveRecordShards.override_connection_handler_methods(methods_to_override)
|