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 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