active_record_shards 3.18.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.
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