switchman 3.0.24 → 3.1.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 +4 -4
- data/Rakefile +1 -1
- 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 +24 -18
- 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/engine.rb +9 -4
- 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
@@ -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
|
@@ -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
|
@@ -92,7 +92,7 @@ module Switchman
|
|
92
92
|
sharded_role = nil
|
93
93
|
connected_to_stack.reverse_each do |hash|
|
94
94
|
shard_role = hash.dig(:shard_roles, target_shard)
|
95
|
-
if shard_role && (hash[:klasses].include?(::ActiveRecord::Base) || hash[:klasses].include?(
|
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
@@ -12,10 +12,14 @@ module Switchman
|
|
12
12
|
|
13
13
|
# after :initialize_dependency_mechanism to ensure autoloading is configured for any downstream initializers that care
|
14
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
|
-
|
16
|
-
|
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
|
17
19
|
|
18
|
-
initializer 'switchman.active_record_patch',
|
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
|
19
23
|
::ActiveSupport.on_load(:active_record) do
|
20
24
|
# Switchman requires postgres, so just always load the pg adapter
|
21
25
|
require 'active_record/connection_adapters/postgresql_adapter'
|
@@ -24,7 +28,7 @@ module Switchman
|
|
24
28
|
self.default_role = :primary
|
25
29
|
|
26
30
|
prepend ActiveRecord::Base
|
27
|
-
|
31
|
+
prepend ActiveRecord::AttributeMethods
|
28
32
|
include ActiveRecord::Persistence
|
29
33
|
singleton_class.prepend ActiveRecord::ModelSchema::ClassMethods
|
30
34
|
|
@@ -46,6 +50,7 @@ module Switchman
|
|
46
50
|
::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::Associations::CollectionProxy)
|
47
51
|
|
48
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'
|
49
54
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::AbstractAdapter)
|
50
55
|
::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool)
|
51
56
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache)
|
@@ -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
|