switchman 1.15.0 → 1.16.0

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: 6521b8f2a993bab5cb400bdc0fa741fd6e2391e7efb9801f058f21fe3c75169f
4
- data.tar.gz: e96468030c6d98b3c93730877ba6943d5c925255f59ed4a09c3ddfa0bce7388a
3
+ metadata.gz: 70787591a2f55963ba4b74b816cb11428d1d764e87d3635309d98b423ed842e1
4
+ data.tar.gz: 5516037d6c39f1215eac8764512d97125dbeaf2cb64aeed234f5a4d55ce18650
5
5
  SHA512:
6
- metadata.gz: 32b791ddb7c5198a5fcd96448ee0abe59d011f0c422ba9115df5b44e50a53c8a6f4ac1bea35e2a98db52cbfb5f46cb501dbf30dde123125bfef599f67c1ccd86
7
- data.tar.gz: ca11fa309c8f3c5bd7f9a9466327e95fddcff2342dc15d7732adaecab09911a2f9bfc0b2ed0d90cdd8899bf470d3a776945b86a470b8c84f5845878f9b46df0c
6
+ metadata.gz: bad00cb3a194c18f792c214caa3f47f90ac53b7186ea458775b9562e1bf2b8fa898e6317ca4514722ce3a9e8652061101e15f657556f2c95344c6ecd5284bb34
7
+ data.tar.gz: '0697c43ffd9934ddfb9d0640295024e1035297ac7f14e9a3018a80f0170e427904924d4efa42892a5cf2f223aa75490c9ccd0da500c7040c4c53da4b04f8d9a7'
@@ -425,6 +425,19 @@ module Switchman
425
425
  end
426
426
  end
427
427
 
428
+ # it's tedious to hold onto this same
429
+ # kind of sign state and transform the
430
+ # result in multiple places, so
431
+ # here we can operate on the absolute value
432
+ # in a provided block and trust the sign will
433
+ # stay as provided. This assumes no consumer
434
+ # will return a nil value from the block.
435
+ def signed_id_operation(input_id)
436
+ sign = input_id < 0 ? -1 : 1
437
+ output = yield input_id.abs
438
+ output * sign
439
+ end
440
+
428
441
  # converts an AR object, integral id, string id, or string short-global-id to a
429
442
  # integral id. nil if it can't be interpreted
430
443
  def integral_id_for(any_id)
@@ -437,12 +450,13 @@ module Switchman
437
450
  case any_id
438
451
  when ::ActiveRecord::Base
439
452
  any_id.id
440
- when /^(\d+)~(\d+)$/
453
+ when /^(\d+)~(-?\d+)$/
441
454
  local_id = $2.to_i
442
- # doesn't make sense to have a double-global id
443
- return nil if local_id > IDS_PER_SHARD
444
- $1.to_i * IDS_PER_SHARD + local_id
445
- when Integer, /^\d+$/
455
+ signed_id_operation(local_id) do |id|
456
+ return nil if id > IDS_PER_SHARD
457
+ $1.to_i * IDS_PER_SHARD + id
458
+ end
459
+ when Integer, /^-?\d+$/
446
460
  any_id.to_i
447
461
  else
448
462
  nil
@@ -457,13 +471,17 @@ module Switchman
457
471
  def local_id_for(any_id)
458
472
  id = integral_id_for(any_id)
459
473
  return NIL_NIL_ID unless id
460
- if id < IDS_PER_SHARD
461
- [id, nil]
462
- elsif shard = lookup(id / IDS_PER_SHARD)
463
- [id % IDS_PER_SHARD, shard]
464
- else
465
- NIL_NIL_ID
474
+ return_shard = nil
475
+ local_id = signed_id_operation(id) do |abs_id|
476
+ if abs_id < IDS_PER_SHARD
477
+ abs_id
478
+ elsif return_shard = lookup(abs_id / IDS_PER_SHARD)
479
+ abs_id % IDS_PER_SHARD
480
+ else
481
+ return NIL_NIL_ID
482
+ end
466
483
  end
484
+ [local_id, return_shard]
467
485
  end
468
486
 
469
487
  # takes an id-ish, and returns an integral id relative to
@@ -494,11 +512,13 @@ module Switchman
494
512
  def global_id_for(any_id, source_shard = nil)
495
513
  id = integral_id_for(any_id)
496
514
  return any_id unless id
497
- if id >= IDS_PER_SHARD
498
- id
499
- else
500
- source_shard ||= Shard.current
501
- source_shard.global_id_for(id)
515
+ signed_id_operation(id) do |abs_id|
516
+ if abs_id >= IDS_PER_SHARD
517
+ abs_id
518
+ else
519
+ source_shard ||= Shard.current
520
+ source_shard.global_id_for(abs_id)
521
+ end
502
522
  end
503
523
  end
504
524
 
@@ -671,7 +691,9 @@ module Switchman
671
691
  # takes an id local to this shard, and returns a global id
672
692
  def global_id_for(local_id)
673
693
  return nil unless local_id
674
- local_id + self.id * IDS_PER_SHARD
694
+ self.class.signed_id_operation(local_id) do |abs_id|
695
+ abs_id + self.id * IDS_PER_SHARD
696
+ end
675
697
  end
676
698
 
677
699
  # skip global_id.hash
@@ -27,10 +27,6 @@ module Switchman
27
27
  quote_table_name(name)
28
28
  end
29
29
 
30
- def use_qualified_names?
31
- false
32
- end
33
-
34
30
  protected
35
31
 
36
32
  def log(*args, &block)
@@ -155,6 +155,15 @@ module Switchman
155
155
  self.class.connection.quote(id)
156
156
  end
157
157
 
158
+ def update_columns(*)
159
+ db = Shard.current(self.class.shard_category).database_server
160
+ if ::Shackles.environment != db.shackles_environment
161
+ return db.unshackle { super }
162
+ else
163
+ super
164
+ end
165
+ end
166
+
158
167
  protected
159
168
 
160
169
  # see also AttributeMethods#shard_category_code_for_reflection
@@ -4,8 +4,6 @@ module Switchman
4
4
  module ActiveRecord
5
5
  module ConnectionHandler
6
6
  def self.make_sharing_automagic(config, shard = Shard.current)
7
- key = config[:adapter] == 'postgresql' ? :schema_search_path : :database
8
-
9
7
  # only load the shard name from the db if we have to
10
8
  if !config[:shard_name]
11
9
  # we may not be able to connect to this shard yet, cause it might be an empty database server
@@ -15,12 +13,6 @@ module Switchman
15
13
 
16
14
  config[:shard_name] ||= shard_name
17
15
  end
18
-
19
- if !config[key] || config[key] == shard_name
20
- # this may truncate the schema_search_path if it was not specified in database.yml
21
- # but that's what our old behavior was anyway, so I guess it's okay
22
- config[key] = '%{shard_name}'
23
- end
24
16
  end
25
17
 
26
18
  def establish_connection(spec)
@@ -84,18 +84,6 @@ module Switchman
84
84
  end
85
85
 
86
86
  spec.config[:shard_name] = self.shard.name
87
- case conn.adapter_name
88
- when 'MySQL', 'Mysql2'
89
- conn.execute("USE #{spec.config[:database]}")
90
- when 'PostgreSQL'
91
- if conn.schema_search_path != spec.config[:schema_search_path]
92
- conn.schema_search_path = spec.config[:schema_search_path]
93
- end
94
- when 'SQLite'
95
- # This is an artifact of the adapter modifying the path to be an absolute path when it is instantiated; just let it slide
96
- else
97
- raise("Cannot switch databases on same DatabaseServer with adapter type: #{conn.adapter_name}. Limit one Shard per DatabaseServer.")
98
- end
99
87
  conn.shard = shard
100
88
  end
101
89
 
@@ -33,5 +33,14 @@ module Switchman
33
33
  ::ActiveRecord::Migrator::MIGRATOR_SALT * shard_name_hash
34
34
  end
35
35
  end
36
+
37
+ module MigrationContext
38
+ def migrations
39
+ return @migrations if instance_variable_defined?(:@migrations)
40
+ migrations_cache = Thread.current[:migrations_cache] ||= {}
41
+ key = Digest::MD5.hexdigest(migration_files.sort.join(','))
42
+ @migrations = migrations_cache[key] ||= super
43
+ end
44
+ end
36
45
  end
37
46
  end
@@ -38,23 +38,17 @@ module Switchman
38
38
  select_values("SELECT * FROM unnest(current_schemas(false))")
39
39
  end
40
40
 
41
- def use_qualified_names?
42
- @config[:use_qualified_names]
43
- end
44
-
45
41
  def tables(name = nil)
46
- schema = shard.name if use_qualified_names?
47
-
48
42
  query(<<-SQL, 'SCHEMA').map { |row| row[0] }
49
43
  SELECT tablename
50
44
  FROM pg_tables
51
- WHERE schemaname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
45
+ WHERE schemaname = '#{shard.name}'
52
46
  SQL
53
47
  end
54
48
 
55
49
  def extract_schema_qualified_name(string)
56
50
  name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(string.to_s)
57
- if string && !name.schema && use_qualified_names?
51
+ if string && !name.schema
58
52
  name.instance_variable_set(:@schema, shard.name)
59
53
  end
60
54
  [name.schema, name.identifier]
@@ -63,7 +57,7 @@ module Switchman
63
57
  def view_exists?(name)
64
58
  name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
65
59
  return false unless name.identifier
66
- if !name.schema && use_qualified_names?
60
+ if !name.schema
67
61
  name.instance_variable_set(:@schema, shard.name)
68
62
  end
69
63
 
@@ -73,13 +67,11 @@ module Switchman
73
67
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
74
68
  WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view
75
69
  AND c.relname = '#{name.identifier}'
76
- AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
70
+ AND n.nspname = '#{shard.name}'
77
71
  SQL
78
72
  end
79
73
 
80
74
  def indexes(table_name)
81
- schema = shard.name if use_qualified_names?
82
-
83
75
  result = query(<<-SQL, 'SCHEMA')
84
76
  SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
85
77
  FROM pg_class t
@@ -88,7 +80,7 @@ module Switchman
88
80
  WHERE i.relkind = 'i'
89
81
  AND d.indisprimary = 'f'
90
82
  AND t.relname = '#{table_name}'
91
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'} )
83
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
92
84
  ORDER BY i.relname
93
85
  SQL
94
86
 
@@ -126,8 +118,6 @@ module Switchman
126
118
  end
127
119
 
128
120
  def index_name_exists?(table_name, index_name, _default = nil)
129
- schema = shard.name if use_qualified_names?
130
-
131
121
  exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
132
122
  SELECT COUNT(*)
133
123
  FROM pg_class t
@@ -136,7 +126,7 @@ module Switchman
136
126
  WHERE i.relkind = 'i'
137
127
  AND i.relname = '#{index_name}'
138
128
  AND t.relname = '#{table_name}'
139
- AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'} )
129
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
140
130
  SQL
141
131
  end
142
132
 
@@ -150,7 +140,7 @@ module Switchman
150
140
  def quote_table_name(name)
151
141
  return quote_local_table_name(name) if @use_local_table_name
152
142
  name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(name.to_s)
153
- if !name.schema && use_qualified_names?
143
+ if !name.schema
154
144
  name.instance_variable_set(:@schema, shard.name)
155
145
  end
156
146
  name.quoted
@@ -165,7 +155,6 @@ module Switchman
165
155
  end
166
156
 
167
157
  def foreign_keys(table_name)
168
- schema = shard.name if use_qualified_names?
169
158
 
170
159
  # mostly copy-pasted from AR - only change is to the nspname condition for qualified names support
171
160
  fk_info = select_all <<-SQL.strip_heredoc
@@ -178,7 +167,7 @@ module Switchman
178
167
  JOIN pg_namespace t3 ON c.connamespace = t3.oid
179
168
  WHERE c.contype = 'f'
180
169
  AND t1.relname = #{quote(table_name)}
181
- AND t3.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
170
+ AND t3.nspname = '#{shard.name}'
182
171
  ORDER BY c.conname
183
172
  SQL
184
173
 
@@ -195,7 +184,7 @@ module Switchman
195
184
  # strip the schema name from to_table if it matches
196
185
  to_table = row['to_table']
197
186
  to_table_qualified_name = ::ActiveRecord::ConnectionAdapters::PostgreSQL::Utils.extract_schema_qualified_name(to_table)
198
- if use_qualified_names? && to_table_qualified_name.schema == shard.name
187
+ if to_table_qualified_name.schema == shard.name
199
188
  to_table = to_table_qualified_name.identifier
200
189
  end
201
190
 
@@ -46,29 +46,16 @@ module Switchman
46
46
  bind_values = bind_map.bind(params, current_shard, target_shard)
47
47
 
48
48
  target_shard.activate(klass.shard_category) do
49
- if connection.use_qualified_names?
50
- sql = qualified_query_builder(target_shard, klass).sql_for(bind_values, connection)
51
- klass.find_by_sql(sql, bind_values)
52
- else
53
- sql = generic_query_builder(connection).sql_for(bind_values, connection)
54
- klass.find_by_sql(sql, bind_values)
55
- end
49
+ sql = qualified_query_builder(target_shard, klass).sql_for(bind_values, connection)
50
+ klass.find_by_sql(sql, bind_values)
56
51
  end
57
52
  end
58
53
 
59
54
  if ::Rails.version < '5.2'
60
- def generic_query_builder(connection)
61
- @query_builder ||= connection.cacheable_query(self.class, @arel)
62
- end
63
-
64
55
  def qualified_query_builder(shard, klass)
65
56
  @qualified_query_builders[shard.id] ||= klass.connection.cacheable_query(self.class, @arel)
66
57
  end
67
58
  else
68
- def generic_query_builder(connection)
69
- @query_builder ||= connection.cacheable_query(self.class, @arel).first
70
- end
71
-
72
59
  def qualified_query_builder(shard, klass)
73
60
  @qualified_query_builders[shard.id] ||= klass.connection.cacheable_query(self.class, @arel).first
74
61
  end
@@ -122,6 +122,7 @@ module Switchman
122
122
  ::ActiveRecord::LogSubscriber.prepend(ActiveRecord::LogSubscriber)
123
123
  ::ActiveRecord::Migration.prepend(ActiveRecord::Migration)
124
124
  ::ActiveRecord::Migration::Compatibility::V5_0.prepend(ActiveRecord::Migration::Compatibility::V5_0)
125
+ ::ActiveRecord::MigrationContext.prepend(ActiveRecord::MigrationContext) if ::Rails.version >= '5.2'
125
126
  ::ActiveRecord::Migrator.prepend(ActiveRecord::Migrator)
126
127
 
127
128
  ::ActiveRecord::Reflection::AbstractReflection.include(ActiveRecord::Reflection::AbstractReflection)
@@ -1,3 +1,3 @@
1
1
  module Switchman
2
- VERSION = "1.15.0"
2
+ VERSION = "1.16.0"
3
3
  end
@@ -218,16 +218,9 @@ module Switchman
218
218
  args = ['-s', '-x', '-O', '-f', filename]
219
219
  args.concat(Array(extra_flags)) if extra_flags
220
220
  search_path = configuration['schema_search_path']
221
- if configuration['use_qualified_names']
222
- shard = Shard.current.name
223
- serialized_search_path = shard
224
- args << "--schema=#{Shellwords.escape(shard)}"
225
- elsif !search_path.blank?
226
- args << search_path.split(',').map do |part|
227
- "--schema=#{part.strip}"
228
- end.join(' ')
229
- serialized_search_path = connection.schema_search_path
230
- end
221
+ shard = Shard.current.name
222
+ serialized_search_path = shard
223
+ args << "--schema=#{Shellwords.escape(shard)}"
231
224
 
232
225
  args << configuration['database']
233
226
  run_cmd('pg_dump', args, 'dumping')
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: switchman
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.15.0
4
+ version: 1.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
8
8
  - James Williams
9
9
  - Jacob Fugal
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-05-18 00:00:00.000000000 Z
13
+ date: 2020-09-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: railties
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: pg
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -229,7 +243,7 @@ homepage: http://www.instructure.com/
229
243
  licenses:
230
244
  - MIT
231
245
  metadata: {}
232
- post_install_message:
246
+ post_install_message:
233
247
  rdoc_options: []
234
248
  require_paths:
235
249
  - lib
@@ -245,7 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
245
259
  version: '0'
246
260
  requirements: []
247
261
  rubygems_version: 3.0.3
248
- signing_key:
262
+ signing_key:
249
263
  specification_version: 4
250
264
  summary: Rails 4 sharding magic
251
265
  test_files: []