switchman 2.0.13 → 2.1.3

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: de9d4ea9f0368bf1bfe15fdb8dd6bef040a8ec107aed386eaa81f8fa444513df
4
- data.tar.gz: 71b7905b40d59a4767576552c8f5d16a2b3bdaec6d910f81f18d803a9c916256
3
+ metadata.gz: 62fa2df72b999208e937ba7df468f122159e573d3e838cf86df601592e64fb0c
4
+ data.tar.gz: 20e83c56b8043a8d5e9fbc433db4b1a26a6ec7291389c59463a47ea2ed0b0b87
5
5
  SHA512:
6
- metadata.gz: bac07aa4f15521f123a1d95f676448d649c71eddd86fdfbbb57504f59d0c71f9ce69618aa7d1e25e93fdc278708cb2a7385d111417f23a8fc0eb1e9b6f6eefb3
7
- data.tar.gz: 1c091615da88fe2f54de51de1e740336007fd13d66d0cac0b977d0e2770a6dfacd6a85bd12b689a0c610b1de5daeab4512743f0c50199e0f610806cc2f4dd629
6
+ metadata.gz: 7c2cdc6f074ddb755b8a80dad67a2828a8f52d2c034f9ec7bdf473d2e2241cf21b80969cc3f6d0dcc33b6713f299ea976370800640a54839d0895cd41c29f01a
7
+ data.tar.gz: 68651c29f71fad57c1e6bbc02b35c6e607a98bc708045841b547c07de4f0ce63048d4a22b22c561d4692a728adc8740fd139ef4d88e5e777cf593e1de679ca9b
@@ -18,7 +18,7 @@ module Switchman
18
18
  :unsharded => [Shard]
19
19
  }
20
20
  private_constant :CATEGORIES
21
- @connection_specification_name = @shard_category = :unsharded
21
+ @connection_specification_name = 'unsharded'
22
22
 
23
23
  if defined?(::ProtectedAttributes)
24
24
  attr_accessible :default, :name, :database_server
@@ -283,17 +283,28 @@ module Switchman
283
283
 
284
284
  with_each_shard(subscope, categories, options) { yield }
285
285
  exception_pipe.last.close
286
- rescue => e
286
+ rescue Exception => e
287
287
  begin
288
288
  dumped = Marshal.dump(e)
289
+ dumped = nil if dumped.length > 64 * 1024
289
290
  rescue
291
+ dumped = nil
292
+ end
293
+
294
+ if dumped.nil?
290
295
  # couldn't dump the exception; create a copy with just
291
296
  # the message and the backtrace
292
297
  e2 = e.class.new(e.message)
293
- e2.set_backtrace(e.backtrace)
298
+ backtrace = e.backtrace
299
+ # truncate excessively long backtraces
300
+ if backtrace.length > 50
301
+ backtrace = backtrace[0...25] + ['...'] + backtrace[-25..-1]
302
+ end
303
+ e2.set_backtrace(backtrace)
294
304
  e2.instance_variable_set(:@active_shards, e.instance_variable_get(:@active_shards))
295
305
  dumped = Marshal.dump(e2)
296
306
  end
307
+
297
308
  exception_pipe.last.set_encoding(dumped.encoding)
298
309
  exception_pipe.last.write(dumped)
299
310
  exception_pipe.last.flush
@@ -38,11 +38,15 @@ module Switchman
38
38
  def define_method_global_attribute(attr_name)
39
39
  if sharded_column?(attr_name)
40
40
  generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
41
- def __temp__
41
+ def __temp_global_attribute__
42
+ raw_value = original_#{attr_name}
43
+ return nil if raw_value.nil?
44
+ return raw_value if raw_value > Shard::IDS_PER_SHARD
45
+
42
46
  Shard.global_id_for(original_#{attr_name}, shard)
43
47
  end
44
- alias_method 'global_#{attr_name}', :__temp__
45
- undef_method :__temp__
48
+ alias_method 'global_#{attr_name}', :__temp_global_attribute__
49
+ undef_method :__temp_global_attribute__
46
50
  RUBY
47
51
  else
48
52
  define_method_unsharded_column(attr_name, 'global')
@@ -52,11 +56,13 @@ module Switchman
52
56
  def define_method_local_attribute(attr_name)
53
57
  if sharded_column?(attr_name)
54
58
  generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
55
- def __temp__
56
- Shard.local_id_for(original_#{attr_name}).first
59
+ def __temp_local_attribute__
60
+ raw_value = original_#{attr_name}
61
+ return nil if raw_value.nil?
62
+ return raw_value % Shard::IDS_PER_SHARD
57
63
  end
58
- alias_method 'local_#{attr_name}', :__temp__
59
- undef_method :__temp__
64
+ alias_method 'local_#{attr_name}', :__temp_local_attribute__
65
+ undef_method :__temp_local_attribute__
60
66
  RUBY
61
67
  else
62
68
  define_method_unsharded_column(attr_name, 'local')
@@ -94,18 +100,33 @@ module Switchman
94
100
  # rename the original method to original_
95
101
  alias_method 'original_#{attr_name}', '#{attr_name}'
96
102
  # and replace with one that transposes the id
97
- def __temp__
98
- Shard.relative_id_for(original_#{attr_name}, shard, Shard.current(#{shard_category_code_for_reflection(reflection)}))
103
+ def __temp_relative_attribute__
104
+ raw_value = original_#{attr_name}
105
+ return nil if raw_value.nil?
106
+
107
+ abs_raw_value = raw_value.abs
108
+ current_shard = Shard.current(#{shard_category_code_for_reflection(reflection)})
109
+ same_shard = shard == current_shard
110
+ return raw_value if same_shard && abs_raw_value < Shard::IDS_PER_SHARD
111
+
112
+ value_shard_id = abs_raw_value / Shard::IDS_PER_SHARD
113
+ # this is a stupid case when someone stuffed a global id for the current shard in instead
114
+ # of a local id
115
+ return raw_value % Shard::IDS_PER_SHARD if value_shard_id == current_shard.id
116
+ return raw_value if !same_shard && abs_raw_value > Shard::IDS_PER_SHARD
117
+ return shard.global_id_for(raw_value) if !same_shard && abs_raw_value < Shard::IDS_PER_SHARD
118
+
119
+ Shard.relative_id_for(raw_value, shard, current_shard)
99
120
  end
100
- alias_method '#{attr_name}', :__temp__
101
- undef_method :__temp__
121
+ alias_method '#{attr_name}', :__temp_relative_attribute__
122
+ undef_method :__temp_relative_attribute__
102
123
 
103
124
  alias_method 'original_#{attr_name}=', '#{attr_name}='
104
- def __temp__(new_value)
125
+ def __temp_relative_attribute_assignment__(new_value)
105
126
  self.original_#{attr_name} = Shard.relative_id_for(new_value, Shard.current(#{shard_category_code_for_reflection(reflection)}), shard)
106
127
  end
107
- alias_method '#{attr_name}=', :__temp__
108
- undef_method :__temp__
128
+ alias_method '#{attr_name}=', :__temp_relative_attribute_assignment__
129
+ undef_method :__temp_relative_attribute_assignment__
109
130
  RUBY
110
131
  else
111
132
  define_method_unsharded_column(attr_name, 'global')
@@ -115,11 +136,11 @@ module Switchman
115
136
  def define_method_unsharded_column(attr_name, prefix)
116
137
  return if columns_hash["#{prefix}_#{attr_name}"]
117
138
  generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
118
- def __temp__
139
+ def __temp_unsharded_attribute__
119
140
  raise NoMethodError, "undefined method `#{prefix}_#{attr_name}'; are you missing an association?"
120
141
  end
121
- alias_method '#{prefix}_#{attr_name}', :__temp__
122
- undef_method :__temp__
142
+ alias_method '#{prefix}_#{attr_name}', :__temp_unsharded_attribute__
143
+ undef_method :__temp_unsharded_attribute__
123
144
  RUBY
124
145
  end
125
146
  end
@@ -112,8 +112,20 @@ module Switchman
112
112
  end
113
113
 
114
114
  def remove_connection(spec_name)
115
+ # also remove pools based on the same spec name that are for shard category purposes
116
+ # can't just use delete_if, because it's a Concurrent::Map, not a Hash
117
+ owner_to_pool.keys.each do |k|
118
+ next if k == spec_name
119
+
120
+ v = owner_to_pool[k]
121
+ owner_to_pool.delete(k) if v.is_a?(ConnectionPoolProxy) && v.default_pool.spec.name == spec_name
122
+ end
123
+
124
+ # unwrap the pool from inside a ConnectionPoolProxy
115
125
  pool = owner_to_pool[spec_name]
116
126
  owner_to_pool[spec_name] = pool.default_pool if pool.is_a?(ConnectionPoolProxy)
127
+
128
+ # now let Rails do its thing with the data type it expects
117
129
  super
118
130
  end
119
131
 
@@ -128,15 +140,21 @@ module Switchman
128
140
  else
129
141
  ancestor_pool.spec
130
142
  end
131
- pool = establish_connection(spec.to_hash)
132
- pool.instance_variable_set(:@schema_cache, ancestor_pool.schema_cache) if ancestor_pool.schema_cache
133
- pool
134
- elsif spec_name != "primary"
143
+ # avoid copying "duplicate" pools that implement shard categories.
144
+ # they'll have a spec.name of primary, but a spec_name of something else, like unsharded
145
+ if spec.name == spec_name
146
+ pool = establish_connection(spec.to_hash)
147
+ pool.instance_variable_set(:@schema_cache, ancestor_pool.schema_cache) if ancestor_pool.schema_cache
148
+ next pool
149
+ end
150
+ end
151
+
152
+ if spec_name != "primary"
135
153
  primary_pool = retrieve_connection_pool("primary")
136
154
  if primary_pool.is_a?(ConnectionPoolProxy)
137
155
  pool = ConnectionPoolProxy.new(spec_name.to_sym, primary_pool.default_pool, @shard_connection_pools)
138
156
  pool.schema_cache.copy_references(primary_pool.schema_cache)
139
- pool
157
+ owner_to_pool[spec_name] = pool
140
158
  else
141
159
  primary_pool
142
160
  end
@@ -28,15 +28,26 @@ module Switchman
28
28
  # this technically belongs on AssociationReflection, but we put it on
29
29
  # ThroughReflection as well, instead of delegating to its internal
30
30
  # HasManyAssociation, losing its proper `klass`
31
- def association_scope_cache(conn, owner, &block)
32
- key = conn.prepared_statements
33
- if polymorphic?
34
- key = [key, owner._read_attribute(@foreign_type)]
31
+ if ::Rails.version < '6.0.4'
32
+ def association_scope_cache(conn, owner, &block)
33
+ key = conn.prepared_statements
34
+ if polymorphic?
35
+ key = [key, owner._read_attribute(@foreign_type)]
36
+ end
37
+ key = [key, shard(owner).id].flatten
38
+ @association_scope_cache[key] ||= @scope_lock.synchronize {
39
+ @association_scope_cache[key] ||= (::Rails.version >= "5.2" ? ::ActiveRecord::StatementCache.create(conn, &block) : block.call)
40
+ }
41
+ end
42
+ else
43
+ def association_scope_cache(klass, owner, &block)
44
+ key = self
45
+ if polymorphic?
46
+ key = [key, owner._read_attribute(@foreign_type)]
47
+ end
48
+ key = [key, shard(owner).id].flatten
49
+ klass.cached_find_by_statement(key, &block)
35
50
  end
36
- key = [key, shard(owner).id].flatten
37
- @association_scope_cache[key] ||= @scope_lock.synchronize {
38
- @association_scope_cache[key] ||= (::Rails.version >= "5.2" ? ::ActiveRecord::StatementCache.create(conn, &block) : block.call)
39
- }
40
51
  end
41
52
  end
42
53
 
@@ -154,7 +154,7 @@ module Switchman
154
154
  end
155
155
  end
156
156
  spec = ::ActiveRecord::ConnectionAdapters::ConnectionSpecification.new(
157
- category,
157
+ default_pool.spec.name,
158
158
  config,
159
159
  "#{config[:adapter]}_connection"
160
160
  )
@@ -17,13 +17,7 @@ module Switchman
17
17
  Shard.default(reload: true)
18
18
  end
19
19
 
20
- # can't auto-create a new shard on the default shard's db server if the
21
- # default shard is split across multiple db servers
22
- if ::ActiveRecord::Base.connection_handler.connection_pool_list.length > 1
23
- server1 = DatabaseServer.create(Shard.default.database_server.config)
24
- else
25
- server1 = Shard.default.database_server
26
- end
20
+ server1 = Shard.default.database_server
27
21
  server2 = DatabaseServer.create(Shard.default.database_server.config.merge(server2: true))
28
22
 
29
23
  if server1 == Shard.default.database_server && server1.config[:shard1] && server1.config[:shard2]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Switchman
4
- VERSION = "2.0.13"
4
+ VERSION = "2.1.3"
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.13
4
+ version: 2.1.3
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-06-11 00:00:00.000000000 Z
13
+ date: 2021-07-29 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.2.15
275
+ rubygems_version: 3.2.24
276
276
  signing_key:
277
277
  specification_version: 4
278
278
  summary: Rails sharding magic