switchman 2.1.0 → 3.0.6
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 +10 -2
- data/app/models/switchman/shard.rb +270 -343
- data/app/models/switchman/unsharded_record.rb +7 -0
- data/db/migrate/20130328212039_create_switchman_shards.rb +1 -1
- data/db/migrate/20130328224244_create_default_shard.rb +5 -5
- data/db/migrate/20161206323434_add_back_default_string_limits_switchman.rb +1 -0
- data/db/migrate/20180828183945_add_default_shard_index.rb +2 -2
- data/db/migrate/20180828192111_add_timestamps_to_shards.rb +8 -6
- data/db/migrate/20190114212900_add_unique_name_indexes.rb +5 -3
- data/lib/switchman/action_controller/caching.rb +2 -2
- data/lib/switchman/active_record/abstract_adapter.rb +0 -8
- data/lib/switchman/active_record/association.rb +78 -89
- data/lib/switchman/active_record/attribute_methods.rb +127 -52
- data/lib/switchman/active_record/base.rb +83 -67
- data/lib/switchman/active_record/calculations.rb +73 -66
- data/lib/switchman/active_record/connection_pool.rb +12 -59
- data/lib/switchman/active_record/database_configurations/database_config.rb +13 -0
- data/lib/switchman/active_record/database_configurations.rb +34 -0
- data/lib/switchman/active_record/finder_methods.rb +11 -16
- data/lib/switchman/active_record/log_subscriber.rb +4 -8
- data/lib/switchman/active_record/migration.rb +19 -45
- data/lib/switchman/active_record/model_schema.rb +1 -1
- data/lib/switchman/active_record/persistence.rb +11 -6
- data/lib/switchman/active_record/postgresql_adapter.rb +33 -161
- data/lib/switchman/active_record/predicate_builder.rb +1 -1
- data/lib/switchman/active_record/query_cache.rb +18 -19
- data/lib/switchman/active_record/query_methods.rb +178 -193
- data/lib/switchman/active_record/reflection.rb +7 -22
- data/lib/switchman/active_record/relation.rb +32 -29
- data/lib/switchman/active_record/spawn_methods.rb +27 -29
- data/lib/switchman/active_record/statement_cache.rb +18 -35
- data/lib/switchman/active_record/tasks/database_tasks.rb +16 -0
- data/lib/switchman/active_record/test_fixtures.rb +43 -0
- data/lib/switchman/active_support/cache.rb +3 -5
- data/lib/switchman/arel.rb +13 -8
- data/lib/switchman/database_server.rb +130 -154
- data/lib/switchman/default_shard.rb +52 -16
- data/lib/switchman/engine.rb +65 -58
- data/lib/switchman/environment.rb +4 -8
- data/lib/switchman/errors.rb +1 -0
- data/lib/switchman/guard_rail/relation.rb +5 -7
- data/lib/switchman/guard_rail.rb +6 -19
- data/lib/switchman/r_spec_helper.rb +29 -57
- data/lib/switchman/rails.rb +14 -12
- data/lib/switchman/sharded_instrumenter.rb +1 -1
- data/lib/switchman/standard_error.rb +15 -3
- data/lib/switchman/test_helper.rb +5 -3
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +3 -3
- data/lib/tasks/switchman.rake +61 -72
- metadata +90 -48
- data/lib/switchman/active_record/batches.rb +0 -11
- data/lib/switchman/active_record/connection_handler.rb +0 -190
- data/lib/switchman/active_record/where_clause_factory.rb +0 -36
- data/lib/switchman/connection_pool_proxy.rb +0 -173
- data/lib/switchman/schema_cache.rb +0 -28
@@ -7,19 +7,18 @@ module Switchman
|
|
7
7
|
return super(id) unless klass.integral_id?
|
8
8
|
|
9
9
|
if shard_source_value != :implicit
|
10
|
-
current_shard = Shard.current(klass.
|
11
|
-
result =
|
10
|
+
current_shard = Shard.current(klass.connection_classes)
|
11
|
+
result = activate do |relation, shard|
|
12
12
|
current_id = Shard.relative_id_for(id, current_shard, shard)
|
13
13
|
# current_id will be nil for non-integral id
|
14
14
|
next unless current_id
|
15
15
|
# skip the shard if the object can't be on it. unless we're only looking at one shard;
|
16
16
|
# we might be expecting a shadow object
|
17
|
-
next if current_id > Shard::IDS_PER_SHARD &&
|
17
|
+
next if current_id > Shard::IDS_PER_SHARD && all_shards.length > 1
|
18
|
+
|
18
19
|
relation.call_super(:find_one, FinderMethods, current_id)
|
19
20
|
end
|
20
|
-
if result.is_a?(Array)
|
21
|
-
result = result.first
|
22
|
-
end
|
21
|
+
result = result.first if result.is_a?(Array)
|
23
22
|
# we may have skipped all shards
|
24
23
|
raise_record_not_found_exception!(id, 0, 1) unless result
|
25
24
|
return result
|
@@ -34,8 +33,8 @@ module Switchman
|
|
34
33
|
end
|
35
34
|
|
36
35
|
def find_some_ordered(ids)
|
37
|
-
current_shard = Shard.current(klass.
|
38
|
-
ids = ids.map{|id| Shard.relative_id_for(id, current_shard, current_shard)}
|
36
|
+
current_shard = Shard.current(klass.connection_classes)
|
37
|
+
ids = ids.map { |id| Shard.relative_id_for(id, current_shard, current_shard) }
|
39
38
|
super(ids)
|
40
39
|
end
|
41
40
|
|
@@ -45,14 +44,12 @@ module Switchman
|
|
45
44
|
|
46
45
|
def exists?(conditions = :none)
|
47
46
|
conditions = conditions.id if ::ActiveRecord::Base === conditions
|
48
|
-
return false
|
47
|
+
return false unless conditions
|
49
48
|
|
50
|
-
relation =
|
51
|
-
apply_join_dependency(eager_loading: false) :
|
52
|
-
apply_join_dependency(self, construct_join_dependency)
|
49
|
+
relation = apply_join_dependency(eager_loading: false)
|
53
50
|
return false if ::ActiveRecord::NullRelation === relation
|
54
51
|
|
55
|
-
relation = relation.except(:select, :order).select(
|
52
|
+
relation = relation.except(:select, :order).select('1 AS one').limit(1)
|
56
53
|
|
57
54
|
case conditions
|
58
55
|
when Array, Hash
|
@@ -62,9 +59,7 @@ module Switchman
|
|
62
59
|
end
|
63
60
|
|
64
61
|
relation.activate do |shard_rel|
|
65
|
-
return true if
|
66
|
-
connection.select_value(shard_rel.arel, "#{name} Exists") :
|
67
|
-
connection.select_value(shard_rel, "#{name} Exists", shard_rel.bound_attributes)
|
62
|
+
return true if connection.select_value(shard_rel.arel, "#{name} Exists")
|
68
63
|
end
|
69
64
|
false
|
70
65
|
end
|
@@ -14,20 +14,16 @@ 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
|
-
|
24
|
-
|
25
|
-
[payload[:binds], payload[:type_casted_binds]] :
|
26
|
-
[payload[:type_casted_binds]]
|
27
|
-
casted_params = type_casted_binds(*args)
|
28
|
-
binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
|
23
|
+
casted_params = type_casted_binds(payload[:type_casted_binds])
|
24
|
+
binds = ' ' + payload[:binds].zip(casted_params).map do |attr, value|
|
29
25
|
render_bind(attr, value)
|
30
|
-
|
26
|
+
end.inspect
|
31
27
|
end
|
32
28
|
|
33
29
|
name = colorize_payload_name(name, payload[:name])
|
@@ -4,73 +4,47 @@ module Switchman
|
|
4
4
|
module ActiveRecord
|
5
5
|
module Migration
|
6
6
|
module Compatibility
|
7
|
-
module V5_0
|
7
|
+
module V5_0 # rubocop:disable Naming/ClassAndModuleCamelCase
|
8
8
|
def create_table(*args, **options)
|
9
|
-
unless options.key?(:id)
|
10
|
-
|
11
|
-
end
|
12
|
-
if block_given?
|
13
|
-
super do |td|
|
14
|
-
yield td
|
15
|
-
end
|
16
|
-
else
|
17
|
-
super
|
18
|
-
end
|
9
|
+
options[:id] = :bigserial unless options.key?(:id)
|
10
|
+
super
|
19
11
|
end
|
20
12
|
end
|
21
13
|
end
|
22
14
|
|
23
15
|
def connection
|
24
16
|
conn = super
|
25
|
-
if conn.shard != ::ActiveRecord::Base.
|
26
|
-
::ActiveRecord::Base.connection_pool.current_pool.switch_database(conn)
|
27
|
-
end
|
17
|
+
::ActiveRecord::Base.connection_pool.switch_database(conn) if conn.shard != ::ActiveRecord::Base.current_switchman_shard
|
28
18
|
conn
|
29
19
|
end
|
30
20
|
end
|
31
21
|
|
32
22
|
module Migrator
|
33
|
-
# significant change:
|
23
|
+
# significant change: just return MIGRATOR_SALT directly
|
24
|
+
# especially if you're going through pgbouncer, the database
|
25
|
+
# name you're accessing may not be consistent. it is NOT allowed
|
26
|
+
# to run migrations against multiple shards in the same database
|
27
|
+
# concurrently
|
34
28
|
def generate_migrator_advisory_lock_id
|
35
|
-
|
36
|
-
::ActiveRecord::Migrator::MIGRATOR_SALT * shard_name_hash
|
29
|
+
::ActiveRecord::Migrator::MIGRATOR_SALT
|
37
30
|
end
|
38
31
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
with_advisory_lock_connection do |connection|
|
45
|
-
got_lock = connection.get_advisory_lock(lock_id)
|
46
|
-
raise ::ActiveRecord::ConcurrentMigrationError unless got_lock
|
47
|
-
load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
|
48
|
-
yield
|
49
|
-
ensure
|
50
|
-
if got_lock && !connection.release_advisory_lock(lock_id)
|
51
|
-
raise ::ActiveRecord::ConcurrentMigrationError.new(
|
52
|
-
::ActiveRecord::ConcurrentMigrationError::RELEASE_LOCK_FAILED_MESSAGE
|
53
|
-
)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
32
|
+
# significant change: strip out prefer_secondary from config
|
33
|
+
def with_advisory_lock_connection
|
34
|
+
pool = ::ActiveRecord::ConnectionAdapters::ConnectionHandler.new.establish_connection(
|
35
|
+
::ActiveRecord::Base.connection_db_config.configuration_hash.except(:prefer_secondary)
|
36
|
+
)
|
57
37
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
::ActiveRecord::Base.connection_config.except(:prefer_secondary)
|
62
|
-
)
|
63
|
-
|
64
|
-
pool.with_connection { |connection| yield(connection) }
|
65
|
-
ensure
|
66
|
-
pool&.disconnect!
|
67
|
-
end
|
38
|
+
pool.with_connection { |connection| yield(connection) } # rubocop:disable Style/ExplicitBlockArgument
|
39
|
+
ensure
|
40
|
+
pool&.disconnect!
|
68
41
|
end
|
69
42
|
end
|
70
43
|
|
71
44
|
module MigrationContext
|
72
45
|
def migrations
|
73
46
|
return @migrations if instance_variable_defined?(:@migrations)
|
47
|
+
|
74
48
|
migrations_cache = Thread.current[:migrations_cache] ||= {}
|
75
49
|
key = Digest::MD5.hexdigest(migration_files.sort.join(','))
|
76
50
|
@migrations = migrations_cache[key] ||= super
|
@@ -6,7 +6,7 @@ module Switchman
|
|
6
6
|
module ClassMethods
|
7
7
|
def quoted_table_name
|
8
8
|
@quoted_table_name ||= {}
|
9
|
-
@quoted_table_name[Shard.current(
|
9
|
+
@quoted_table_name[Shard.current(connection_classes).id] ||= connection.quote_table_name(table_name)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -5,14 +5,19 @@ module Switchman
|
|
5
5
|
module Persistence
|
6
6
|
# touch reads the id attribute directly, so it's not relative to the current shard
|
7
7
|
def touch(*, **)
|
8
|
-
shard.activate(self.class.
|
8
|
+
shard.activate(self.class.connection_classes) { super }
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def update_columns(*)
|
12
|
+
shard.activate(self.class.connection_classes) { super }
|
13
|
+
end
|
14
|
+
|
15
|
+
def delete
|
16
|
+
db = shard.database_server
|
17
|
+
return db.unguard { super } unless ::GuardRail.environment == db.guard_rail_environment
|
18
|
+
|
19
|
+
super
|
15
20
|
end
|
16
21
|
end
|
17
22
|
end
|
18
|
-
end
|
23
|
+
end
|
@@ -9,22 +9,22 @@ module Switchman
|
|
9
9
|
|
10
10
|
option_string = options.sum do |key, value|
|
11
11
|
case key
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
12
|
+
when :owner
|
13
|
+
" OWNER = \"#{value}\""
|
14
|
+
when :template
|
15
|
+
" TEMPLATE = \"#{value}\""
|
16
|
+
when :encoding
|
17
|
+
" ENCODING = '#{value}'"
|
18
|
+
when :collation
|
19
|
+
" LC_COLLATE = '#{value}'"
|
20
|
+
when :ctype
|
21
|
+
" LC_CTYPE = '#{value}'"
|
22
|
+
when :tablespace
|
23
|
+
" TABLESPACE = \"#{value}\""
|
24
|
+
when :connection_limit
|
25
|
+
" CONNECTION LIMIT = #{value}"
|
26
|
+
else
|
27
|
+
''
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -32,19 +32,17 @@ module Switchman
|
|
32
32
|
end
|
33
33
|
|
34
34
|
# copy/paste; use quote_local_table_name
|
35
|
-
def drop_database(name)
|
35
|
+
def drop_database(name) # :nodoc:
|
36
36
|
execute "DROP DATABASE IF EXISTS #{quote_local_table_name(name)}"
|
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)
|
44
44
|
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(string.to_s)
|
45
|
-
if string && !name.schema
|
46
|
-
name.instance_variable_set(:@schema, shard.name)
|
47
|
-
end
|
45
|
+
name.instance_variable_set(:@schema, shard.name) if string && !name.schema
|
48
46
|
[name.schema, name.identifier]
|
49
47
|
end
|
50
48
|
|
@@ -52,12 +50,12 @@ module Switchman
|
|
52
50
|
def quoted_scope(name = nil, type: nil)
|
53
51
|
schema, name = extract_schema_qualified_name(name)
|
54
52
|
type = \
|
55
|
-
case type
|
56
|
-
when
|
53
|
+
case type # rubocop:disable Style/HashLikeCase
|
54
|
+
when 'BASE TABLE'
|
57
55
|
"'r','p'"
|
58
|
-
when
|
56
|
+
when 'VIEW'
|
59
57
|
"'v','m'"
|
60
|
-
when
|
58
|
+
when 'FOREIGN TABLE'
|
61
59
|
"'f'"
|
62
60
|
end
|
63
61
|
scope = {}
|
@@ -67,135 +65,10 @@ module Switchman
|
|
67
65
|
scope
|
68
66
|
end
|
69
67
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
FROM pg_tables
|
75
|
-
WHERE schemaname = '#{shard.name}'
|
76
|
-
SQL
|
77
|
-
end
|
78
|
-
|
79
|
-
def view_exists?(name)
|
80
|
-
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
|
81
|
-
return false unless name.identifier
|
82
|
-
if !name.schema
|
83
|
-
name.instance_variable_set(:@schema, shard.name)
|
84
|
-
end
|
85
|
-
|
86
|
-
select_values(<<-SQL, 'SCHEMA').any?
|
87
|
-
SELECT c.relname
|
88
|
-
FROM pg_class c
|
89
|
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
90
|
-
WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
|
91
|
-
AND c.relname = '#{name.identifier}'
|
92
|
-
AND n.nspname = '#{shard.name}'
|
93
|
-
SQL
|
94
|
-
end
|
95
|
-
|
96
|
-
def indexes(table_name)
|
97
|
-
result = query(<<-SQL, 'SCHEMA')
|
98
|
-
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
|
99
|
-
FROM pg_class t
|
100
|
-
INNER JOIN pg_index d ON t.oid = d.indrelid
|
101
|
-
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
102
|
-
WHERE i.relkind = 'i'
|
103
|
-
AND d.indisprimary = 'f'
|
104
|
-
AND t.relname = '#{table_name}'
|
105
|
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
|
106
|
-
ORDER BY i.relname
|
107
|
-
SQL
|
108
|
-
|
109
|
-
|
110
|
-
result.map do |row|
|
111
|
-
index_name = row[0]
|
112
|
-
unique = row[1] == true || row[1] == 't'
|
113
|
-
indkey = row[2].split(" ")
|
114
|
-
inddef = row[3]
|
115
|
-
oid = row[4]
|
116
|
-
|
117
|
-
columns = Hash[query(<<-SQL, "SCHEMA")]
|
118
|
-
SELECT a.attnum, a.attname
|
119
|
-
FROM pg_attribute a
|
120
|
-
WHERE a.attrelid = #{oid}
|
121
|
-
AND a.attnum IN (#{indkey.join(",")})
|
122
|
-
SQL
|
123
|
-
|
124
|
-
column_names = columns.stringify_keys.values_at(*indkey).compact
|
125
|
-
|
126
|
-
unless column_names.empty?
|
127
|
-
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
|
128
|
-
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
|
129
|
-
orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
|
130
|
-
where = inddef.scan(/WHERE (.+)$/).flatten[0]
|
131
|
-
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
132
|
-
|
133
|
-
if ::Rails.version >= "5.2"
|
134
|
-
::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names, orders: orders, where: where, using: using)
|
135
|
-
else
|
136
|
-
::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end.compact
|
140
|
-
end
|
141
|
-
|
142
|
-
def index_name_exists?(table_name, index_name, _default = nil)
|
143
|
-
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
|
144
|
-
SELECT COUNT(*)
|
145
|
-
FROM pg_class t
|
146
|
-
INNER JOIN pg_index d ON t.oid = d.indrelid
|
147
|
-
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
148
|
-
WHERE i.relkind = 'i'
|
149
|
-
AND i.relname = '#{index_name}'
|
150
|
-
AND t.relname = '#{table_name}'
|
151
|
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
|
152
|
-
SQL
|
153
|
-
end
|
154
|
-
|
155
|
-
def foreign_keys(table_name)
|
156
|
-
# mostly copy-pasted from AR - only change is to the nspname condition for qualified names support
|
157
|
-
fk_info = select_all <<-SQL.strip_heredoc
|
158
|
-
SELECT t2.oid::regclass::text AS to_table, a1.attname AS column, a2.attname AS primary_key, c.conname AS name, c.confupdtype AS on_update, c.confdeltype AS on_delete
|
159
|
-
FROM pg_constraint c
|
160
|
-
JOIN pg_class t1 ON c.conrelid = t1.oid
|
161
|
-
JOIN pg_class t2 ON c.confrelid = t2.oid
|
162
|
-
JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
|
163
|
-
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
164
|
-
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
165
|
-
WHERE c.contype = 'f'
|
166
|
-
AND t1.relname = #{quote(table_name)}
|
167
|
-
AND t3.nspname = '#{shard.name}'
|
168
|
-
ORDER BY c.conname
|
169
|
-
SQL
|
170
|
-
|
171
|
-
fk_info.map do |row|
|
172
|
-
options = {
|
173
|
-
column: row['column'],
|
174
|
-
name: row['name'],
|
175
|
-
primary_key: row['primary_key']
|
176
|
-
}
|
177
|
-
|
178
|
-
options[:on_delete] = extract_foreign_key_action(row['on_delete'])
|
179
|
-
options[:on_update] = extract_foreign_key_action(row['on_update'])
|
180
|
-
|
181
|
-
# strip the schema name from to_table if it matches
|
182
|
-
to_table = row['to_table']
|
183
|
-
to_table_qualified_name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(to_table)
|
184
|
-
if to_table_qualified_name.schema == shard.name
|
185
|
-
to_table = to_table_qualified_name.identifier
|
186
|
-
end
|
187
|
-
|
188
|
-
::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, to_table, options)
|
189
|
-
end
|
190
|
-
end
|
191
|
-
else
|
192
|
-
def foreign_keys(table_name)
|
193
|
-
super.each do |fk|
|
194
|
-
to_table_qualified_name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(fk.to_table)
|
195
|
-
if to_table_qualified_name.schema == shard.name
|
196
|
-
fk.to_table = to_table_qualified_name.identifier
|
197
|
-
end
|
198
|
-
end
|
68
|
+
def foreign_keys(table_name)
|
69
|
+
super.each do |fk|
|
70
|
+
to_table_qualified_name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(fk.to_table)
|
71
|
+
fk.to_table = to_table_qualified_name.identifier if to_table_qualified_name.schema == shard.name
|
199
72
|
end
|
200
73
|
end
|
201
74
|
|
@@ -208,10 +81,9 @@ module Switchman
|
|
208
81
|
|
209
82
|
def quote_table_name(name)
|
210
83
|
return quote_local_table_name(name) if @use_local_table_name
|
84
|
+
|
211
85
|
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
|
212
|
-
|
213
|
-
name.instance_variable_set(:@schema, shard.name)
|
214
|
-
end
|
86
|
+
name.instance_variable_set(:@schema, shard.name) unless name.schema
|
215
87
|
name.quoted
|
216
88
|
end
|
217
89
|
|
@@ -219,7 +91,7 @@ module Switchman
|
|
219
91
|
with_local_table_name(false, &block)
|
220
92
|
end
|
221
93
|
|
222
|
-
def with_local_table_name(enable = true)
|
94
|
+
def with_local_table_name(enable = true) # rubocop:disable Style/OptionalBooleanParameter
|
223
95
|
old_value = @use_local_table_name
|
224
96
|
@use_local_table_name = enable
|
225
97
|
yield
|
@@ -228,9 +100,9 @@ module Switchman
|
|
228
100
|
end
|
229
101
|
|
230
102
|
def add_index_options(_table_name, _column_name, **)
|
231
|
-
|
232
|
-
algorithm = nil if DatabaseServer.creating_new_shard && algorithm ==
|
233
|
-
[
|
103
|
+
index, algorithm, if_not_exists = super
|
104
|
+
algorithm = nil if DatabaseServer.creating_new_shard && algorithm == 'CONCURRENTLY'
|
105
|
+
[index, algorithm, if_not_exists]
|
234
106
|
end
|
235
107
|
|
236
108
|
def rename_table(table_name, new_name)
|
@@ -3,31 +3,30 @@
|
|
3
3
|
module Switchman
|
4
4
|
module ActiveRecord
|
5
5
|
module QueryCache
|
6
|
-
|
7
6
|
private
|
8
7
|
|
9
8
|
def cache_sql(sql, name, binds)
|
10
9
|
# have to include the shard id in the cache key because of switching dbs on the same connection
|
11
|
-
sql = "#{
|
10
|
+
sql = "#{shard.id}::#{sql}"
|
12
11
|
@lock.synchronize do
|
13
12
|
result =
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
}
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
13
|
+
if query_cache[sql].key?(binds)
|
14
|
+
args = {
|
15
|
+
sql: sql,
|
16
|
+
binds: binds,
|
17
|
+
name: name,
|
18
|
+
connection_id: object_id,
|
19
|
+
cached: true,
|
20
|
+
type_casted_binds: -> { type_casted_binds(binds) }
|
21
|
+
}
|
22
|
+
::ActiveSupport::Notifications.instrument(
|
23
|
+
'sql.active_record',
|
24
|
+
args
|
25
|
+
)
|
26
|
+
query_cache[sql][binds]
|
27
|
+
else
|
28
|
+
query_cache[sql][binds] = yield
|
29
|
+
end
|
31
30
|
result.dup
|
32
31
|
end
|
33
32
|
end
|