switchman 1.15.0 → 2.0.1
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/app/models/switchman/shard.rb +47 -22
- data/db/migrate/20130328224244_create_default_shard.rb +1 -1
- data/lib/switchman.rb +1 -1
- data/lib/switchman/active_record/abstract_adapter.rb +2 -6
- data/lib/switchman/active_record/base.rb +18 -9
- data/lib/switchman/active_record/connection_handler.rb +6 -14
- data/lib/switchman/active_record/connection_pool.rb +0 -12
- data/lib/switchman/active_record/migration.rb +9 -0
- data/lib/switchman/active_record/persistence.rb +1 -1
- data/lib/switchman/active_record/postgresql_adapter.rb +10 -21
- data/lib/switchman/active_record/relation.rb +5 -5
- data/lib/switchman/active_record/statement_cache.rb +3 -16
- data/lib/switchman/active_record/table_definition.rb +2 -2
- data/lib/switchman/active_support/cache.rb +2 -2
- data/lib/switchman/connection_pool_proxy.rb +11 -11
- data/lib/switchman/database_server.rb +16 -16
- data/lib/switchman/engine.rb +9 -8
- data/lib/switchman/{shackles.rb → guard_rail.rb} +4 -4
- data/lib/switchman/{shackles → guard_rail}/relation.rb +5 -5
- data/lib/switchman/r_spec_helper.rb +5 -5
- data/lib/switchman/sharded_instrumenter.rb +1 -1
- data/lib/switchman/test_helper.rb +3 -3
- data/lib/switchman/version.rb +1 -1
- data/lib/tasks/switchman.rake +6 -13
- 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: 0d6fc62ea8d63155621448b9dc70e5aab0bfa3e15b766ad6bc8b7282e124933b
|
4
|
+
data.tar.gz: 210c939e4c6a5c1c4b340e81c91fa68c0e019adeabf7dd0500f42eaa3605ad1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 780cc1c19606c00ecfb8df648a3f2d1e9744f375ff0a1e7793482108f81221f313254e16873db977af14e19adb0cb3d1bd9af3318c15c92ba3e54e01a91eb072
|
7
|
+
data.tar.gz: fe688c62be4f0b5e36279b93925377d3165d238a19c37124d90d464b9eac08691f2b8f0f550e56ccf5dd092cd872a3bf1ec23780fc719b8b2cf3a62539a06113
|
@@ -38,7 +38,10 @@ module Switchman
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def default(reload_deprecated = false, reload: false, with_fallback: false)
|
41
|
-
|
41
|
+
if reload_deprecated
|
42
|
+
reload = reload_deprecated
|
43
|
+
::ActiveSupport::Deprecation.warn("positional reload parameter to Switchman::Shard.default is deprecated; use `reload: true`")
|
44
|
+
end
|
42
45
|
if !@default || reload
|
43
46
|
# Have to create a dummy object so that several key methods still work
|
44
47
|
# (it's easier to do this in one place here, and just assume that sharding
|
@@ -351,8 +354,8 @@ module Switchman
|
|
351
354
|
# prune the prior connection unless it happened to be the same
|
352
355
|
if previous_shard && shard != previous_shard && !previous_shard.database_server.shareable?
|
353
356
|
previous_shard.activate do
|
354
|
-
::
|
355
|
-
::
|
357
|
+
::GuardRail.activated_environments.each do |env|
|
358
|
+
::GuardRail.activate(env) do
|
356
359
|
if ::ActiveRecord::Base.connected? && ::ActiveRecord::Base.connection.open_transactions == 0
|
357
360
|
::ActiveRecord::Base.connection_pool.current_pool.disconnect!
|
358
361
|
end
|
@@ -425,6 +428,19 @@ module Switchman
|
|
425
428
|
end
|
426
429
|
end
|
427
430
|
|
431
|
+
# it's tedious to hold onto this same
|
432
|
+
# kind of sign state and transform the
|
433
|
+
# result in multiple places, so
|
434
|
+
# here we can operate on the absolute value
|
435
|
+
# in a provided block and trust the sign will
|
436
|
+
# stay as provided. This assumes no consumer
|
437
|
+
# will return a nil value from the block.
|
438
|
+
def signed_id_operation(input_id)
|
439
|
+
sign = input_id < 0 ? -1 : 1
|
440
|
+
output = yield input_id.abs
|
441
|
+
output * sign
|
442
|
+
end
|
443
|
+
|
428
444
|
# converts an AR object, integral id, string id, or string short-global-id to a
|
429
445
|
# integral id. nil if it can't be interpreted
|
430
446
|
def integral_id_for(any_id)
|
@@ -437,12 +453,13 @@ module Switchman
|
|
437
453
|
case any_id
|
438
454
|
when ::ActiveRecord::Base
|
439
455
|
any_id.id
|
440
|
-
when /^(\d+)~(
|
456
|
+
when /^(\d+)~(-?\d+)$/
|
441
457
|
local_id = $2.to_i
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
458
|
+
signed_id_operation(local_id) do |id|
|
459
|
+
return nil if id > IDS_PER_SHARD
|
460
|
+
$1.to_i * IDS_PER_SHARD + id
|
461
|
+
end
|
462
|
+
when Integer, /^-?\d+$/
|
446
463
|
any_id.to_i
|
447
464
|
else
|
448
465
|
nil
|
@@ -457,13 +474,17 @@ module Switchman
|
|
457
474
|
def local_id_for(any_id)
|
458
475
|
id = integral_id_for(any_id)
|
459
476
|
return NIL_NIL_ID unless id
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
477
|
+
return_shard = nil
|
478
|
+
local_id = signed_id_operation(id) do |abs_id|
|
479
|
+
if abs_id < IDS_PER_SHARD
|
480
|
+
abs_id
|
481
|
+
elsif return_shard = lookup(abs_id / IDS_PER_SHARD)
|
482
|
+
abs_id % IDS_PER_SHARD
|
483
|
+
else
|
484
|
+
return NIL_NIL_ID
|
485
|
+
end
|
466
486
|
end
|
487
|
+
[local_id, return_shard]
|
467
488
|
end
|
468
489
|
|
469
490
|
# takes an id-ish, and returns an integral id relative to
|
@@ -494,11 +515,13 @@ module Switchman
|
|
494
515
|
def global_id_for(any_id, source_shard = nil)
|
495
516
|
id = integral_id_for(any_id)
|
496
517
|
return any_id unless id
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
518
|
+
signed_id_operation(id) do |abs_id|
|
519
|
+
if abs_id >= IDS_PER_SHARD
|
520
|
+
abs_id
|
521
|
+
else
|
522
|
+
source_shard ||= Shard.current
|
523
|
+
source_shard.global_id_for(abs_id)
|
524
|
+
end
|
502
525
|
end
|
503
526
|
end
|
504
527
|
|
@@ -639,7 +662,7 @@ module Switchman
|
|
639
662
|
case adapter
|
640
663
|
when 'mysql', 'mysql2'
|
641
664
|
self.activate do
|
642
|
-
::
|
665
|
+
::GuardRail.activate(:deploy) do
|
643
666
|
drop_statement ||= "DROP DATABASE #{self.name}"
|
644
667
|
Array(drop_statement).each do |stmt|
|
645
668
|
::ActiveRecord::Base.connection.execute(stmt)
|
@@ -648,7 +671,7 @@ module Switchman
|
|
648
671
|
end
|
649
672
|
when 'postgresql'
|
650
673
|
self.activate do
|
651
|
-
::
|
674
|
+
::GuardRail.activate(:deploy) do
|
652
675
|
# Shut up, Postgres!
|
653
676
|
conn = ::ActiveRecord::Base.connection
|
654
677
|
old_proc = conn.raw_connection.set_notice_processor {}
|
@@ -671,7 +694,9 @@ module Switchman
|
|
671
694
|
# takes an id local to this shard, and returns a global id
|
672
695
|
def global_id_for(local_id)
|
673
696
|
return nil unless local_id
|
674
|
-
|
697
|
+
self.class.signed_id_operation(local_id) do |abs_id|
|
698
|
+
abs_id + self.id * IDS_PER_SHARD
|
699
|
+
end
|
675
700
|
end
|
676
701
|
|
677
702
|
# skip global_id.hash
|
@@ -3,7 +3,7 @@ class CreateDefaultShard < ActiveRecord::Migration[4.2]
|
|
3
3
|
unless Switchman::Shard.default.is_a?(Switchman::Shard)
|
4
4
|
Switchman::Shard.reset_column_information
|
5
5
|
Switchman::Shard.create!(:default => true)
|
6
|
-
Switchman::Shard.default(true)
|
6
|
+
Switchman::Shard.default(reload: true)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
data/lib/switchman.rb
CHANGED
@@ -4,8 +4,8 @@ module Switchman
|
|
4
4
|
module ActiveRecord
|
5
5
|
module AbstractAdapter
|
6
6
|
module ForeignKeyCheck
|
7
|
-
def add_column(table, name, type,
|
8
|
-
Engine.foreign_key_check(name, type,
|
7
|
+
def add_column(table, name, type, limit: nil, **)
|
8
|
+
Engine.foreign_key_check(name, type, limit: limit)
|
9
9
|
super
|
10
10
|
end
|
11
11
|
end
|
@@ -27,10 +27,6 @@ module Switchman
|
|
27
27
|
quote_table_name(name)
|
28
28
|
end
|
29
29
|
|
30
|
-
def use_qualified_names?
|
31
|
-
false
|
32
|
-
end
|
33
|
-
|
34
30
|
protected
|
35
31
|
|
36
32
|
def log(*args, &block)
|
@@ -31,20 +31,20 @@ module Switchman
|
|
31
31
|
@integral_id
|
32
32
|
end
|
33
33
|
|
34
|
-
def transaction(
|
34
|
+
def transaction(**)
|
35
35
|
if self != ::ActiveRecord::Base && current_scope
|
36
36
|
current_scope.activate do
|
37
37
|
db = Shard.current(shard_category).database_server
|
38
|
-
if ::
|
39
|
-
db.
|
38
|
+
if ::GuardRail.environment != db.guard_rail_environment
|
39
|
+
db.unguard { super }
|
40
40
|
else
|
41
41
|
super
|
42
42
|
end
|
43
43
|
end
|
44
44
|
else
|
45
45
|
db = Shard.current(shard_category).database_server
|
46
|
-
if ::
|
47
|
-
db.
|
46
|
+
if ::GuardRail.environment != db.guard_rail_environment
|
47
|
+
db.unguard { super }
|
48
48
|
else
|
49
49
|
super
|
50
50
|
end
|
@@ -105,12 +105,12 @@ module Switchman
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
108
|
-
def save(
|
108
|
+
def save(*, **)
|
109
109
|
@shard_set_in_stone = true
|
110
110
|
(self.class.current_scope || self.class.default_scoped).shard(shard, :implicit).scoping { super }
|
111
111
|
end
|
112
112
|
|
113
|
-
def save!(
|
113
|
+
def save!(*, **)
|
114
114
|
@shard_set_in_stone = true
|
115
115
|
(self.class.current_scope || self.class.default_scoped).shard(shard, :implicit).scoping { super }
|
116
116
|
end
|
@@ -128,9 +128,9 @@ module Switchman
|
|
128
128
|
result
|
129
129
|
end
|
130
130
|
|
131
|
-
def transaction(
|
131
|
+
def transaction(**kwargs, &block)
|
132
132
|
shard.activate(self.class.shard_category) do
|
133
|
-
self.class.transaction(
|
133
|
+
self.class.transaction(**kwargs, &block)
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
@@ -155,6 +155,15 @@ module Switchman
|
|
155
155
|
self.class.connection.quote(id)
|
156
156
|
end
|
157
157
|
|
158
|
+
def update_columns(*)
|
159
|
+
db = Shard.current(self.class.shard_category).database_server
|
160
|
+
if ::GuardRail.environment != db.guard_rail_environment
|
161
|
+
return db.unguard { super }
|
162
|
+
else
|
163
|
+
super
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
158
167
|
protected
|
159
168
|
|
160
169
|
# see also AttributeMethods#shard_category_code_for_reflection
|
@@ -4,8 +4,6 @@ module Switchman
|
|
4
4
|
module ActiveRecord
|
5
5
|
module ConnectionHandler
|
6
6
|
def self.make_sharing_automagic(config, shard = Shard.current)
|
7
|
-
key = config[:adapter] == 'postgresql' ? :schema_search_path : :database
|
8
|
-
|
9
7
|
# only load the shard name from the db if we have to
|
10
8
|
if !config[:shard_name]
|
11
9
|
# we may not be able to connect to this shard yet, cause it might be an empty database server
|
@@ -15,12 +13,6 @@ module Switchman
|
|
15
13
|
|
16
14
|
config[:shard_name] ||= shard_name
|
17
15
|
end
|
18
|
-
|
19
|
-
if !config[key] || config[key] == shard_name
|
20
|
-
# this may truncate the schema_search_path if it was not specified in database.yml
|
21
|
-
# but that's what our old behavior was anyway, so I guess it's okay
|
22
|
-
config[key] = '%{shard_name}'
|
23
|
-
end
|
24
16
|
end
|
25
17
|
|
26
18
|
def establish_connection(spec)
|
@@ -63,7 +55,7 @@ module Switchman
|
|
63
55
|
Shard.default.remove_instance_variable(:@name) if Shard.default.instance_variable_defined?(:@name)
|
64
56
|
end
|
65
57
|
|
66
|
-
@shard_connection_pools ||= { [:
|
58
|
+
@shard_connection_pools ||= { [:primary, Shard.default.database_server.shareable? ? ::Rails.env : Shard.default] => pool}
|
67
59
|
|
68
60
|
category = pool.spec.name.to_sym
|
69
61
|
proxy = ConnectionPoolProxy.new(category,
|
@@ -72,13 +64,13 @@ module Switchman
|
|
72
64
|
owner_to_pool[pool.spec.name] = proxy
|
73
65
|
|
74
66
|
if first_time
|
75
|
-
if Shard.default.database_server.config[:
|
76
|
-
Shard.default.database_server.
|
67
|
+
if Shard.default.database_server.config[:prefer_secondary]
|
68
|
+
Shard.default.database_server.guard!
|
77
69
|
end
|
78
70
|
|
79
|
-
if Shard.default.is_a?(DefaultShard) && Shard.default.database_server.config[:
|
80
|
-
Shard.default.database_server.
|
81
|
-
Shard.default(true)
|
71
|
+
if Shard.default.is_a?(DefaultShard) && Shard.default.database_server.config[:secondary]
|
72
|
+
Shard.default.database_server.guard!
|
73
|
+
Shard.default(reload: true)
|
82
74
|
end
|
83
75
|
end
|
84
76
|
|
@@ -84,18 +84,6 @@ module Switchman
|
|
84
84
|
end
|
85
85
|
|
86
86
|
spec.config[:shard_name] = self.shard.name
|
87
|
-
case conn.adapter_name
|
88
|
-
when 'MySQL', 'Mysql2'
|
89
|
-
conn.execute("USE #{spec.config[:database]}")
|
90
|
-
when 'PostgreSQL'
|
91
|
-
if conn.schema_search_path != spec.config[:schema_search_path]
|
92
|
-
conn.schema_search_path = spec.config[:schema_search_path]
|
93
|
-
end
|
94
|
-
when 'SQLite'
|
95
|
-
# This is an artifact of the adapter modifying the path to be an absolute path when it is instantiated; just let it slide
|
96
|
-
else
|
97
|
-
raise("Cannot switch databases on same DatabaseServer with adapter type: #{conn.adapter_name}. Limit one Shard per DatabaseServer.")
|
98
|
-
end
|
99
87
|
conn.shard = shard
|
100
88
|
end
|
101
89
|
|
@@ -33,5 +33,14 @@ module Switchman
|
|
33
33
|
::ActiveRecord::Migrator::MIGRATOR_SALT * shard_name_hash
|
34
34
|
end
|
35
35
|
end
|
36
|
+
|
37
|
+
module MigrationContext
|
38
|
+
def migrations
|
39
|
+
return @migrations if instance_variable_defined?(:@migrations)
|
40
|
+
migrations_cache = Thread.current[:migrations_cache] ||= {}
|
41
|
+
key = Digest::MD5.hexdigest(migration_files.sort.join(','))
|
42
|
+
@migrations = migrations_cache[key] ||= super
|
43
|
+
end
|
44
|
+
end
|
36
45
|
end
|
37
46
|
end
|
@@ -38,23 +38,17 @@ module Switchman
|
|
38
38
|
select_values("SELECT * FROM unnest(current_schemas(false))")
|
39
39
|
end
|
40
40
|
|
41
|
-
def use_qualified_names?
|
42
|
-
@config[:use_qualified_names]
|
43
|
-
end
|
44
|
-
|
45
41
|
def tables(name = nil)
|
46
|
-
schema = shard.name if use_qualified_names?
|
47
|
-
|
48
42
|
query(<<-SQL, 'SCHEMA').map { |row| row[0] }
|
49
43
|
SELECT tablename
|
50
44
|
FROM pg_tables
|
51
|
-
WHERE schemaname =
|
45
|
+
WHERE schemaname = '#{shard.name}'
|
52
46
|
SQL
|
53
47
|
end
|
54
48
|
|
55
49
|
def extract_schema_qualified_name(string)
|
56
50
|
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(string.to_s)
|
57
|
-
if string && !name.schema
|
51
|
+
if string && !name.schema
|
58
52
|
name.instance_variable_set(:@schema, shard.name)
|
59
53
|
end
|
60
54
|
[name.schema, name.identifier]
|
@@ -63,7 +57,7 @@ module Switchman
|
|
63
57
|
def view_exists?(name)
|
64
58
|
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
|
65
59
|
return false unless name.identifier
|
66
|
-
if !name.schema
|
60
|
+
if !name.schema
|
67
61
|
name.instance_variable_set(:@schema, shard.name)
|
68
62
|
end
|
69
63
|
|
@@ -73,13 +67,11 @@ module Switchman
|
|
73
67
|
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
74
68
|
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
|
75
69
|
AND c.relname = '#{name.identifier}'
|
76
|
-
AND n.nspname = #{
|
70
|
+
AND n.nspname = '#{shard.name}'
|
77
71
|
SQL
|
78
72
|
end
|
79
73
|
|
80
74
|
def indexes(table_name)
|
81
|
-
schema = shard.name if use_qualified_names?
|
82
|
-
|
83
75
|
result = query(<<-SQL, 'SCHEMA')
|
84
76
|
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
|
85
77
|
FROM pg_class t
|
@@ -88,7 +80,7 @@ module Switchman
|
|
88
80
|
WHERE i.relkind = 'i'
|
89
81
|
AND d.indisprimary = 'f'
|
90
82
|
AND t.relname = '#{table_name}'
|
91
|
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname =
|
83
|
+
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
|
92
84
|
ORDER BY i.relname
|
93
85
|
SQL
|
94
86
|
|
@@ -126,8 +118,6 @@ module Switchman
|
|
126
118
|
end
|
127
119
|
|
128
120
|
def index_name_exists?(table_name, index_name, _default = nil)
|
129
|
-
schema = shard.name if use_qualified_names?
|
130
|
-
|
131
121
|
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
132
122
|
SELECT COUNT(*)
|
133
123
|
FROM pg_class t
|
@@ -136,7 +126,7 @@ module Switchman
|
|
136
126
|
WHERE i.relkind = 'i'
|
137
127
|
AND i.relname = '#{index_name}'
|
138
128
|
AND t.relname = '#{table_name}'
|
139
|
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname =
|
129
|
+
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
|
140
130
|
SQL
|
141
131
|
end
|
142
132
|
|
@@ -150,7 +140,7 @@ module Switchman
|
|
150
140
|
def quote_table_name(name)
|
151
141
|
return quote_local_table_name(name) if @use_local_table_name
|
152
142
|
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
|
153
|
-
if !name.schema
|
143
|
+
if !name.schema
|
154
144
|
name.instance_variable_set(:@schema, shard.name)
|
155
145
|
end
|
156
146
|
name.quoted
|
@@ -165,7 +155,6 @@ module Switchman
|
|
165
155
|
end
|
166
156
|
|
167
157
|
def foreign_keys(table_name)
|
168
|
-
schema = shard.name if use_qualified_names?
|
169
158
|
|
170
159
|
# mostly copy-pasted from AR - only change is to the nspname condition for qualified names support
|
171
160
|
fk_info = select_all <<-SQL.strip_heredoc
|
@@ -178,7 +167,7 @@ module Switchman
|
|
178
167
|
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
179
168
|
WHERE c.contype = 'f'
|
180
169
|
AND t1.relname = #{quote(table_name)}
|
181
|
-
AND t3.nspname =
|
170
|
+
AND t3.nspname = '#{shard.name}'
|
182
171
|
ORDER BY c.conname
|
183
172
|
SQL
|
184
173
|
|
@@ -195,7 +184,7 @@ module Switchman
|
|
195
184
|
# strip the schema name from to_table if it matches
|
196
185
|
to_table = row['to_table']
|
197
186
|
to_table_qualified_name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(to_table)
|
198
|
-
if
|
187
|
+
if to_table_qualified_name.schema == shard.name
|
199
188
|
to_table = to_table_qualified_name.identifier
|
200
189
|
end
|
201
190
|
|
@@ -203,7 +192,7 @@ module Switchman
|
|
203
192
|
end
|
204
193
|
end
|
205
194
|
|
206
|
-
def add_index_options(_table_name, _column_name,
|
195
|
+
def add_index_options(_table_name, _column_name, **)
|
207
196
|
index_name, index_type, index_columns, index_options, algorithm, using = super
|
208
197
|
algorithm = nil if DatabaseServer.creating_new_shard && algorithm == "CONCURRENTLY"
|
209
198
|
[index_name, index_type, index_columns, index_options, algorithm, using]
|
@@ -5,7 +5,7 @@ module Switchman
|
|
5
5
|
klass::SINGLE_VALUE_METHODS.concat [ :shard, :shard_source ]
|
6
6
|
end
|
7
7
|
|
8
|
-
def initialize(
|
8
|
+
def initialize(*, **)
|
9
9
|
super
|
10
10
|
self.shard_value = Shard.current(klass ? klass.shard_category : :primary) unless shard_value
|
11
11
|
self.shard_source_value = :implicit unless shard_source_value
|
@@ -17,7 +17,7 @@ module Switchman
|
|
17
17
|
result
|
18
18
|
end
|
19
19
|
|
20
|
-
def merge(*
|
20
|
+
def merge(*)
|
21
21
|
relation = super
|
22
22
|
if relation.shard_value != self.shard_value && relation.shard_source_value == :implicit
|
23
23
|
relation.shard_value = self.shard_value
|
@@ -26,15 +26,15 @@ module Switchman
|
|
26
26
|
relation
|
27
27
|
end
|
28
28
|
|
29
|
-
def new(
|
29
|
+
def new(*, &block)
|
30
30
|
primary_shard.activate(klass.shard_category) { super }
|
31
31
|
end
|
32
32
|
|
33
|
-
def create(
|
33
|
+
def create(*, &block)
|
34
34
|
primary_shard.activate(klass.shard_category) { super }
|
35
35
|
end
|
36
36
|
|
37
|
-
def create!(
|
37
|
+
def create!(*, &block)
|
38
38
|
primary_shard.activate(klass.shard_category) { super }
|
39
39
|
end
|
40
40
|
|
@@ -2,7 +2,7 @@ module Switchman
|
|
2
2
|
module ActiveRecord
|
3
3
|
module StatementCache
|
4
4
|
module ClassMethods
|
5
|
-
def create(connection, block
|
5
|
+
def create(connection, &block)
|
6
6
|
relation = block.call ::ActiveRecord::StatementCache::Params.new
|
7
7
|
|
8
8
|
if ::Rails.version >= "5.2"
|
@@ -46,29 +46,16 @@ module Switchman
|
|
46
46
|
bind_values = bind_map.bind(params, current_shard, target_shard)
|
47
47
|
|
48
48
|
target_shard.activate(klass.shard_category) do
|
49
|
-
|
50
|
-
|
51
|
-
klass.find_by_sql(sql, bind_values)
|
52
|
-
else
|
53
|
-
sql = generic_query_builder(connection).sql_for(bind_values, connection)
|
54
|
-
klass.find_by_sql(sql, bind_values)
|
55
|
-
end
|
49
|
+
sql = qualified_query_builder(target_shard, klass).sql_for(bind_values, connection)
|
50
|
+
klass.find_by_sql(sql, bind_values)
|
56
51
|
end
|
57
52
|
end
|
58
53
|
|
59
54
|
if ::Rails.version < '5.2'
|
60
|
-
def generic_query_builder(connection)
|
61
|
-
@query_builder ||= connection.cacheable_query(self.class, @arel)
|
62
|
-
end
|
63
|
-
|
64
55
|
def qualified_query_builder(shard, klass)
|
65
56
|
@qualified_query_builders[shard.id] ||= klass.connection.cacheable_query(self.class, @arel)
|
66
57
|
end
|
67
58
|
else
|
68
|
-
def generic_query_builder(connection)
|
69
|
-
@query_builder ||= connection.cacheable_query(self.class, @arel).first
|
70
|
-
end
|
71
|
-
|
72
59
|
def qualified_query_builder(shard, klass)
|
73
60
|
@qualified_query_builders[shard.id] ||= klass.connection.cacheable_query(self.class, @arel).first
|
74
61
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Switchman
|
2
2
|
module ActiveRecord
|
3
3
|
module TableDefinition
|
4
|
-
def column(name, type,
|
5
|
-
Engine.foreign_key_check(name, type,
|
4
|
+
def column(name, type, limit: nil, **)
|
5
|
+
Engine.foreign_key_check(name, type, limit: limit)
|
6
6
|
super
|
7
7
|
end
|
8
8
|
end
|
@@ -15,12 +15,12 @@ module Switchman
|
|
15
15
|
end
|
16
16
|
|
17
17
|
module RedisCacheStore
|
18
|
-
def clear(
|
18
|
+
def clear(namespace: nil, **)
|
19
19
|
# RedisCacheStore tries to be smart and only clear the cache under your namespace, if you have one set
|
20
20
|
# unfortunately, it uses the keys command, which is extraordinarily inefficient in a large redis instance
|
21
21
|
# fortunately, we can assume we control the entire instance, because we set up the namespacing, so just
|
22
22
|
# always unset it temporarily for clear calls
|
23
|
-
|
23
|
+
namespace = nil
|
24
24
|
super
|
25
25
|
end
|
26
26
|
end
|
@@ -37,13 +37,13 @@ module Switchman
|
|
37
37
|
Shard.current(@category)
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
41
|
-
::Rails.env.test? ? :
|
40
|
+
def active_guard_rail_environment
|
41
|
+
::Rails.env.test? ? :primary : active_shard.database_server.guard_rail_environment
|
42
42
|
end
|
43
43
|
|
44
44
|
def current_pool
|
45
45
|
current_active_shard = active_shard
|
46
|
-
pool = self.default_pool if current_active_shard.database_server == Shard.default.database_server &&
|
46
|
+
pool = self.default_pool if current_active_shard.database_server == Shard.default.database_server && active_guard_rail_environment == :primary && (current_active_shard.default? || current_active_shard.database_server.shareable?)
|
47
47
|
pool = @connection_pools[pool_key] ||= create_pool unless pool
|
48
48
|
pool.shard = current_active_shard
|
49
49
|
pool
|
@@ -60,8 +60,8 @@ module Switchman
|
|
60
60
|
connection.instance_variable_set(:@schema_cache, @schema_cache) unless ::Rails.version >= '6'
|
61
61
|
connection
|
62
62
|
rescue ConnectionError
|
63
|
-
raise if active_shard.database_server == Shard.default.database_server &&
|
64
|
-
configs = active_shard.database_server.config(
|
63
|
+
raise if active_shard.database_server == Shard.default.database_server && active_guard_rail_environment == :primary
|
64
|
+
configs = active_shard.database_server.config(active_guard_rail_environment)
|
65
65
|
raise unless configs.is_a?(Array)
|
66
66
|
configs.each_with_index do |config, idx|
|
67
67
|
pool = create_pool(config.dup)
|
@@ -120,7 +120,7 @@ module Switchman
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def pool_key
|
123
|
-
[
|
123
|
+
[active_guard_rail_environment,
|
124
124
|
active_shard.database_server.shareable? ? active_shard.database_server.pool_key : active_shard]
|
125
125
|
end
|
126
126
|
|
@@ -128,7 +128,7 @@ module Switchman
|
|
128
128
|
shard = active_shard
|
129
129
|
unless config
|
130
130
|
if shard != Shard.default
|
131
|
-
config = shard.database_server.config(
|
131
|
+
config = shard.database_server.config(active_guard_rail_environment)
|
132
132
|
config = config.first if config.is_a?(Array)
|
133
133
|
config = config.dup
|
134
134
|
else
|
@@ -138,10 +138,10 @@ module Switchman
|
|
138
138
|
# different models could be using different configs on the default
|
139
139
|
# shard, and database server wouldn't know about that
|
140
140
|
config = default_pool.spec.instance_variable_get(:@config)
|
141
|
-
if config[
|
142
|
-
config = config.merge(config[
|
143
|
-
elsif config[
|
144
|
-
config = config.merge(config[
|
141
|
+
if config[active_guard_rail_environment].is_a?(Hash)
|
142
|
+
config = config.merge(config[active_guard_rail_environment])
|
143
|
+
elsif config[active_guard_rail_environment].is_a?(Array)
|
144
|
+
config = config.merge(config[active_guard_rail_environment].first)
|
145
145
|
else
|
146
146
|
config = config.dup
|
147
147
|
end
|
@@ -69,12 +69,12 @@ module Switchman
|
|
69
69
|
@fake
|
70
70
|
end
|
71
71
|
|
72
|
-
def config(environment = :
|
72
|
+
def config(environment = :primary)
|
73
73
|
@configs[environment] ||= begin
|
74
74
|
if @config[environment].is_a?(Array)
|
75
75
|
@config[environment].map do |config|
|
76
76
|
config = @config.merge((config || {}).symbolize_keys)
|
77
|
-
# make sure
|
77
|
+
# make sure GuardRail doesn't get any brilliant ideas about choosing the first possible server
|
78
78
|
config.delete(environment)
|
79
79
|
config
|
80
80
|
end
|
@@ -86,33 +86,33 @@ module Switchman
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
def
|
90
|
-
@
|
89
|
+
def guard_rail_environment
|
90
|
+
@guard_rail_environment || ::GuardRail.environment
|
91
91
|
end
|
92
92
|
|
93
93
|
# locks this db to a specific environment, except for
|
94
94
|
# when doing writes (then it falls back to the current
|
95
|
-
# value of
|
96
|
-
def
|
97
|
-
@
|
95
|
+
# value of GuardRail.environment)
|
96
|
+
def guard!(environment = :secondary)
|
97
|
+
@guard_rail_environment = environment
|
98
98
|
end
|
99
99
|
|
100
|
-
def
|
101
|
-
@
|
100
|
+
def unguard!
|
101
|
+
@guard_rail_environment = nil
|
102
102
|
end
|
103
103
|
|
104
|
-
def
|
105
|
-
old_env = @
|
106
|
-
|
104
|
+
def unguard
|
105
|
+
old_env = @guard_rail_environment
|
106
|
+
unguard!
|
107
107
|
yield
|
108
108
|
ensure
|
109
|
-
|
109
|
+
guard!(old_env)
|
110
110
|
end
|
111
111
|
|
112
112
|
def shareable?
|
113
113
|
@shareable_environment_key ||= []
|
114
|
-
environment =
|
115
|
-
explicit_user = ::
|
114
|
+
environment = guard_rail_environment
|
115
|
+
explicit_user = ::GuardRail.global_config[:username]
|
116
116
|
return @shareable if @shareable_environment_key == [environment, explicit_user]
|
117
117
|
@shareable_environment_key = [environment, explicit_user]
|
118
118
|
if explicit_user
|
@@ -190,7 +190,7 @@ module Switchman
|
|
190
190
|
begin
|
191
191
|
self.class.creating_new_shard = true
|
192
192
|
shard.activate(*Shard.categories) do
|
193
|
-
::
|
193
|
+
::GuardRail.activate(:deploy) do
|
194
194
|
begin
|
195
195
|
if create_statement
|
196
196
|
if (::ActiveRecord::Base.connection.select_value("SELECT 1 FROM pg_namespace WHERE nspname=#{::ActiveRecord::Base.connection.quote(name)}"))
|
data/lib/switchman/engine.rb
CHANGED
@@ -87,7 +87,7 @@ module Switchman
|
|
87
87
|
require "switchman/arel"
|
88
88
|
require "switchman/call_super"
|
89
89
|
require "switchman/rails"
|
90
|
-
require "switchman/
|
90
|
+
require "switchman/guard_rail/relation"
|
91
91
|
require_dependency "switchman/shard"
|
92
92
|
require "switchman/standard_error"
|
93
93
|
|
@@ -122,6 +122,7 @@ module Switchman
|
|
122
122
|
::ActiveRecord::LogSubscriber.prepend(ActiveRecord::LogSubscriber)
|
123
123
|
::ActiveRecord::Migration.prepend(ActiveRecord::Migration)
|
124
124
|
::ActiveRecord::Migration::Compatibility::V5_0.prepend(ActiveRecord::Migration::Compatibility::V5_0)
|
125
|
+
::ActiveRecord::MigrationContext.prepend(ActiveRecord::MigrationContext) if ::Rails.version >= '5.2'
|
125
126
|
::ActiveRecord::Migrator.prepend(ActiveRecord::Migrator)
|
126
127
|
|
127
128
|
::ActiveRecord::Reflection::AbstractReflection.include(ActiveRecord::Reflection::AbstractReflection)
|
@@ -132,7 +133,7 @@ module Switchman
|
|
132
133
|
::ActiveRecord::Relation.prepend(ActiveRecord::Calculations)
|
133
134
|
::ActiveRecord::Relation.include(ActiveRecord::FinderMethods)
|
134
135
|
::ActiveRecord::Relation.include(ActiveRecord::QueryMethods)
|
135
|
-
::ActiveRecord::Relation.prepend(
|
136
|
+
::ActiveRecord::Relation.prepend(GuardRail::Relation)
|
136
137
|
::ActiveRecord::Relation.prepend(ActiveRecord::Relation)
|
137
138
|
::ActiveRecord::Relation.include(ActiveRecord::SpawnMethods)
|
138
139
|
::ActiveRecord::Relation.include(CallSuper)
|
@@ -149,8 +150,8 @@ module Switchman
|
|
149
150
|
end
|
150
151
|
end
|
151
152
|
|
152
|
-
def self.foreign_key_check(name, type,
|
153
|
-
if name.to_s =~ /_id\z/ && type.to_s == 'integer' &&
|
153
|
+
def self.foreign_key_check(name, type, limit: nil)
|
154
|
+
if name.to_s =~ /_id\z/ && type.to_s == 'integer' && limit.to_i < 8
|
154
155
|
puts "WARNING: All foreign keys need to be 8-byte integers. #{name} looks like a foreign key. If so, please add the option: `:limit => 8`"
|
155
156
|
end
|
156
157
|
end
|
@@ -184,15 +185,15 @@ module Switchman
|
|
184
185
|
end
|
185
186
|
end
|
186
187
|
|
187
|
-
initializer 'switchman.
|
188
|
+
initializer 'switchman.extend_guard_rail', :before => "switchman.extend_ar" do
|
188
189
|
::ActiveSupport.on_load(:active_record) do
|
189
|
-
require "switchman/
|
190
|
+
require "switchman/guard_rail"
|
190
191
|
|
191
|
-
::
|
192
|
+
::GuardRail.singleton_class.prepend(GuardRail::ClassMethods)
|
192
193
|
end
|
193
194
|
end
|
194
195
|
|
195
|
-
initializer 'switchman.extend_controller', :after => "
|
196
|
+
initializer 'switchman.extend_controller', :after => "guard_rail.extend_ar" do
|
196
197
|
::ActiveSupport.on_load(:action_controller) do
|
197
198
|
require "switchman/action_controller/caching"
|
198
199
|
|
@@ -1,17 +1,17 @@
|
|
1
1
|
module Switchman
|
2
|
-
module
|
2
|
+
module GuardRail
|
3
3
|
module ClassMethods
|
4
4
|
def self.prepended(klass)
|
5
5
|
klass.send(:remove_method, :ensure_handler)
|
6
6
|
end
|
7
7
|
|
8
8
|
# drops the save_handler and ensure_handler calls from the vanilla
|
9
|
-
#
|
9
|
+
# GuardRail' implementation.
|
10
10
|
def activate!(environment)
|
11
|
-
environment ||= :
|
11
|
+
environment ||= :primary
|
12
12
|
activated_environments << environment
|
13
13
|
old_environment = self.environment
|
14
|
-
Thread.current[:
|
14
|
+
Thread.current[:guard_rail_environment] = environment
|
15
15
|
old_environment
|
16
16
|
end
|
17
17
|
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module Switchman
|
2
|
-
module
|
2
|
+
module GuardRail
|
3
3
|
module Relation
|
4
4
|
def exec_queries(*args)
|
5
5
|
if self.lock_value
|
6
6
|
db = Shard.current(shard_category).database_server
|
7
|
-
if ::
|
8
|
-
return db.
|
7
|
+
if ::GuardRail.environment != db.guard_rail_environment
|
8
|
+
return db.unguard { super }
|
9
9
|
end
|
10
10
|
end
|
11
11
|
super
|
@@ -15,8 +15,8 @@ module Switchman
|
|
15
15
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
16
16
|
def #{method}(*args)
|
17
17
|
db = Shard.current(shard_category).database_server
|
18
|
-
if ::
|
19
|
-
db.
|
18
|
+
if ::GuardRail.environment != db.guard_rail_environment
|
19
|
+
db.unguard { super }
|
20
20
|
else
|
21
21
|
super
|
22
22
|
end
|
@@ -65,7 +65,7 @@ module Switchman
|
|
65
65
|
(@@shard3.drop_database if @@shard3) rescue nil
|
66
66
|
@@shard1 = @@shard2 = @@shard3 = nil
|
67
67
|
Shard.delete_all
|
68
|
-
Shard.default(true)
|
68
|
+
Shard.default(reload: true)
|
69
69
|
next
|
70
70
|
end
|
71
71
|
end
|
@@ -73,7 +73,7 @@ module Switchman
|
|
73
73
|
# in the db before then
|
74
74
|
Shard.delete_all
|
75
75
|
Switchman.cache.delete("default_shard")
|
76
|
-
Shard.default(true)
|
76
|
+
Shard.default(reload: true)
|
77
77
|
puts "Done!"
|
78
78
|
|
79
79
|
at_exit do
|
@@ -102,7 +102,7 @@ module Switchman
|
|
102
102
|
dup.id = @@default_shard.id
|
103
103
|
dup.save!
|
104
104
|
Switchman.cache.delete("default_shard")
|
105
|
-
Shard.default(true)
|
105
|
+
Shard.default(reload: true)
|
106
106
|
dup = @@shard1.dup
|
107
107
|
dup.id = @@shard1.id
|
108
108
|
dup.save!
|
@@ -122,7 +122,7 @@ module Switchman
|
|
122
122
|
raise "Sharding did not set up correctly" if @@sharding_failed
|
123
123
|
Shard.clear_cache
|
124
124
|
if use_transactional_tests
|
125
|
-
Shard.default(true)
|
125
|
+
Shard.default(reload: true)
|
126
126
|
@shard1 = Shard.find(@shard1.id)
|
127
127
|
@shard2 = Shard.find(@shard2.id)
|
128
128
|
shards = [@shard2]
|
@@ -151,7 +151,7 @@ module Switchman
|
|
151
151
|
klass.after(:all) do
|
152
152
|
Shard.connection.update("TRUNCATE #{Shard.quoted_table_name} CASCADE")
|
153
153
|
Switchman.cache.delete("default_shard")
|
154
|
-
Shard.default(true)
|
154
|
+
Shard.default(reload: true)
|
155
155
|
end
|
156
156
|
end
|
157
157
|
end
|
@@ -3,8 +3,8 @@ module Switchman
|
|
3
3
|
class << self
|
4
4
|
def recreate_persistent_test_shards(dont_create: false)
|
5
5
|
# recreate the default shard (it got buhleted)
|
6
|
-
::
|
7
|
-
if Shard.default(true).is_a?(DefaultShard)
|
6
|
+
::GuardRail.activate(:deploy) { Switchman.cache.clear }
|
7
|
+
if Shard.default(reload: true).is_a?(DefaultShard)
|
8
8
|
begin
|
9
9
|
Shard.create!(default: true)
|
10
10
|
rescue
|
@@ -12,7 +12,7 @@ module Switchman
|
|
12
12
|
# database doesn't exist yet, presumably cause we're creating it right now
|
13
13
|
return [nil, nil]
|
14
14
|
end
|
15
|
-
Shard.default(true)
|
15
|
+
Shard.default(reload: true)
|
16
16
|
end
|
17
17
|
|
18
18
|
# can't auto-create a new shard on the default shard's db server if the
|
data/lib/switchman/version.rb
CHANGED
data/lib/tasks/switchman.rake
CHANGED
@@ -64,8 +64,8 @@ module Switchman
|
|
64
64
|
TestHelper.recreate_persistent_test_shards(dont_create: true)
|
65
65
|
end
|
66
66
|
|
67
|
-
::
|
68
|
-
Shard.default.database_server.
|
67
|
+
::GuardRail.activate(:deploy) do
|
68
|
+
Shard.default.database_server.unguard do
|
69
69
|
begin
|
70
70
|
categories = categories.call if categories.respond_to?(:call)
|
71
71
|
Shard.with_each_shard(scope, categories, options) do
|
@@ -83,7 +83,7 @@ module Switchman
|
|
83
83
|
|
84
84
|
::ActiveRecord::Base.configurations = new_configs
|
85
85
|
end
|
86
|
-
shard.database_server.
|
86
|
+
shard.database_server.unguard do
|
87
87
|
old_actions.each { |action| action.call(*task_args) }
|
88
88
|
end
|
89
89
|
nil
|
@@ -218,16 +218,9 @@ module Switchman
|
|
218
218
|
args = ['-s', '-x', '-O', '-f', filename]
|
219
219
|
args.concat(Array(extra_flags)) if extra_flags
|
220
220
|
search_path = configuration['schema_search_path']
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
args << "--schema=#{Shellwords.escape(shard)}"
|
225
|
-
elsif !search_path.blank?
|
226
|
-
args << search_path.split(',').map do |part|
|
227
|
-
"--schema=#{part.strip}"
|
228
|
-
end.join(' ')
|
229
|
-
serialized_search_path = connection.schema_search_path
|
230
|
-
end
|
221
|
+
shard = Shard.current.name
|
222
|
+
serialized_search_path = shard
|
223
|
+
args << "--schema=#{Shellwords.escape(shard)}"
|
231
224
|
|
232
225
|
args << configuration['database']
|
233
226
|
run_cmd('pg_dump', args, 'dumping')
|
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
|
+
version: 2.0.1
|
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: 2020-
|
13
|
+
date: 2020-10-13 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: railties
|
@@ -53,19 +53,19 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '6.1'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: guardrail
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 2.0.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 2.0.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: open4
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: pry
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: pg
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -214,12 +228,12 @@ files:
|
|
214
228
|
- lib/switchman/engine.rb
|
215
229
|
- lib/switchman/environment.rb
|
216
230
|
- lib/switchman/errors.rb
|
231
|
+
- lib/switchman/guard_rail.rb
|
232
|
+
- lib/switchman/guard_rail/relation.rb
|
217
233
|
- lib/switchman/open4.rb
|
218
234
|
- lib/switchman/r_spec_helper.rb
|
219
235
|
- lib/switchman/rails.rb
|
220
236
|
- lib/switchman/schema_cache.rb
|
221
|
-
- lib/switchman/shackles.rb
|
222
|
-
- lib/switchman/shackles/relation.rb
|
223
237
|
- lib/switchman/sharded_instrumenter.rb
|
224
238
|
- lib/switchman/standard_error.rb
|
225
239
|
- lib/switchman/test_helper.rb
|
@@ -229,7 +243,7 @@ homepage: http://www.instructure.com/
|
|
229
243
|
licenses:
|
230
244
|
- MIT
|
231
245
|
metadata: {}
|
232
|
-
post_install_message:
|
246
|
+
post_install_message:
|
233
247
|
rdoc_options: []
|
234
248
|
require_paths:
|
235
249
|
- lib
|
@@ -237,15 +251,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
237
251
|
requirements:
|
238
252
|
- - ">="
|
239
253
|
- !ruby/object:Gem::Version
|
240
|
-
version: '2.
|
254
|
+
version: '2.5'
|
241
255
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
242
256
|
requirements:
|
243
257
|
- - ">="
|
244
258
|
- !ruby/object:Gem::Version
|
245
259
|
version: '0'
|
246
260
|
requirements: []
|
247
|
-
rubygems_version: 3.
|
248
|
-
signing_key:
|
261
|
+
rubygems_version: 3.1.2
|
262
|
+
signing_key:
|
249
263
|
specification_version: 4
|
250
|
-
summary: Rails
|
264
|
+
summary: Rails sharding magic
|
251
265
|
test_files: []
|