switchman 4.0.1 → 4.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/lib/switchman/active_record/abstract_adapter.rb +1 -1
- data/lib/switchman/active_record/associations.rb +2 -2
- data/lib/switchman/active_record/attribute_methods.rb +4 -4
- data/lib/switchman/active_record/base.rb +10 -55
- data/lib/switchman/active_record/calculations.rb +6 -6
- data/lib/switchman/active_record/connection_pool.rb +34 -5
- data/lib/switchman/active_record/finder_methods.rb +3 -3
- data/lib/switchman/active_record/migration.rb +9 -3
- data/lib/switchman/active_record/persistence.rb +1 -1
- data/lib/switchman/active_record/postgresql_adapter.rb +26 -12
- data/lib/switchman/active_record/query_cache.rb +16 -8
- data/lib/switchman/active_record/query_methods.rb +63 -22
- data/lib/switchman/active_record/reflection.rb +9 -2
- data/lib/switchman/active_record/relation.rb +56 -3
- data/lib/switchman/active_record/spawn_methods.rb +1 -5
- data/lib/switchman/active_record/table_definition.rb +1 -1
- data/lib/switchman/active_record/test_fixtures.rb +75 -35
- data/lib/switchman/database_server.rb +4 -4
- data/lib/switchman/engine.rb +9 -0
- data/lib/switchman/shard.rb +8 -6
- data/lib/switchman/sharded_instrumenter.rb +1 -1
- data/lib/switchman/test_helper.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/tasks/switchman.rake +1 -1
- metadata +27 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e03940e1a5f3d0e7b933d99b3cbd68e3998b55f14ac0e7eec91ef81a193445e1
|
4
|
+
data.tar.gz: 923793689418332ab6ce2ffef2981567bd74b6358916794d958172638b0ec160
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6950ac12cf6532f98e3674ded8890e7bb2f9ef50f33657220096de50508715ab047250c163546dc7396c61d15197ee2b517ae342f84cbe007ae22a217ed7751f
|
7
|
+
data.tar.gz: c45df067998ec5868bfe2c6a0f52608ffd9de9a19e2ce55ba55fc9986e077833df9964c5783c0f394a9683cb21a610e7f2a781d88e690fbc2bfeef5e8e352826
|
@@ -174,7 +174,7 @@ module Switchman
|
|
174
174
|
end
|
175
175
|
|
176
176
|
# Disabling to keep closer to rails original
|
177
|
-
# rubocop:disable Naming/AccessorMethodName
|
177
|
+
# rubocop:disable Naming/AccessorMethodName
|
178
178
|
# significant changes:
|
179
179
|
# * globalize the key to lookup
|
180
180
|
def set_inverse(record)
|
@@ -191,7 +191,7 @@ module Switchman
|
|
191
191
|
association.set_inverse_instance(record)
|
192
192
|
end
|
193
193
|
end
|
194
|
-
# rubocop:enable Naming/AccessorMethodName
|
194
|
+
# rubocop:enable Naming/AccessorMethodName
|
195
195
|
|
196
196
|
# significant changes:
|
197
197
|
# * partition_by_shard the records_for call
|
@@ -59,9 +59,9 @@ module Switchman
|
|
59
59
|
if ::Rails.version < "7.1.4"
|
60
60
|
# https://github.com/rails/rails/commit/a2a12fc2e3f4e6d06f81d4c74c88f8e6b3369ee6#diff-5b59ece6d9396b596f06271cec0ea726e3360911383511c49b1a66f454bfc2b6L30
|
61
61
|
# These arguments were effectively swapped in Rails 7.1.4, so previous versions need them reversed
|
62
|
-
owner.define_cached_method(as, namespace
|
62
|
+
owner.define_cached_method(as, namespace:, as: name, &block)
|
63
63
|
else
|
64
|
-
owner.define_cached_method(name, namespace
|
64
|
+
owner.define_cached_method(name, namespace:, as:, &block)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -136,14 +136,14 @@ module Switchman
|
|
136
136
|
safe_class_name = class_name.unpack1("h*")
|
137
137
|
define_cached_method(owner,
|
138
138
|
"sharded_#{safe_class_name}_#{attr_name}",
|
139
|
-
as
|
139
|
+
as:,
|
140
140
|
namespace: :switchman) do |batch|
|
141
141
|
batch << build_sharded_getter("sharded_#{safe_class_name}_#{attr_name}",
|
142
142
|
"original_#{as}",
|
143
143
|
class_name)
|
144
144
|
end
|
145
145
|
else
|
146
|
-
define_cached_method(owner, "plain_#{attr_name}", as
|
146
|
+
define_cached_method(owner, "plain_#{attr_name}", as:, namespace: :switchman) do |batch|
|
147
147
|
batch << <<-RUBY
|
148
148
|
def plain_#{attr_name}
|
149
149
|
_read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
|
@@ -6,9 +6,9 @@ module Switchman
|
|
6
6
|
module ClassMethods
|
7
7
|
delegate :shard, to: :all
|
8
8
|
|
9
|
-
def find_ids_in_ranges(opts = {}, &
|
9
|
+
def find_ids_in_ranges(opts = {}, &)
|
10
10
|
opts.reverse_merge!(loose: true)
|
11
|
-
all.find_ids_in_ranges(opts, &
|
11
|
+
all.find_ids_in_ranges(opts, &)
|
12
12
|
end
|
13
13
|
|
14
14
|
def sharded_model
|
@@ -34,56 +34,6 @@ module Switchman
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
# NOTE: `returning` values are _not_ transposed back to the current shard
|
38
|
-
%w[insert_all upsert_all].each do |method|
|
39
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
40
|
-
def #{method}(attributes, returning: nil, **)
|
41
|
-
scope = self != ::ActiveRecord::Base && current_scope
|
42
|
-
if (target_shard = scope&.primary_shard) == (current_shard = Shard.current(connection_class_for_self))
|
43
|
-
scope = nil
|
44
|
-
end
|
45
|
-
if scope
|
46
|
-
dupped = false
|
47
|
-
attributes.each_with_index do |hash, i|
|
48
|
-
if dupped || hash.any? { |k, v| sharded_column?(k) }
|
49
|
-
unless dupped
|
50
|
-
attributes = attributes.dup
|
51
|
-
dupped = true
|
52
|
-
end
|
53
|
-
attributes[i] = hash.to_h do |k, v|
|
54
|
-
if sharded_column?(k)
|
55
|
-
[k, Shard.relative_id_for(v, current_shard, target_shard)]
|
56
|
-
else
|
57
|
-
[k, v]
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
if scope
|
65
|
-
scope.activate do
|
66
|
-
db = Shard.current(connection_class_for_self).database_server
|
67
|
-
result = db.unguard { super }
|
68
|
-
if result&.columns&.any? { |c| sharded_column?(c) }
|
69
|
-
transposed_rows = result.rows.map do |row|
|
70
|
-
row.map.with_index do |value, i|
|
71
|
-
sharded_column?(result.columns[i]) ? Shard.relative_id_for(value, target_shard, current_shard) : value
|
72
|
-
end
|
73
|
-
end
|
74
|
-
result = ::ActiveRecord::Result.new(result.columns, transposed_rows, result.column_types)
|
75
|
-
end
|
76
|
-
|
77
|
-
result
|
78
|
-
end
|
79
|
-
else
|
80
|
-
db = Shard.current(connection_class_for_self).database_server
|
81
|
-
db.unguard { super }
|
82
|
-
end
|
83
|
-
end
|
84
|
-
RUBY
|
85
|
-
end
|
86
|
-
|
87
37
|
def reset_column_information
|
88
38
|
@sharded_column_values = {}
|
89
39
|
super
|
@@ -109,7 +59,11 @@ module Switchman
|
|
109
59
|
::ActiveRecord::Base.connection_handler.connection_pool_list(:all)
|
110
60
|
end
|
111
61
|
pools.each do |pool|
|
112
|
-
|
62
|
+
if ::Rails.version < "7.2"
|
63
|
+
pool.connection(switch_shard: false).clear_query_cache if pool.active_connection?
|
64
|
+
elsif pool.active_connection?
|
65
|
+
pool.lease_connection(switch_shard: false).clear_query_cache
|
66
|
+
end
|
113
67
|
end
|
114
68
|
end
|
115
69
|
|
@@ -132,7 +86,7 @@ module Switchman
|
|
132
86
|
:"#{current_shard}/#{current_role}"
|
133
87
|
end
|
134
88
|
|
135
|
-
super
|
89
|
+
super
|
136
90
|
end
|
137
91
|
|
138
92
|
def connected_to_stack
|
@@ -189,6 +143,7 @@ module Switchman
|
|
189
143
|
|
190
144
|
def self.prepended(klass)
|
191
145
|
klass.singleton_class.prepend(ClassMethods)
|
146
|
+
klass.singleton_class.prepend(Switchman::ActiveRecord::Relation::InsertUpsertAll) if ::Rails.version < "7.2"
|
192
147
|
klass.scope :non_shadow, lambda { |key = primary_key|
|
193
148
|
where(key => (QueryMethods::NonTransposingValue.new(0)..
|
194
149
|
QueryMethods::NonTransposingValue.new(Shard::IDS_PER_SHARD)))
|
@@ -241,7 +196,7 @@ module Switchman
|
|
241
196
|
end
|
242
197
|
end
|
243
198
|
target_shard.activate do
|
244
|
-
self.class.
|
199
|
+
self.class.upsert_all([shadow_attrs], unique_by: self.class.primary_key)
|
245
200
|
end
|
246
201
|
end
|
247
202
|
|
@@ -159,7 +159,7 @@ module Switchman
|
|
159
159
|
end
|
160
160
|
|
161
161
|
def grouped_calculation_options(operation, column_name, distinct)
|
162
|
-
opts = { operation
|
162
|
+
opts = { operation:, column_name:, distinct: }
|
163
163
|
|
164
164
|
# Rails 7.0.5
|
165
165
|
if defined?(::ActiveRecord::Calculations::ColumnAliasTracker)
|
@@ -188,11 +188,11 @@ module Switchman
|
|
188
188
|
group_columns = group_aliases.zip(group_fields).map do |aliaz, field|
|
189
189
|
[aliaz, type_for(field), column_name_for(field)]
|
190
190
|
end
|
191
|
-
opts.merge!(association
|
192
|
-
associated
|
193
|
-
group_aliases
|
194
|
-
group_columns
|
195
|
-
group_fields:
|
191
|
+
opts.merge!(association:,
|
192
|
+
associated:,
|
193
|
+
group_aliases:,
|
194
|
+
group_columns:,
|
195
|
+
group_fields:)
|
196
196
|
|
197
197
|
opts
|
198
198
|
end
|
@@ -11,7 +11,7 @@ module Switchman
|
|
11
11
|
self.schema_cache
|
12
12
|
end
|
13
13
|
|
14
|
-
# rubocop:disable Naming/AccessorMethodName override method
|
14
|
+
# rubocop:disable Naming/AccessorMethodName -- override method
|
15
15
|
def set_schema_cache(cache)
|
16
16
|
schema_cache = get_schema_cache(cache.connection)
|
17
17
|
|
@@ -19,13 +19,14 @@ module Switchman
|
|
19
19
|
schema_cache.instance_variable_set(x, cache.instance_variable_get(x))
|
20
20
|
end
|
21
21
|
end
|
22
|
-
# rubocop:enable Naming/AccessorMethodName override method
|
22
|
+
# rubocop:enable Naming/AccessorMethodName -- override method
|
23
23
|
end
|
24
24
|
|
25
25
|
def default_schema
|
26
|
-
connection
|
26
|
+
connection_method = (::Rails.version < "7.2") ? :connection : :lease_connection
|
27
|
+
send(connection_method) unless @schemas
|
27
28
|
# default shard will not switch databases immediately, so it won't be set yet
|
28
|
-
@schemas ||=
|
29
|
+
@schemas ||= send(connection_method).current_schemas
|
29
30
|
@schemas.first
|
30
31
|
end
|
31
32
|
|
@@ -43,8 +44,36 @@ module Switchman
|
|
43
44
|
conn
|
44
45
|
end
|
45
46
|
|
47
|
+
unless ::Rails.version < "7.2"
|
48
|
+
def active_connection(switch_shard: true)
|
49
|
+
conn = super()
|
50
|
+
return nil if conn.nil?
|
51
|
+
raise Errors::NonExistentShardError if current_shard.new_record?
|
52
|
+
|
53
|
+
switch_database(conn) if conn.shard != current_shard && switch_shard
|
54
|
+
conn
|
55
|
+
end
|
56
|
+
|
57
|
+
def lease_connection(switch_shard: true)
|
58
|
+
conn = super()
|
59
|
+
raise Errors::NonExistentShardError if current_shard.new_record?
|
60
|
+
|
61
|
+
switch_database(conn) if conn.shard != current_shard && switch_shard
|
62
|
+
conn
|
63
|
+
end
|
64
|
+
|
65
|
+
def with_connection(switch_shard: true, **kwargs)
|
66
|
+
super(**kwargs) do |conn|
|
67
|
+
raise Errors::NonExistentShardError if current_shard.new_record?
|
68
|
+
|
69
|
+
switch_database(conn) if conn.shard != current_shard && switch_shard
|
70
|
+
yield conn
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
46
75
|
def release_connection(with_id = Thread.current)
|
47
|
-
super
|
76
|
+
super
|
48
77
|
|
49
78
|
flush
|
50
79
|
end
|
@@ -4,7 +4,7 @@ module Switchman
|
|
4
4
|
module ActiveRecord
|
5
5
|
module FinderMethods
|
6
6
|
def find_one(id)
|
7
|
-
return super
|
7
|
+
return super unless klass.integral_id?
|
8
8
|
|
9
9
|
if shard_source_value != :implicit
|
10
10
|
current_shard = Shard.current(klass.connection_class_for_self)
|
@@ -28,14 +28,14 @@ module Switchman
|
|
28
28
|
if shard
|
29
29
|
shard.activate { super(local_id) }
|
30
30
|
else
|
31
|
-
super
|
31
|
+
super
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
35
|
def find_some_ordered(ids)
|
36
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
|
-
super
|
38
|
+
super
|
39
39
|
end
|
40
40
|
|
41
41
|
def find_or_instantiator_by_attributes(match, attributes, *args)
|
@@ -31,8 +31,10 @@ module Switchman
|
|
31
31
|
# Store in internalmetadata to allow other tools to be able to lock out migrations
|
32
32
|
if ::Rails.version < "7.1"
|
33
33
|
::ActiveRecord::InternalMetadata[:migrator_advisory_lock_id] = shard_name_hash
|
34
|
-
|
34
|
+
elsif ::Rails.version < "7.2"
|
35
35
|
::ActiveRecord::InternalMetadata.new(connection)[:migrator_advisory_lock_id] = shard_name_hash
|
36
|
+
else
|
37
|
+
::ActiveRecord::InternalMetadata.new(connection.pool)[:migrator_advisory_lock_id] = shard_name_hash
|
36
38
|
end
|
37
39
|
shard_name_hash
|
38
40
|
end
|
@@ -73,9 +75,13 @@ module Switchman
|
|
73
75
|
end
|
74
76
|
|
75
77
|
begin
|
76
|
-
super
|
78
|
+
super
|
77
79
|
ensure
|
78
|
-
|
80
|
+
if ::Rails.version < "7.2"
|
81
|
+
schema_cache_holder.set_schema_cache(previous_schema_cache)
|
82
|
+
else
|
83
|
+
schema_cache_holder.instance_variable_set(:@cache, previous_schema_cache)
|
84
|
+
end
|
79
85
|
reset_column_information
|
80
86
|
end
|
81
87
|
end
|
@@ -73,23 +73,37 @@ module Switchman
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
+
module ClassMethods
|
77
|
+
def quote_local_table_name(name)
|
78
|
+
# postgres quotes tables and columns the same; just pass through
|
79
|
+
# (differs from quote_table_name_with_shard below by no logic to
|
80
|
+
# explicitly qualify the table)
|
81
|
+
quote_column_name(name)
|
82
|
+
end
|
83
|
+
|
84
|
+
def quote_table_name(name, shard: nil)
|
85
|
+
# This looks kind of weird at first glance, but older Rails versions do not actually import
|
86
|
+
# these methods as class methods.
|
87
|
+
shard = self.shard if shard.nil? && ::Rails.version < "7.2" && !@use_local_table_name
|
88
|
+
|
89
|
+
return quote_local_table_name(name) unless shard
|
90
|
+
|
91
|
+
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
|
92
|
+
name.instance_variable_set(:@schema, shard.name) unless name.schema
|
93
|
+
name.quoted
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
76
97
|
def quote_local_table_name(name)
|
77
|
-
|
78
|
-
# (differs from quote_table_name_with_shard below by no logic to
|
79
|
-
# explicitly qualify the table)
|
80
|
-
quote_column_name(name)
|
98
|
+
self.class.quote_local_table_name(name)
|
81
99
|
end
|
82
100
|
|
83
101
|
def quote_table_name(name)
|
84
|
-
|
85
|
-
|
86
|
-
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
|
87
|
-
name.instance_variable_set(:@schema, shard.name) unless name.schema
|
88
|
-
name.quoted
|
102
|
+
self.class.quote_table_name(name, shard: @use_local_table_name ? nil : shard)
|
89
103
|
end
|
90
104
|
|
91
|
-
def with_global_table_name(&
|
92
|
-
with_local_table_name(false, &
|
105
|
+
def with_global_table_name(&)
|
106
|
+
with_local_table_name(false, &)
|
93
107
|
end
|
94
108
|
|
95
109
|
def with_local_table_name(enable = true) # rubocop:disable Style/OptionalBooleanParameter
|
@@ -129,7 +143,7 @@ module Switchman
|
|
129
143
|
end
|
130
144
|
|
131
145
|
def columns(*)
|
132
|
-
|
146
|
+
with_global_table_name { super }
|
133
147
|
end
|
134
148
|
end
|
135
149
|
end
|
@@ -13,9 +13,9 @@ module Switchman
|
|
13
13
|
result =
|
14
14
|
if query_cache[sql].key?(binds)
|
15
15
|
args = {
|
16
|
-
sql
|
17
|
-
binds
|
18
|
-
name
|
16
|
+
sql:,
|
17
|
+
binds:,
|
18
|
+
name:,
|
19
19
|
connection_id: object_id,
|
20
20
|
cached: true,
|
21
21
|
type_casted_binds: -> { type_casted_binds(binds) }
|
@@ -40,12 +40,20 @@ module Switchman
|
|
40
40
|
hit = false
|
41
41
|
|
42
42
|
@lock.synchronize do
|
43
|
-
if
|
44
|
-
|
45
|
-
|
43
|
+
if ::Rails.version < "7.2"
|
44
|
+
if (result = @query_cache.delete(key))
|
45
|
+
hit = true
|
46
|
+
@query_cache[key] = result
|
47
|
+
else
|
48
|
+
result = @query_cache[key] = yield
|
49
|
+
@query_cache.shift if @query_cache_max_size && @query_cache.size > @query_cache_max_size
|
50
|
+
end
|
46
51
|
else
|
47
|
-
|
48
|
-
|
52
|
+
hit = true
|
53
|
+
result = @query_cache.compute_if_absent(key) do
|
54
|
+
hit = false
|
55
|
+
yield
|
56
|
+
end
|
49
57
|
end
|
50
58
|
end
|
51
59
|
|
@@ -34,13 +34,29 @@ module Switchman
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def shard_value=(value)
|
37
|
-
|
37
|
+
if @loaded
|
38
|
+
error_class = if ::Rails.version < "7.2"
|
39
|
+
::ActiveRecord::ImmutableRelation
|
40
|
+
else
|
41
|
+
::ActiveRecord::UnmodifiableRelation
|
42
|
+
end
|
43
|
+
|
44
|
+
raise error_class
|
45
|
+
end
|
38
46
|
|
39
47
|
@values[:shard] = value
|
40
48
|
end
|
41
49
|
|
42
50
|
def shard_source_value=(value)
|
43
|
-
|
51
|
+
if @loaded
|
52
|
+
error_class = if ::Rails.version < "7.2"
|
53
|
+
::ActiveRecord::ImmutableRelation
|
54
|
+
else
|
55
|
+
::ActiveRecord::UnmodifiableRelation
|
56
|
+
end
|
57
|
+
|
58
|
+
raise error_class
|
59
|
+
end
|
44
60
|
|
45
61
|
@values[:shard_source] = value
|
46
62
|
end
|
@@ -107,6 +123,10 @@ module Switchman
|
|
107
123
|
|
108
124
|
protected
|
109
125
|
|
126
|
+
def arel_columns(columns)
|
127
|
+
connection.with_local_table_name { super }
|
128
|
+
end
|
129
|
+
|
110
130
|
def remove_nonlocal_primary_keys!
|
111
131
|
each_transposable_predicate_value do |value, predicate, _relation, _column, type|
|
112
132
|
next value unless
|
@@ -243,31 +263,49 @@ module Switchman
|
|
243
263
|
end
|
244
264
|
end
|
245
265
|
|
246
|
-
def arel_columns(columns)
|
247
|
-
connection.with_local_table_name { super }
|
248
|
-
end
|
249
|
-
|
250
266
|
def arel_column(columns)
|
251
267
|
connection.with_local_table_name { super }
|
252
268
|
end
|
253
269
|
|
254
270
|
def table_name_matches?(from)
|
255
|
-
|
271
|
+
if ::Rails.version < "7.2"
|
272
|
+
connection.with_global_table_name { super }
|
273
|
+
else
|
274
|
+
connection.with_global_table_name do
|
275
|
+
table_name = Regexp.escape(table.name)
|
276
|
+
# INST: adapter_class -> connection
|
277
|
+
quoted_table_name = Regexp.escape(connection.quote_table_name(table.name))
|
278
|
+
/(?:\A|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
unless ::Rails.version < "7.2"
|
284
|
+
def order_column(field)
|
285
|
+
arel_column(field) do |attr_name|
|
286
|
+
if attr_name == "count" && !group_values.empty?
|
287
|
+
table[attr_name]
|
288
|
+
else
|
289
|
+
# INST: adapter_class -> connection
|
290
|
+
::Arel.sql(connection.quote_table_name(attr_name), retryable: true)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
256
294
|
end
|
257
295
|
|
258
|
-
def each_predicate(predicates = nil, &
|
259
|
-
return predicates.map(&
|
296
|
+
def each_predicate(predicates = nil, &)
|
297
|
+
return predicates.map(&) if predicates
|
260
298
|
|
261
|
-
each_predicate_cb(:having_clause, :having_clause=, &
|
262
|
-
each_predicate_cb(:where_clause, :where_clause=, &
|
299
|
+
each_predicate_cb(:having_clause, :having_clause=, &)
|
300
|
+
each_predicate_cb(:where_clause, :where_clause=, &)
|
263
301
|
end
|
264
302
|
|
265
|
-
def each_predicate_cb(clause_getter, clause_setter, &
|
303
|
+
def each_predicate_cb(clause_getter, clause_setter, &)
|
266
304
|
old_clause = send(clause_getter)
|
267
305
|
old_predicates = old_clause.send(:predicates)
|
268
306
|
return if old_predicates.empty?
|
269
307
|
|
270
|
-
new_predicates = old_predicates.map(&
|
308
|
+
new_predicates = old_predicates.map(&)
|
271
309
|
return if new_predicates == old_predicates
|
272
310
|
|
273
311
|
new_clause = old_clause.dup
|
@@ -289,7 +327,10 @@ module Switchman
|
|
289
327
|
|
290
328
|
next predicate if new_left == old_left && new_right == old_right
|
291
329
|
|
292
|
-
next predicate.class.new predicate.expr.class.new(new_left, new_right)
|
330
|
+
next predicate.class.new predicate.expr.class.new(new_left, new_right) if ::Rails.version < "7.2"
|
331
|
+
|
332
|
+
next predicate.class.new predicate.expr.class.new([new_left, new_right])
|
333
|
+
|
293
334
|
when ::Arel::Nodes::SelectStatement
|
294
335
|
new_cores = predicate.cores.map do |core|
|
295
336
|
next core unless core.is_a?(::Arel::Nodes::SelectCore) # just in case something weird is going on
|
@@ -344,33 +385,33 @@ module Switchman
|
|
344
385
|
end
|
345
386
|
end
|
346
387
|
|
347
|
-
def each_transposable_predicate_value_cb(node, original_block, &
|
388
|
+
def each_transposable_predicate_value_cb(node, original_block, &)
|
348
389
|
case node
|
349
390
|
when Array
|
350
|
-
node.filter_map { |val| each_transposable_predicate_value_cb(val, original_block, &
|
391
|
+
node.filter_map { |val| each_transposable_predicate_value_cb(val, original_block, &).presence }
|
351
392
|
when ::ActiveModel::Attribute
|
352
393
|
old_value = node.value_before_type_cast
|
353
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
394
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
354
395
|
|
355
396
|
(old_value == new_value) ? node : node.class.new(node.name, new_value, node.type)
|
356
397
|
when ::Arel::Nodes::And
|
357
398
|
old_value = node.children
|
358
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
399
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
359
400
|
|
360
401
|
(old_value == new_value) ? node : node.class.new(new_value)
|
361
402
|
when ::Arel::Nodes::BindParam
|
362
403
|
old_value = node.value
|
363
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
404
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
364
405
|
|
365
406
|
(old_value == new_value) ? node : node.class.new(new_value)
|
366
407
|
when ::Arel::Nodes::Casted
|
367
408
|
old_value = node.value
|
368
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
409
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
369
410
|
|
370
411
|
(old_value == new_value) ? node : node.class.new(new_value, node.attribute)
|
371
412
|
when ::Arel::Nodes::HomogeneousIn
|
372
413
|
old_value = node.values
|
373
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
414
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
374
415
|
|
375
416
|
# switch to a regular In, so that Relation::WhereClause#contradiction? knows about it
|
376
417
|
if new_value.empty?
|
@@ -381,7 +422,7 @@ module Switchman
|
|
381
422
|
end
|
382
423
|
when ::Arel::Nodes::Binary
|
383
424
|
old_value = node.right
|
384
|
-
new_value = each_transposable_predicate_value_cb(old_value, original_block, &
|
425
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &)
|
385
426
|
|
386
427
|
(old_value == new_value) ? node : node.class.new(node.left, new_value)
|
387
428
|
when ::Arel::Nodes::SelectStatement
|
@@ -28,11 +28,18 @@ module Switchman
|
|
28
28
|
# this technically belongs on AssociationReflection, but we put it on
|
29
29
|
# ThroughReflection as well, instead of delegating to its internal
|
30
30
|
# HasManyAssociation, losing its proper `klass`
|
31
|
-
def association_scope_cache(klass, owner, &
|
31
|
+
def association_scope_cache(klass, owner, &)
|
32
32
|
key = self
|
33
33
|
key = [key, owner._read_attribute(@foreign_type)] if polymorphic?
|
34
34
|
key = [key, shard(owner).id].flatten
|
35
|
-
|
35
|
+
|
36
|
+
if ::Rails.version < "7.2"
|
37
|
+
klass.cached_find_by_statement(key, &)
|
38
|
+
else
|
39
|
+
klass.with_connection do |connection|
|
40
|
+
klass.cached_find_by_statement(connection, key, &)
|
41
|
+
end
|
42
|
+
end
|
36
43
|
end
|
37
44
|
end
|
38
45
|
|
@@ -28,15 +28,15 @@ module Switchman
|
|
28
28
|
relation
|
29
29
|
end
|
30
30
|
|
31
|
-
def new(*, &
|
31
|
+
def new(*, &)
|
32
32
|
primary_shard.activate(klass.connection_class_for_self) { super }
|
33
33
|
end
|
34
34
|
|
35
|
-
def create(*, &
|
35
|
+
def create(*, &)
|
36
36
|
primary_shard.activate(klass.connection_class_for_self) { super }
|
37
37
|
end
|
38
38
|
|
39
|
-
def create!(*, &
|
39
|
+
def create!(*, &)
|
40
40
|
primary_shard.activate(klass.connection_class_for_self) { super }
|
41
41
|
end
|
42
42
|
|
@@ -73,6 +73,59 @@ module Switchman
|
|
73
73
|
RUBY
|
74
74
|
end
|
75
75
|
|
76
|
+
# https://github.com/rails/rails/commit/ed2c15b52450ff927a05629f031376f25b670335
|
77
|
+
# once the minimum version is Rails 7.2, we can drop this separate module
|
78
|
+
module InsertUpsertAll
|
79
|
+
%w[insert_all upsert_all].each do |method|
|
80
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
81
|
+
def #{method}(attributes, returning: nil, **)
|
82
|
+
scope = self != ::ActiveRecord::Base && current_scope
|
83
|
+
if (target_shard = scope&.primary_shard) == (current_shard = Shard.current(connection_class_for_self))
|
84
|
+
scope = nil
|
85
|
+
end
|
86
|
+
if scope
|
87
|
+
dupped = false
|
88
|
+
attributes.each_with_index do |hash, i|
|
89
|
+
if dupped || hash.any? { |k, v| sharded_column?(k) }
|
90
|
+
unless dupped
|
91
|
+
attributes = attributes.dup
|
92
|
+
dupped = true
|
93
|
+
end
|
94
|
+
attributes[i] = hash.to_h do |k, v|
|
95
|
+
if sharded_column?(k)
|
96
|
+
[k, Shard.relative_id_for(v, current_shard, target_shard)]
|
97
|
+
else
|
98
|
+
[k, v]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
if scope
|
106
|
+
scope.activate do
|
107
|
+
db = Shard.current(connection_class_for_self).database_server
|
108
|
+
result = db.unguard { super }
|
109
|
+
if result&.columns&.any? { |c| sharded_column?(c) }
|
110
|
+
transposed_rows = result.rows.map do |row|
|
111
|
+
row.map.with_index do |value, i|
|
112
|
+
sharded_column?(result.columns[i]) ? Shard.relative_id_for(value, target_shard, current_shard) : value
|
113
|
+
end
|
114
|
+
end
|
115
|
+
result = ::ActiveRecord::Result.new(result.columns, transposed_rows, result.column_types)
|
116
|
+
end
|
117
|
+
|
118
|
+
result
|
119
|
+
end
|
120
|
+
else
|
121
|
+
db = Shard.current(connection_class_for_self).database_server
|
122
|
+
db.unguard { super }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
RUBY
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
76
129
|
def find_ids_in_ranges(options = {})
|
77
130
|
is_integer = columns_hash[primary_key.to_s].type == :integer
|
78
131
|
loose_mode = options[:loose] && is_integer
|
@@ -62,16 +62,12 @@ module Switchman
|
|
62
62
|
if primary_shard != final_primary_shard && rhs.primary_shard != final_primary_shard
|
63
63
|
shard!(final_primary_shard)
|
64
64
|
rhs = rhs.shard(final_primary_shard)
|
65
|
-
super(rhs)
|
66
65
|
elsif primary_shard != final_primary_shard
|
67
66
|
shard!(final_primary_shard)
|
68
|
-
super(rhs)
|
69
67
|
elsif rhs.primary_shard != final_primary_shard
|
70
68
|
rhs = rhs.shard(final_primary_shard)
|
71
|
-
super(rhs)
|
72
|
-
else
|
73
|
-
super
|
74
69
|
end
|
70
|
+
super
|
75
71
|
|
76
72
|
self.shard_value = final_shard_value
|
77
73
|
self.shard_source_value = final_shard_source_value
|
@@ -4,49 +4,89 @@ module Switchman
|
|
4
4
|
module ActiveRecord
|
5
5
|
module TestFixtures
|
6
6
|
FORBIDDEN_DB_ENVS = %i[development production].freeze
|
7
|
-
def setup_fixtures(config = ::ActiveRecord::Base)
|
8
|
-
super
|
9
|
-
|
10
|
-
return unless run_in_transaction?
|
11
|
-
|
12
|
-
# Replace the one that activerecord natively uses with a switchman-optimized one
|
13
|
-
::ActiveSupport::Notifications.unsubscribe(@connection_subscriber)
|
14
|
-
# Code adapted from the code in rails proper
|
15
|
-
@connection_subscriber =
|
16
|
-
::ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
|
17
|
-
spec_name = if ::Rails.version < "7.1"
|
18
|
-
payload[:spec_name] if payload.key?(:spec_name)
|
19
|
-
elsif payload.key?(:connection_name)
|
20
|
-
payload[:connection_name]
|
21
|
-
end
|
22
|
-
shard = payload[:shard] if payload.key?(:shard)
|
23
7
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
rescue ::ActiveRecord::ConnectionNotEstablished, ::ActiveRecord::NoDatabaseError
|
29
|
-
connection = nil
|
30
|
-
end
|
8
|
+
if ::Rails.version < "7.2"
|
9
|
+
def setup_fixtures(config = ::ActiveRecord::Base)
|
10
|
+
super
|
11
|
+
return unless run_in_transaction?
|
31
12
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
13
|
+
# Replace the one that activerecord natively uses with a switchman-optimized one
|
14
|
+
::ActiveSupport::Notifications.unsubscribe(@connection_subscriber)
|
15
|
+
# Code adapted from the code in rails proper
|
16
|
+
@connection_subscriber =
|
17
|
+
::ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
|
18
|
+
spec_name = if ::Rails.version < "7.1"
|
19
|
+
payload[:spec_name] if payload.key?(:spec_name)
|
20
|
+
elsif payload.key?(:connection_name)
|
21
|
+
payload[:connection_name]
|
22
|
+
end
|
23
|
+
shard = payload[:shard] if payload.key?(:shard)
|
24
|
+
|
25
|
+
if spec_name && !FORBIDDEN_DB_ENVS.include?(shard)
|
26
|
+
begin
|
27
|
+
connection = ::ActiveRecord::Base.connection_handler.retrieve_connection(spec_name, shard: shard)
|
28
|
+
connection.connect! if ::Rails.version >= "7.1" # eagerly validate the connection
|
29
|
+
rescue ::ActiveRecord::ConnectionNotEstablished
|
30
|
+
connection = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
if connection
|
34
|
+
setup_shared_connection_pool
|
35
|
+
unless @fixture_connections.include?(connection)
|
36
|
+
connection.begin_transaction joinable: false, _lazy: false
|
37
|
+
connection.pool.lock_thread = true if lock_threads
|
38
|
+
@fixture_connections << connection
|
39
|
+
end
|
38
40
|
end
|
39
41
|
end
|
40
42
|
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def enlist_fixture_connections
|
46
|
+
setup_shared_connection_pool
|
47
|
+
|
48
|
+
::ActiveRecord::Base.connection_handler.connection_pool_list(:primary).reject do |cp|
|
49
|
+
FORBIDDEN_DB_ENVS.include?(cp.db_config.env_name.to_sym)
|
50
|
+
end.map(&:connection)
|
51
|
+
end
|
52
|
+
else
|
53
|
+
def setup_transactional_fixtures
|
54
|
+
setup_shared_connection_pool
|
55
|
+
|
56
|
+
# Begin transactions for connections already established
|
57
|
+
# INST: :writing -> :primary
|
58
|
+
@fixture_connection_pools = ::ActiveRecord::Base.connection_handler.connection_pool_list(:primary)
|
59
|
+
# INST: filter by FORBIDDEN_DB_ENVS
|
60
|
+
@fixture_connection_pools = @fixture_connection_pools.reject do |cp|
|
61
|
+
FORBIDDEN_DB_ENVS.include?(cp.db_config.env_name.to_sym)
|
41
62
|
end
|
42
|
-
end
|
43
63
|
|
44
|
-
|
45
|
-
|
64
|
+
@fixture_connection_pools.each do |pool|
|
65
|
+
pool.pin_connection!(lock_threads)
|
66
|
+
pool.lease_connection
|
67
|
+
end
|
68
|
+
|
69
|
+
# When connections are established in the future, begin a transaction too
|
70
|
+
@connection_subscriber = ::ActiveSupport::Notifications
|
71
|
+
.subscribe("!connection.active_record") do |_, _, _, _, payload|
|
72
|
+
connection_name = payload[:connection_name] if payload.key?(:connection_name)
|
73
|
+
shard = payload[:shard] if payload.key?(:shard)
|
74
|
+
|
75
|
+
# INST: filter by FORBIDDEN_DB_ENVS
|
76
|
+
if connection_name && !FORBIDDEN_DB_ENVS.include?(shard)
|
77
|
+
pool = ::ActiveRecord::Base.connection_handler.retrieve_connection_pool(connection_name, shard: shard)
|
78
|
+
if pool
|
79
|
+
setup_shared_connection_pool
|
46
80
|
|
47
|
-
|
48
|
-
|
49
|
-
|
81
|
+
unless @fixture_connection_pools.include?(pool)
|
82
|
+
pool.pin_connection!(lock_threads)
|
83
|
+
pool.lease_connection
|
84
|
+
@fixture_connection_pools << pool
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
50
90
|
end
|
51
91
|
end
|
52
92
|
end
|
@@ -121,7 +121,7 @@ module Switchman
|
|
121
121
|
Shard.sharded_models.each do |klass|
|
122
122
|
self.class.all_roles.each do |role|
|
123
123
|
klass.connection_handler.remove_connection_pool(klass.connection_specification_name,
|
124
|
-
role
|
124
|
+
role:,
|
125
125
|
shard: id.to_sym)
|
126
126
|
end
|
127
127
|
end
|
@@ -218,7 +218,7 @@ module Switchman
|
|
218
218
|
if config_create_statement
|
219
219
|
create_commands = Array(config_create_statement).dup
|
220
220
|
create_statement = lambda {
|
221
|
-
create_commands.map { |statement| format(statement, name
|
221
|
+
create_commands.map { |statement| format(statement, name:, password:) }
|
222
222
|
}
|
223
223
|
end
|
224
224
|
|
@@ -236,8 +236,8 @@ module Switchman
|
|
236
236
|
self.class.creating_new_shard = true
|
237
237
|
DatabaseServer.send(:reference_role, :deploy)
|
238
238
|
::ActiveRecord::Base.connected_to(shard: self.id.to_sym, role: :deploy) do
|
239
|
-
shard = Shard.create!(id
|
240
|
-
name
|
239
|
+
shard = Shard.create!(id:,
|
240
|
+
name:,
|
241
241
|
database_server_id: self.id)
|
242
242
|
if create_statement
|
243
243
|
if ::ActiveRecord::Base.connection.select_value(
|
data/lib/switchman/engine.rb
CHANGED
@@ -53,6 +53,14 @@ module Switchman
|
|
53
53
|
::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool)
|
54
54
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache)
|
55
55
|
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter)
|
56
|
+
# https://github.com/rails/rails/commit/0016280f4fde55d96738887093dc333aae0d107b
|
57
|
+
if ::Rails.version < "7.2"
|
58
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter::ClassMethods)
|
59
|
+
else
|
60
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.singleton_class.prepend(
|
61
|
+
ActiveRecord::PostgreSQLAdapter::ClassMethods
|
62
|
+
)
|
63
|
+
end
|
56
64
|
|
57
65
|
::ActiveRecord::DatabaseConfigurations.prepend(ActiveRecord::DatabaseConfigurations)
|
58
66
|
::ActiveRecord::DatabaseConfigurations::DatabaseConfig.prepend(
|
@@ -79,6 +87,7 @@ module Switchman
|
|
79
87
|
::ActiveRecord::Relation.include(ActiveRecord::QueryMethods)
|
80
88
|
::ActiveRecord::Relation.prepend(GuardRail::Relation)
|
81
89
|
::ActiveRecord::Relation.prepend(ActiveRecord::Relation)
|
90
|
+
::ActiveRecord::Relation.prepend(ActiveRecord::Relation::InsertUpsertAll) if ::Rails.version >= "7.2"
|
82
91
|
::ActiveRecord::Relation.include(ActiveRecord::SpawnMethods)
|
83
92
|
::ActiveRecord::Relation.include(CallSuper)
|
84
93
|
|
data/lib/switchman/shard.rb
CHANGED
@@ -5,8 +5,10 @@ module Switchman
|
|
5
5
|
# ten trillion possible ids per shard. yup.
|
6
6
|
IDS_PER_SHARD = 10_000_000_000_000
|
7
7
|
|
8
|
+
# rubocop:disable Style/SymbolProc -- transforming to a lambda produces "no receiver given"
|
8
9
|
# only allow one default
|
9
10
|
validates_uniqueness_of :default, if: ->(s) { s.default? }
|
11
|
+
# rubocop:enable Style/SymbolProc
|
10
12
|
|
11
13
|
after_save :clear_cache
|
12
14
|
after_destroy :clear_cache
|
@@ -51,7 +53,7 @@ module Switchman
|
|
51
53
|
return [default] unless default.is_a?(Switchman::Shard)
|
52
54
|
return all if !Switchman.region || DatabaseServer.none?(&:region)
|
53
55
|
|
54
|
-
in_region(Switchman.region, include_regionless:
|
56
|
+
in_region(Switchman.region, include_regionless:)
|
55
57
|
end)
|
56
58
|
|
57
59
|
class << self
|
@@ -141,7 +143,7 @@ module Switchman
|
|
141
143
|
|
142
144
|
unless cached_shards.key?(id)
|
143
145
|
cached_shards[id] = Shard.default.activate do
|
144
|
-
find_cached(["shard", id]) { find_by(id:
|
146
|
+
find_cached(["shard", id]) { find_by(id:) }
|
145
147
|
end
|
146
148
|
end
|
147
149
|
cached_shards[id]
|
@@ -233,7 +235,7 @@ module Switchman
|
|
233
235
|
Switchman.config[:on_fork_proc]&.call
|
234
236
|
with_each_shard(subscope,
|
235
237
|
classes,
|
236
|
-
exception
|
238
|
+
exception:,
|
237
239
|
output: output || :decorated) do
|
238
240
|
last_description = Shard.current.description
|
239
241
|
Parallel::ResultWrapper.new(yield)
|
@@ -459,7 +461,7 @@ module Switchman
|
|
459
461
|
connects_to_hash.each do |(db_name, role_hash)|
|
460
462
|
role_hash.each_key do |role|
|
461
463
|
role_hash.delete(role) if klass.connection_handler.retrieve_connection_pool(
|
462
|
-
klass.connection_specification_name, role
|
464
|
+
klass.connection_specification_name, role:, shard: db_name
|
463
465
|
)
|
464
466
|
end
|
465
467
|
end
|
@@ -590,9 +592,9 @@ module Switchman
|
|
590
592
|
id
|
591
593
|
end
|
592
594
|
|
593
|
-
def activate(*classes, &
|
595
|
+
def activate(*classes, &)
|
594
596
|
shards = hashify_classes(classes)
|
595
|
-
Shard.activate(shards, &
|
597
|
+
Shard.activate(shards, &)
|
596
598
|
end
|
597
599
|
|
598
600
|
# for use from console ONLY
|
@@ -63,7 +63,7 @@ module Switchman
|
|
63
63
|
|
64
64
|
def find_existing_test_shard(server, name)
|
65
65
|
if server == Shard.default.database_server
|
66
|
-
server.shards.where(name:
|
66
|
+
server.shards.where(name:).first
|
67
67
|
else
|
68
68
|
shard = Shard.where("database_server_id IS NOT NULL AND name=?", name).first
|
69
69
|
# if somehow databases got created in a different order, change the shard to match
|
data/lib/switchman/version.rb
CHANGED
data/lib/tasks/switchman.rake
CHANGED
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: switchman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0
|
4
|
+
version: 4.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cody Cutrer
|
8
8
|
- James Williams
|
9
9
|
- Jacob Fugal
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2025-
|
13
|
+
date: 2025-03-21 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: '7.0'
|
22
22
|
- - "<"
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: '7.
|
24
|
+
version: '7.3'
|
25
25
|
type: :runtime
|
26
26
|
prerelease: false
|
27
27
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -31,7 +31,7 @@ dependencies:
|
|
31
31
|
version: '7.0'
|
32
32
|
- - "<"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '7.
|
34
|
+
version: '7.3'
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: guardrail
|
37
37
|
requirement: !ruby/object:Gem::Requirement
|
@@ -69,7 +69,7 @@ dependencies:
|
|
69
69
|
version: '7.0'
|
70
70
|
- - "<"
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version: '7.
|
72
|
+
version: '7.3'
|
73
73
|
type: :runtime
|
74
74
|
prerelease: false
|
75
75
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -79,7 +79,7 @@ dependencies:
|
|
79
79
|
version: '7.0'
|
80
80
|
- - "<"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '7.
|
82
|
+
version: '7.3'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: debug
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -198,14 +198,28 @@ dependencies:
|
|
198
198
|
requirements:
|
199
199
|
- - "~>"
|
200
200
|
- !ruby/object:Gem::Version
|
201
|
-
version: '
|
201
|
+
version: '3.0'
|
202
202
|
type: :development
|
203
203
|
prerelease: false
|
204
204
|
version_requirements: !ruby/object:Gem::Requirement
|
205
205
|
requirements:
|
206
206
|
- - "~>"
|
207
207
|
- !ruby/object:Gem::Version
|
208
|
-
version: '
|
208
|
+
version: '3.0'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: rubocop-rspec_rails
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - "~>"
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '2.29'
|
216
|
+
type: :development
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - "~>"
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '2.29'
|
209
223
|
- !ruby/object:Gem::Dependency
|
210
224
|
name: simplecov
|
211
225
|
requirement: !ruby/object:Gem::Requirement
|
@@ -290,7 +304,7 @@ licenses:
|
|
290
304
|
metadata:
|
291
305
|
rubygems_mfa_required: 'true'
|
292
306
|
source_code_uri: https://github.com/instructure/switchman
|
293
|
-
post_install_message:
|
307
|
+
post_install_message:
|
294
308
|
rdoc_options: []
|
295
309
|
require_paths:
|
296
310
|
- lib
|
@@ -298,15 +312,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
298
312
|
requirements:
|
299
313
|
- - ">="
|
300
314
|
- !ruby/object:Gem::Version
|
301
|
-
version: '3.
|
315
|
+
version: '3.1'
|
302
316
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
303
317
|
requirements:
|
304
318
|
- - ">="
|
305
319
|
- !ruby/object:Gem::Version
|
306
320
|
version: '0'
|
307
321
|
requirements: []
|
308
|
-
rubygems_version: 3.
|
309
|
-
signing_key:
|
322
|
+
rubygems_version: 3.3.27
|
323
|
+
signing_key:
|
310
324
|
specification_version: 4
|
311
325
|
summary: Rails sharding magic
|
312
326
|
test_files: []
|