makara 0.5.1 → 0.6.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/CI.yml +0 -4
- data/.rubocop_todo.yml +6 -6
- data/CHANGELOG.md +3 -1
- data/README.md +51 -51
- data/lib/active_record/connection_adapters/makara_abstract_adapter.rb +32 -16
- data/lib/makara/config_parser.rb +63 -104
- data/lib/makara/connection_wrapper.rb +1 -1
- data/lib/makara/context.rb +1 -1
- data/lib/makara/logging/subscriber.rb +1 -1
- data/lib/makara/middleware.rb +1 -1
- data/lib/makara/pool.rb +2 -2
- data/lib/makara/proxy.rb +67 -52
- data/lib/makara/version.rb +5 -3
- data/spec/active_record/connection_adapters/makara_abstract_adapter_error_handling_spec.rb +2 -2
- data/spec/active_record/connection_adapters/makara_abstract_adapter_spec.rb +6 -6
- data/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb +28 -26
- data/spec/active_record/connection_adapters/makara_postgis_adapter_spec.rb +15 -15
- data/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb +25 -23
- data/spec/config_parser_spec.rb +26 -26
- data/spec/connection_wrapper_spec.rb +2 -2
- data/spec/context_spec.rb +1 -1
- data/spec/middleware_spec.rb +4 -4
- data/spec/pool_spec.rb +9 -9
- data/spec/proxy_spec.rb +74 -74
- data/spec/spec_helper.rb +7 -0
- data/spec/strategies/priority_failover_spec.rb +2 -2
- data/spec/strategies/shard_aware_spec.rb +16 -16
- data/spec/support/helpers.rb +6 -6
- data/spec/support/mock_objects.rb +1 -1
- data/spec/support/mysql2_database.yml +4 -4
- data/spec/support/mysql2_database_with_custom_errors.yml +4 -4
- data/spec/support/postgis_database.yml +4 -4
- data/spec/support/postgresql_database.yml +4 -4
- data/spec/support/proxy_extensions.rb +3 -3
- metadata +5 -5
@@ -52,16 +52,16 @@ if RUBY_ENGINE == 'ruby' &&
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
it 'should have one
|
56
|
-
expect(connection.
|
57
|
-
expect(connection.
|
55
|
+
it 'should have one primary and two replicas' do
|
56
|
+
expect(connection.primary_pool.connection_count).to eq(1)
|
57
|
+
expect(connection.replica_pool.connection_count).to eq(2)
|
58
58
|
end
|
59
59
|
|
60
60
|
it 'should allow real queries to work' do
|
61
61
|
connection.execute('INSERT INTO users (name) VALUES (\'John\')')
|
62
62
|
|
63
|
-
connection.
|
64
|
-
expect(
|
63
|
+
connection.primary_pool.connections.each do |primary|
|
64
|
+
expect(primary).to receive(:execute).never
|
65
65
|
end
|
66
66
|
|
67
67
|
change_context
|
@@ -71,28 +71,28 @@ if RUBY_ENGINE == 'ruby' &&
|
|
71
71
|
end
|
72
72
|
|
73
73
|
it 'should send SET operations to each connection' do
|
74
|
-
connection.
|
74
|
+
connection.primary_pool.connections.each do |con|
|
75
75
|
expect(con).to receive(:execute).with("SET TimeZone = 'UTC'").once
|
76
76
|
end
|
77
77
|
|
78
|
-
connection.
|
78
|
+
connection.replica_pool.connections.each do |con|
|
79
79
|
expect(con).to receive(:execute).with("SET TimeZone = 'UTC'").once
|
80
80
|
end
|
81
81
|
connection.execute("SET TimeZone = 'UTC'")
|
82
82
|
end
|
83
83
|
|
84
|
-
it 'should send reads to the
|
84
|
+
it 'should send reads to the replica' do
|
85
85
|
# ensure the next connection will be the first one
|
86
86
|
allow_any_instance_of(Makara::Strategies::RoundRobin).to receive(:single_one?){ true }
|
87
87
|
|
88
|
-
con = connection.
|
88
|
+
con = connection.replica_pool.connections.first
|
89
89
|
expect(con).to receive(:execute).with('SELECT * FROM users').once
|
90
90
|
|
91
91
|
connection.execute('SELECT * FROM users')
|
92
92
|
end
|
93
93
|
|
94
|
-
it 'should send writes to
|
95
|
-
con = connection.
|
94
|
+
it 'should send writes to primary' do
|
95
|
+
con = connection.primary_pool.connections.first
|
96
96
|
expect(con).to receive(:execute).with('UPDATE users SET name = "bob" WHERE id = 1')
|
97
97
|
connection.execute('UPDATE users SET name = "bob" WHERE id = 1')
|
98
98
|
end
|
@@ -115,10 +115,10 @@ if RUBY_ENGINE == 'ruby' &&
|
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
-
context 'with only
|
118
|
+
context 'with only primary connection' do
|
119
119
|
it 'should not raise errors on read and write' do
|
120
120
|
custom_config = config.deep_dup
|
121
|
-
custom_config['makara']['connections'].select{|h| h['role'] == '
|
121
|
+
custom_config['makara']['connections'].select{|h| h['role'] == 'replica' }.each{|h| h['port'] = '1'}
|
122
122
|
|
123
123
|
ActiveRecord::Base.establish_connection(custom_config)
|
124
124
|
load(File.dirname(__FILE__) + '/../../support/schema.rb')
|
@@ -128,14 +128,14 @@ if RUBY_ENGINE == 'ruby' &&
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
|
131
|
-
context 'with only
|
131
|
+
context 'with only replica connection' do
|
132
132
|
it 'should raise error only on write' do
|
133
133
|
ActiveRecord::Base.establish_connection(config)
|
134
134
|
load(File.dirname(__FILE__) + '/../../support/schema.rb')
|
135
135
|
ActiveRecord::Base.clear_all_connections!
|
136
136
|
|
137
137
|
custom_config = config.deep_dup
|
138
|
-
custom_config['makara']['connections'].select{|h| h['role'] == '
|
138
|
+
custom_config['makara']['connections'].select{|h| h['role'] == 'primary' }.each{|h| h['port'] = '1'}
|
139
139
|
|
140
140
|
ActiveRecord::Base.establish_connection(custom_config)
|
141
141
|
|
@@ -26,16 +26,16 @@ describe 'MakaraPostgreSQLAdapter' do
|
|
26
26
|
change_context
|
27
27
|
end
|
28
28
|
|
29
|
-
it 'should have one
|
30
|
-
expect(connection.
|
31
|
-
expect(connection.
|
29
|
+
it 'should have one primary and two replicas' do
|
30
|
+
expect(connection.primary_pool.connection_count).to eq(1)
|
31
|
+
expect(connection.replica_pool.connection_count).to eq(2)
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'should allow real queries to work' do
|
35
35
|
connection.execute('INSERT INTO users (name) VALUES (\'John\')')
|
36
36
|
|
37
|
-
connection.
|
38
|
-
expect(
|
37
|
+
connection.primary_pool.connections.each do |primary|
|
38
|
+
expect(primary).to receive(:execute).never
|
39
39
|
end
|
40
40
|
|
41
41
|
change_context
|
@@ -45,41 +45,43 @@ describe 'MakaraPostgreSQLAdapter' do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
it 'should send SET operations to each connection' do
|
48
|
-
connection.
|
48
|
+
connection.primary_pool.connections.each do |con|
|
49
49
|
expect(con).to receive(:execute).with("SET TimeZone = 'UTC'").once
|
50
50
|
end
|
51
51
|
|
52
|
-
connection.
|
52
|
+
connection.replica_pool.connections.each do |con|
|
53
53
|
expect(con).to receive(:execute).with("SET TimeZone = 'UTC'").once
|
54
54
|
end
|
55
55
|
connection.execute("SET TimeZone = 'UTC'")
|
56
56
|
end
|
57
57
|
|
58
|
-
it 'should send reads to the
|
58
|
+
it 'should send reads to the replica' do
|
59
59
|
# ensure the next connection will be the first one
|
60
60
|
allow_any_instance_of(Makara::Strategies::RoundRobin).to receive(:single_one?){ true }
|
61
61
|
|
62
|
-
con = connection.
|
62
|
+
con = connection.replica_pool.connections.first
|
63
63
|
expect(con).to receive(:execute).with('SELECT * FROM users').once
|
64
64
|
|
65
65
|
connection.execute('SELECT * FROM users')
|
66
66
|
end
|
67
67
|
|
68
|
-
it 'should send exists? to
|
68
|
+
it 'should send exists? to replica' do
|
69
69
|
allow_any_instance_of(Makara::Strategies::RoundRobin).to receive(:single_one?){ true }
|
70
70
|
Test::User.exists? # flush other (schema) things that need to happen
|
71
71
|
|
72
|
-
con = connection.
|
72
|
+
con = connection.replica_pool.connections.first
|
73
73
|
|
74
74
|
expect(con).to receive(:exec_query) do |query|
|
75
75
|
expect(query).to match(/SELECT\s+1\s*(AS one)?\s+FROM .?users.?\s+LIMIT\s+.?1/)
|
76
|
-
end.once.
|
76
|
+
end.once.
|
77
|
+
# and_call_original # Switch back to this once https://github.com/rspec/rspec-mocks/pull/1385 is released
|
78
|
+
and_wrap_original { |m, *args| m.call(*args.first(3)) }
|
77
79
|
|
78
80
|
Test::User.exists?
|
79
81
|
end
|
80
82
|
|
81
|
-
it 'should send writes to
|
82
|
-
con = connection.
|
83
|
+
it 'should send writes to primary' do
|
84
|
+
con = connection.primary_pool.connections.first
|
83
85
|
expect(con).to receive(:execute).with('UPDATE users SET name = "bob" WHERE id = 1')
|
84
86
|
connection.execute('UPDATE users SET name = "bob" WHERE id = 1')
|
85
87
|
end
|
@@ -95,10 +97,10 @@ describe 'MakaraPostgreSQLAdapter' do
|
|
95
97
|
end
|
96
98
|
end
|
97
99
|
|
98
|
-
context 'with only
|
100
|
+
context 'with only primary connection' do
|
99
101
|
it 'should not raise errors on read and write' do
|
100
102
|
custom_config = config.deep_dup
|
101
|
-
custom_config['makara']['connections'].select{|h| h['role'] == '
|
103
|
+
custom_config['makara']['connections'].select{|h| h['role'] == 'replica' }.each{|h| h['port'] = '1'}
|
102
104
|
|
103
105
|
establish_connection(custom_config)
|
104
106
|
load(File.dirname(__FILE__) + '/../../support/schema.rb')
|
@@ -108,14 +110,14 @@ describe 'MakaraPostgreSQLAdapter' do
|
|
108
110
|
end
|
109
111
|
end
|
110
112
|
|
111
|
-
context 'with only
|
113
|
+
context 'with only replica connection' do
|
112
114
|
it 'should raise error only on write' do
|
113
115
|
establish_connection(config)
|
114
116
|
load(File.dirname(__FILE__) + '/../../support/schema.rb')
|
115
117
|
ActiveRecord::Base.clear_all_connections!
|
116
118
|
|
117
119
|
custom_config = config.deep_dup
|
118
|
-
custom_config['makara']['connections'].select{|h| h['role'] == '
|
120
|
+
custom_config['makara']['connections'].select{|h| h['role'] == 'primary' }.each{|h| h['port'] = '1'}
|
119
121
|
|
120
122
|
establish_connection(custom_config)
|
121
123
|
|
@@ -131,13 +133,13 @@ describe 'MakaraPostgreSQLAdapter' do
|
|
131
133
|
load(File.dirname(__FILE__) + '/../../support/schema.rb')
|
132
134
|
change_context
|
133
135
|
|
134
|
-
# Pre-loads the attributes so that schema queries don't hit
|
136
|
+
# Pre-loads the attributes so that schema queries don't hit replica
|
135
137
|
# user = User.create(name: 'hello')
|
136
|
-
connection.
|
138
|
+
connection.replica_pool.connections.each do |replica|
|
137
139
|
# Using method missing to help with back trace, literally
|
138
|
-
# no query should be executed on
|
139
|
-
expect(
|
140
|
-
expect(
|
140
|
+
# no query should be executed on replica once a transaction is opened
|
141
|
+
expect(replica).to receive(:method_missing).never
|
142
|
+
expect(replica).to receive(:execute).never
|
141
143
|
end
|
142
144
|
end
|
143
145
|
|
data/spec/config_parser_spec.rb
CHANGED
@@ -7,14 +7,14 @@ describe Makara::ConfigParser do
|
|
7
7
|
makara: {
|
8
8
|
connections: [
|
9
9
|
{
|
10
|
-
role: '
|
11
|
-
name: '
|
10
|
+
role: 'primary',
|
11
|
+
name: 'theprimary'
|
12
12
|
},
|
13
13
|
{
|
14
|
-
name: '
|
14
|
+
name: 'replica1'
|
15
15
|
},
|
16
16
|
{
|
17
|
-
name: '
|
17
|
+
name: 'replica2'
|
18
18
|
}
|
19
19
|
]
|
20
20
|
}
|
@@ -24,7 +24,7 @@ describe Makara::ConfigParser do
|
|
24
24
|
context '::merge_and_resolve_default_url_config' do
|
25
25
|
let(:config_without_url) do
|
26
26
|
{
|
27
|
-
|
27
|
+
primary_ttl: 5,
|
28
28
|
blacklist_duration: 30,
|
29
29
|
sticky: true,
|
30
30
|
adapter: 'mysql2_makara',
|
@@ -39,7 +39,7 @@ describe Makara::ConfigParser do
|
|
39
39
|
|
40
40
|
let(:config_with_url) do
|
41
41
|
{
|
42
|
-
|
42
|
+
primary_ttl: 5,
|
43
43
|
blacklist_duration: 30,
|
44
44
|
sticky: true,
|
45
45
|
adapter: 'mysql2_makara',
|
@@ -106,32 +106,32 @@ describe Makara::ConfigParser do
|
|
106
106
|
expect(parser.id).to eq('myproxyidwithreservedcharacters')
|
107
107
|
end
|
108
108
|
|
109
|
-
context '
|
110
|
-
it 'should provide
|
109
|
+
context 'primary and replica configs' do
|
110
|
+
it 'should provide primary and replica configs' do
|
111
111
|
parser = described_class.new(config)
|
112
|
-
expect(parser.
|
112
|
+
expect(parser.primary_configs).to eq([
|
113
113
|
{
|
114
|
-
name: '
|
114
|
+
name: 'theprimary',
|
115
115
|
top_level: 'value',
|
116
116
|
sticky: true,
|
117
117
|
blacklist_duration: 30,
|
118
|
-
|
118
|
+
primary_ttl: 5
|
119
119
|
}
|
120
120
|
])
|
121
|
-
expect(parser.
|
121
|
+
expect(parser.replica_configs).to eq([
|
122
122
|
{
|
123
|
-
name: '
|
123
|
+
name: 'replica1',
|
124
124
|
top_level: 'value',
|
125
125
|
sticky: true,
|
126
126
|
blacklist_duration: 30,
|
127
|
-
|
127
|
+
primary_ttl: 5
|
128
128
|
},
|
129
129
|
{
|
130
|
-
name: '
|
130
|
+
name: 'replica2',
|
131
131
|
top_level: 'value',
|
132
132
|
sticky: true,
|
133
133
|
blacklist_duration: 30,
|
134
|
-
|
134
|
+
primary_ttl: 5
|
135
135
|
}
|
136
136
|
])
|
137
137
|
end
|
@@ -139,32 +139,32 @@ describe Makara::ConfigParser do
|
|
139
139
|
it 'connection configuration should override makara config' do
|
140
140
|
config[:makara][:blacklist_duration] = 123
|
141
141
|
config[:makara][:connections][0][:blacklist_duration] = 456
|
142
|
-
config[:makara][:connections][1][:top_level] = '
|
142
|
+
config[:makara][:connections][1][:top_level] = 'replica value'
|
143
143
|
|
144
144
|
parser = described_class.new(config)
|
145
|
-
expect(parser.
|
145
|
+
expect(parser.primary_configs).to eq([
|
146
146
|
{
|
147
|
-
name: '
|
147
|
+
name: 'theprimary',
|
148
148
|
top_level: 'value',
|
149
149
|
sticky: true,
|
150
150
|
blacklist_duration: 456,
|
151
|
-
|
151
|
+
primary_ttl: 5
|
152
152
|
}
|
153
153
|
])
|
154
|
-
expect(parser.
|
154
|
+
expect(parser.replica_configs).to eq([
|
155
155
|
{
|
156
|
-
name: '
|
157
|
-
top_level: '
|
156
|
+
name: 'replica1',
|
157
|
+
top_level: 'replica value',
|
158
158
|
sticky: true,
|
159
159
|
blacklist_duration: 123,
|
160
|
-
|
160
|
+
primary_ttl: 5
|
161
161
|
},
|
162
162
|
{
|
163
|
-
name: '
|
163
|
+
name: 'replica2',
|
164
164
|
top_level: 'value',
|
165
165
|
sticky: true,
|
166
166
|
blacklist_duration: 123,
|
167
|
-
|
167
|
+
primary_ttl: 5
|
168
168
|
}
|
169
169
|
])
|
170
170
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Makara::ConnectionWrapper do
|
4
|
-
let(:proxy){ FakeProxy.new({makara: {blacklist_duration: 5, connections: [{role: '
|
4
|
+
let(:proxy){ FakeProxy.new({makara: {blacklist_duration: 5, connections: [{role: 'primary'}, {role: 'replica'}, {role: 'replica'}]}}) }
|
5
5
|
let(:connection){ subject._makara_connection }
|
6
6
|
|
7
|
-
subject{ proxy.
|
7
|
+
subject{ proxy.primary_pool.connections.first }
|
8
8
|
|
9
9
|
it 'should extend the connection with new functionality' do
|
10
10
|
expect(connection).to respond_to(:_makara_name)
|
data/spec/context_spec.rb
CHANGED
@@ -49,7 +49,7 @@ describe Makara::Context do
|
|
49
49
|
Makara::Context.set_current(context_data)
|
50
50
|
end
|
51
51
|
|
52
|
-
it 'sticks a proxy to
|
52
|
+
it 'sticks a proxy to primary for the current request' do
|
53
53
|
expect(Makara::Context.stuck?('mariadb')).to be_falsey
|
54
54
|
|
55
55
|
Makara::Context.stick('mariadb', 10)
|
data/spec/middleware_spec.rb
CHANGED
@@ -32,7 +32,7 @@ describe Makara::Middleware do
|
|
32
32
|
_, headers, body = middleware.call(env)
|
33
33
|
|
34
34
|
expect(headers).to eq({})
|
35
|
-
expect(body).to eq('
|
35
|
+
expect(body).to eq('replica/1')
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'should use the cookie-provided context if present' do
|
@@ -41,15 +41,15 @@ describe Makara::Middleware do
|
|
41
41
|
_, headers, body = middleware.call(env)
|
42
42
|
|
43
43
|
expect(headers).to eq({})
|
44
|
-
expect(body).to eq('
|
44
|
+
expect(body).to eq('primary/1')
|
45
45
|
end
|
46
46
|
|
47
|
-
it 'should set the cookie if
|
47
|
+
it 'should set the cookie if primary is used' do
|
48
48
|
env[:query] = 'update users set name = "phil"'
|
49
49
|
|
50
50
|
_, headers, body = middleware.call(env)
|
51
51
|
|
52
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
|
-
expect(body).to eq('
|
53
|
+
expect(body).to eq('primary/1')
|
54
54
|
end
|
55
55
|
end
|
data/spec/pool_spec.rb
CHANGED
@@ -4,7 +4,7 @@ describe Makara::Pool do
|
|
4
4
|
let(:proxy){ FakeProxy.new({makara: pool_config.merge(connections: [])}) }
|
5
5
|
let(:pool){ Makara::Pool.new('test', proxy) }
|
6
6
|
let(:pool_config){ {blacklist_duration: 5} }
|
7
|
-
let(:
|
7
|
+
let(:primary_pool){ Makara::Pool.new('primary', proxy) }
|
8
8
|
|
9
9
|
it 'should wrap connections with a ConnectionWrapper as theyre added to the pool' do
|
10
10
|
expect(pool.connections).to be_empty
|
@@ -147,10 +147,10 @@ describe Makara::Pool do
|
|
147
147
|
end
|
148
148
|
|
149
149
|
it 'should error out while blacklisted in transaction' do
|
150
|
-
wrapper_a =
|
151
|
-
|
150
|
+
wrapper_a = primary_pool.add(pool_config){ FakeConnection.new(open_transactions: 1) }
|
151
|
+
primary_pool.add(pool_config){ FakeConnection.new }
|
152
152
|
expect {
|
153
|
-
|
153
|
+
primary_pool.provide do |connection|
|
154
154
|
if connection == wrapper_a
|
155
155
|
raise Makara::Errors::BlacklistConnection.new(wrapper_a, StandardError.new('failure'))
|
156
156
|
end
|
@@ -158,14 +158,14 @@ describe Makara::Pool do
|
|
158
158
|
}.to raise_error(Makara::Errors::BlacklistedWhileInTransaction)
|
159
159
|
end
|
160
160
|
|
161
|
-
it 'skips blacklisted connections in
|
162
|
-
wrapper_a =
|
163
|
-
|
164
|
-
|
161
|
+
it 'skips blacklisted connections in primary pool when not in transaction' do
|
162
|
+
wrapper_a = primary_pool.add(pool_config){ FakeConnection.new(open_transactions: 0) }
|
163
|
+
primary_pool.add(pool_config){ FakeConnection.new }
|
164
|
+
primary_pool.provide do |connection|
|
165
165
|
if connection == wrapper_a
|
166
166
|
raise Makara::Errors::BlacklistConnection.new(wrapper_a, StandardError.new('failure'))
|
167
167
|
end
|
168
168
|
end
|
169
|
-
10.times{
|
169
|
+
10.times{ primary_pool.provide{|connection| expect(connection).not_to eq(wrapper_a) } }
|
170
170
|
end
|
171
171
|
end
|
data/spec/proxy_spec.rb
CHANGED
@@ -3,82 +3,82 @@ require 'spec_helper'
|
|
3
3
|
describe Makara::Proxy do
|
4
4
|
let(:klass){ FakeProxy }
|
5
5
|
|
6
|
-
it 'sets up a
|
6
|
+
it 'sets up a primary and replica pool no matter the number of connections' do
|
7
7
|
proxy = klass.new(config(0, 0))
|
8
|
-
expect(proxy.
|
9
|
-
expect(proxy.
|
8
|
+
expect(proxy.primary_pool).to be_a(Makara::Pool)
|
9
|
+
expect(proxy.replica_pool).to be_a(Makara::Pool)
|
10
10
|
|
11
11
|
proxy = klass.new(config(2, 0))
|
12
|
-
expect(proxy.
|
13
|
-
expect(proxy.
|
12
|
+
expect(proxy.primary_pool).to be_a(Makara::Pool)
|
13
|
+
expect(proxy.replica_pool).to be_a(Makara::Pool)
|
14
14
|
|
15
15
|
proxy = klass.new(config(0, 2))
|
16
|
-
expect(proxy.
|
17
|
-
expect(proxy.
|
16
|
+
expect(proxy.primary_pool).to be_a(Makara::Pool)
|
17
|
+
expect(proxy.replica_pool).to be_a(Makara::Pool)
|
18
18
|
|
19
19
|
proxy = klass.new(config(2, 2))
|
20
|
-
expect(proxy.
|
21
|
-
expect(proxy.
|
20
|
+
expect(proxy.primary_pool).to be_a(Makara::Pool)
|
21
|
+
expect(proxy.replica_pool).to be_a(Makara::Pool)
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'instantiates N connections within each pool' do
|
25
25
|
proxy = klass.new(config(1, 2))
|
26
26
|
|
27
|
-
expect(proxy.
|
28
|
-
expect(proxy.
|
27
|
+
expect(proxy.primary_pool.connection_count).to eq(1)
|
28
|
+
expect(proxy.replica_pool.connection_count).to eq(2)
|
29
29
|
end
|
30
30
|
|
31
|
-
it 'should delegate any unknown method to a connection in the
|
31
|
+
it 'should delegate any unknown method to a connection in the primary pool' do
|
32
32
|
proxy = klass.new(config(1, 2))
|
33
33
|
|
34
|
-
con = proxy.
|
34
|
+
con = proxy.primary_pool.connections.first
|
35
35
|
allow(con).to receive(:irespondtothis){ 'hello!' }
|
36
36
|
|
37
37
|
expect(proxy).to respond_to(:irespondtothis)
|
38
38
|
expect(proxy.irespondtothis).to eq('hello!')
|
39
39
|
end
|
40
40
|
|
41
|
-
describe '#
|
41
|
+
describe '#stick_to_primary' do
|
42
42
|
let(:proxy) { klass.new(config(1, 2)) }
|
43
43
|
|
44
|
-
it 'should use
|
45
|
-
expect(proxy.
|
44
|
+
it 'should use primary if manually forced' do
|
45
|
+
expect(proxy.primary_for?('select * from users')).to eq(false)
|
46
46
|
|
47
|
-
proxy.
|
47
|
+
proxy.stick_to_primary!
|
48
48
|
|
49
|
-
expect(proxy.
|
49
|
+
expect(proxy.primary_for?('select * from users')).to eq(true)
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'should persist stickiness by default' do
|
53
53
|
now = Time.now
|
54
|
-
proxy.
|
54
|
+
proxy.stick_to_primary!
|
55
55
|
|
56
56
|
next_context = Makara::Context.next
|
57
57
|
expect(next_context[proxy.id]).to be >= (now + 5).to_f
|
58
58
|
|
59
59
|
proxy = klass.new(config(1, 2))
|
60
|
-
expect(proxy.
|
60
|
+
expect(proxy.primary_for?('select * from users')).to eq(true)
|
61
61
|
end
|
62
62
|
|
63
63
|
it 'optionally skips stickiness persistence, so it applies only to the current request' do
|
64
64
|
now = Time.now
|
65
|
-
proxy.
|
65
|
+
proxy.stick_to_primary!(false)
|
66
66
|
|
67
|
-
expect(proxy.
|
67
|
+
expect(proxy.primary_for?('select * from users')).to eq(true)
|
68
68
|
next_context = Makara::Context.next
|
69
69
|
expect(next_context).to be_nil # Nothing to persist, so context is empty
|
70
70
|
|
71
71
|
proxy = klass.new(config(1, 2))
|
72
|
-
expect(proxy.
|
72
|
+
expect(proxy.primary_for?('select * from users')).to eq(false)
|
73
73
|
end
|
74
74
|
|
75
|
-
it 'supports a float
|
75
|
+
it 'supports a float primary_ttl for stickiness duration' do
|
76
76
|
now = Time.now
|
77
77
|
config = config(1, 2).dup
|
78
|
-
config[:makara][:
|
78
|
+
config[:makara][:primary_ttl] = 0.5
|
79
79
|
proxy = klass.new(config)
|
80
80
|
|
81
|
-
proxy.
|
81
|
+
proxy.stick_to_primary!
|
82
82
|
|
83
83
|
next_context = Makara::Context.next
|
84
84
|
expect(next_context[proxy.id]).to be >= (now + 0.5).to_f
|
@@ -93,90 +93,90 @@ describe Makara::Proxy do
|
|
93
93
|
expect(proxy.sticky).to eq(true)
|
94
94
|
end
|
95
95
|
|
96
|
-
it 'should provide the
|
97
|
-
expect(proxy.
|
96
|
+
it 'should provide the replica pool for a read' do
|
97
|
+
expect(proxy.primary_for?('select * from users')).to eq(false)
|
98
98
|
end
|
99
99
|
|
100
|
-
it 'should provide the
|
101
|
-
expect(proxy.
|
100
|
+
it 'should provide the primary pool for a write' do
|
101
|
+
expect(proxy.primary_for?('insert into users values (a,b,c)')).to eq(true)
|
102
102
|
end
|
103
103
|
|
104
|
-
#
|
105
|
-
it 'should stick to
|
106
|
-
expect(proxy.
|
107
|
-
expect(proxy.
|
104
|
+
# primary is used, it should continue being used for the duration of the context
|
105
|
+
it 'should stick to primary once used for a sticky operation' do
|
106
|
+
expect(proxy.primary_for?('insert into users values (a,b,c)')).to eq(true)
|
107
|
+
expect(proxy.primary_for?('select * from users')).to eq(true)
|
108
108
|
end
|
109
109
|
|
110
|
-
it 'should not stick to
|
110
|
+
it 'should not stick to primary if stickiness is disabled' do
|
111
111
|
proxy.sticky = false
|
112
|
-
expect(proxy.
|
113
|
-
expect(proxy.
|
112
|
+
expect(proxy.primary_for?('insert into users values (a,b,c)')).to eq(true)
|
113
|
+
expect(proxy.primary_for?('select * from users')).to eq(false)
|
114
114
|
end
|
115
115
|
|
116
|
-
it 'should not stick to
|
117
|
-
expect(proxy.
|
116
|
+
it 'should not stick to primary if we are in a without_sticking block' do
|
117
|
+
expect(proxy.primary_for?('insert into users values (a,b,c)')).to eq(true)
|
118
118
|
|
119
119
|
proxy.without_sticking do
|
120
|
-
expect(proxy.
|
121
|
-
expect(proxy.
|
120
|
+
expect(proxy.primary_for?('insert into users values (a,b,c)')).to eq(true)
|
121
|
+
expect(proxy.primary_for?('select * from users')).to eq(false)
|
122
122
|
end
|
123
123
|
|
124
|
-
expect(proxy.
|
125
|
-
expect(proxy.
|
126
|
-
expect(proxy.
|
124
|
+
expect(proxy.primary_for?('select * from users')).to eq(true)
|
125
|
+
expect(proxy.primary_for?('insert into users values (a,b,c)')).to eq(true)
|
126
|
+
expect(proxy.primary_for?('select * from users')).to eq(true)
|
127
127
|
end
|
128
128
|
|
129
|
-
it 'should not stick to
|
130
|
-
expect(proxy.
|
129
|
+
it 'should not stick to primary after without_sticking block if there is a write in it' do
|
130
|
+
expect(proxy.primary_for?('select * from users')).to eq(false)
|
131
131
|
|
132
132
|
proxy.without_sticking do
|
133
|
-
expect(proxy.
|
134
|
-
expect(proxy.
|
133
|
+
expect(proxy.primary_for?('insert into users values (a,b,c)')).to eq(true)
|
134
|
+
expect(proxy.primary_for?('select * from users')).to eq(false)
|
135
135
|
end
|
136
136
|
|
137
|
-
expect(proxy.
|
137
|
+
expect(proxy.primary_for?('select * from users')).to eq(false)
|
138
138
|
end
|
139
139
|
|
140
|
-
it "should not release
|
141
|
-
expect(proxy.
|
142
|
-
expect(proxy.
|
140
|
+
it "should not release primary if it was stuck in the same request (no context changes yet)" do
|
141
|
+
expect(proxy.primary_for?('insert into users values (a,b,c)')).to eq(true)
|
142
|
+
expect(proxy.primary_for?('select * from users')).to eq(true)
|
143
143
|
|
144
144
|
Timecop.travel Time.now + 10 do
|
145
|
-
#
|
145
|
+
# primary_ttl has passed but we are still in the same request, so current context
|
146
146
|
# is still relevant
|
147
|
-
expect(proxy.
|
147
|
+
expect(proxy.primary_for?('select * from users')).to eq(true)
|
148
148
|
end
|
149
149
|
end
|
150
150
|
|
151
|
-
it 'should release
|
152
|
-
expect(proxy.
|
153
|
-
expect(proxy.
|
151
|
+
it 'should release primary if all stuck connections are released' do
|
152
|
+
expect(proxy.primary_for?('insert into users values (a,b,c)')).to eq(true)
|
153
|
+
expect(proxy.primary_for?('select * from users')).to eq(true)
|
154
154
|
|
155
155
|
Makara::Context.release_all
|
156
156
|
|
157
|
-
expect(proxy.
|
157
|
+
expect(proxy.primary_for?('select * from users')).to eq(false)
|
158
158
|
end
|
159
159
|
|
160
|
-
it 'should use
|
161
|
-
allow(proxy.
|
162
|
-
expect(proxy.
|
160
|
+
it 'should use primary if all replicas are blacklisted' do
|
161
|
+
allow(proxy.replica_pool).to receive(:completely_blacklisted?){ true }
|
162
|
+
expect(proxy.primary_for?('select * from users')).to eq(true)
|
163
163
|
end
|
164
164
|
|
165
|
-
it 'should use
|
166
|
-
allow(proxy.
|
165
|
+
it 'should use primary if all replicas become blacklisted as part of the invocation' do
|
166
|
+
allow(proxy.replica_pool).to receive(:next).and_return(nil)
|
167
167
|
|
168
168
|
test = double
|
169
169
|
expect(test).to receive(:blacklisting).once
|
170
|
-
expect(test).to receive(:
|
170
|
+
expect(test).to receive(:using_primary).once
|
171
171
|
|
172
172
|
proxy.send(:appropriate_pool, :execute, ['select * from users']) do |pool|
|
173
|
-
if pool == proxy.
|
173
|
+
if pool == proxy.replica_pool
|
174
174
|
test.blacklisting
|
175
175
|
pool.instance_variable_get('@blacklist_errors') << StandardError.new('some connection issue')
|
176
176
|
pool.connections.each(&:_makara_blacklist!)
|
177
177
|
pool.provide
|
178
178
|
else
|
179
|
-
test.
|
179
|
+
test.using_primary
|
180
180
|
end
|
181
181
|
end
|
182
182
|
end
|
@@ -185,23 +185,23 @@ describe Makara::Proxy do
|
|
185
185
|
proxy.ping
|
186
186
|
|
187
187
|
# weird setup to allow for the correct
|
188
|
-
proxy.
|
189
|
-
proxy.
|
190
|
-
proxy.
|
191
|
-
proxy.
|
188
|
+
proxy.replica_pool.connections.each(&:_makara_blacklist!)
|
189
|
+
proxy.replica_pool.instance_variable_get('@blacklist_errors') << StandardError.new('some replica connection issue')
|
190
|
+
proxy.primary_pool.connections.each(&:_makara_blacklist!)
|
191
|
+
proxy.primary_pool.instance_variable_get('@blacklist_errors') << StandardError.new('some primary connection issue')
|
192
192
|
|
193
|
-
allow(proxy).to receive(:_appropriate_pool).and_return(proxy.
|
193
|
+
allow(proxy).to receive(:_appropriate_pool).and_return(proxy.replica_pool, proxy.primary_pool)
|
194
194
|
|
195
195
|
begin
|
196
196
|
proxy.send(:appropriate_pool, :execute, ['select * from users']) do |pool|
|
197
197
|
pool.provide{|c| c }
|
198
198
|
end
|
199
199
|
rescue Makara::Errors::AllConnectionsBlacklisted => e
|
200
|
-
expect(e.message).to eq('[Makara/
|
200
|
+
expect(e.message).to eq('[Makara/primary] All connections are blacklisted -> some primary connection issue -> [Makara/replica] All connections are blacklisted -> some replica connection issue')
|
201
201
|
end
|
202
202
|
|
203
|
-
proxy.
|
204
|
-
proxy.
|
203
|
+
proxy.replica_pool.connections.each{|con| expect(con._makara_blacklisted?).to eq(false) }
|
204
|
+
proxy.primary_pool.connections.each{|con| expect(con._makara_blacklisted?).to eq(false) }
|
205
205
|
end
|
206
206
|
end
|
207
207
|
end
|