switchman 3.0.0 → 3.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 +1 -0
- data/db/migrate/20180828183945_add_default_shard_index.rb +1 -1
- data/lib/switchman/active_record/attribute_methods.rb +48 -0
- data/lib/switchman/active_record/migration.rb +13 -1
- data/lib/switchman/active_record/postgresql_adapter.rb +22 -110
- data/lib/switchman/database_server.rb +2 -3
- data/lib/switchman/test_helper.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- metadata +3 -4
- data/lib/switchman/schema_cache.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cfc736ed07b0e8b80b62928efb9e523a6e8e6ab31841ef4f7022ffbba7a7b57
|
4
|
+
data.tar.gz: 1879cbef2d1e8ab2d451fcf31ebe3b0a37c10e27eb1099ca96e73f58446adfae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c82b93120355776072460d8e2ec1a648ed76454ed178ee3a102b66303dc9716b23bdea855a8047995c454b2528bbe580ceccba43a36edc4afb2d4458e984b493
|
7
|
+
data.tar.gz: a97fb461a01e06ad8498635382d316ca84e0830a54984e45ce4c1cb575c3ff11e121185c67618246d175ff61eee2c1931acc5b48876235d65b6802a349621077
|
@@ -141,6 +141,54 @@ module Switchman
|
|
141
141
|
self.class.define_attribute_methods
|
142
142
|
super
|
143
143
|
end
|
144
|
+
|
145
|
+
# these are called if the specific methods haven't been defined yet
|
146
|
+
def attribute(attr_name)
|
147
|
+
return super unless self.class.sharded_column?(attr_name)
|
148
|
+
|
149
|
+
reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
|
150
|
+
::Switchman::Shard.relative_id_for(super, shard, ::Switchman::Shard.current(connection_classes_for_reflection(reflection)))
|
151
|
+
end
|
152
|
+
|
153
|
+
def attribute=(attr_name, new_value)
|
154
|
+
unless self.class.sharded_column?(attr_name)
|
155
|
+
super
|
156
|
+
return
|
157
|
+
end
|
158
|
+
|
159
|
+
reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
|
160
|
+
super(::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(connection_classes_for_reflection(reflection)), shard))
|
161
|
+
end
|
162
|
+
|
163
|
+
def global_attribute(attr_name)
|
164
|
+
if self.class.sharded_column?(attr_name)
|
165
|
+
::Switchman::Shard.global_id_for(attribute(attr_name), shard)
|
166
|
+
else
|
167
|
+
attribute(attr_name)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def local_attribute(attr_name)
|
172
|
+
if self.class.sharded_column?(attr_name)
|
173
|
+
::Switchman::Shard.local_id_for(attribute(attr_name), shard).first
|
174
|
+
else
|
175
|
+
attribute(attr_name)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def connection_classes_for_reflection(reflection)
|
182
|
+
if reflection
|
183
|
+
if reflection.options[:polymorphic]
|
184
|
+
read_attribute(reflection.foreign_type)&.constantize&.connection_classes
|
185
|
+
else
|
186
|
+
reflection.klass.connection_classes
|
187
|
+
end
|
188
|
+
else
|
189
|
+
self.class.connection_classes
|
190
|
+
end
|
191
|
+
end
|
144
192
|
end
|
145
193
|
end
|
146
194
|
end
|
@@ -20,10 +20,22 @@ module Switchman
|
|
20
20
|
end
|
21
21
|
|
22
22
|
module Migrator
|
23
|
+
# significant change: hash shard name, not database name
|
23
24
|
def generate_migrator_advisory_lock_id
|
24
|
-
shard_name_hash = Zlib.crc32(
|
25
|
+
shard_name_hash = Zlib.crc32(Shard.current.name)
|
25
26
|
::ActiveRecord::Migrator::MIGRATOR_SALT * shard_name_hash
|
26
27
|
end
|
28
|
+
|
29
|
+
# significant change: strip out prefer_secondary from config
|
30
|
+
def with_advisory_lock_connection
|
31
|
+
pool = ::ActiveRecord::ConnectionAdapters::ConnectionHandler.new.establish_connection(
|
32
|
+
::ActiveRecord::Base.connection_db_config.configuration_hash.except(:prefer_secondary)
|
33
|
+
)
|
34
|
+
|
35
|
+
pool.with_connection { |connection| yield(connection) } # rubocop:disable Style/ExplicitBlockArgument
|
36
|
+
ensure
|
37
|
+
pool&.disconnect!
|
38
|
+
end
|
27
39
|
end
|
28
40
|
|
29
41
|
module MigrationContext
|
@@ -40,89 +40,36 @@ module Switchman
|
|
40
40
|
select_values('SELECT * FROM unnest(current_schemas(false))')
|
41
41
|
end
|
42
42
|
|
43
|
-
def tables(_name = nil)
|
44
|
-
query(<<-SQL, 'SCHEMA').map { |row| row[0] }
|
45
|
-
SELECT tablename
|
46
|
-
FROM pg_tables
|
47
|
-
WHERE schemaname = '#{shard.name}'
|
48
|
-
SQL
|
49
|
-
end
|
50
|
-
|
51
43
|
def extract_schema_qualified_name(string)
|
52
44
|
name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(string.to_s)
|
53
45
|
name.instance_variable_set(:@schema, shard.name) if string && !name.schema
|
54
46
|
[name.schema, name.identifier]
|
55
47
|
end
|
56
48
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
result = query(<<-SQL, 'SCHEMA')
|
75
|
-
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
|
76
|
-
FROM pg_class t
|
77
|
-
INNER JOIN pg_index d ON t.oid = d.indrelid
|
78
|
-
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
79
|
-
WHERE i.relkind = 'i'
|
80
|
-
AND d.indisprimary = 'f'
|
81
|
-
AND t.relname = '#{table_name}'
|
82
|
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
|
83
|
-
ORDER BY i.relname
|
84
|
-
SQL
|
85
|
-
|
86
|
-
result.map do |row|
|
87
|
-
index_name = row[0]
|
88
|
-
unique = row[1] == true || row[1] == 't'
|
89
|
-
indkey = row[2].split
|
90
|
-
inddef = row[3]
|
91
|
-
oid = row[4]
|
92
|
-
|
93
|
-
columns = Hash[query(<<-SQL, 'SCHEMA')] # rubocop:disable Style/HashConversion
|
94
|
-
SELECT a.attnum, a.attname
|
95
|
-
FROM pg_attribute a
|
96
|
-
WHERE a.attrelid = #{oid}
|
97
|
-
AND a.attnum IN (#{indkey.join(',')})
|
98
|
-
SQL
|
99
|
-
|
100
|
-
column_names = columns.stringify_keys.values_at(*indkey).compact
|
101
|
-
|
102
|
-
next if column_names.empty?
|
103
|
-
|
104
|
-
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
|
105
|
-
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
|
106
|
-
orders = desc_order_columns.any? ? Hash[desc_order_columns.map { |order_column| [order_column, :desc] }] : {} # rubocop:disable Style/HashConversion
|
107
|
-
where = inddef.scan(/WHERE (.+)$/).flatten[0]
|
108
|
-
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
|
109
|
-
|
110
|
-
::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names,
|
111
|
-
orders: orders, where: where, using: using)
|
112
|
-
end.compact
|
49
|
+
# significant change: use the shard name if no explicit schema
|
50
|
+
def quoted_scope(name = nil, type: nil)
|
51
|
+
schema, name = extract_schema_qualified_name(name)
|
52
|
+
type = \
|
53
|
+
case type # rubocop:disable Style/HashLikeCase
|
54
|
+
when 'BASE TABLE'
|
55
|
+
"'r','p'"
|
56
|
+
when 'VIEW'
|
57
|
+
"'v','m'"
|
58
|
+
when 'FOREIGN TABLE'
|
59
|
+
"'f'"
|
60
|
+
end
|
61
|
+
scope = {}
|
62
|
+
scope[:schema] = quote(schema || shard.name)
|
63
|
+
scope[:name] = quote(name) if name
|
64
|
+
scope[:type] = type if type
|
65
|
+
scope
|
113
66
|
end
|
114
67
|
|
115
|
-
def
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
INNER JOIN pg_class i ON d.indexrelid = i.oid
|
121
|
-
WHERE i.relkind = 'i'
|
122
|
-
AND i.relname = '#{index_name}'
|
123
|
-
AND t.relname = '#{table_name}'
|
124
|
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
|
125
|
-
SQL
|
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
|
72
|
+
end
|
126
73
|
end
|
127
74
|
|
128
75
|
def quote_local_table_name(name)
|
@@ -148,41 +95,6 @@ module Switchman
|
|
148
95
|
@use_local_table_name = old_value
|
149
96
|
end
|
150
97
|
|
151
|
-
def foreign_keys(table_name)
|
152
|
-
# mostly copy-pasted from AR - only change is to the nspname condition for qualified names support
|
153
|
-
fk_info = select_all <<-SQL.strip_heredoc
|
154
|
-
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
|
155
|
-
FROM pg_constraint c
|
156
|
-
JOIN pg_class t1 ON c.conrelid = t1.oid
|
157
|
-
JOIN pg_class t2 ON c.confrelid = t2.oid
|
158
|
-
JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
|
159
|
-
JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
|
160
|
-
JOIN pg_namespace t3 ON c.connamespace = t3.oid
|
161
|
-
WHERE c.contype = 'f'
|
162
|
-
AND t1.relname = #{quote(table_name)}
|
163
|
-
AND t3.nspname = '#{shard.name}'
|
164
|
-
ORDER BY c.conname
|
165
|
-
SQL
|
166
|
-
|
167
|
-
fk_info.map do |row|
|
168
|
-
options = {
|
169
|
-
column: row['column'],
|
170
|
-
name: row['name'],
|
171
|
-
primary_key: row['primary_key']
|
172
|
-
}
|
173
|
-
|
174
|
-
options[:on_delete] = extract_foreign_key_action(row['on_delete'])
|
175
|
-
options[:on_update] = extract_foreign_key_action(row['on_update'])
|
176
|
-
|
177
|
-
# strip the schema name from to_table if it matches
|
178
|
-
to_table = row['to_table']
|
179
|
-
to_table_qualified_name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(to_table)
|
180
|
-
to_table = to_table_qualified_name.identifier if to_table_qualified_name.schema == shard.name
|
181
|
-
|
182
|
-
::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, to_table, options)
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
98
|
def add_index_options(_table_name, _column_name, **)
|
187
99
|
index, algorithm, if_not_exists = super
|
188
100
|
algorithm = nil if DatabaseServer.creating_new_shard && algorithm == 'CONCURRENTLY'
|
@@ -113,7 +113,7 @@ module Switchman
|
|
113
113
|
end
|
114
114
|
|
115
115
|
def config(environment = :primary)
|
116
|
-
@configs[environment] ||=
|
116
|
+
@configs[environment] ||=
|
117
117
|
case @config[environment]
|
118
118
|
when Array
|
119
119
|
@config[environment].map do |config|
|
@@ -127,7 +127,6 @@ module Switchman
|
|
127
127
|
else
|
128
128
|
@config
|
129
129
|
end
|
130
|
-
end
|
131
130
|
end
|
132
131
|
|
133
132
|
def guard_rail_environment
|
@@ -214,7 +213,7 @@ module Switchman
|
|
214
213
|
::ActiveRecord::Migration.verbose = false
|
215
214
|
|
216
215
|
unless schema == false
|
217
|
-
shard.activate do
|
216
|
+
shard.activate(*Shard.sharded_models) do
|
218
217
|
reset_column_information
|
219
218
|
|
220
219
|
::ActiveRecord::Base.connection.transaction(requires_new: true) do
|
@@ -19,7 +19,7 @@ module Switchman
|
|
19
19
|
end
|
20
20
|
|
21
21
|
server1 = Shard.default.database_server
|
22
|
-
server2 = DatabaseServer.create(Shard.default.database_server.config)
|
22
|
+
server2 = DatabaseServer.create(Shard.default.database_server.config.merge(server2: true))
|
23
23
|
|
24
24
|
if server1 == Shard.default.database_server && server1.config[:shard1] && server1.config[:shard2]
|
25
25
|
# look for the shards in the db already
|
data/lib/switchman/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: switchman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cody Cutrer
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2021-
|
13
|
+
date: 2021-05-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -289,7 +289,6 @@ files:
|
|
289
289
|
- lib/switchman/open4.rb
|
290
290
|
- lib/switchman/r_spec_helper.rb
|
291
291
|
- lib/switchman/rails.rb
|
292
|
-
- lib/switchman/schema_cache.rb
|
293
292
|
- lib/switchman/sharded_instrumenter.rb
|
294
293
|
- lib/switchman/standard_error.rb
|
295
294
|
- lib/switchman/test_helper.rb
|
@@ -314,7 +313,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
314
313
|
- !ruby/object:Gem::Version
|
315
314
|
version: '0'
|
316
315
|
requirements: []
|
317
|
-
rubygems_version: 3.
|
316
|
+
rubygems_version: 3.2.15
|
318
317
|
signing_key:
|
319
318
|
specification_version: 4
|
320
319
|
summary: Rails sharding magic
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Switchman
|
4
|
-
class SchemaCache < ::ActiveRecord::ConnectionAdapters::SchemaCache
|
5
|
-
delegate :connection, to: :pool
|
6
|
-
attr_reader :pool
|
7
|
-
|
8
|
-
def initialize(pool)
|
9
|
-
@pool = pool
|
10
|
-
super(nil)
|
11
|
-
end
|
12
|
-
|
13
|
-
def copy_values(other_cache)
|
14
|
-
# use the same cached values but still fall back to the correct pool
|
15
|
-
%i[@columns @columns_hash @primary_keys @data_sources].each do |iv|
|
16
|
-
instance_variable_set(iv, other_cache.instance_variable_get(iv))
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|