switchman 3.0.22 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/lib/switchman/action_controller/caching.rb +2 -2
- data/lib/switchman/active_record/abstract_adapter.rb +1 -1
- data/lib/switchman/active_record/associations.rb +23 -8
- data/lib/switchman/active_record/attribute_methods.rb +144 -84
- data/lib/switchman/active_record/base.rb +28 -22
- data/lib/switchman/active_record/calculations.rb +11 -4
- data/lib/switchman/active_record/connection_pool.rb +1 -1
- data/lib/switchman/active_record/finder_methods.rb +2 -2
- data/lib/switchman/active_record/model_schema.rb +1 -1
- data/lib/switchman/active_record/persistence.rb +2 -2
- data/lib/switchman/active_record/postgresql_adapter.rb +1 -1
- data/lib/switchman/active_record/query_methods.rb +20 -11
- data/lib/switchman/active_record/reflection.rb +1 -1
- data/lib/switchman/active_record/relation.rb +15 -18
- data/lib/switchman/active_record/statement_cache.rb +2 -2
- data/lib/switchman/database_server.rb +1 -1
- data/lib/switchman/engine.rb +13 -3
- data/lib/switchman/guard_rail/relation.rb +2 -2
- data/lib/switchman/sharded_instrumenter.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48b27124d5233adf585aed105d0b2522c76249bdd1e13fbcfd7e904241d3a624
|
4
|
+
data.tar.gz: fea1ccc0ad63f00633fb90bee1d7bd4606f6de0e18564250865c1b1dde61ef8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c05b445111dd0e4b16ecbfaea6ab84557fa2cbadc6102fa0af6fb43cff2f6bbb6606e825f76ed20f6b8a4a2a2966e31babadf66c5eb065a6c4d8a7eea6a8b92a
|
7
|
+
data.tar.gz: 0af9e15706142f90772d24e2187db93ec83331997bccbb25c1b89cec2dde1bd2414d2774fec3785c7bea1a1b06d3fa5ad49e15b716f0aa35794e2e5734c848a8
|
data/Rakefile
CHANGED
@@ -13,8 +13,8 @@ module Switchman
|
|
13
13
|
# disallow assigning to ActionController::Base.cache_store or
|
14
14
|
# ActionController::Base#cache_store for the same reasons we disallow
|
15
15
|
# assigning to Rails.cache
|
16
|
-
def cache_store=(
|
17
|
-
raise NoMethodError
|
16
|
+
def cache_store=(cache)
|
17
|
+
raise NoMethodError unless cache == ::Rails.cache
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -27,7 +27,7 @@ module Switchman
|
|
27
27
|
shards = reflection.options[:multishard] && owner.respond_to?(:associated_shards) ? owner.associated_shards : [shard]
|
28
28
|
# activate both the owner and the target's shard category, so that Reflection#join_id_for,
|
29
29
|
# when called for the owner, will be returned relative to shard the query will execute on
|
30
|
-
Shard.with_each_shard(shards, [klass.
|
30
|
+
Shard.with_each_shard(shards, [klass.connection_class_for_self, owner.class.connection_class_for_self].uniq) do
|
31
31
|
super
|
32
32
|
end
|
33
33
|
end
|
@@ -83,10 +83,23 @@ module Switchman
|
|
83
83
|
|
84
84
|
module Preloader
|
85
85
|
module Association
|
86
|
+
module LoaderQuery
|
87
|
+
def load_records_in_batch(loaders)
|
88
|
+
# While in theory loading multiple associations that end up being effectively the same would be nice
|
89
|
+
# it's not very switchman compatible, so just don't bother trying to use that logic
|
90
|
+
# raw_records = records_for(loaders)
|
91
|
+
|
92
|
+
loaders.each do |loader|
|
93
|
+
loader.load_records(nil)
|
94
|
+
loader.run
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
86
99
|
# Copypasta from Activerecord but with added global_id_for goodness.
|
87
100
|
def records_for(ids)
|
88
101
|
scope.where(association_key_name => ids).load do |record|
|
89
|
-
global_key = if model.
|
102
|
+
global_key = if model.connection_class_for_self == UnshardedRecord
|
90
103
|
convert_key(record[association_key_name])
|
91
104
|
else
|
92
105
|
Shard.global_id_for(record[association_key_name], record.shard)
|
@@ -100,13 +113,15 @@ module Switchman
|
|
100
113
|
# significant changes:
|
101
114
|
# * partition_by_shard the records_for call
|
102
115
|
# * re-globalize the fetched owner id before looking up in the map
|
103
|
-
|
116
|
+
# TODO: the ignored param currently loads records; we should probably not waste effort double-loading them
|
117
|
+
# Change introduced here: https://github.com/rails/rails/commit/c6c0b2e8af64509b699b782aadfecaa430700ece
|
118
|
+
def load_records(raw_records = nil)
|
104
119
|
# owners can be duplicated when a relation has a collection association join
|
105
120
|
# #compare_by_identity makes such owners different hash keys
|
106
121
|
@records_by_owner = {}.compare_by_identity
|
107
122
|
|
108
|
-
if owner_keys.empty?
|
109
|
-
raw_records
|
123
|
+
if ::Rails.version < '7.0' && owner_keys.empty?
|
124
|
+
raw_records ||= []
|
110
125
|
else
|
111
126
|
# determine the shard to search for each owner
|
112
127
|
if reflection.macro == :belongs_to
|
@@ -123,12 +138,12 @@ module Switchman
|
|
123
138
|
partition_proc = ->(owner) { owner.shard }
|
124
139
|
end
|
125
140
|
|
126
|
-
raw_records
|
141
|
+
raw_records ||= Shard.partition_by_shard(owners, partition_proc) do |partitioned_owners|
|
127
142
|
relative_owner_keys = partitioned_owners.map do |owner|
|
128
143
|
key = owner[owner_key_name]
|
129
144
|
if key && owner.class.sharded_column?(owner_key_name)
|
130
145
|
key = Shard.relative_id_for(key, owner.shard,
|
131
|
-
Shard.current(klass.
|
146
|
+
Shard.current(klass.connection_class_for_self))
|
132
147
|
end
|
133
148
|
convert_key(key)
|
134
149
|
end
|
@@ -200,7 +215,7 @@ module Switchman
|
|
200
215
|
# this seems counter-intuitive, but the autosave code will assign to attribute bypassing switchman,
|
201
216
|
# after reading the id attribute _without_ bypassing switchman. So we need Shard.current for the
|
202
217
|
# category of the associated record to match Shard.current for the category of self
|
203
|
-
shard.activate(
|
218
|
+
shard.activate(connection_class_for_self_for_reflection(reflection)) { super }
|
204
219
|
end
|
205
220
|
end
|
206
221
|
end
|
@@ -25,6 +25,16 @@ module Switchman
|
|
25
25
|
@sharded_column_values[column_name]
|
26
26
|
end
|
27
27
|
|
28
|
+
def define_attribute_methods
|
29
|
+
super
|
30
|
+
# ensure that we're using the sharded attribute method
|
31
|
+
# and not the silly one in AR::AttributeMethods::PrimaryKey
|
32
|
+
return unless sharded_column?(@primary_key)
|
33
|
+
|
34
|
+
class_eval(build_sharded_getter('id', '_read_attribute(@primary_key)', "::#{connection_class_for_self.name}"), __FILE__, __LINE__)
|
35
|
+
class_eval(build_sharded_setter('id', @primary_key, "::#{connection_class_for_self.name}"), __FILE__, __LINE__)
|
36
|
+
end
|
37
|
+
|
28
38
|
protected
|
29
39
|
|
30
40
|
def reflection_for_integer_attribute(attr_name)
|
@@ -36,17 +46,30 @@ module Switchman
|
|
36
46
|
raise if connection.open_transactions.positive?
|
37
47
|
end
|
38
48
|
|
49
|
+
# rubocop:disable Naming/MethodParameterName
|
50
|
+
def define_cached_method(owner, name, namespace:, as:, &block)
|
51
|
+
if ::Rails.version < '7.0'
|
52
|
+
yield owner
|
53
|
+
owner.rename_method(as, name)
|
54
|
+
else
|
55
|
+
owner.define_cached_method(name, namespace: namespace, as: as, &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
# rubocop:enable Naming/MethodParameterName
|
59
|
+
|
39
60
|
def define_method_global_attribute(attr_name, owner:)
|
40
61
|
if sharded_column?(attr_name)
|
41
|
-
owner
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
62
|
+
define_cached_method(owner, "global_#{attr_name}", as: "sharded_global_#{attr_name}", namespace: :switchman) do |batch|
|
63
|
+
batch << <<-RUBY
|
64
|
+
def sharded_global_#{attr_name}
|
65
|
+
raw_value = original_#{attr_name}
|
66
|
+
return nil if raw_value.nil?
|
67
|
+
return raw_value if raw_value > ::Switchman::Shard::IDS_PER_SHARD
|
68
|
+
|
69
|
+
::Switchman::Shard.global_id_for(raw_value, shard)
|
70
|
+
end
|
71
|
+
RUBY
|
72
|
+
end
|
50
73
|
else
|
51
74
|
define_method_unsharded_column(attr_name, 'global', owner)
|
52
75
|
end
|
@@ -54,113 +77,150 @@ module Switchman
|
|
54
77
|
|
55
78
|
def define_method_local_attribute(attr_name, owner:)
|
56
79
|
if sharded_column?(attr_name)
|
57
|
-
owner
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
80
|
+
define_cached_method(owner, "local_#{attr_name}", as: "sharded_local_#{attr_name}", namespace: :switchman) do |batch|
|
81
|
+
batch << <<-RUBY
|
82
|
+
def sharded_local_#{attr_name}
|
83
|
+
raw_value = original_#{attr_name}
|
84
|
+
return nil if raw_value.nil?
|
85
|
+
return raw_value % ::Switchman::Shard::IDS_PER_SHARD
|
86
|
+
end
|
87
|
+
RUBY
|
88
|
+
end
|
64
89
|
else
|
65
90
|
define_method_unsharded_column(attr_name, 'local', owner)
|
66
91
|
end
|
67
92
|
end
|
68
93
|
|
69
|
-
# see also Base#
|
94
|
+
# see also Base#connection_class_for_self_for_reflection
|
70
95
|
# the difference being this will output static strings for the common cases, making them
|
71
96
|
# more performant
|
72
|
-
def
|
97
|
+
def connection_class_for_self_code_for_reflection(reflection)
|
73
98
|
if reflection
|
74
99
|
if reflection.options[:polymorphic]
|
75
100
|
# a polymorphic association has to be discovered at runtime. This code ends up being something like
|
76
|
-
# context_type.&.constantize&.
|
77
|
-
"read_attribute(:#{reflection.foreign_type})&.constantize&.
|
101
|
+
# context_type.&.constantize&.connection_class_for_self
|
102
|
+
"read_attribute(:#{reflection.foreign_type})&.constantize&.connection_class_for_self"
|
78
103
|
else
|
79
104
|
# otherwise we can just return a symbol for the statically known type of the association
|
80
|
-
"::#{reflection.klass.
|
105
|
+
"::#{reflection.klass.connection_class_for_self.name}"
|
81
106
|
end
|
82
107
|
else
|
83
|
-
"::#{
|
108
|
+
"::#{connection_class_for_self.name}"
|
84
109
|
end
|
85
110
|
end
|
86
111
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
112
|
+
def define_method_attribute(attr_name, owner:)
|
113
|
+
if sharded_column?(attr_name)
|
114
|
+
reflection = reflection_for_integer_attribute(attr_name)
|
115
|
+
class_name = connection_class_for_self_code_for_reflection(reflection)
|
116
|
+
safe_class_name = class_name.unpack1('h*')
|
117
|
+
define_cached_method(owner, attr_name, as: "sharded_#{safe_class_name}_#{attr_name}", namespace: :switchman) do |batch|
|
118
|
+
batch << build_sharded_getter("sharded_#{safe_class_name}_#{attr_name}", "original_#{attr_name}", class_name)
|
119
|
+
end
|
120
|
+
else
|
121
|
+
define_cached_method(owner, attr_name, as: "plain_#{attr_name}", namespace: :switchman) do |batch|
|
122
|
+
batch << <<-RUBY
|
123
|
+
def plain_#{attr_name}
|
124
|
+
_read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
|
125
|
+
end
|
126
|
+
RUBY
|
127
|
+
end
|
92
128
|
end
|
129
|
+
end
|
93
130
|
|
94
|
-
|
95
|
-
|
96
|
-
|
131
|
+
def build_sharded_getter(attr_name, raw_expr, attr_connection_class)
|
132
|
+
<<-RUBY
|
133
|
+
def #{attr_name}
|
134
|
+
raw_value = #{raw_expr}
|
135
|
+
return nil if raw_value.nil?
|
136
|
+
|
137
|
+
abs_raw_value = raw_value.abs
|
138
|
+
current_shard = ::Switchman::Shard.current(#{attr_connection_class})
|
139
|
+
same_shard = shard == current_shard
|
140
|
+
return raw_value if same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
|
141
|
+
|
142
|
+
value_shard_id = abs_raw_value / ::Switchman::Shard::IDS_PER_SHARD
|
143
|
+
# this is a stupid case when someone stuffed a global id for the current shard in instead
|
144
|
+
# of a local id
|
145
|
+
return raw_value % ::Switchman::Shard::IDS_PER_SHARD if value_shard_id == current_shard.id
|
146
|
+
return raw_value if !same_shard && abs_raw_value > ::Switchman::Shard::IDS_PER_SHARD
|
147
|
+
return shard.global_id_for(raw_value) if !same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
|
148
|
+
|
149
|
+
::Switchman::Shard.relative_id_for(raw_value, shard, current_shard)
|
150
|
+
end
|
151
|
+
RUBY
|
97
152
|
end
|
98
153
|
|
99
|
-
def
|
154
|
+
def define_method_attribute=(attr_name, owner:)
|
100
155
|
if sharded_column?(attr_name)
|
101
156
|
reflection = reflection_for_integer_attribute(attr_name)
|
102
|
-
|
103
|
-
|
157
|
+
class_name = connection_class_for_self_code_for_reflection(reflection)
|
158
|
+
safe_class_name = class_name.unpack1('h*')
|
159
|
+
define_cached_method(owner, "#{attr_name}=", as: "sharded_#{safe_class_name}_#{attr_name}=", namespace: :switchman) do |batch|
|
160
|
+
batch << build_sharded_setter("sharded_#{safe_class_name}_#{attr_name}", attr_name, class_name)
|
161
|
+
end
|
162
|
+
else
|
163
|
+
define_cached_method(owner, "#{attr_name}=", as: "plain_#{attr_name}=", namespace: :switchman) do |batch|
|
164
|
+
batch << <<-RUBY
|
165
|
+
def plain_#{attr_name}=(new_value)
|
166
|
+
_write_attribute('#{attr_name}', new_value)
|
167
|
+
end
|
168
|
+
RUBY
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
104
172
|
|
105
|
-
|
173
|
+
def build_sharded_setter(attr_name, attr_field, attr_connection_class)
|
174
|
+
<<-RUBY
|
175
|
+
def #{attr_name}=(new_value)
|
176
|
+
self.original_#{attr_field} = ::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(#{attr_connection_class}), shard)
|
106
177
|
end
|
178
|
+
RUBY
|
179
|
+
end
|
107
180
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
return raw_value % ::Switchman::Shard::IDS_PER_SHARD if value_shard_id == current_shard.id
|
125
|
-
return raw_value if !same_shard && abs_raw_value > ::Switchman::Shard::IDS_PER_SHARD
|
126
|
-
return shard.global_id_for(raw_value) if !same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
|
127
|
-
|
128
|
-
::Switchman::Shard.relative_id_for(raw_value, shard, current_shard)
|
129
|
-
end
|
181
|
+
def define_method_original_attribute(attr_name, owner:)
|
182
|
+
if sharded_column?(attr_name)
|
183
|
+
define_cached_method(owner, "original_#{attr_name}", as: "sharded_original_#{attr_name}", namespace: :switchman) do |batch|
|
184
|
+
batch << <<-RUBY
|
185
|
+
def sharded_original_#{attr_name}
|
186
|
+
_read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
|
187
|
+
end
|
188
|
+
RUBY
|
189
|
+
end
|
190
|
+
else
|
191
|
+
define_method_unsharded_column(attr_name, 'global', owner)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def define_method_original_attribute=(attr_name, owner:)
|
196
|
+
return unless sharded_column?(attr_name)
|
130
197
|
|
131
|
-
|
132
|
-
|
133
|
-
|
198
|
+
define_cached_method(owner, "original_#{attr_name}=", as: "sharded_original_#{attr_name}=", namespace: :switchman) do |batch|
|
199
|
+
batch << <<-RUBY
|
200
|
+
def sharded_original_#{attr_name}=(new_value)
|
201
|
+
_write_attribute('#{attr_name}', new_value)
|
134
202
|
end
|
135
203
|
RUBY
|
136
|
-
else
|
137
|
-
define_method_unsharded_column(attr_name, 'global', owner)
|
138
204
|
end
|
139
205
|
end
|
140
206
|
|
141
207
|
def define_method_unsharded_column(attr_name, prefix, owner)
|
142
|
-
return if columns_hash["#{prefix}_#{attr_name}"]
|
208
|
+
return if columns_hash["#{prefix}_#{attr_name}"] || attr_name == 'id'
|
143
209
|
|
144
|
-
owner
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
210
|
+
define_cached_method(owner, "#{prefix}_#{attr_name}", as: "unsharded_#{prefix}_#{attr_name}", namespace: :switchman) do |batch|
|
211
|
+
batch << <<-RUBY
|
212
|
+
def unsharded_#{prefix}_#{attr_name}
|
213
|
+
raise NoMethodError, "undefined method `#{prefix}_#{attr_name}'; are you missing an association?"
|
214
|
+
end
|
215
|
+
RUBY
|
216
|
+
end
|
149
217
|
end
|
150
218
|
end
|
151
219
|
|
152
|
-
def self.
|
153
|
-
klass.singleton_class.
|
220
|
+
def self.prepended(klass)
|
221
|
+
klass.singleton_class.prepend(ClassMethods)
|
154
222
|
klass.attribute_method_prefix 'global_', 'local_', 'original_'
|
155
|
-
|
156
|
-
|
157
|
-
# ensure that we're using the sharded attribute method
|
158
|
-
# and not the silly one in AR::AttributeMethods::PrimaryKey
|
159
|
-
def id
|
160
|
-
return super if is_a?(Shard)
|
161
|
-
|
162
|
-
self.class.define_attribute_methods
|
163
|
-
super
|
223
|
+
klass.attribute_method_affix prefix: 'original_', suffix: '='
|
164
224
|
end
|
165
225
|
|
166
226
|
# these are called if the specific methods haven't been defined yet
|
@@ -168,7 +228,7 @@ module Switchman
|
|
168
228
|
return super unless self.class.sharded_column?(attr_name)
|
169
229
|
|
170
230
|
reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
|
171
|
-
::Switchman::Shard.relative_id_for(super, shard, ::Switchman::Shard.current(
|
231
|
+
::Switchman::Shard.relative_id_for(super, shard, ::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection)))
|
172
232
|
end
|
173
233
|
|
174
234
|
def attribute=(attr_name, new_value)
|
@@ -178,7 +238,7 @@ module Switchman
|
|
178
238
|
end
|
179
239
|
|
180
240
|
reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
|
181
|
-
super(::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(
|
241
|
+
super(::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection)), shard))
|
182
242
|
end
|
183
243
|
|
184
244
|
def global_attribute(attr_name)
|
@@ -199,15 +259,15 @@ module Switchman
|
|
199
259
|
|
200
260
|
private
|
201
261
|
|
202
|
-
def
|
262
|
+
def connection_class_for_self_for_reflection(reflection)
|
203
263
|
if reflection
|
204
264
|
if reflection.options[:polymorphic]
|
205
|
-
read_attribute(reflection.foreign_type)&.constantize&.
|
265
|
+
read_attribute(reflection.foreign_type)&.constantize&.connection_class_for_self
|
206
266
|
else
|
207
|
-
reflection.klass.
|
267
|
+
reflection.klass.connection_class_for_self
|
208
268
|
end
|
209
269
|
else
|
210
|
-
self.class.
|
270
|
+
self.class.connection_class_for_self
|
211
271
|
end
|
212
272
|
end
|
213
273
|
end
|
@@ -25,11 +25,11 @@ module Switchman
|
|
25
25
|
def transaction(**)
|
26
26
|
if self != ::ActiveRecord::Base && current_scope
|
27
27
|
current_scope.activate do
|
28
|
-
db = Shard.current(
|
28
|
+
db = Shard.current(connection_class_for_self).database_server
|
29
29
|
db.unguard { super }
|
30
30
|
end
|
31
31
|
else
|
32
|
-
db = Shard.current(
|
32
|
+
db = Shard.current(connection_class_for_self).database_server
|
33
33
|
db.unguard { super }
|
34
34
|
end
|
35
35
|
end
|
@@ -58,8 +58,8 @@ module Switchman
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
def
|
62
|
-
current_role != current_role(without_overrides: true)
|
61
|
+
def role_overriden?(shard_id)
|
62
|
+
current_role(target_shard: shard_id) != current_role(without_overrides: true)
|
63
63
|
end
|
64
64
|
|
65
65
|
def establish_connection(config_or_env = nil)
|
@@ -78,7 +78,7 @@ module Switchman
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def connected_to_stack
|
81
|
-
return super if Thread.current.thread_variable?(:ar_connected_to_stack)
|
81
|
+
return super if ::Rails.version < '7.0' ? Thread.current.thread_variable?(:ar_connected_to_stack) : ::ActiveSupport::IsolatedExecutionState.key?(:active_record_connected_to_stack)
|
82
82
|
|
83
83
|
ret = super
|
84
84
|
DatabaseServer.guard_servers
|
@@ -86,13 +86,13 @@ module Switchman
|
|
86
86
|
end
|
87
87
|
|
88
88
|
# significant change: Allow per-shard roles
|
89
|
-
def current_role(without_overrides: false)
|
89
|
+
def current_role(without_overrides: false, target_shard: current_shard)
|
90
90
|
return super() if without_overrides
|
91
91
|
|
92
92
|
sharded_role = nil
|
93
93
|
connected_to_stack.reverse_each do |hash|
|
94
|
-
shard_role = hash.dig(:shard_roles,
|
95
|
-
if shard_role && (hash[:klasses].include?(::ActiveRecord::Base) || hash[:klasses].include?(
|
94
|
+
shard_role = hash.dig(:shard_roles, target_shard)
|
95
|
+
if shard_role && (hash[:klasses].include?(::ActiveRecord::Base) || hash[:klasses].include?(connection_class_for_self))
|
96
96
|
sharded_role = shard_role
|
97
97
|
break
|
98
98
|
end
|
@@ -107,7 +107,7 @@ module Switchman
|
|
107
107
|
# i.e. other sharded models don't inherit the current shard of Base
|
108
108
|
def current_shard
|
109
109
|
connected_to_stack.reverse_each do |hash|
|
110
|
-
return hash[:shard] if hash[:shard] && hash[:klasses].include?(
|
110
|
+
return hash[:shard] if hash[:shard] && hash[:klasses].include?(connection_class_for_self)
|
111
111
|
end
|
112
112
|
|
113
113
|
default_shard
|
@@ -115,11 +115,17 @@ module Switchman
|
|
115
115
|
|
116
116
|
def current_switchman_shard
|
117
117
|
connected_to_stack.reverse_each do |hash|
|
118
|
-
return hash[:switchman_shard] if hash[:switchman_shard] && hash[:klasses].include?(
|
118
|
+
return hash[:switchman_shard] if hash[:switchman_shard] && hash[:klasses].include?(connection_class_for_self)
|
119
119
|
end
|
120
120
|
|
121
121
|
Shard.default
|
122
122
|
end
|
123
|
+
|
124
|
+
if ::Rails.version < '7.0'
|
125
|
+
def connection_class_for_self
|
126
|
+
connection_classes
|
127
|
+
end
|
128
|
+
end
|
123
129
|
end
|
124
130
|
|
125
131
|
def self.prepended(klass)
|
@@ -128,15 +134,15 @@ module Switchman
|
|
128
134
|
|
129
135
|
def _run_initialize_callbacks
|
130
136
|
@shard ||= if self.class.sharded_primary_key?
|
131
|
-
Shard.shard_for(self[self.class.primary_key], Shard.current(self.class.
|
137
|
+
Shard.shard_for(self[self.class.primary_key], Shard.current(self.class.connection_class_for_self))
|
132
138
|
else
|
133
|
-
Shard.current(self.class.
|
139
|
+
Shard.current(self.class.connection_class_for_self)
|
134
140
|
end
|
135
141
|
super
|
136
142
|
end
|
137
143
|
|
138
144
|
def shard
|
139
|
-
@shard || Shard.current(self.class.
|
145
|
+
@shard || Shard.current(self.class.connection_class_for_self) || Shard.default
|
140
146
|
end
|
141
147
|
|
142
148
|
def shard=(new_shard)
|
@@ -161,7 +167,7 @@ module Switchman
|
|
161
167
|
end
|
162
168
|
|
163
169
|
def destroy
|
164
|
-
shard.activate(self.class.
|
170
|
+
shard.activate(self.class.connection_class_for_self) { super }
|
165
171
|
end
|
166
172
|
|
167
173
|
def clone
|
@@ -174,14 +180,14 @@ module Switchman
|
|
174
180
|
end
|
175
181
|
|
176
182
|
def transaction(**kwargs, &block)
|
177
|
-
shard.activate(self.class.
|
183
|
+
shard.activate(self.class.connection_class_for_self) do
|
178
184
|
self.class.transaction(**kwargs, &block)
|
179
185
|
end
|
180
186
|
end
|
181
187
|
|
182
188
|
def with_transaction_returning_status
|
183
|
-
shard.activate(self.class.
|
184
|
-
db = Shard.current(self.class.
|
189
|
+
shard.activate(self.class.connection_class_for_self) do
|
190
|
+
db = Shard.current(self.class.connection_class_for_self).database_server
|
185
191
|
db.unguard { super }
|
186
192
|
end
|
187
193
|
end
|
@@ -218,22 +224,22 @@ module Switchman
|
|
218
224
|
|
219
225
|
protected
|
220
226
|
|
221
|
-
# see also AttributeMethods#
|
222
|
-
def
|
227
|
+
# see also AttributeMethods#connection_class_for_self_code_for_reflection
|
228
|
+
def connection_class_for_self_for_reflection(reflection)
|
223
229
|
if reflection
|
224
230
|
if reflection.options[:polymorphic]
|
225
231
|
begin
|
226
|
-
read_attribute(reflection.foreign_type)&.constantize&.
|
232
|
+
read_attribute(reflection.foreign_type)&.constantize&.connection_class_for_self || ::ActiveRecord::Base
|
227
233
|
rescue NameError
|
228
234
|
# in case someone is abusing foreign_type to not point to an actual class
|
229
235
|
::ActiveRecord::Base
|
230
236
|
end
|
231
237
|
else
|
232
238
|
# otherwise we can just return a symbol for the statically known type of the association
|
233
|
-
reflection.klass.
|
239
|
+
reflection.klass.connection_class_for_self
|
234
240
|
end
|
235
241
|
else
|
236
|
-
self.class.
|
242
|
+
self.class.connection_class_for_self
|
237
243
|
end
|
238
244
|
end
|
239
245
|
end
|
@@ -4,7 +4,7 @@ module Switchman
|
|
4
4
|
module ActiveRecord
|
5
5
|
module Calculations
|
6
6
|
def pluck(*column_names)
|
7
|
-
target_shard = Shard.current(klass.
|
7
|
+
target_shard = Shard.current(klass.connection_class_for_self)
|
8
8
|
shard_count = 0
|
9
9
|
result = activate do |relation, shard|
|
10
10
|
shard_count += 1
|
@@ -109,11 +109,18 @@ module Switchman
|
|
109
109
|
private
|
110
110
|
|
111
111
|
def type_cast_calculated_value_switchman(value, column_name, operation)
|
112
|
-
|
112
|
+
if ::Rails.version < '7.0'
|
113
|
+
type_cast_calculated_value(value, operation) do |val|
|
114
|
+
column = aggregate_column(column_name)
|
115
|
+
type ||= column.try(:type_caster) ||
|
116
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || ::ActiveRecord::Type.default_value
|
117
|
+
type.deserialize(val)
|
118
|
+
end
|
119
|
+
else
|
113
120
|
column = aggregate_column(column_name)
|
114
121
|
type ||= column.try(:type_caster) ||
|
115
|
-
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
116
|
-
type
|
122
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || ::ActiveRecord::Type.default_value
|
123
|
+
type_cast_calculated_value(value, operation, type)
|
117
124
|
end
|
118
125
|
end
|
119
126
|
|
@@ -7,7 +7,7 @@ module Switchman
|
|
7
7
|
return super(id) unless klass.integral_id?
|
8
8
|
|
9
9
|
if shard_source_value != :implicit
|
10
|
-
current_shard = Shard.current(klass.
|
10
|
+
current_shard = Shard.current(klass.connection_class_for_self)
|
11
11
|
result = activate do |relation, shard|
|
12
12
|
current_id = Shard.relative_id_for(id, current_shard, shard)
|
13
13
|
# current_id will be nil for non-integral id
|
@@ -33,7 +33,7 @@ module Switchman
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def find_some_ordered(ids)
|
36
|
-
current_shard = Shard.current(klass.
|
36
|
+
current_shard = Shard.current(klass.connection_class_for_self)
|
37
37
|
ids = ids.map { |id| Shard.relative_id_for(id, current_shard, current_shard) }
|
38
38
|
super(ids)
|
39
39
|
end
|
@@ -6,7 +6,7 @@ module Switchman
|
|
6
6
|
module ClassMethods
|
7
7
|
def quoted_table_name
|
8
8
|
@quoted_table_name ||= {}
|
9
|
-
@quoted_table_name[Shard.current(
|
9
|
+
@quoted_table_name[Shard.current(connection_class_for_self).id] ||= connection.quote_table_name(table_name)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -5,11 +5,11 @@ module Switchman
|
|
5
5
|
module Persistence
|
6
6
|
# touch reads the id attribute directly, so it's not relative to the current shard
|
7
7
|
def touch(*, **)
|
8
|
-
shard.activate(self.class.
|
8
|
+
shard.activate(self.class.connection_class_for_self) { super }
|
9
9
|
end
|
10
10
|
|
11
11
|
def update_columns(*)
|
12
|
-
shard.activate(self.class.
|
12
|
+
shard.activate(self.class.connection_class_for_self) { super }
|
13
13
|
end
|
14
14
|
|
15
15
|
def delete
|
@@ -7,7 +7,7 @@ module Switchman
|
|
7
7
|
def create_database(name, options = {})
|
8
8
|
options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
|
9
9
|
|
10
|
-
option_string = options.sum do |key, value|
|
10
|
+
option_string = options.sum('') do |key, value|
|
11
11
|
case key
|
12
12
|
when :owner
|
13
13
|
" OWNER = \"#{value}\""
|
@@ -65,7 +65,7 @@ module Switchman
|
|
65
65
|
when ::ActiveRecord::Relation
|
66
66
|
Shard.default
|
67
67
|
when nil
|
68
|
-
Shard.current(klass.
|
68
|
+
Shard.current(klass.connection_class_for_self)
|
69
69
|
else
|
70
70
|
raise ArgumentError, "invalid shard value #{shard_value}"
|
71
71
|
end
|
@@ -79,7 +79,7 @@ module Switchman
|
|
79
79
|
when ::ActiveRecord::Base
|
80
80
|
shard_value.respond_to?(:associated_shards) ? shard_value.associated_shards : [shard_value.shard]
|
81
81
|
when nil
|
82
|
-
[Shard.current(klass.
|
82
|
+
[Shard.current(klass.connection_class_for_self)]
|
83
83
|
else
|
84
84
|
shard_value
|
85
85
|
end
|
@@ -131,7 +131,7 @@ module Switchman
|
|
131
131
|
id_shards = Set.new
|
132
132
|
right.each do |value|
|
133
133
|
local_id, id_shard = Shard.local_id_for(value)
|
134
|
-
id_shard ||= Shard.current(klass.
|
134
|
+
id_shard ||= Shard.current(klass.connection_class_for_self) if local_id
|
135
135
|
id_shards << id_shard if id_shard
|
136
136
|
end
|
137
137
|
return if id_shards.empty?
|
@@ -151,10 +151,13 @@ module Switchman
|
|
151
151
|
end
|
152
152
|
when ::Arel::Nodes::BindParam
|
153
153
|
local_id, id_shard = Shard.local_id_for(right.value.value_before_type_cast)
|
154
|
-
id_shard ||= Shard.current(klass.
|
154
|
+
id_shard ||= Shard.current(klass.connection_class_for_self) if local_id
|
155
|
+
when ::ActiveModel::Attribute
|
156
|
+
local_id, id_shard = Shard.local_id_for(right.value_before_type_cast)
|
157
|
+
id_shard ||= Shard.current(klass.connection_class_for_self) if local_id
|
155
158
|
else
|
156
159
|
local_id, id_shard = Shard.local_id_for(right)
|
157
|
-
id_shard ||= Shard.current(klass.
|
160
|
+
id_shard ||= Shard.current(klass.connection_class_for_self) if local_id
|
158
161
|
end
|
159
162
|
|
160
163
|
return if !id_shard || id_shard == primary_shard
|
@@ -193,9 +196,9 @@ module Switchman
|
|
193
196
|
reflection = model.send(:reflection_for_integer_attribute, column)
|
194
197
|
break if reflection
|
195
198
|
end
|
196
|
-
return Shard.current(klass.
|
199
|
+
return Shard.current(klass.connection_class_for_self) if reflection.options[:polymorphic]
|
197
200
|
|
198
|
-
Shard.current(reflection.klass.
|
201
|
+
Shard.current(reflection.klass.connection_class_for_self)
|
199
202
|
end
|
200
203
|
|
201
204
|
def relation_and_column(attribute)
|
@@ -291,7 +294,7 @@ module Switchman
|
|
291
294
|
if source_shard
|
292
295
|
source_shard
|
293
296
|
elsif type == :primary
|
294
|
-
Shard.current(klass.
|
297
|
+
Shard.current(klass.connection_class_for_self)
|
295
298
|
elsif type == :foreign
|
296
299
|
source_shard_for_foreign_key(relation, column)
|
297
300
|
end
|
@@ -332,8 +335,9 @@ module Switchman
|
|
332
335
|
end
|
333
336
|
|
334
337
|
def transpose_predicate_value(value, current_shard, target_shard, attribute_type, remove_non_local_ids)
|
335
|
-
|
336
|
-
|
338
|
+
case value
|
339
|
+
when ::Arel::Nodes::BindParam, ::ActiveModel::Attribute
|
340
|
+
query_att = value.is_a?(::ActiveModel::Attribute) ? value : value.value
|
337
341
|
current_id = query_att.value_before_type_cast
|
338
342
|
if current_id.is_a?(::ActiveRecord::StatementCache::Substitute)
|
339
343
|
current_id.sharded = true # mark for transposition later
|
@@ -346,7 +350,12 @@ module Switchman
|
|
346
350
|
# make a new bind param
|
347
351
|
value
|
348
352
|
else
|
349
|
-
|
353
|
+
new_att = query_att.class.new(query_att.name, local_id, query_att.type)
|
354
|
+
if value.is_a?(::ActiveModel::Attribute)
|
355
|
+
new_att
|
356
|
+
else
|
357
|
+
::Arel::Nodes::BindParam.new(new_att)
|
358
|
+
end
|
350
359
|
end
|
351
360
|
end
|
352
361
|
else
|
@@ -5,7 +5,7 @@ module Switchman
|
|
5
5
|
module Reflection
|
6
6
|
module AbstractReflection
|
7
7
|
def shard(owner)
|
8
|
-
if polymorphic? || klass.
|
8
|
+
if polymorphic? || klass.connection_class_for_self == owner.class.connection_class_for_self
|
9
9
|
# polymorphic associations assume the same shard as the owning item
|
10
10
|
owner.shard
|
11
11
|
else
|
@@ -9,13 +9,13 @@ module Switchman
|
|
9
9
|
|
10
10
|
def initialize(*, **)
|
11
11
|
super
|
12
|
-
self.shard_value = Shard.current(klass ? klass.
|
12
|
+
self.shard_value = Shard.current(klass ? klass.connection_class_for_self : :primary) unless shard_value
|
13
13
|
self.shard_source_value = :implicit unless shard_source_value
|
14
14
|
end
|
15
15
|
|
16
16
|
def clone
|
17
17
|
result = super
|
18
|
-
result.shard_value = Shard.current(klass ? klass.
|
18
|
+
result.shard_value = Shard.current(klass ? klass.connection_class_for_self : :primary) unless shard_value
|
19
19
|
result
|
20
20
|
end
|
21
21
|
|
@@ -29,35 +29,32 @@ module Switchman
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def new(*, &block)
|
32
|
-
primary_shard.activate(klass.
|
32
|
+
primary_shard.activate(klass.connection_class_for_self) { super }
|
33
33
|
end
|
34
34
|
|
35
35
|
def create(*, &block)
|
36
|
-
primary_shard.activate(klass.
|
36
|
+
primary_shard.activate(klass.connection_class_for_self) { super }
|
37
37
|
end
|
38
38
|
|
39
39
|
def create!(*, &block)
|
40
|
-
primary_shard.activate(klass.
|
40
|
+
primary_shard.activate(klass.connection_class_for_self) { super }
|
41
41
|
end
|
42
42
|
|
43
43
|
def to_sql
|
44
|
-
primary_shard.activate(klass.
|
44
|
+
primary_shard.activate(klass.connection_class_for_self) { super }
|
45
45
|
end
|
46
46
|
|
47
47
|
def explain
|
48
48
|
activate { |relation| relation.call_super(:explain, Relation) }
|
49
49
|
end
|
50
50
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
results = activate { |relation| relation.call_super(:records, Relation) }
|
55
|
-
case shard_value
|
56
|
-
when Array, ::ActiveRecord::Relation, ::ActiveRecord::Base
|
57
|
-
@records = results
|
51
|
+
def load(&block)
|
52
|
+
if !loaded? || (::Rails.version >= '7.0' && scheduled?)
|
53
|
+
@records = activate { |relation| relation.send(:exec_queries, &block) }
|
58
54
|
@loaded = true
|
59
55
|
end
|
60
|
-
|
56
|
+
|
57
|
+
self
|
61
58
|
end
|
62
59
|
|
63
60
|
%I[update_all delete_all].each do |method|
|
@@ -106,19 +103,19 @@ module Switchman
|
|
106
103
|
def activate(unordered: false, &block)
|
107
104
|
shards = all_shards
|
108
105
|
if Array === shards && shards.length == 1
|
109
|
-
if shards.first == DefaultShard || shards.first == Shard.current(klass.
|
106
|
+
if shards.first == DefaultShard || shards.first == Shard.current(klass.connection_class_for_self)
|
110
107
|
yield(self, shards.first)
|
111
108
|
else
|
112
|
-
shards.first.activate(klass.
|
109
|
+
shards.first.activate(klass.connection_class_for_self) { yield(self, shards.first) }
|
113
110
|
end
|
114
111
|
else
|
115
112
|
result_count = 0
|
116
113
|
can_order = false
|
117
|
-
result = Shard.with_each_shard(shards, [klass.
|
114
|
+
result = Shard.with_each_shard(shards, [klass.connection_class_for_self]) do
|
118
115
|
# don't even query other shards if we're already past the limit
|
119
116
|
next if limit_value && result_count >= limit_value && order_values.empty?
|
120
117
|
|
121
|
-
relation = shard(Shard.current(klass.
|
118
|
+
relation = shard(Shard.current(klass.connection_class_for_self), :to_a)
|
122
119
|
# do a minimal query if possible
|
123
120
|
relation = relation.limit(limit_value - result_count) if limit_value && !result_count.zero? && order_values.empty?
|
124
121
|
|
@@ -33,12 +33,12 @@ module Switchman
|
|
33
33
|
primary_value = params[primary_index]
|
34
34
|
target_shard = Shard.local_id_for(primary_value)[1]
|
35
35
|
end
|
36
|
-
current_shard = Shard.current(klass.
|
36
|
+
current_shard = Shard.current(klass.connection_class_for_self)
|
37
37
|
target_shard ||= current_shard
|
38
38
|
|
39
39
|
bind_values = bind_map.bind(params, current_shard, target_shard)
|
40
40
|
|
41
|
-
target_shard.activate(klass.
|
41
|
+
target_shard.activate(klass.connection_class_for_self) do
|
42
42
|
sql = qualified_query_builder(target_shard, klass).sql_for(bind_values, connection)
|
43
43
|
klass.find_by_sql(sql, bind_values)
|
44
44
|
end
|
data/lib/switchman/engine.rb
CHANGED
@@ -10,7 +10,16 @@ module Switchman
|
|
10
10
|
|
11
11
|
::GuardRail.singleton_class.prepend(GuardRail::ClassMethods)
|
12
12
|
|
13
|
-
|
13
|
+
# after :initialize_dependency_mechanism to ensure autoloading is configured for any downstream initializers that care
|
14
|
+
# In rails 7.0 we should be able to just use an explicit after on configuring the once autoloaders and not need to go monkey around with initializer order
|
15
|
+
if ::Rails.version < '7.0'
|
16
|
+
initialize_dependency_mechanism = ::Rails::Application::Bootstrap.initializers.find { |i| i.name == :initialize_dependency_mechanism }
|
17
|
+
initialize_dependency_mechanism.instance_variable_get(:@options)[:after] = :set_autoload_paths
|
18
|
+
end
|
19
|
+
|
20
|
+
initializer 'switchman.active_record_patch',
|
21
|
+
before: 'active_record.initialize_database',
|
22
|
+
after: (::Rails.version < '7.0' ? :initialize_dependency_mechanism : :setup_once_autoloader) do
|
14
23
|
::ActiveSupport.on_load(:active_record) do
|
15
24
|
# Switchman requires postgres, so just always load the pg adapter
|
16
25
|
require 'active_record/connection_adapters/postgresql_adapter'
|
@@ -19,7 +28,7 @@ module Switchman
|
|
19
28
|
self.default_role = :primary
|
20
29
|
|
21
30
|
prepend ActiveRecord::Base
|
22
|
-
|
31
|
+
prepend ActiveRecord::AttributeMethods
|
23
32
|
include ActiveRecord::Persistence
|
24
33
|
singleton_class.prepend ActiveRecord::ModelSchema::ClassMethods
|
25
34
|
|
@@ -41,6 +50,7 @@ module Switchman
|
|
41
50
|
::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::Associations::CollectionProxy)
|
42
51
|
|
43
52
|
::ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecord::Associations::Preloader::Association)
|
53
|
+
::ActiveRecord::Associations::Preloader::Association::LoaderQuery.prepend(ActiveRecord::Associations::Preloader::Association::LoaderQuery) unless ::Rails.version < '7.0'
|
44
54
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::AbstractAdapter)
|
45
55
|
::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool)
|
46
56
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache)
|
@@ -96,7 +106,7 @@ module Switchman
|
|
96
106
|
end
|
97
107
|
end
|
98
108
|
|
99
|
-
initializer 'switchman.initialize_cache', before:
|
109
|
+
initializer 'switchman.initialize_cache', before: :initialize_cache, after: 'active_record.initialize_database' do
|
100
110
|
::ActiveSupport::Cache.singleton_class.prepend(ActiveSupport::Cache::ClassMethods)
|
101
111
|
|
102
112
|
# if we haven't already setup our cache map out-of-band, set it up from
|
@@ -5,7 +5,7 @@ module Switchman
|
|
5
5
|
module Relation
|
6
6
|
def exec_queries(*args)
|
7
7
|
if lock_value
|
8
|
-
db = Shard.current(
|
8
|
+
db = Shard.current(connection_class_for_self).database_server
|
9
9
|
db.unguard { super }
|
10
10
|
else
|
11
11
|
super
|
@@ -15,7 +15,7 @@ module Switchman
|
|
15
15
|
%w[update_all delete_all].each do |method|
|
16
16
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
17
17
|
def #{method}(*args)
|
18
|
-
db = Shard.current(
|
18
|
+
db = Shard.current(connection_class_for_self).database_server
|
19
19
|
db.unguard { super }
|
20
20
|
end
|
21
21
|
RUBY
|
@@ -16,7 +16,7 @@ module Switchman
|
|
16
16
|
payload[:shard] = {
|
17
17
|
database_server_id: shard.database_server.id,
|
18
18
|
id: shard.id,
|
19
|
-
env: @shard_host.pool.connection_klass&.current_role
|
19
|
+
env: ::Rails.version < '7.0' ? @shard_host.pool.connection_klass&.current_role : @shard_host.pool.connection_class&.current_role
|
20
20
|
}
|
21
21
|
end
|
22
22
|
super name, payload
|
data/lib/switchman/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: switchman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cody Cutrer
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2022-
|
13
|
+
date: 2022-06-02 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: 6.1.4
|
22
22
|
- - "<"
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: '
|
24
|
+
version: '7.1'
|
25
25
|
type: :runtime
|
26
26
|
prerelease: false
|
27
27
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -31,21 +31,21 @@ dependencies:
|
|
31
31
|
version: 6.1.4
|
32
32
|
- - "<"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '
|
34
|
+
version: '7.1'
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: guardrail
|
37
37
|
requirement: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: 3.0.
|
41
|
+
version: 3.0.1
|
42
42
|
type: :runtime
|
43
43
|
prerelease: false
|
44
44
|
version_requirements: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
46
|
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: 3.0.
|
48
|
+
version: 3.0.1
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
50
|
name: parallel
|
51
51
|
requirement: !ruby/object:Gem::Requirement
|
@@ -69,7 +69,7 @@ dependencies:
|
|
69
69
|
version: '6.1'
|
70
70
|
- - "<"
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version: '
|
72
|
+
version: '7.1'
|
73
73
|
type: :runtime
|
74
74
|
prerelease: false
|
75
75
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -79,7 +79,7 @@ dependencies:
|
|
79
79
|
version: '6.1'
|
80
80
|
- - "<"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '7.1'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: appraisal
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -308,14 +308,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
308
308
|
requirements:
|
309
309
|
- - ">="
|
310
310
|
- !ruby/object:Gem::Version
|
311
|
-
version: '2.
|
311
|
+
version: '2.7'
|
312
312
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
313
313
|
requirements:
|
314
314
|
- - ">="
|
315
315
|
- !ruby/object:Gem::Version
|
316
316
|
version: '0'
|
317
317
|
requirements: []
|
318
|
-
rubygems_version: 3.1.
|
318
|
+
rubygems_version: 3.1.6
|
319
319
|
signing_key:
|
320
320
|
specification_version: 4
|
321
321
|
summary: Rails sharding magic
|