switchman 3.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb89193fead8142e30280a6ab2706f6e9af798ff18820fa144be3dd9ac77591f
4
- data.tar.gz: e1157f5656f06ae9bdb7c62a2c9ae1611e2073cf47ca4278bae8605296603d21
3
+ metadata.gz: 8cfc736ed07b0e8b80b62928efb9e523a6e8e6ab31841ef4f7022ffbba7a7b57
4
+ data.tar.gz: 1879cbef2d1e8ab2d451fcf31ebe3b0a37c10e27eb1099ca96e73f58446adfae
5
5
  SHA512:
6
- metadata.gz: fbb9b4d9385dad87ba827400dc9245085c31e133d2445daf64b3d3e81f3102c189f8feafb328d085649146c8ccd2782fd7ba3cb410c262d06a31e014175cac99
7
- data.tar.gz: 55c8010cbc73d112262d674d131b1fe0acbd39b5b482e73b9b7165aac23a25a93d5bbca4320a56b97450f68af8526045aede39c13cfb3426189073acba8f6a3f
6
+ metadata.gz: c82b93120355776072460d8e2ec1a648ed76454ed178ee3a102b66303dc9716b23bdea855a8047995c454b2528bbe580ceccba43a36edc4afb2d4458e984b493
7
+ data.tar.gz: a97fb461a01e06ad8498635382d316ca84e0830a54984e45ce4c1cb575c3ff11e121185c67618246d175ff61eee2c1931acc5b48876235d65b6802a349621077
@@ -690,6 +690,7 @@ module Switchman
690
690
  Switchman.cache.delete(['shard', id].join('/'))
691
691
  Switchman.cache.delete('default_shard') if default?
692
692
  end
693
+ self.class.clear_cache
693
694
  end
694
695
 
695
696
  def default_name
@@ -10,6 +10,6 @@ class AddDefaultShardIndex < ActiveRecord::Migration[4.2]
10
10
  else
11
11
  {}
12
12
  end
13
- add_index :switchman_shards, :default, options
13
+ add_index :switchman_shards, :default, **options
14
14
  end
15
15
  end
@@ -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("#{Shard.current.id}:#{Shard.current.name}")
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
- def view_exists?(name)
58
- name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
59
- return false unless name.identifier
60
-
61
- name.instance_variable_set(:@schema, shard.name) unless name.schema
62
-
63
- select_values(<<-SQL, 'SCHEMA').any?
64
- SELECT c.relname
65
- FROM pg_class c
66
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
67
- WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
68
- AND c.relname = '#{name.identifier}'
69
- AND n.nspname = '#{shard.name}'
70
- SQL
71
- end
72
-
73
- def indexes(table_name)
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 index_name_exists?(table_name, index_name, _default = nil)
116
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i.positive?
117
- SELECT COUNT(*)
118
- FROM pg_class t
119
- INNER JOIN pg_index d ON t.oid = d.indrelid
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] ||= begin
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Switchman
4
- VERSION = '3.0.0'
4
+ VERSION = '3.0.1'
5
5
  end
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.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-03-17 00:00:00.000000000 Z
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.1.4
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