makara 0.5.1 → 0.6.0.pre
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/.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
|