makara 0.3.5
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 +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +28 -0
- data/CHANGELOG.md +27 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +22 -0
- data/README.md +278 -0
- data/Rakefile +9 -0
- data/gemfiles/ar30.gemfile +30 -0
- data/gemfiles/ar31.gemfile +29 -0
- data/gemfiles/ar32.gemfile +29 -0
- data/gemfiles/ar40.gemfile +15 -0
- data/gemfiles/ar41.gemfile +15 -0
- data/gemfiles/ar42.gemfile +15 -0
- data/lib/active_record/connection_adapters/jdbcmysql_makara_adapter.rb +25 -0
- data/lib/active_record/connection_adapters/jdbcpostgresql_makara_adapter.rb +25 -0
- data/lib/active_record/connection_adapters/makara_abstract_adapter.rb +209 -0
- data/lib/active_record/connection_adapters/makara_jdbcmysql_adapter.rb +25 -0
- data/lib/active_record/connection_adapters/makara_jdbcpostgresql_adapter.rb +25 -0
- data/lib/active_record/connection_adapters/makara_mysql2_adapter.rb +44 -0
- data/lib/active_record/connection_adapters/makara_postgresql_adapter.rb +44 -0
- data/lib/active_record/connection_adapters/mysql2_makara_adapter.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql_makara_adapter.rb +44 -0
- data/lib/makara.rb +25 -0
- data/lib/makara/cache.rb +53 -0
- data/lib/makara/cache/memory_store.rb +28 -0
- data/lib/makara/cache/noop_store.rb +15 -0
- data/lib/makara/config_parser.rb +200 -0
- data/lib/makara/connection_wrapper.rb +170 -0
- data/lib/makara/context.rb +46 -0
- data/lib/makara/error_handler.rb +39 -0
- data/lib/makara/errors/all_connections_blacklisted.rb +13 -0
- data/lib/makara/errors/blacklist_connection.rb +14 -0
- data/lib/makara/errors/no_connections_available.rb +14 -0
- data/lib/makara/logging/logger.rb +23 -0
- data/lib/makara/logging/subscriber.rb +38 -0
- data/lib/makara/middleware.rb +109 -0
- data/lib/makara/pool.rb +188 -0
- data/lib/makara/proxy.rb +277 -0
- data/lib/makara/railtie.rb +14 -0
- data/lib/makara/version.rb +15 -0
- data/makara.gemspec +19 -0
- data/spec/active_record/connection_adapters/makara_abstract_adapter_error_handling_spec.rb +92 -0
- data/spec/active_record/connection_adapters/makara_abstract_adapter_spec.rb +114 -0
- data/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb +183 -0
- data/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb +121 -0
- data/spec/cache_spec.rb +59 -0
- data/spec/config_parser_spec.rb +102 -0
- data/spec/connection_wrapper_spec.rb +33 -0
- data/spec/context_spec.rb +107 -0
- data/spec/middleware_spec.rb +84 -0
- data/spec/pool_spec.rb +158 -0
- data/spec/proxy_spec.rb +182 -0
- data/spec/spec_helper.rb +46 -0
- data/spec/support/configurator.rb +13 -0
- data/spec/support/deep_dup.rb +12 -0
- data/spec/support/mock_objects.rb +67 -0
- data/spec/support/mysql2_database.yml +17 -0
- data/spec/support/mysql2_database_with_custom_errors.yml +17 -0
- data/spec/support/pool_extensions.rb +14 -0
- data/spec/support/postgresql_database.yml +13 -0
- data/spec/support/proxy_extensions.rb +33 -0
- data/spec/support/schema.rb +7 -0
- metadata +144 -0
data/spec/cache_spec.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Makara::Cache do
|
4
|
+
|
5
|
+
it 'should not require a store be set' do
|
6
|
+
described_class.store = nil
|
7
|
+
|
8
|
+
expect(
|
9
|
+
described_class.send(:store)
|
10
|
+
).to be_nil
|
11
|
+
|
12
|
+
expect{
|
13
|
+
described_class.read('test')
|
14
|
+
}.not_to raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'provides a few stores for testing purposes' do
|
18
|
+
described_class.store = :memory
|
19
|
+
described_class.write('test', 'value', 10)
|
20
|
+
expect(described_class.read('test')).to eq('value')
|
21
|
+
|
22
|
+
described_class.store = :noop
|
23
|
+
described_class.write('test', 'value', 10)
|
24
|
+
expect(described_class.read('test')).to be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
# this will be used in tests so we have to ensure this works as expected
|
29
|
+
context Makara::Cache::MemoryStore do
|
30
|
+
|
31
|
+
let(:store){ Makara::Cache::MemoryStore.new }
|
32
|
+
let(:data){ store.instance_variable_get('@data') }
|
33
|
+
|
34
|
+
it 'should read and write keys' do
|
35
|
+
expect(store.read('test')).to be_nil
|
36
|
+
store.write('test', 'value')
|
37
|
+
expect(store.read('test')).to eq('value')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'provides time based expiration' do
|
41
|
+
store.write('test', 'value', :expires_in => 5)
|
42
|
+
expect(store.read('test')).to eq('value')
|
43
|
+
|
44
|
+
Timecop.travel Time.now + 6 do
|
45
|
+
expect(store.read('test')).to be_nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'cleans the data' do
|
50
|
+
store.write('test', 'value', :expires_in => -5)
|
51
|
+
expect(store.read('test')).to be_nil
|
52
|
+
expect(data).not_to have_key('test')
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Makara::ConfigParser do
|
4
|
+
|
5
|
+
let(:config){
|
6
|
+
{
|
7
|
+
:top_level => 'value',
|
8
|
+
:makara => {
|
9
|
+
:connections => [
|
10
|
+
{
|
11
|
+
:role => 'master',
|
12
|
+
:name => 'themaster'
|
13
|
+
},
|
14
|
+
{
|
15
|
+
:name => 'slave1'
|
16
|
+
},
|
17
|
+
{
|
18
|
+
:name => 'slave2'
|
19
|
+
}
|
20
|
+
]
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
context '::merge_and_resolve_default_url_config' do
|
26
|
+
let(:config_without_url) do
|
27
|
+
{
|
28
|
+
:master_ttl => 5,
|
29
|
+
:blacklist_duration => 30,
|
30
|
+
:sticky => true,
|
31
|
+
:adapter => 'mysql2_makara',
|
32
|
+
:encoding => 'utf8',
|
33
|
+
:host => 'localhost',
|
34
|
+
:database => 'db_name',
|
35
|
+
:username => 'db_username',
|
36
|
+
:password => 'db_password',
|
37
|
+
:port => 3306
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:config_with_url) do
|
42
|
+
{
|
43
|
+
:master_ttl => 5,
|
44
|
+
:blacklist_duration => 30,
|
45
|
+
:sticky => true,
|
46
|
+
:adapter => 'mysql2_makara',
|
47
|
+
:encoding => 'utf8',
|
48
|
+
:url => 'mysql2://db_username:db_password@localhost:3306/db_name'
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'does nothing to a config without a url parameter' do
|
53
|
+
config = config_without_url.dup
|
54
|
+
expected = config_without_url.dup
|
55
|
+
actual = described_class.merge_and_resolve_default_url_config(config)
|
56
|
+
expect(actual).to eq(expected)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'parses the url parameter and merges it into the config' do
|
60
|
+
config = config_with_url.dup
|
61
|
+
expected = config_without_url.dup
|
62
|
+
actual = described_class.merge_and_resolve_default_url_config(config)
|
63
|
+
expect(actual).to eq(expected)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'does not use DATABASE_URL env variable' do
|
67
|
+
database_url = ENV['DATABASE_URL']
|
68
|
+
ENV['DATABASE_URL'] = config_with_url[:url]
|
69
|
+
begin
|
70
|
+
config = config_with_url.dup
|
71
|
+
config.delete(:url)
|
72
|
+
expected = config.dup
|
73
|
+
actual = described_class.merge_and_resolve_default_url_config(config)
|
74
|
+
expect(actual).to eq(expected)
|
75
|
+
ensure
|
76
|
+
ENV['DATABASE_URL'] = database_url
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should provide an id based on the recursively sorted config' do
|
83
|
+
parsera = described_class.new(config)
|
84
|
+
parserb = described_class.new(config.merge(:other => 'value'))
|
85
|
+
parserc = described_class.new(config)
|
86
|
+
|
87
|
+
expect(parsera.id).not_to eq(parserb.id)
|
88
|
+
expect(parsera.id).to eq(parserc.id)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should provide master and slave configs' do
|
92
|
+
parser = described_class.new(config)
|
93
|
+
expect(parser.master_configs).to eq([
|
94
|
+
{:name => 'themaster', :top_level => 'value', :blacklist_duration => 30, :master_ttl => 5, :sticky => true}
|
95
|
+
])
|
96
|
+
expect(parser.slave_configs).to eq([
|
97
|
+
{:name => 'slave1', :top_level => 'value', :blacklist_duration => 30, :master_ttl => 5, :sticky => true},
|
98
|
+
{:name => 'slave2', :top_level => 'value', :blacklist_duration => 30, :master_ttl => 5, :sticky => true}
|
99
|
+
])
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Makara::ConnectionWrapper do
|
4
|
+
|
5
|
+
let(:proxy){ FakeProxy.new({:makara => {:blacklist_duration => 5, :connections => [{:role => 'master'}, {:role => 'slave'}, {:role => 'slave'}]}}) }
|
6
|
+
let(:connection){ subject._makara_connection }
|
7
|
+
|
8
|
+
subject{ proxy.master_pool.connections.first }
|
9
|
+
|
10
|
+
it 'should extend the connection with new functionality' do
|
11
|
+
expect(connection).to respond_to(:_makara_name)
|
12
|
+
expect(connection).to respond_to(:_makara)
|
13
|
+
expect(connection).to respond_to(:_makara_hijack)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should invoke hijacked methods on the proxy when invoked directly' do
|
17
|
+
expect(proxy).to receive(:execute).with('test').once
|
18
|
+
connection.execute("test")
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should have a default weight of 1' do
|
22
|
+
expect(subject._makara_weight).to eq(1)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should store the blacklist status' do
|
26
|
+
expect(subject._makara_blacklisted?).to eq(false)
|
27
|
+
subject._makara_blacklist!
|
28
|
+
expect(subject._makara_blacklisted?).to eq(true)
|
29
|
+
subject._makara_whitelist!
|
30
|
+
expect(subject._makara_blacklisted?).to eq(false)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Makara::Context do
|
4
|
+
|
5
|
+
describe 'get_current' do
|
6
|
+
it 'does not share context acorss threads' do
|
7
|
+
uniq_curret_contexts = []
|
8
|
+
threads = []
|
9
|
+
|
10
|
+
1.upto(3).map do |i|
|
11
|
+
threads << Thread.new do
|
12
|
+
current = Makara::Context.get_current
|
13
|
+
expect(current).to_not be_nil
|
14
|
+
expect(uniq_curret_contexts).to_not include(current)
|
15
|
+
uniq_curret_contexts << current
|
16
|
+
|
17
|
+
sleep(0.2)
|
18
|
+
end
|
19
|
+
sleep(0.1)
|
20
|
+
end
|
21
|
+
|
22
|
+
threads.map(&:join)
|
23
|
+
expect(uniq_curret_contexts.uniq.count).to eq(3)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'set_current' do
|
28
|
+
|
29
|
+
it 'does not share context acorss threads' do
|
30
|
+
uniq_curret_contexts = []
|
31
|
+
threads = []
|
32
|
+
|
33
|
+
1.upto(3).map do |i|
|
34
|
+
threads << Thread.new do
|
35
|
+
|
36
|
+
current = Makara::Context.set_current("val#{i}")
|
37
|
+
expect(current).to_not be_nil
|
38
|
+
expect(current).to eq("val#{i}")
|
39
|
+
|
40
|
+
sleep(0.2)
|
41
|
+
|
42
|
+
current = Makara::Context.get_current
|
43
|
+
expect(current).to_not be_nil
|
44
|
+
expect(current).to eq("val#{i}")
|
45
|
+
|
46
|
+
uniq_curret_contexts << current
|
47
|
+
end
|
48
|
+
sleep(0.1)
|
49
|
+
end
|
50
|
+
|
51
|
+
threads.map(&:join)
|
52
|
+
expect(uniq_curret_contexts.uniq.count).to eq(3)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'get_previous' do
|
57
|
+
it 'does not share context acorss threads' do
|
58
|
+
uniq_curret_contexts = []
|
59
|
+
threads = []
|
60
|
+
|
61
|
+
1.upto(3).map do |i|
|
62
|
+
threads << Thread.new do
|
63
|
+
current = Makara::Context.get_previous
|
64
|
+
expect(current).to_not be_nil
|
65
|
+
expect(uniq_curret_contexts).to_not include(current)
|
66
|
+
uniq_curret_contexts << current
|
67
|
+
|
68
|
+
sleep(0.2)
|
69
|
+
end
|
70
|
+
sleep(0.1)
|
71
|
+
end
|
72
|
+
|
73
|
+
threads.map(&:join)
|
74
|
+
expect(uniq_curret_contexts.uniq.count).to eq(3)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'set_previous' do
|
79
|
+
it 'does not share context acorss threads' do
|
80
|
+
uniq_curret_contexts = []
|
81
|
+
threads = []
|
82
|
+
|
83
|
+
1.upto(3).map do |i|
|
84
|
+
threads << Thread.new do
|
85
|
+
|
86
|
+
current = Makara::Context.set_previous("val#{i}")
|
87
|
+
expect(current).to_not be_nil
|
88
|
+
expect(current).to eq("val#{i}")
|
89
|
+
|
90
|
+
sleep(0.2)
|
91
|
+
|
92
|
+
current = Makara::Context.get_previous
|
93
|
+
expect(current).to_not be_nil
|
94
|
+
expect(current).to eq("val#{i}")
|
95
|
+
|
96
|
+
uniq_curret_contexts << current
|
97
|
+
end
|
98
|
+
sleep(0.1)
|
99
|
+
end
|
100
|
+
|
101
|
+
threads.map(&:join)
|
102
|
+
expect(uniq_curret_contexts.uniq.count).to eq(3)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Makara::Middleware do
|
4
|
+
|
5
|
+
let(:app){
|
6
|
+
lambda{|env|
|
7
|
+
proxy.query(env[:query] || 'select * from users')
|
8
|
+
[200, {}, ["#{Makara::Context.get_current}-#{Makara::Context.get_previous}"]]
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
let(:env){ {} }
|
13
|
+
let(:proxy){ FakeProxy.new(config(1,2)) }
|
14
|
+
let(:middleware){ described_class.new(app) }
|
15
|
+
|
16
|
+
let(:key){ Makara::Middleware::IDENTIFIER }
|
17
|
+
|
18
|
+
it 'should set the context before the request' do
|
19
|
+
Makara::Context.set_previous 'old'
|
20
|
+
Makara::Context.set_current 'old'
|
21
|
+
|
22
|
+
response = middleware.call(env)
|
23
|
+
current, prev = context_from(response)
|
24
|
+
|
25
|
+
expect(current).not_to eq('old')
|
26
|
+
expect(prev).not_to eq('old')
|
27
|
+
|
28
|
+
expect(current).to eq(Makara::Context.get_current)
|
29
|
+
expect(prev).to eq(Makara::Context.get_previous)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should use the cookie-provided context if present' do
|
33
|
+
env['HTTP_COOKIE'] = "#{key}=abcdefg--200; path=/; max-age=5"
|
34
|
+
|
35
|
+
response = middleware.call(env)
|
36
|
+
current, prev = context_from(response)
|
37
|
+
|
38
|
+
expect(prev).to eq('abcdefg')
|
39
|
+
expect(current).to eq(Makara::Context.get_current)
|
40
|
+
expect(current).not_to eq('abcdefg')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should use the param-provided context if present' do
|
44
|
+
env['QUERY_STRING'] = "dog=true&#{key}=abcdefg&cat=false"
|
45
|
+
|
46
|
+
response = middleware.call(env)
|
47
|
+
current, prev = context_from(response)
|
48
|
+
|
49
|
+
expect(prev).to eq('abcdefg')
|
50
|
+
expect(current).to eq(Makara::Context.get_current)
|
51
|
+
expect(current).not_to eq('abcdefg')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should set the cookie if master is used' do
|
55
|
+
env[:query] = 'update users set name = "phil"'
|
56
|
+
|
57
|
+
status, headers, body = middleware.call(env)
|
58
|
+
|
59
|
+
expect(headers['Set-Cookie']).to eq("#{key}=#{Makara::Context.get_current}--200; path=/; max-age=5; HttpOnly")
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should preserve the same context if the previous request was a redirect' do
|
63
|
+
env['HTTP_COOKIE'] = "#{key}=abcdefg--301; path=/; max-age=5"
|
64
|
+
|
65
|
+
response = middleware.call(env)
|
66
|
+
curr, prev = context_from(response)
|
67
|
+
|
68
|
+
expect(curr).to eq('abcdefg')
|
69
|
+
expect(prev).to eq('abcdefg')
|
70
|
+
|
71
|
+
env['HTTP_COOKIE'] = response[1]['Set-Cookie']
|
72
|
+
|
73
|
+
response = middleware.call(env)
|
74
|
+
curr2, prev2 = context_from(response)
|
75
|
+
|
76
|
+
expect(prev2).to eq('abcdefg')
|
77
|
+
expect(curr2).to eq(Makara::Context.get_current)
|
78
|
+
end
|
79
|
+
|
80
|
+
def context_from(response)
|
81
|
+
response[2][0].split('-')
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/spec/pool_spec.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Makara::Pool do
|
4
|
+
|
5
|
+
let(:proxy){ FakeProxy.new({:makara => pool_config.merge(:connections => [])}) }
|
6
|
+
let(:pool){ Makara::Pool.new('test', proxy) }
|
7
|
+
let(:pool_config){ {:blacklist_duration => 5} }
|
8
|
+
|
9
|
+
it 'should wrap connections with a ConnectionWrapper as theyre added to the pool' do
|
10
|
+
expect(pool.connections).to be_empty
|
11
|
+
|
12
|
+
connection_a = FakeConnection.new
|
13
|
+
connection_a.something = 'a'
|
14
|
+
|
15
|
+
wrapper_a = pool.add(pool_config){ connection_a }
|
16
|
+
wrapper_b = pool.add(pool_config.merge(:weight => 2)){ FakeConnection.new }
|
17
|
+
|
18
|
+
expect(pool.connections.length).to eq(3)
|
19
|
+
|
20
|
+
expect(wrapper_a).to be_a(Makara::ConnectionWrapper)
|
21
|
+
expect(wrapper_a.irespondtothis).to eq('hey!')
|
22
|
+
|
23
|
+
as, bs = pool.connections.partition{|c| c.something == 'a'}
|
24
|
+
expect(as.length).to eq(1)
|
25
|
+
expect(bs.length).to eq(2)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should determine if its completely blacklisted' do
|
29
|
+
|
30
|
+
pool.add(pool_config){ FakeConnection.new }
|
31
|
+
pool.add(pool_config){ FakeConnection.new }
|
32
|
+
|
33
|
+
expect(pool).not_to be_completely_blacklisted
|
34
|
+
|
35
|
+
pool.connections.each(&:_makara_blacklist!)
|
36
|
+
|
37
|
+
expect(pool).to be_completely_blacklisted
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'sends methods to all underlying objects if asked to' do
|
41
|
+
|
42
|
+
a = FakeConnection.new
|
43
|
+
b = FakeConnection.new
|
44
|
+
|
45
|
+
pool.add(pool_config){ a }
|
46
|
+
pool.add(pool_config){ b }
|
47
|
+
|
48
|
+
expect(a).to receive(:query).with('test').once
|
49
|
+
expect(b).to receive(:query).with('test').once
|
50
|
+
|
51
|
+
pool.send_to_all :query, 'test'
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'only sends methods to underlying objects which are not blacklisted' do
|
56
|
+
|
57
|
+
a = FakeConnection.new
|
58
|
+
b = FakeConnection.new
|
59
|
+
c = FakeConnection.new
|
60
|
+
|
61
|
+
pool.add(pool_config){ a }
|
62
|
+
pool.add(pool_config){ b }
|
63
|
+
wrapper_c = pool.add(pool_config){ c }
|
64
|
+
|
65
|
+
expect(a).to receive(:query).with('test').once
|
66
|
+
expect(b).to receive(:query).with('test').once
|
67
|
+
expect(c).to receive(:query).with('test').never
|
68
|
+
|
69
|
+
wrapper_c._makara_blacklist!
|
70
|
+
|
71
|
+
pool.send_to_all :query, 'test'
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'provides the next connection and blacklists' do
|
76
|
+
|
77
|
+
connection_a = FakeConnection.new
|
78
|
+
connection_b = FakeConnection.new
|
79
|
+
|
80
|
+
wrapper_a = pool.add(pool_config){ connection_a }
|
81
|
+
wrapper_b = pool.add(pool_config){ connection_b }
|
82
|
+
|
83
|
+
pool.provide do |connection|
|
84
|
+
if connection == wrapper_a
|
85
|
+
raise Makara::Errors::BlacklistConnection.new(wrapper_a, StandardError.new('failure'))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
expect(wrapper_a._makara_blacklisted?).to eq(true)
|
90
|
+
expect(wrapper_b._makara_blacklisted?).to eq(false)
|
91
|
+
|
92
|
+
Timecop.travel Time.now + 10 do
|
93
|
+
expect(wrapper_a._makara_blacklisted?).to eq(false)
|
94
|
+
expect(wrapper_b._makara_blacklisted?).to eq(false)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'provides the same connection if the context has not changed and the proxy is sticky' do
|
100
|
+
allow(proxy).to receive(:sticky){ true }
|
101
|
+
|
102
|
+
pool.add(pool_config){ FakeConnection.new }
|
103
|
+
pool.add(pool_config){ FakeConnection.new }
|
104
|
+
|
105
|
+
provided = []
|
106
|
+
|
107
|
+
10.times{ pool.provide{|con| provided << con } }
|
108
|
+
|
109
|
+
expect(provided.uniq.length).to eq(1)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'does not provide the same connection if the proxy is not sticky' do
|
113
|
+
allow(proxy).to receive(:sticky){ false }
|
114
|
+
|
115
|
+
pool.add(pool_config){ FakeConnection.new }
|
116
|
+
pool.add(pool_config){ FakeConnection.new }
|
117
|
+
|
118
|
+
provided = []
|
119
|
+
|
120
|
+
10.times{ pool.provide{|con| provided << con } }
|
121
|
+
|
122
|
+
expect(provided.uniq.length).to eq(2)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'raises an error when all connections are blacklisted' do
|
126
|
+
|
127
|
+
wrapper_a = pool.add(pool_config.dup){ FakeConnection.new }
|
128
|
+
wrapper_b = pool.add(pool_config.dup){ FakeConnection.new }
|
129
|
+
|
130
|
+
# make the connection
|
131
|
+
pool.send_to_all :to_s
|
132
|
+
|
133
|
+
allow(pool).to receive(:next).and_return(wrapper_a, wrapper_b, nil)
|
134
|
+
|
135
|
+
|
136
|
+
begin
|
137
|
+
pool.provide do |connection|
|
138
|
+
raise Makara::Errors::BlacklistConnection.new(connection, StandardError.new('failure'))
|
139
|
+
end
|
140
|
+
rescue Makara::Errors::AllConnectionsBlacklisted => e
|
141
|
+
expect(e).to be_present
|
142
|
+
expect(e.message).to eq("[Makara/test] All connections are blacklisted -> [Makara/test/2] failure -> [Makara/test/1] failure")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'skips blacklisted connections when choosing the next one' do
|
147
|
+
|
148
|
+
pool.add(pool_config){ FakeConnection.new }
|
149
|
+
pool.add(pool_config){ FakeConnection.new }
|
150
|
+
|
151
|
+
wrapper_b = pool.add(pool_config){ FakeConnection.new }
|
152
|
+
wrapper_b._makara_blacklist!
|
153
|
+
|
154
|
+
10.times{ pool.provide{|connection| expect(connection).not_to eq(wrapper_b) } }
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|