switchman 2.0.3 → 2.0.9

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: 5205c32993056a0fa71d272a95c5306d17762b31971c6242b0fd40e3b587a591
4
- data.tar.gz: fd445dbeb2e0654e591a461637fa3720a7b335d1307e6ecf289ce88caac17d2d
3
+ metadata.gz: af9f8893be986771668662ceca8d3e36e28f5babf889a18e1e3efc237f7eae66
4
+ data.tar.gz: 675bd5b18076e8da86feb8a6e6271fffc1cd27939dedc1eb51c61bf165122b63
5
5
  SHA512:
6
- metadata.gz: a7db2fab2fb62cae74a845bff7373584c75057e2e9464ffacc130d053e78b6949e6c971aa29372b0f31ef6d25aba389b1c420fc601e18ae7cd8f1ac89b6b85e5
7
- data.tar.gz: aa2fcb0523b80507b35f5e2b9cdf14f3793b8354ba26bf8dddd000d6da7d6a4abfc50eb7ef45b4013516282111aabe3dd31b264764378fc9abbc1cb1f5bee9e9
6
+ metadata.gz: aadb4a342920dfe24e6110546380aa83a9aa4f61722e8c4c7265d0bf4494a2e8e04975d00504f7a26f143b87595bdb8c47ea09b4f9344f4925af08055c9d23e5
7
+ data.tar.gz: 295a3b4490f511380dcaeb44a6a6985cdf97745e678054eaf46315fc89d3adafd7a84a7a3665ec4521a6baa3b9f787adfccdc6d814baab10999c148b75041773
@@ -718,6 +718,7 @@ module Switchman
718
718
  Switchman.cache.delete(['shard', id].join('/'))
719
719
  Switchman.cache.delete("default_shard") if default?
720
720
  end
721
+ self.class.clear_cache
721
722
  end
722
723
 
723
724
  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
@@ -135,7 +135,7 @@ module Switchman
135
135
  primary_pool = retrieve_connection_pool("primary")
136
136
  if primary_pool.is_a?(ConnectionPoolProxy)
137
137
  pool = ConnectionPoolProxy.new(spec_name.to_sym, primary_pool.default_pool, @shard_connection_pools)
138
- pool.schema_cache.copy_values(primary_pool.schema_cache)
138
+ pool.schema_cache.copy_references(primary_pool.schema_cache)
139
139
  pool
140
140
  else
141
141
  primary_pool
@@ -30,10 +30,42 @@ module Switchman
30
30
  end
31
31
 
32
32
  module Migrator
33
+ # significant change: hash shard id, not database name
33
34
  def generate_migrator_advisory_lock_id
34
35
  shard_name_hash = Zlib.crc32(Shard.current.name)
35
36
  ::ActiveRecord::Migrator::MIGRATOR_SALT * shard_name_hash
36
37
  end
38
+
39
+ if ::Rails.version >= '6.0'
40
+ # copy/paste from Rails 6.1
41
+ def with_advisory_lock
42
+ lock_id = generate_migrator_advisory_lock_id
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
57
+
58
+ # significant change: strip out prefer_secondary from config
59
+ def with_advisory_lock_connection
60
+ pool = ::ActiveRecord::ConnectionAdapters::ConnectionHandler.new.establish_connection(
61
+ ::ActiveRecord::Base.connection_config.except(:prefer_secondary)
62
+ )
63
+
64
+ pool.with_connection { |connection| yield(connection) }
65
+ ensure
66
+ pool&.disconnect!
67
+ end
68
+ end
37
69
  end
38
70
 
39
71
  module MigrationContext
@@ -40,14 +40,6 @@ 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
  if string && !name.schema
@@ -56,80 +48,155 @@ module Switchman
56
48
  [name.schema, name.identifier]
57
49
  end
58
50
 
59
- def view_exists?(name)
60
- name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
61
- return false unless name.identifier
62
- if !name.schema
63
- name.instance_variable_set(:@schema, shard.name)
64
- end
65
-
66
- select_values(<<-SQL, 'SCHEMA').any?
67
- SELECT c.relname
68
- FROM pg_class c
69
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
70
- WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
71
- AND c.relname = '#{name.identifier}'
72
- AND n.nspname = '#{shard.name}'
73
- SQL
51
+ # significant change: use the shard name if no explicit schema
52
+ def quoted_scope(name = nil, type: nil)
53
+ schema, name = extract_schema_qualified_name(name)
54
+ type = \
55
+ case type
56
+ when "BASE TABLE"
57
+ "'r','p'"
58
+ when "VIEW"
59
+ "'v','m'"
60
+ when "FOREIGN TABLE"
61
+ "'f'"
62
+ end
63
+ scope = {}
64
+ scope[:schema] = quote(schema || shard.name)
65
+ scope[:name] = quote(name) if name
66
+ scope[:type] = type if type
67
+ scope
74
68
  end
75
69
 
76
- def indexes(table_name)
77
- result = query(<<-SQL, 'SCHEMA')
78
- SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
79
- FROM pg_class t
80
- INNER JOIN pg_index d ON t.oid = d.indrelid
81
- INNER JOIN pg_class i ON d.indexrelid = i.oid
82
- WHERE i.relkind = 'i'
83
- AND d.indisprimary = 'f'
84
- AND t.relname = '#{table_name}'
85
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
86
- ORDER BY i.relname
87
- SQL
88
-
89
-
90
- result.map do |row|
91
- index_name = row[0]
92
- unique = row[1] == true || row[1] == 't'
93
- indkey = row[2].split(" ")
94
- inddef = row[3]
95
- oid = row[4]
96
-
97
- columns = Hash[query(<<-SQL, "SCHEMA")]
98
- SELECT a.attnum, a.attname
99
- FROM pg_attribute a
100
- WHERE a.attrelid = #{oid}
101
- AND a.attnum IN (#{indkey.join(",")})
70
+ if ::Rails.version < '6.0'
71
+ def tables(name = nil)
72
+ query(<<-SQL, 'SCHEMA').map { |row| row[0] }
73
+ SELECT tablename
74
+ FROM pg_tables
75
+ WHERE schemaname = '#{shard.name}'
102
76
  SQL
77
+ end
103
78
 
104
- column_names = columns.stringify_keys.values_at(*indkey).compact
105
-
106
- unless column_names.empty?
107
- # add info on sort order for columns (only desc order is explicitly specified, asc is the default)
108
- desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
109
- orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
110
- where = inddef.scan(/WHERE (.+)$/).flatten[0]
111
- using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
112
-
113
- if ::Rails.version >= "5.2"
114
- ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names, orders: orders, where: where, using: using)
115
- else
116
- ::ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
117
- end
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)
118
84
  end
119
- end.compact
120
- end
121
85
 
122
- def index_name_exists?(table_name, index_name, _default = nil)
123
- exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
124
- SELECT COUNT(*)
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
125
99
  FROM pg_class t
126
100
  INNER JOIN pg_index d ON t.oid = d.indrelid
127
101
  INNER JOIN pg_class i ON d.indexrelid = i.oid
128
102
  WHERE i.relkind = 'i'
129
- AND i.relname = '#{index_name}'
103
+ AND d.indisprimary = 'f'
130
104
  AND t.relname = '#{table_name}'
131
105
  AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
132
- SQL
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
199
+ end
133
200
  end
134
201
 
135
202
  def quote_local_table_name(name)
@@ -156,44 +223,6 @@ module Switchman
156
223
  @use_local_table_name = old_value
157
224
  end
158
225
 
159
- def foreign_keys(table_name)
160
-
161
- # mostly copy-pasted from AR - only change is to the nspname condition for qualified names support
162
- fk_info = select_all <<-SQL.strip_heredoc
163
- 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
164
- FROM pg_constraint c
165
- JOIN pg_class t1 ON c.conrelid = t1.oid
166
- JOIN pg_class t2 ON c.confrelid = t2.oid
167
- JOIN pg_attribute a1 ON a1.attnum = c.conkey[1] AND a1.attrelid = t1.oid
168
- JOIN pg_attribute a2 ON a2.attnum = c.confkey[1] AND a2.attrelid = t2.oid
169
- JOIN pg_namespace t3 ON c.connamespace = t3.oid
170
- WHERE c.contype = 'f'
171
- AND t1.relname = #{quote(table_name)}
172
- AND t3.nspname = '#{shard.name}'
173
- ORDER BY c.conname
174
- SQL
175
-
176
- fk_info.map do |row|
177
- options = {
178
- column: row['column'],
179
- name: row['name'],
180
- primary_key: row['primary_key']
181
- }
182
-
183
- options[:on_delete] = extract_foreign_key_action(row['on_delete'])
184
- options[:on_update] = extract_foreign_key_action(row['on_update'])
185
-
186
- # strip the schema name from to_table if it matches
187
- to_table = row['to_table']
188
- to_table_qualified_name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(to_table)
189
- if to_table_qualified_name.schema == shard.name
190
- to_table = to_table_qualified_name.identifier
191
- end
192
-
193
- ::ActiveRecord::ConnectionAdapters::ForeignKeyDefinition.new(table_name, to_table, options)
194
- end
195
- end
196
-
197
226
  def add_index_options(_table_name, _column_name, **)
198
227
  index_name, index_type, index_columns, index_options, algorithm, using = super
199
228
  algorithm = nil if DatabaseServer.creating_new_shard && algorithm == "CONCURRENTLY"
@@ -84,6 +84,10 @@ module Switchman
84
84
  @schema_cache
85
85
  end
86
86
 
87
+ def set_schema_cache(cache)
88
+ @schema_cache.copy_values(cache)
89
+ end
90
+
87
91
  %w{release_connection
88
92
  disconnect!
89
93
  flush!
@@ -5,14 +5,22 @@ module Switchman
5
5
  delegate :connection, to: :pool
6
6
  attr_reader :pool
7
7
 
8
+ SHARED_IVS = %i{@columns @columns_hash @primary_keys @data_sources @indexes}.freeze
9
+
8
10
  def initialize(pool)
9
11
  @pool = pool
10
12
  super(nil)
11
13
  end
12
14
 
13
15
  def copy_values(other_cache)
16
+ SHARED_IVS.each do |iv|
17
+ instance_variable_get(iv).replace(other_cache.instance_variable_get(iv))
18
+ end
19
+ end
20
+
21
+ def copy_references(other_cache)
14
22
  # use the same cached values but still fall back to the correct pool
15
- [:@columns, :@columns_hash, :@primary_keys, :@data_sources].each do |iv|
23
+ SHARED_IVS.each do |iv|
16
24
  instance_variable_set(iv, other_cache.instance_variable_get(iv))
17
25
  end
18
26
  end
@@ -8,7 +8,7 @@ module Switchman
8
8
  end
9
9
 
10
10
  def current_shard(category = :primary)
11
- @active_shards[category] || Shard.default
11
+ @active_shards&.[](category) || Shard.default
12
12
  end
13
13
  end
14
14
  end
@@ -24,7 +24,7 @@ module Switchman
24
24
  else
25
25
  server1 = Shard.default.database_server
26
26
  end
27
- server2 = DatabaseServer.create(Shard.default.database_server.config)
27
+ server2 = DatabaseServer.create(Shard.default.database_server.config.merge(server2: true))
28
28
 
29
29
  if server1 == Shard.default.database_server && server1.config[:shard1] && server1.config[:shard2]
30
30
  # 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 = "2.0.3"
4
+ VERSION = "2.0.9"
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: 2.0.3
4
+ version: 2.0.9
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-01-21 00:00:00.000000000 Z
13
+ date: 2021-04-16 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: railties
@@ -272,7 +272,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
272
272
  - !ruby/object:Gem::Version
273
273
  version: '0'
274
274
  requirements: []
275
- rubygems_version: 3.1.4
275
+ rubygems_version: 3.2.15
276
276
  signing_key:
277
277
  specification_version: 4
278
278
  summary: Rails sharding magic