switchman 3.4.2 → 3.5.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 +15 -14
- data/db/migrate/20180828183945_add_default_shard_index.rb +1 -1
- data/db/migrate/20190114212900_add_unique_name_indexes.rb +10 -4
- data/lib/switchman/active_record/associations.rb +16 -5
- data/lib/switchman/active_record/attribute_methods.rb +67 -22
- data/lib/switchman/active_record/base.rb +32 -12
- data/lib/switchman/active_record/calculations.rb +37 -33
- data/lib/switchman/active_record/connection_pool.rb +4 -2
- data/lib/switchman/active_record/database_configurations.rb +12 -7
- data/lib/switchman/active_record/finder_methods.rb +1 -1
- data/lib/switchman/active_record/log_subscriber.rb +2 -2
- data/lib/switchman/active_record/migration.rb +4 -2
- data/lib/switchman/active_record/postgresql_adapter.rb +11 -10
- data/lib/switchman/active_record/query_cache.rb +1 -1
- data/lib/switchman/active_record/query_methods.rb +72 -26
- data/lib/switchman/active_record/relation.rb +13 -7
- data/lib/switchman/active_record/spawn_methods.rb +2 -2
- data/lib/switchman/active_record/statement_cache.rb +2 -2
- data/lib/switchman/active_record/tasks/database_tasks.rb +1 -1
- data/lib/switchman/active_record/test_fixtures.rb +19 -16
- data/lib/switchman/active_support/cache.rb +4 -1
- data/lib/switchman/arel.rb +6 -6
- data/lib/switchman/call_super.rb +1 -1
- data/lib/switchman/database_server.rb +20 -16
- data/lib/switchman/default_shard.rb +3 -3
- data/lib/switchman/engine.rb +33 -18
- data/lib/switchman/environment.rb +2 -2
- data/lib/switchman/errors.rb +4 -1
- data/lib/switchman/guard_rail/relation.rb +1 -1
- data/lib/switchman/parallel.rb +1 -1
- data/lib/switchman/r_spec_helper.rb +10 -10
- data/lib/switchman/shard.rb +30 -23
- data/lib/switchman/sharded_instrumenter.rb +5 -1
- data/lib/switchman/test_helper.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +10 -4
- data/lib/tasks/switchman.rake +40 -37
- metadata +9 -9
@@ -10,9 +10,9 @@ module Switchman
|
|
10
10
|
def configs_for(include_replicas: false, name: nil, **)
|
11
11
|
res = super
|
12
12
|
if name && !include_replicas
|
13
|
-
return nil unless name.end_with?(
|
13
|
+
return nil unless name.end_with?("primary")
|
14
14
|
elsif !include_replicas
|
15
|
-
return res.select { |config| config.name.end_with?(
|
15
|
+
return res.select { |config| config.name.end_with?("primary") }
|
16
16
|
end
|
17
17
|
res
|
18
18
|
end
|
@@ -29,19 +29,24 @@ module Switchman
|
|
29
29
|
db_configs = configs.flat_map do |env_name, config|
|
30
30
|
# It would be nice to do the auto-fallback that we want here, but we haven't
|
31
31
|
# actually done that for years (or maybe ever) and it will be a big lift to get working
|
32
|
-
roles = config.keys.select
|
32
|
+
roles = config.keys.select do |k|
|
33
|
+
config[k].is_a?(Hash) || (config[k].is_a?(Array) && config[k].all?(Hash))
|
34
|
+
end
|
33
35
|
base_config = config.except(*roles)
|
34
36
|
|
35
37
|
name = "#{env_name}/primary"
|
36
|
-
name =
|
38
|
+
name = "primary" if env_name == default_env
|
37
39
|
base_db = build_db_config_from_raw_config(env_name, name, base_config)
|
38
40
|
[base_db] + roles.map do |role|
|
39
|
-
build_db_config_from_raw_config(
|
40
|
-
|
41
|
+
build_db_config_from_raw_config(
|
42
|
+
env_name,
|
43
|
+
"#{env_name}/#{role}",
|
44
|
+
base_config.merge(config[role].is_a?(Array) ? config[role].first : config[role])
|
45
|
+
)
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
44
|
-
db_configs << environment_url_config(default_env,
|
49
|
+
db_configs << environment_url_config(default_env, "primary", {}) unless db_configs.find(&:for_current_env?)
|
45
50
|
|
46
51
|
merge_db_environment_variables(default_env, db_configs.compact)
|
47
52
|
end
|
@@ -49,7 +49,7 @@ module Switchman
|
|
49
49
|
relation = apply_join_dependency(eager_loading: false)
|
50
50
|
return false if ::ActiveRecord::NullRelation === relation
|
51
51
|
|
52
|
-
relation = relation.except(:select, :order).select(
|
52
|
+
relation = relation.except(:select, :order).select("1 AS one").limit(1)
|
53
53
|
|
54
54
|
case conditions
|
55
55
|
when Array, Hash
|
@@ -14,14 +14,14 @@ module Switchman
|
|
14
14
|
|
15
15
|
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
|
16
16
|
name = "CACHE #{name}" if payload[:cached]
|
17
|
-
sql = payload[:sql].squeeze(
|
17
|
+
sql = payload[:sql].squeeze(" ")
|
18
18
|
binds = nil
|
19
19
|
shard = payload[:shard]
|
20
20
|
shard = " [#{shard[:database_server_id]}:#{shard[:id]} #{shard[:env]}]" if shard
|
21
21
|
|
22
22
|
unless (payload[:binds] || []).empty?
|
23
23
|
casted_params = type_casted_binds(payload[:type_casted_binds])
|
24
|
-
binds =
|
24
|
+
binds = " " + payload[:binds].zip(casted_params).map do |attr, value|
|
25
25
|
render_bind(attr, value)
|
26
26
|
end.inspect
|
27
27
|
end
|
@@ -14,7 +14,9 @@ module Switchman
|
|
14
14
|
|
15
15
|
def connection
|
16
16
|
conn = super
|
17
|
-
|
17
|
+
if conn.shard != ::ActiveRecord::Base.current_switchman_shard
|
18
|
+
::ActiveRecord::Base.connection_pool.switch_database(conn)
|
19
|
+
end
|
18
20
|
conn
|
19
21
|
end
|
20
22
|
end
|
@@ -65,7 +67,7 @@ module Switchman
|
|
65
67
|
return @migrations if instance_variable_defined?(:@migrations)
|
66
68
|
|
67
69
|
migrations_cache = Thread.current[:migrations_cache] ||= {}
|
68
|
-
key = Digest::MD5.hexdigest(migration_files.sort.join(
|
70
|
+
key = Digest::MD5.hexdigest(migration_files.sort.join(","))
|
69
71
|
@migrations = migrations_cache[key] ||= super
|
70
72
|
end
|
71
73
|
|
@@ -5,9 +5,9 @@ module Switchman
|
|
5
5
|
module PostgreSQLAdapter
|
6
6
|
# copy/paste; use quote_local_table_name
|
7
7
|
def create_database(name, options = {})
|
8
|
-
options = { encoding:
|
8
|
+
options = { encoding: "utf8" }.merge!(options.symbolize_keys)
|
9
9
|
|
10
|
-
option_string = options.sum(
|
10
|
+
option_string = options.sum("") do |key, value|
|
11
11
|
case key
|
12
12
|
when :owner
|
13
13
|
" OWNER = \"#{value}\""
|
@@ -24,7 +24,7 @@ module Switchman
|
|
24
24
|
when :connection_limit
|
25
25
|
" CONNECTION LIMIT = #{value}"
|
26
26
|
else
|
27
|
-
|
27
|
+
""
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -37,7 +37,7 @@ module Switchman
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def current_schemas
|
40
|
-
select_values(
|
40
|
+
select_values("SELECT * FROM unnest(current_schemas(false))")
|
41
41
|
end
|
42
42
|
|
43
43
|
def extract_schema_qualified_name(string)
|
@@ -49,13 +49,13 @@ module Switchman
|
|
49
49
|
# significant change: use the shard name if no explicit schema
|
50
50
|
def quoted_scope(name = nil, type: nil)
|
51
51
|
schema, name = extract_schema_qualified_name(name)
|
52
|
-
type =
|
52
|
+
type =
|
53
53
|
case type # rubocop:disable Style/HashLikeCase
|
54
|
-
when
|
54
|
+
when "BASE TABLE"
|
55
55
|
"'r','p'"
|
56
|
-
when
|
56
|
+
when "VIEW"
|
57
57
|
"'v','m'"
|
58
|
-
when
|
58
|
+
when "FOREIGN TABLE"
|
59
59
|
"'f'"
|
60
60
|
end
|
61
61
|
scope = {}
|
@@ -67,7 +67,8 @@ module Switchman
|
|
67
67
|
|
68
68
|
def foreign_keys(table_name)
|
69
69
|
super.each do |fk|
|
70
|
-
to_table_qualified_name =
|
70
|
+
to_table_qualified_name =
|
71
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(fk.to_table)
|
71
72
|
fk.to_table = to_table_qualified_name.identifier if to_table_qualified_name.schema == shard.name
|
72
73
|
end
|
73
74
|
end
|
@@ -101,7 +102,7 @@ module Switchman
|
|
101
102
|
|
102
103
|
def add_index_options(_table_name, _column_name, **)
|
103
104
|
index, algorithm, if_not_exists = super
|
104
|
-
algorithm = nil if DatabaseServer.creating_new_shard && algorithm ==
|
105
|
+
algorithm = nil if DatabaseServer.creating_new_shard && algorithm == "CONCURRENTLY"
|
105
106
|
[index, algorithm, if_not_exists]
|
106
107
|
end
|
107
108
|
|
@@ -84,6 +84,14 @@ module Switchman
|
|
84
84
|
super(other.shard(primary_shard))
|
85
85
|
end
|
86
86
|
|
87
|
+
# use a temp variable so that the new where clause is built before self.where_clause is read,
|
88
|
+
# since build_where_clause might mutate self.where_clause
|
89
|
+
def where!(opts, *rest)
|
90
|
+
new_clause = build_where_clause(opts, rest)
|
91
|
+
self.where_clause += new_clause
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
87
95
|
protected
|
88
96
|
|
89
97
|
def remove_nonlocal_primary_keys!
|
@@ -93,7 +101,7 @@ module Switchman
|
|
93
101
|
predicate.left.relation.klass == klass &&
|
94
102
|
(predicate.is_a?(::Arel::Nodes::Equality) || predicate.is_a?(::Arel::Nodes::HomogeneousIn))
|
95
103
|
|
96
|
-
value.is_a?(Integer) && value > Shard::IDS_PER_SHARD ? [] : value
|
104
|
+
(value.is_a?(Integer) && value > Shard::IDS_PER_SHARD) ? [] : value
|
97
105
|
end
|
98
106
|
self
|
99
107
|
end
|
@@ -172,7 +180,7 @@ module Switchman
|
|
172
180
|
|
173
181
|
def sharded_primary_key?(relation, column)
|
174
182
|
column = column.to_s
|
175
|
-
return column ==
|
183
|
+
return column == "id" if relation.klass == ::ActiveRecord::Base
|
176
184
|
|
177
185
|
relation.klass.primary_key == column && relation.klass.integral_id?
|
178
186
|
end
|
@@ -199,12 +207,11 @@ module Switchman
|
|
199
207
|
|
200
208
|
case opts
|
201
209
|
when String, Array
|
202
|
-
values = Hash === rest.first ? rest.first.values : rest
|
210
|
+
values = (Hash === rest.first) ? rest.first.values : rest
|
203
211
|
|
204
|
-
values.grep(ActiveRecord::Relation)
|
205
|
-
|
206
|
-
|
207
|
-
rel.shard!(primary_shard) if rel.shard_source_value == :implicit && rel.primary_shard != primary_shard
|
212
|
+
if values.grep(ActiveRecord::Relation).first
|
213
|
+
raise "Sub-queries are not allowed as simple substitutions; " \
|
214
|
+
"please build your relation with more structured methods so that Switchman is able to introspect it."
|
208
215
|
end
|
209
216
|
|
210
217
|
super
|
@@ -254,9 +261,10 @@ module Switchman
|
|
254
261
|
send(clause_setter, new_clause)
|
255
262
|
end
|
256
263
|
|
257
|
-
def each_transposable_predicate(predicates
|
264
|
+
def each_transposable_predicate(predicates, &block)
|
258
265
|
each_predicate(predicates) do |predicate|
|
259
|
-
|
266
|
+
case predicate
|
267
|
+
when ::Arel::Nodes::Grouping
|
260
268
|
next predicate unless predicate.expr.is_a?(::Arel::Nodes::Or)
|
261
269
|
|
262
270
|
or_expr = predicate.expr
|
@@ -267,6 +275,40 @@ module Switchman
|
|
267
275
|
next predicate if new_left == old_left && new_right == old_right
|
268
276
|
|
269
277
|
next predicate.class.new predicate.expr.class.new(new_left, new_right)
|
278
|
+
when ::Arel::Nodes::SelectStatement
|
279
|
+
new_cores = predicate.cores.map do |core|
|
280
|
+
next core unless core.is_a?(::Arel::Nodes::SelectCore) # just in case something weird is going on
|
281
|
+
|
282
|
+
new_wheres = each_transposable_predicate(core.wheres, &block)
|
283
|
+
new_havings = each_transposable_predicate(core.havings, &block)
|
284
|
+
|
285
|
+
next core if core.wheres == new_wheres && core.havings == new_havings
|
286
|
+
|
287
|
+
new_core = core.clone
|
288
|
+
new_core.wheres = new_wheres
|
289
|
+
new_core.havings = new_havings
|
290
|
+
new_core
|
291
|
+
end
|
292
|
+
|
293
|
+
next predicate if predicate.cores == new_cores
|
294
|
+
|
295
|
+
new_node = predicate.clone
|
296
|
+
new_node.instance_variable_set(:@cores, new_cores)
|
297
|
+
next new_node
|
298
|
+
when ::Arel::Nodes::Not
|
299
|
+
old_value = predicate.expr
|
300
|
+
new_value = each_transposable_predicate([old_value], &block).first
|
301
|
+
|
302
|
+
next predicate if old_value == new_value
|
303
|
+
|
304
|
+
next predicate.class.new(new_value)
|
305
|
+
when ::Arel::Nodes::Exists
|
306
|
+
old_value = predicate.expressions
|
307
|
+
new_value = each_transposable_predicate([old_value], &block).first
|
308
|
+
|
309
|
+
next predicate if old_value == new_value
|
310
|
+
|
311
|
+
next predicate.class.new(new_value)
|
270
312
|
end
|
271
313
|
|
272
314
|
next predicate unless predicate.is_a?(::Arel::Nodes::Binary) || predicate.is_a?(::Arel::Nodes::HomogeneousIn)
|
@@ -279,54 +321,56 @@ module Switchman
|
|
279
321
|
end
|
280
322
|
end
|
281
323
|
|
282
|
-
def each_transposable_predicate_value(predicates = nil)
|
324
|
+
def each_transposable_predicate_value(predicates = nil, &block)
|
283
325
|
each_transposable_predicate(predicates) do |predicate, relation, column, type|
|
284
|
-
each_transposable_predicate_value_cb(predicate) do |value|
|
326
|
+
each_transposable_predicate_value_cb(predicate, block) do |value|
|
285
327
|
yield(value, predicate, relation, column, type)
|
286
328
|
end
|
287
329
|
end
|
288
330
|
end
|
289
331
|
|
290
|
-
def each_transposable_predicate_value_cb(node, &block)
|
332
|
+
def each_transposable_predicate_value_cb(node, original_block, &block)
|
291
333
|
case node
|
292
334
|
when Array
|
293
|
-
node.
|
335
|
+
node.filter_map { |val| each_transposable_predicate_value_cb(val, original_block, &block).presence }
|
294
336
|
when ::ActiveModel::Attribute
|
295
337
|
old_value = node.value_before_type_cast
|
296
|
-
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
338
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &block)
|
297
339
|
|
298
|
-
old_value == new_value ? node : node.class.new(node.name, new_value, node.type)
|
340
|
+
(old_value == new_value) ? node : node.class.new(node.name, new_value, node.type)
|
299
341
|
when ::Arel::Nodes::And
|
300
342
|
old_value = node.children
|
301
|
-
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
343
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &block)
|
302
344
|
|
303
|
-
old_value == new_value ? node : node.class.new(new_value)
|
345
|
+
(old_value == new_value) ? node : node.class.new(new_value)
|
304
346
|
when ::Arel::Nodes::BindParam
|
305
347
|
old_value = node.value
|
306
|
-
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
348
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &block)
|
307
349
|
|
308
|
-
old_value == new_value ? node : node.class.new(new_value)
|
350
|
+
(old_value == new_value) ? node : node.class.new(new_value)
|
309
351
|
when ::Arel::Nodes::Casted
|
310
352
|
old_value = node.value
|
311
|
-
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
353
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &block)
|
312
354
|
|
313
|
-
old_value == new_value ? node : node.class.new(new_value, node.attribute)
|
355
|
+
(old_value == new_value) ? node : node.class.new(new_value, node.attribute)
|
314
356
|
when ::Arel::Nodes::HomogeneousIn
|
315
357
|
old_value = node.values
|
316
|
-
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
358
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &block)
|
317
359
|
|
318
360
|
# switch to a regular In, so that Relation::WhereClause#contradiction? knows about it
|
319
361
|
if new_value.empty?
|
320
|
-
klass = node.type == :in ? ::Arel::Nodes::In : ::Arel::Nodes::NotIn
|
362
|
+
klass = (node.type == :in) ? ::Arel::Nodes::In : ::Arel::Nodes::NotIn
|
321
363
|
klass.new(node.attribute, new_value)
|
322
364
|
else
|
323
|
-
old_value == new_value ? node : node.class.new(new_value, node.attribute, node.type)
|
365
|
+
(old_value == new_value) ? node : node.class.new(new_value, node.attribute, node.type)
|
324
366
|
end
|
325
367
|
when ::Arel::Nodes::Binary
|
326
368
|
old_value = node.right
|
327
|
-
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
369
|
+
new_value = each_transposable_predicate_value_cb(old_value, original_block, &block)
|
328
370
|
|
329
|
-
old_value == new_value ? node : node.class.new(node.left, new_value)
|
371
|
+
(old_value == new_value) ? node : node.class.new(node.left, new_value)
|
372
|
+
when ::Arel::Nodes::SelectStatement
|
373
|
+
each_transposable_predicate_value([node], &original_block).first
|
330
374
|
else
|
331
375
|
yield(node)
|
332
376
|
end
|
@@ -343,6 +387,8 @@ module Switchman
|
|
343
387
|
Shard.current(klass.connection_class_for_self)
|
344
388
|
elsif type == :foreign
|
345
389
|
source_shard_for_foreign_key(relation, column)
|
390
|
+
else
|
391
|
+
primary_shard
|
346
392
|
end
|
347
393
|
|
348
394
|
transpose_predicate_value(value, current_source_shard, target_shard, type)
|
@@ -49,7 +49,7 @@ module Switchman
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def load(&block)
|
52
|
-
if !loaded? || (::Rails.version >=
|
52
|
+
if !loaded? || (::Rails.version >= "7.0" && scheduled?)
|
53
53
|
@records = activate { |relation| relation.send(:exec_queries, &block) }
|
54
54
|
@loaded = true
|
55
55
|
end
|
@@ -58,7 +58,7 @@ module Switchman
|
|
58
58
|
end
|
59
59
|
|
60
60
|
%I[update_all delete_all].each do |method|
|
61
|
-
arg_params = RUBY_VERSION <=
|
61
|
+
arg_params = (RUBY_VERSION <= "2.8") ? "*args" : "*args, **kwargs"
|
62
62
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
63
63
|
def #{method}(#{arg_params})
|
64
64
|
result = self.activate(unordered: true) { |relation| relation.call_super(#{method.inspect}, Relation, #{arg_params}) }
|
@@ -73,12 +73,16 @@ module Switchman
|
|
73
73
|
loose_mode = options[:loose] && is_integer
|
74
74
|
# loose_mode: if we don't care about getting exactly batch_size ids in between
|
75
75
|
# don't get the max - just get the min and add batch_size so we get that many _at most_
|
76
|
-
values = loose_mode ?
|
76
|
+
values = loose_mode ? "MIN(id)" : "MIN(id), MAX(id)"
|
77
77
|
|
78
78
|
batch_size = options[:batch_size].try(:to_i) || 1000
|
79
|
-
quoted_primary_key =
|
80
|
-
|
81
|
-
|
79
|
+
quoted_primary_key =
|
80
|
+
"#{klass.connection.quote_local_table_name(table_name)}.#{klass.connection.quote_column_name(primary_key)}"
|
81
|
+
as_id = " AS id" unless primary_key == "id"
|
82
|
+
subquery_scope = except(:select)
|
83
|
+
.select("#{quoted_primary_key}#{as_id}")
|
84
|
+
.reorder(primary_key.to_sym)
|
85
|
+
.limit(loose_mode ? 1 : batch_size)
|
82
86
|
subquery_scope = subquery_scope.where("#{quoted_primary_key} <= ?", options[:end_at]) if options[:end_at]
|
83
87
|
|
84
88
|
first_subquery_scope = if options[:start_at]
|
@@ -121,7 +125,9 @@ module Switchman
|
|
121
125
|
relation = shard(Shard.current(klass.connection_class_for_self))
|
122
126
|
relation.remove_nonlocal_primary_keys!
|
123
127
|
# do a minimal query if possible
|
124
|
-
|
128
|
+
if limit_value && !result_count.zero? && order_values.empty?
|
129
|
+
relation = relation.limit(limit_value - result_count)
|
130
|
+
end
|
125
131
|
|
126
132
|
shard_results = relation.activate(&block)
|
127
133
|
|
@@ -17,7 +17,7 @@ module Switchman
|
|
17
17
|
final_shard_source_value = %i[explicit association].detect do |source_value|
|
18
18
|
shard_source_value == source_value || rhs.shard_source_value == source_value
|
19
19
|
end
|
20
|
-
raise
|
20
|
+
raise "unknown shard_source_value" unless final_shard_source_value
|
21
21
|
|
22
22
|
# have to merge shard_value
|
23
23
|
lhs_shard_value = all_shards
|
@@ -36,7 +36,7 @@ module Switchman
|
|
36
36
|
final_shard_source_value = %i[explicit association implicit].detect do |source_value|
|
37
37
|
shard_source_value == source_value || rhs.shard_source_value == source_value
|
38
38
|
end
|
39
|
-
raise
|
39
|
+
raise "unknown shard_source_value" unless final_shard_source_value
|
40
40
|
end
|
41
41
|
|
42
42
|
[final_shard_value, final_primary_shard, final_shard_source_value]
|
@@ -4,8 +4,8 @@ module Switchman
|
|
4
4
|
module ActiveRecord
|
5
5
|
module StatementCache
|
6
6
|
module ClassMethods
|
7
|
-
def create(connection
|
8
|
-
relation =
|
7
|
+
def create(connection)
|
8
|
+
relation = yield ::ActiveRecord::StatementCache::Params.new
|
9
9
|
|
10
10
|
_query_builder, binds = connection.cacheable_query(self, relation.arel)
|
11
11
|
bind_map = ::ActiveRecord::StatementCache::BindMap.new(binds)
|
@@ -12,31 +12,34 @@ module Switchman
|
|
12
12
|
# Replace the one that activerecord natively uses with a switchman-optimized one
|
13
13
|
::ActiveSupport::Notifications.unsubscribe(@connection_subscriber)
|
14
14
|
# Code adapted from the code in rails proper
|
15
|
-
@connection_subscriber =
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
@connection_subscriber =
|
16
|
+
::ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
|
17
|
+
spec_name = payload[:spec_name] if payload.key?(:spec_name)
|
18
|
+
shard = payload[:shard] if payload.key?(:shard)
|
19
|
+
setup_shared_connection_pool
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
if spec_name && !FORBIDDEN_DB_ENVS.include?(shard)
|
22
|
+
begin
|
23
|
+
connection = ::ActiveRecord::Base.connection_handler.retrieve_connection(spec_name, shard: shard)
|
24
|
+
rescue ::ActiveRecord::ConnectionNotEstablished, ::ActiveRecord::NoDatabaseError
|
25
|
+
connection = nil
|
26
|
+
end
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
if connection && !@fixture_connections.include?(connection)
|
29
|
+
connection.begin_transaction joinable: false, _lazy: false
|
30
|
+
connection.pool.lock_thread = true if lock_threads
|
31
|
+
@fixture_connections << connection
|
32
|
+
end
|
31
33
|
end
|
32
34
|
end
|
33
|
-
end
|
34
35
|
end
|
35
36
|
|
36
37
|
def enlist_fixture_connections
|
37
38
|
setup_shared_connection_pool
|
38
39
|
|
39
|
-
::ActiveRecord::Base.connection_handler.connection_pool_list.reject
|
40
|
+
::ActiveRecord::Base.connection_handler.connection_pool_list.reject do |cp|
|
41
|
+
FORBIDDEN_DB_ENVS.include?(cp.db_config.env_name.to_sym)
|
42
|
+
end.map(&:connection)
|
40
43
|
end
|
41
44
|
end
|
42
45
|
end
|
@@ -24,7 +24,10 @@ module Switchman
|
|
24
24
|
store = super
|
25
25
|
# can't use defined?, because it's a _ruby_ autoloaded constant,
|
26
26
|
# so just checking that will cause it to get required
|
27
|
-
|
27
|
+
if store.instance_of?(ActiveSupport::Cache::RedisCacheStore) &&
|
28
|
+
!::ActiveSupport::Cache::RedisCacheStore <= RedisCacheStore
|
29
|
+
::ActiveSupport::Cache::RedisCacheStore.prepend(RedisCacheStore)
|
30
|
+
end
|
28
31
|
store.options[:namespace] ||= -> { Shard.current.default? ? nil : "shard_#{Shard.current.id}" }
|
29
32
|
store
|
30
33
|
end
|
data/lib/switchman/arel.rb
CHANGED
@@ -15,24 +15,24 @@ module Switchman
|
|
15
15
|
|
16
16
|
def visit_Arel_Nodes_TableAlias(o, collector)
|
17
17
|
collector = visit o.relation, collector
|
18
|
-
collector <<
|
18
|
+
collector << " "
|
19
19
|
collector << quote_local_table_name(o.name)
|
20
20
|
end
|
21
21
|
|
22
22
|
def visit_Arel_Attributes_Attribute(o, collector)
|
23
23
|
join_name = o.relation.table_alias || o.relation.name
|
24
|
-
collector << quote_local_table_name(join_name) <<
|
24
|
+
collector << quote_local_table_name(join_name) << "." << quote_column_name(o.name)
|
25
25
|
end
|
26
26
|
|
27
27
|
def visit_Arel_Nodes_HomogeneousIn(o, collector)
|
28
28
|
collector.preparable = false
|
29
29
|
|
30
|
-
collector << quote_local_table_name(o.table_name) <<
|
30
|
+
collector << quote_local_table_name(o.table_name) << "." << quote_column_name(o.column_name)
|
31
31
|
|
32
32
|
collector << if o.type == :in
|
33
|
-
|
33
|
+
" IN ("
|
34
34
|
else
|
35
|
-
|
35
|
+
" NOT IN ("
|
36
36
|
end
|
37
37
|
|
38
38
|
values = o.casted_values
|
@@ -43,7 +43,7 @@ module Switchman
|
|
43
43
|
collector.add_binds(values, o.proc_for_binds, &bind_block)
|
44
44
|
end
|
45
45
|
|
46
|
-
collector <<
|
46
|
+
collector << ")"
|
47
47
|
collector
|
48
48
|
end
|
49
49
|
|
data/lib/switchman/call_super.rb
CHANGED