makara 0.4.1 → 0.5.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 +5 -5
- data/.github/workflows/gem-publish-public.yml +36 -0
- data/.travis.yml +36 -10
- data/CHANGELOG.md +61 -48
- data/Gemfile +1 -1
- data/README.md +6 -8
- data/gemfiles/ar-head.gemfile +1 -1
- data/gemfiles/ar52.gemfile +24 -0
- data/gemfiles/ar60.gemfile +24 -0
- data/lib/active_record/connection_adapters/makara_abstract_adapter.rb +105 -0
- data/lib/makara.rb +7 -4
- data/lib/makara/config_parser.rb +2 -1
- data/lib/makara/connection_wrapper.rb +24 -0
- data/lib/makara/errors/blacklisted_while_in_transaction.rb +14 -0
- data/lib/makara/errors/invalid_shard.rb +16 -0
- data/lib/makara/pool.rb +47 -24
- data/lib/makara/proxy.rb +32 -4
- data/lib/makara/strategies/shard_aware.rb +47 -0
- data/lib/makara/version.rb +2 -2
- data/makara.gemspec +5 -1
- data/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb +57 -0
- data/spec/cookie_spec.rb +3 -3
- data/spec/middleware_spec.rb +1 -1
- data/spec/pool_spec.rb +24 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/strategies/shard_aware_spec.rb +219 -0
- data/spec/support/mock_objects.rb +4 -0
- metadata +16 -7
data/spec/middleware_spec.rb
CHANGED
@@ -49,7 +49,7 @@ describe Makara::Middleware do
|
|
49
49
|
|
50
50
|
_, headers, body = middleware.call(env)
|
51
51
|
|
52
|
-
expect(headers['Set-Cookie']).to eq("#{key}=mock_mysql%3A#{(now + 5).to_f}; path=/; max-age=10; expires=#{(Time.now + 10).
|
52
|
+
expect(headers['Set-Cookie']).to eq("#{key}=mock_mysql%3A#{(now + 5).to_f}; path=/; max-age=10; expires=#{(Time.now + 10).httpdate}; secure; HttpOnly")
|
53
53
|
expect(body).to eq('master/1')
|
54
54
|
end
|
55
55
|
end
|
data/spec/pool_spec.rb
CHANGED
@@ -5,6 +5,7 @@ describe Makara::Pool do
|
|
5
5
|
let(:proxy){ FakeProxy.new({:makara => pool_config.merge(:connections => [])}) }
|
6
6
|
let(:pool){ Makara::Pool.new('test', proxy) }
|
7
7
|
let(:pool_config){ {:blacklist_duration => 5} }
|
8
|
+
let(:master_pool){ Makara::Pool.new('master', proxy) }
|
8
9
|
|
9
10
|
it 'should wrap connections with a ConnectionWrapper as theyre added to the pool' do
|
10
11
|
expect(pool.connections).to be_empty
|
@@ -157,4 +158,27 @@ describe Makara::Pool do
|
|
157
158
|
|
158
159
|
end
|
159
160
|
|
161
|
+
it 'should error out while blacklisted in transaction' do
|
162
|
+
wrapper_a = master_pool.add(pool_config){ FakeConnection.new(open_transactions: 1) }
|
163
|
+
master_pool.add(pool_config){ FakeConnection.new }
|
164
|
+
expect {
|
165
|
+
master_pool.provide do |connection|
|
166
|
+
if connection == wrapper_a
|
167
|
+
raise Makara::Errors::BlacklistConnection.new(wrapper_a, StandardError.new('failure'))
|
168
|
+
end
|
169
|
+
end
|
170
|
+
}.to raise_error(Makara::Errors::BlacklistedWhileInTransaction)
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'skips blacklisted connections in master pool when not in transaction' do
|
174
|
+
wrapper_a = master_pool.add(pool_config){ FakeConnection.new(open_transactions: 0) }
|
175
|
+
master_pool.add(pool_config){ FakeConnection.new }
|
176
|
+
master_pool.provide do |connection|
|
177
|
+
if connection == wrapper_a
|
178
|
+
raise Makara::Errors::BlacklistConnection.new(wrapper_a, StandardError.new('failure'))
|
179
|
+
end
|
180
|
+
end
|
181
|
+
10.times{ master_pool.provide{|connection| expect(connection).not_to eq(wrapper_a) } }
|
182
|
+
end
|
183
|
+
|
160
184
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Makara::Strategies::ShardAware do
|
4
|
+
|
5
|
+
def with_shard(shard_id)
|
6
|
+
begin
|
7
|
+
Thread.current['makara_shard_id'] = shard_id
|
8
|
+
yield
|
9
|
+
ensure
|
10
|
+
Thread.current['makara_shard_id'] = nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "failover strategy with shard awareness," do
|
15
|
+
let(:proxy){ FakeProxy.new({:makara => pool_config.merge(makara_config).merge(:connections => [])}) }
|
16
|
+
let(:pool){ Makara::Pool.new('master', proxy) }
|
17
|
+
let(:pool_config){ { blacklist_duration: 5} }
|
18
|
+
let(:makara_config) { {
|
19
|
+
master_strategy: 'failover',
|
20
|
+
master_shard_aware: true,
|
21
|
+
master_default_shard: 'shard2'
|
22
|
+
} }
|
23
|
+
let(:strategy) { pool.strategy }
|
24
|
+
|
25
|
+
it 'should use the strategy' do
|
26
|
+
expect(pool.strategy).to be_instance_of(Makara::Strategies::ShardAware)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should take the top weight for a given shard' do
|
30
|
+
wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
|
31
|
+
wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1', weight: 2)){ FakeConnection.new(something: 'b') }
|
32
|
+
wrapper_c = pool.add(pool_config.merge(weight: 2, shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
|
33
|
+
|
34
|
+
# default shard
|
35
|
+
expect(strategy.current.something).to eql('c')
|
36
|
+
expect(strategy.next.something).to eql('c')
|
37
|
+
expect(strategy.next.something).to eql('c')
|
38
|
+
|
39
|
+
# shard1
|
40
|
+
with_shard('shard1') do
|
41
|
+
expect(strategy.current.something).to eql('b')
|
42
|
+
expect(strategy.next.something).to eql('b')
|
43
|
+
expect(strategy.next.something).to eql('b')
|
44
|
+
end
|
45
|
+
|
46
|
+
# shard2
|
47
|
+
with_shard('shard2') do
|
48
|
+
expect(strategy.current.something).to eql('c')
|
49
|
+
expect(strategy.next.something).to eql('c')
|
50
|
+
expect(strategy.next.something).to eql('c')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should take given order within shard if no weights' do
|
55
|
+
wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
|
56
|
+
wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'b') }
|
57
|
+
wrapper_c = pool.add(pool_config.merge(shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
|
58
|
+
|
59
|
+
# default shard
|
60
|
+
expect(strategy.current.something).to eql('c')
|
61
|
+
expect(strategy.next.something).to eql('c')
|
62
|
+
expect(strategy.next.something).to eql('c')
|
63
|
+
|
64
|
+
# shard1
|
65
|
+
with_shard('shard1') do
|
66
|
+
expect(strategy.current.something).to eql('a')
|
67
|
+
expect(strategy.next.something).to eql('a')
|
68
|
+
end
|
69
|
+
|
70
|
+
# shard2
|
71
|
+
with_shard('shard2') do
|
72
|
+
expect(strategy.current.something).to eql('c')
|
73
|
+
expect(strategy.next.something).to eql('c')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should handle failover to next one within shard' do
|
78
|
+
wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
|
79
|
+
wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'b') }
|
80
|
+
wrapper_c = pool.add(pool_config.merge(shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
|
81
|
+
|
82
|
+
# default shard
|
83
|
+
expect(strategy.current.something).to eql('c')
|
84
|
+
expect(strategy.next.something).to eql('c')
|
85
|
+
expect(strategy.next.something).to eql('c')
|
86
|
+
|
87
|
+
# skips a for shard1
|
88
|
+
with_shard('shard1') do
|
89
|
+
pool.provide do |connection|
|
90
|
+
if connection == wrapper_a
|
91
|
+
raise Makara::Errors::BlacklistConnection.new(wrapper_a, StandardError.new('failure'))
|
92
|
+
end
|
93
|
+
end
|
94
|
+
expect(strategy.current.something).to eql('b')
|
95
|
+
expect(strategy.next.something).to eql('b')
|
96
|
+
end
|
97
|
+
|
98
|
+
# shard2
|
99
|
+
with_shard('shard2') do
|
100
|
+
expect(strategy.current.something).to eql('c')
|
101
|
+
expect(strategy.next.something).to eql('c')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
it 'raises error for invalid shard' do
|
105
|
+
with_shard('shard3') do
|
106
|
+
expect{strategy.current.something }.to raise_error(Makara::Errors::InvalidShard)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "round_robin strategy with shard awareness," do
|
112
|
+
let(:proxy){ FakeProxy.new({:makara => pool_config.merge(makara_config).merge(:connections => [])}) }
|
113
|
+
let(:pool){ Makara::Pool.new('master', proxy) }
|
114
|
+
let(:pool_config){ { blacklist_duration: 5} }
|
115
|
+
let(:makara_config) { {
|
116
|
+
master_strategy: 'round_robin',
|
117
|
+
master_shard_aware: true,
|
118
|
+
master_default_shard: 'shard2'
|
119
|
+
} }
|
120
|
+
let(:strategy) { pool.strategy }
|
121
|
+
|
122
|
+
it 'should use the strategy' do
|
123
|
+
expect(pool.strategy).to be_instance_of(Makara::Strategies::ShardAware)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should loop through with weights within shard' do
|
127
|
+
wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
|
128
|
+
wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1', weight: 2)){ FakeConnection.new(something: 'b') }
|
129
|
+
wrapper_c = pool.add(pool_config.merge(weight: 2, shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
|
130
|
+
|
131
|
+
# default shard
|
132
|
+
expect(strategy.current.something).to eql('c')
|
133
|
+
expect(strategy.next.something).to eql('c')
|
134
|
+
expect(strategy.next.something).to eql('c')
|
135
|
+
|
136
|
+
# shard1
|
137
|
+
with_shard('shard1') do
|
138
|
+
expect(strategy.current.something).to eql('a')
|
139
|
+
expect(strategy.next.something).to eql('b')
|
140
|
+
expect(strategy.next.something).to eql('b')
|
141
|
+
expect(strategy.next.something).to eql('a')
|
142
|
+
end
|
143
|
+
|
144
|
+
# shard2
|
145
|
+
with_shard('shard2') do
|
146
|
+
expect(strategy.current.something).to eql('c')
|
147
|
+
expect(strategy.next.something).to eql('c')
|
148
|
+
expect(strategy.next.something).to eql('c')
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should handle failover to next one within shard' do
|
153
|
+
wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
|
154
|
+
wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'b') }
|
155
|
+
wrapper_c = pool.add(pool_config.merge(shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
|
156
|
+
|
157
|
+
# default shard
|
158
|
+
expect(strategy.current.something).to eql('c')
|
159
|
+
expect(strategy.next.something).to eql('c')
|
160
|
+
expect(strategy.next.something).to eql('c')
|
161
|
+
|
162
|
+
# skips a for shard1
|
163
|
+
with_shard('shard1') do
|
164
|
+
pool.provide do |connection|
|
165
|
+
if connection == wrapper_a
|
166
|
+
raise Makara::Errors::BlacklistConnection.new(wrapper_a, StandardError.new('failure'))
|
167
|
+
end
|
168
|
+
end
|
169
|
+
expect(strategy.current.something).to eql('b')
|
170
|
+
expect(strategy.next.something).to eql('b')
|
171
|
+
expect(strategy.next.something).to eql('b')
|
172
|
+
end
|
173
|
+
|
174
|
+
# shard2
|
175
|
+
with_shard('shard2') do
|
176
|
+
expect(strategy.current.something).to eql('c')
|
177
|
+
expect(strategy.next.something).to eql('c')
|
178
|
+
expect(strategy.next.something).to eql('c')
|
179
|
+
end
|
180
|
+
end
|
181
|
+
it 'raises error for invalid shard' do
|
182
|
+
with_shard('shard3') do
|
183
|
+
expect{strategy.current.something }.to raise_error(Makara::Errors::InvalidShard)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "uses the configured failover strategy when shard_aware set to false," do
|
189
|
+
let(:proxy){ FakeProxy.new({:makara => pool_config.merge(makara_config).merge(:connections => [])}) }
|
190
|
+
let(:pool){ Makara::Pool.new('master', proxy) }
|
191
|
+
let(:pool_config){ { blacklist_duration: 5} }
|
192
|
+
let(:makara_config) { {
|
193
|
+
master_strategy: 'failover',
|
194
|
+
master_shard_aware: false,
|
195
|
+
master_default_shard: 'shard2'
|
196
|
+
} }
|
197
|
+
let(:strategy) { pool.strategy }
|
198
|
+
|
199
|
+
it 'should use the failover strategy' do
|
200
|
+
expect(pool.strategy).to be_instance_of(Makara::Strategies::PriorityFailover)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "uses the configured roundrobin strategy when shard_aware set to false," do
|
205
|
+
let(:proxy){ FakeProxy.new({:makara => pool_config.merge(makara_config).merge(:connections => [])}) }
|
206
|
+
let(:pool){ Makara::Pool.new('master', proxy) }
|
207
|
+
let(:pool_config){ { blacklist_duration: 5} }
|
208
|
+
let(:makara_config) { {
|
209
|
+
master_strategy: 'round_robin',
|
210
|
+
master_shard_aware: false,
|
211
|
+
master_default_shard: 'shard2'
|
212
|
+
} }
|
213
|
+
let(:strategy) { pool.strategy }
|
214
|
+
|
215
|
+
it 'should use the failover strategy' do
|
216
|
+
expect(pool.strategy).to be_instance_of(Makara::Strategies::RoundRobin)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: makara
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Nelson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -31,6 +31,7 @@ executables: []
|
|
31
31
|
extensions: []
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
|
+
- ".github/workflows/gem-publish-public.yml"
|
34
35
|
- ".gitignore"
|
35
36
|
- ".rspec"
|
36
37
|
- ".ruby-gemset"
|
@@ -49,6 +50,8 @@ files:
|
|
49
50
|
- gemfiles/ar42.gemfile
|
50
51
|
- gemfiles/ar50.gemfile
|
51
52
|
- gemfiles/ar51.gemfile
|
53
|
+
- gemfiles/ar52.gemfile
|
54
|
+
- gemfiles/ar60.gemfile
|
52
55
|
- lib/active_record/connection_adapters/jdbcmysql_makara_adapter.rb
|
53
56
|
- lib/active_record/connection_adapters/jdbcpostgresql_makara_adapter.rb
|
54
57
|
- lib/active_record/connection_adapters/makara_abstract_adapter.rb
|
@@ -68,6 +71,8 @@ files:
|
|
68
71
|
- lib/makara/error_handler.rb
|
69
72
|
- lib/makara/errors/all_connections_blacklisted.rb
|
70
73
|
- lib/makara/errors/blacklist_connection.rb
|
74
|
+
- lib/makara/errors/blacklisted_while_in_transaction.rb
|
75
|
+
- lib/makara/errors/invalid_shard.rb
|
71
76
|
- lib/makara/errors/makara_error.rb
|
72
77
|
- lib/makara/errors/no_connections_available.rb
|
73
78
|
- lib/makara/logging/logger.rb
|
@@ -79,6 +84,7 @@ files:
|
|
79
84
|
- lib/makara/strategies/abstract.rb
|
80
85
|
- lib/makara/strategies/priority_failover.rb
|
81
86
|
- lib/makara/strategies/round_robin.rb
|
87
|
+
- lib/makara/strategies/shard_aware.rb
|
82
88
|
- lib/makara/version.rb
|
83
89
|
- makara.gemspec
|
84
90
|
- spec/active_record/connection_adapters/makara_abstract_adapter_error_handling_spec.rb
|
@@ -97,6 +103,7 @@ files:
|
|
97
103
|
- spec/spec_helper.rb
|
98
104
|
- spec/strategies/priority_failover_spec.rb
|
99
105
|
- spec/strategies/round_robin_spec.rb
|
106
|
+
- spec/strategies/shard_aware_spec.rb
|
100
107
|
- spec/support/deep_dup.rb
|
101
108
|
- spec/support/helpers.rb
|
102
109
|
- spec/support/mock_objects.rb
|
@@ -109,9 +116,11 @@ files:
|
|
109
116
|
- spec/support/proxy_extensions.rb
|
110
117
|
- spec/support/schema.rb
|
111
118
|
- spec/support/user.rb
|
112
|
-
homepage:
|
113
|
-
licenses:
|
114
|
-
|
119
|
+
homepage: https://github.com/taskrabbit/makara
|
120
|
+
licenses:
|
121
|
+
- MIT
|
122
|
+
metadata:
|
123
|
+
source_code_uri: https://github.com/taskrabbit/makara
|
115
124
|
post_install_message:
|
116
125
|
rdoc_options: []
|
117
126
|
require_paths:
|
@@ -127,8 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
136
|
- !ruby/object:Gem::Version
|
128
137
|
version: '0'
|
129
138
|
requirements: []
|
130
|
-
|
131
|
-
rubygems_version: 2.5.2
|
139
|
+
rubygems_version: 3.1.4
|
132
140
|
signing_key:
|
133
141
|
specification_version: 4
|
134
142
|
summary: Read-write split your DB yo
|
@@ -149,6 +157,7 @@ test_files:
|
|
149
157
|
- spec/spec_helper.rb
|
150
158
|
- spec/strategies/priority_failover_spec.rb
|
151
159
|
- spec/strategies/round_robin_spec.rb
|
160
|
+
- spec/strategies/shard_aware_spec.rb
|
152
161
|
- spec/support/deep_dup.rb
|
153
162
|
- spec/support/helpers.rb
|
154
163
|
- spec/support/mock_objects.rb
|