active_record_shards 3.18.0 → 3.19.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: 1f573041b49f55115d94db028525da537bbbadffc4526d8a4f220f3d4ab1d4fd
4
- data.tar.gz: f29528996589d7cef3f14671df47d10a8d4cf4a3b0ad81b022aaddf7b229178d
3
+ metadata.gz: 24b77105aa8df10b1e79313f01b54fe87dba293938c9d41dc103f41cac64ec84
4
+ data.tar.gz: ec1da959a6dc2adbb6f287f73a3346c68fd49db30f7922dbc9adca7368532af2
5
5
  SHA512:
6
- metadata.gz: 782a638a2d4ce88a06ac0f6d93d277de20831d918ee1014fec1b2a01f95631b5857fb2df1d83b15ced1e52b1749283ec497c43ccfc0d85ffd6e3bd271d4d9f28
7
- data.tar.gz: f28464784b171c3dd8a888574f1bb362e645f3949dcf3b07a70a9c195e73ae00a1c5a7319c107666f4060e37c04c3daacd8415187124dbda891aae5cb19a7ecf
6
+ metadata.gz: 5ccae049b9c4a450f04cc2ae6b0cfca9eba75ac72d5f795c0c49ae5fc19e37fad1341d14f3ba34f75c684696131586a663fb306bcd35c2dad9fc625ad36955f3
7
+ data.tar.gz: 0b46d1cafa4fecae447a01106bfc4de592b7271fc17fef4c254a44a80cc22b22f5155e460d6aba036baad1a229f6f0b6935c3d108c935c5fd5cec9e6271eab83
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![CircleCI build status](https://circleci.com/gh/zendesk/active_record_shards/tree/master.svg?style=svg)](https://circleci.com/gh/zendesk/active_record_shards/tree/master)
1
+ [![Build Status](https://github.com/zendesk/active_record_shards/workflows/CI/badge.svg)](https://github.com/zendesk/active_record_shards/actions?query=workflow%3ACI)
2
2
 
3
3
  # ActiveRecord Shards
4
4
 
@@ -32,8 +32,68 @@ ActiveRecord::SchemaDumper.prepend(ActiveRecordShards::SchemaDumperExtension)
32
32
  case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
33
33
  when '4.2'
34
34
  require 'active_record_shards/patches-4-2'
35
- when '5.0', '5.1', '5.2', '6.0'
36
- :ok
35
+
36
+ # https://github.com/rails/rails/blob/v4.2.11.3/activerecord/lib/active_record/associations/association.rb#L97
37
+ ActiveRecord::Associations::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationAssociationScopePatch)
38
+
39
+ # https://github.com/rails/rails/blob/v4.2.11.3/activerecord/lib/active_record/associations/singular_association.rb#L44-L53
40
+ ActiveRecord::Associations::SingularAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationGetRecordsPatch)
41
+
42
+ # https://github.com/rails/rails/blob/v4.2.11.3/activerecord/lib/active_record/associations/collection_association.rb#L447-L456
43
+ ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationGetRecordsPatch)
44
+
45
+ # https://github.com/rails/rails/blob/v4.2.11.3/activerecord/lib/active_record/associations/preloader/association.rb#L89
46
+ ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsPreloaderAssociationAssociatedRecordsByOwnerPatch)
47
+ when '5.0'
48
+ # https://github.com/rails/rails/blob/v5.0.7/activerecord/lib/active_record/associations/association.rb#L97
49
+ ActiveRecord::Associations::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationAssociationScopePatch)
50
+
51
+ # https://github.com/rails/rails/blob/v5.0.7/activerecord/lib/active_record/associations/singular_association.rb#L56-L65
52
+ ActiveRecord::Associations::SingularAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationGetRecordsPatch)
53
+
54
+ # https://github.com/rails/rails/blob/v5.0.7/activerecord/lib/active_record/associations/collection_association.rb#L442-L451
55
+ ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationGetRecordsPatch)
56
+
57
+ # https://github.com/rails/rails/blob/v5.0.7/activerecord/lib/active_record/associations/preloader/association.rb#L120
58
+ ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsPreloaderAssociationLoadRecordsPatch)
59
+ when '5.1'
60
+ # https://github.com/rails/rails/blob/v5.1.7/activerecord/lib/active_record/associations/association.rb#L97
61
+ ActiveRecord::Associations::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationAssociationScopePatch)
62
+
63
+ # https://github.com/rails/rails/blob/v5.1.7/activerecord/lib/active_record/associations/singular_association.rb#L41
64
+ ActiveRecord::Associations::SingularAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationFindTargetPatch)
65
+
66
+ # https://github.com/rails/rails/blob/v5.1.7/activerecord/lib/active_record/associations/collection_association.rb#L305
67
+ ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationFindTargetPatch)
68
+
69
+ # https://github.com/rails/rails/blob/v5.1.7/activerecord/lib/active_record/associations/preloader/association.rb#L120
70
+ ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsPreloaderAssociationLoadRecordsPatch)
71
+ when '5.2'
72
+ # https://github.com/rails/rails/blob/v5.2.6/activerecord/lib/active_record/relation.rb#L530
73
+ # But the #exec_queries method also calls #connection, and I don't know if we should patch that one, too...
74
+ ActiveRecord::Relation.prepend(ActiveRecordShards::DefaultReplicaPatches::Rails52RelationPatches)
75
+
76
+ # https://github.com/rails/rails/blob/v5.2.6/activerecord/lib/active_record/associations/singular_association.rb#L42
77
+ ActiveRecord::Associations::SingularAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationFindTargetPatch)
78
+
79
+ # https://github.com/rails/rails/blob/v5.2.6/activerecord/lib/active_record/associations/collection_association.rb#L308
80
+ ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationFindTargetPatch)
81
+
82
+ # https://github.com/rails/rails/blob/v5.2.6/activerecord/lib/active_record/associations/preloader/association.rb#L96
83
+ ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsPreloaderAssociationLoadRecordsPatch)
84
+ when '6.0'
85
+ # https://github.com/rails/rails/blob/v6.0.4/activerecord/lib/active_record/type_caster/connection.rb#L28
86
+ ActiveRecord::TypeCaster::Connection.prepend(ActiveRecordShards::DefaultReplicaPatches::TypeCasterConnectionConnectionPatch)
87
+
88
+ # https://github.com/rails/rails/blob/v6.0.4/activerecord/lib/active_record/schema.rb#L53-L54
89
+ ActiveRecord::Schema.prepend(ActiveRecordShards::DefaultReplicaPatches::SchemaDefinePatch)
90
+
91
+ # https://github.com/rails/rails/blob/v6.0.4/activerecord/lib/active_record/relation.rb#L739
92
+ # But the #exec_queries and #compute_cache_version methods also call #connection, and I don't know if we should patch those, too...
93
+ ActiveRecord::Relation.prepend(ActiveRecordShards::DefaultReplicaPatches::Rails52RelationPatches)
94
+
95
+ # https://github.com/rails/rails/blob/v6.0.4/activerecord/lib/active_record/associations/association.rb#L213
96
+ ActiveRecord::Associations::Association.prepend(ActiveRecordShards::DefaultReplicaPatches::AssociationsAssociationFindTargetPatch)
37
97
  else
38
98
  raise "ActiveRecordShards is not compatible with #{ActiveRecord::VERSION::STRING}"
39
99
  end
@@ -64,8 +124,6 @@ ActiveRecordShards::Deprecation.deprecate_methods(
64
124
 
65
125
  ActiveRecordShards::Deprecation.deprecate_methods(
66
126
  ActiveRecordShards::DefaultReplicaPatches,
67
- columns_with_force_slave: :columns_with_force_replica,
68
- table_exists_with_force_slave?: :table_exists_with_force_replica?,
69
127
  transaction_with_slave_off: :transaction_with_replica_off,
70
128
  on_slave_unless_tx: :on_replica_unless_tx
71
129
  )
@@ -187,6 +187,8 @@ module ActiveRecordShards
187
187
  ActiveRecordShards.rails_env
188
188
  end
189
189
 
190
+ # Make these few schema related methods available before having switched to
191
+ # a shard.
190
192
  def with_default_shard(&block)
191
193
  if is_sharded? && current_shard_id.nil? && table_name != ActiveRecord::SchemaMigration.table_name
192
194
  on_first_shard(&block)
@@ -37,28 +37,14 @@ module ActiveRecordShards
37
37
  wrap_method_in_on_replica(*args)
38
38
  end
39
39
 
40
- def columns_with_force_replica(*args, &block)
41
- on_cx_switch_block(:replica, construct_ro_scope: false, force: true) do
42
- columns_without_force_replica(*args, &block)
43
- end
44
- end
45
- alias_method :columns_with_force_slave, :columns_with_force_replica
46
-
47
- def table_exists_with_force_replica?(*args, &block)
48
- on_cx_switch_block(:replica, construct_ro_scope: false, force: true) do
49
- table_exists_without_force_replica?(*args, &block)
50
- end
51
- end
52
- alias_method :table_exists_with_force_slave?, :table_exists_with_force_replica?
53
-
54
40
  def transaction_with_replica_off(*args, &block)
55
41
  if on_replica_by_default?
56
42
  begin
57
- old_val = Thread.current[:_active_record_shards_replica_off]
58
- Thread.current[:_active_record_shards_replica_off] = true
43
+ old_val = Thread.current[:_active_record_shards_in_tx]
44
+ Thread.current[:_active_record_shards_in_tx] = true
59
45
  transaction_without_replica_off(*args, &block)
60
46
  ensure
61
- Thread.current[:_active_record_shards_replica_off] = old_val
47
+ Thread.current[:_active_record_shards_in_tx] = old_val
62
48
  end
63
49
  else
64
50
  transaction_without_replica_off(*args, &block)
@@ -71,20 +57,26 @@ module ActiveRecordShards
71
57
  def quote_value(*args, &block)
72
58
  self.class.quote_value(*args, &block)
73
59
  end
60
+
61
+ def on_replica_unless_tx
62
+ self.class.on_replica_unless_tx { yield }
63
+ end
74
64
  end
75
65
 
76
66
  CLASS_REPLICA_METHODS = [
77
67
  :calculate,
78
68
  :count_by_sql,
79
69
  :exists?,
70
+ :find,
71
+ :find_by,
80
72
  :find_by_sql,
81
73
  :find_every,
82
74
  :find_one,
83
- :find_some
75
+ :find_some,
76
+ :get_primary_key
84
77
  ].freeze
85
78
 
86
79
  CLASS_FORCE_REPLICA_METHODS = [
87
- :columns,
88
80
  :replace_bind_variable,
89
81
  :replace_bind_variables,
90
82
  :sanitize_sql_array,
@@ -97,7 +89,15 @@ module ActiveRecordShards
97
89
 
98
90
  def self.extended(base)
99
91
  CLASS_REPLICA_METHODS.each { |m| ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(true, base, m) }
100
- CLASS_FORCE_SLAVE_METHODS.each { |m| ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(true, base, m, force_on_replica: true) }
92
+ CLASS_FORCE_REPLICA_METHODS.each { |m| ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(true, base, m, force_on_replica: true) }
93
+
94
+ if ActiveRecord::VERSION::MAJOR >= 5
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
+
100
+ ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(false, base, :reload)
101
101
 
102
102
  base.class_eval do
103
103
  include InstanceMethods
@@ -107,15 +107,14 @@ module ActiveRecordShards
107
107
  alias_method :transaction, :transaction_with_replica_off
108
108
  end
109
109
  end
110
- if ActiveRecord::Associations.const_defined?(:HasAndBelongsToManyAssociation)
111
- ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(false, ActiveRecord::Associations::HasAndBelongsToManyAssociation, :construct_sql)
112
- ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(false, ActiveRecord::Associations::HasAndBelongsToManyAssociation, :construct_find_options!)
113
- end
114
110
  end
115
111
 
116
- def on_replica_unless_tx
117
- if on_replica_by_default? && !Thread.current[:_active_record_shards_replica_off]
118
- on_replica { yield }
112
+ def on_replica_unless_tx(&block)
113
+ return yield if Thread.current[:_active_record_shards_in_migration]
114
+ return yield if Thread.current[:_active_record_shards_in_tx]
115
+
116
+ if on_replica_by_default?
117
+ on_replica(&block)
119
118
  else
120
119
  yield
121
120
  end
@@ -123,6 +122,8 @@ module ActiveRecordShards
123
122
  alias_method :on_slave_unless_tx, :on_replica_unless_tx
124
123
 
125
124
  def force_on_replica(&block)
125
+ return yield if Thread.current[:_active_record_shards_in_migration]
126
+
126
127
  on_cx_switch_block(:replica, construct_ro_scope: false, force: true, &block)
127
128
  end
128
129
 
@@ -145,6 +146,19 @@ module ActiveRecordShards
145
146
  end
146
147
  end
147
148
 
149
+ module Rails52RelationPatches
150
+ def connection
151
+ return super if Thread.current[:_active_record_shards_in_migration]
152
+ return super if Thread.current[:_active_record_shards_in_tx]
153
+
154
+ if @klass.on_replica_by_default?
155
+ @klass.on_replica.connection
156
+ else
157
+ super
158
+ end
159
+ end
160
+ end
161
+
148
162
  # in rails 4.1+, they create a join class that's used to pull in records for HABTM.
149
163
  # this simplifies the hell out of our existence, because all we have to do is inerit on-replica-by-default
150
164
  # down from the parent now.
@@ -167,5 +181,98 @@ module ActiveRecordShards
167
181
  model
168
182
  end
169
183
  end
184
+
185
+ module AssociationsAssociationAssociationScopePatch
186
+ def association_scope
187
+ if klass
188
+ on_replica_unless_tx { super }
189
+ else
190
+ super
191
+ end
192
+ end
193
+
194
+ def on_replica_unless_tx
195
+ klass.on_replica_unless_tx { yield }
196
+ end
197
+ end
198
+
199
+ module AssociationsAssociationFindTargetPatch
200
+ def find_target
201
+ if klass
202
+ on_replica_unless_tx { super }
203
+ else
204
+ super
205
+ end
206
+ end
207
+
208
+ def on_replica_unless_tx
209
+ klass.on_replica_unless_tx { yield }
210
+ end
211
+ end
212
+
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
+ module AssociationsPreloaderAssociationAssociatedRecordsByOwnerPatch
228
+ def associated_records_by_owner(preloader)
229
+ if klass
230
+ on_replica_unless_tx { super }
231
+ else
232
+ super
233
+ end
234
+ end
235
+
236
+ def on_replica_unless_tx
237
+ klass.on_replica_unless_tx { yield }
238
+ end
239
+ end
240
+
241
+ module AssociationsPreloaderAssociationLoadRecordsPatch
242
+ def load_records
243
+ if klass
244
+ on_replica_unless_tx { super }
245
+ else
246
+ super
247
+ end
248
+ end
249
+
250
+ def on_replica_unless_tx
251
+ klass.on_replica_unless_tx { yield }
252
+ end
253
+ end
254
+
255
+ module TypeCasterConnectionConnectionPatch
256
+ def connection
257
+ return super if Thread.current[:_active_record_shards_in_migration]
258
+ return super if Thread.current[:_active_record_shards_in_tx]
259
+
260
+ if @klass.on_replica_by_default?
261
+ @klass.on_replica.connection
262
+ else
263
+ super
264
+ end
265
+ end
266
+ end
267
+
268
+ module SchemaDefinePatch
269
+ def define(info, &block)
270
+ old_val = Thread.current[:_active_record_shards_in_migration]
271
+ Thread.current[:_active_record_shards_in_migration] = true
272
+ super
273
+ ensure
274
+ Thread.current[:_active_record_shards_in_migration] = old_val
275
+ end
276
+ end
170
277
  end
171
278
  end
@@ -3,8 +3,11 @@ module ActiveRecordShards
3
3
  module SqlComments
4
4
  module Methods
5
5
  def execute(query, name = nil)
6
+ shard = ActiveRecord::Base.current_shard_selection.shard
7
+ shard_text = shard ? "shard #{shard}" : 'unsharded'
6
8
  replica = ActiveRecord::Base.current_shard_selection.on_replica?
7
- query += " /* #{replica ? 'replica' : 'primary'} */"
9
+ replica_text = replica ? 'replica' : 'primary'
10
+ query = "/* #{shard_text} #{replica_text} */ " + query
8
11
  super(query, name)
9
12
  end
10
13
  end
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.18.0
4
+ version: 3.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Quorning
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2021-06-21 00:00:00.000000000 Z
16
+ date: 2021-07-09 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: activerecord
@@ -125,20 +125,6 @@ dependencies:
125
125
  - - ">="
126
126
  - !ruby/object:Gem::Version
127
127
  version: '0'
128
- - !ruby/object:Gem::Dependency
129
- name: phenix
130
- requirement: !ruby/object:Gem::Requirement
131
- requirements:
132
- - - ">="
133
- - !ruby/object:Gem::Version
134
- version: 0.6.0
135
- type: :development
136
- prerelease: false
137
- version_requirements: !ruby/object:Gem::Requirement
138
- requirements:
139
- - - ">="
140
- - !ruby/object:Gem::Version
141
- version: 0.6.0
142
128
  - !ruby/object:Gem::Dependency
143
129
  name: rake
144
130
  requirement: !ruby/object:Gem::Requirement