switchman 2.0.13 → 2.1.3
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 +4 -4
- data/app/models/switchman/shard.rb +14 -3
- data/lib/switchman/active_record/attribute_methods.rb +38 -17
- data/lib/switchman/active_record/connection_handler.rb +23 -5
- data/lib/switchman/active_record/reflection.rb +19 -8
- data/lib/switchman/connection_pool_proxy.rb +1 -1
- data/lib/switchman/test_helper.rb +1 -7
- data/lib/switchman/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62fa2df72b999208e937ba7df468f122159e573d3e838cf86df601592e64fb0c
|
4
|
+
data.tar.gz: 20e83c56b8043a8d5e9fbc433db4b1a26a6ec7291389c59463a47ea2ed0b0b87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 =
|
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
|
-
|
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
|
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}', :
|
45
|
-
undef_method :
|
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
|
56
|
-
|
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}', :
|
59
|
-
undef_method :
|
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
|
98
|
-
|
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}', :
|
101
|
-
undef_method :
|
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
|
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}=', :
|
108
|
-
undef_method :
|
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
|
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}', :
|
122
|
-
undef_method :
|
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
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
|
@@ -17,13 +17,7 @@ module Switchman
|
|
17
17
|
Shard.default(reload: true)
|
18
18
|
end
|
19
19
|
|
20
|
-
|
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]
|
data/lib/switchman/version.rb
CHANGED
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.
|
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-
|
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.
|
275
|
+
rubygems_version: 3.2.24
|
276
276
|
signing_key:
|
277
277
|
specification_version: 4
|
278
278
|
summary: Rails sharding magic
|