makara 0.4.1 → 0.5.1
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/dependabot.yml +11 -0
- data/.github/workflows/CI.yml +88 -0
- data/.github/workflows/gem-publish-public.yml +36 -0
- data/.rspec +1 -1
- data/.rubocop.yml +15 -0
- data/.rubocop_todo.yml +670 -0
- data/CHANGELOG.md +69 -48
- data/Gemfile +1 -16
- data/README.md +8 -9
- data/Rakefile +1 -1
- data/gemfiles/activerecord_5.2.gemfile +8 -0
- data/gemfiles/activerecord_6.0.gemfile +8 -0
- data/gemfiles/activerecord_6.1.gemfile +8 -0
- data/gemfiles/activerecord_head.gemfile +6 -0
- data/lib/active_record/connection_adapters/jdbcmysql_makara_adapter.rb +4 -18
- data/lib/active_record/connection_adapters/jdbcpostgresql_makara_adapter.rb +4 -18
- data/lib/active_record/connection_adapters/makara_abstract_adapter.rb +107 -30
- data/lib/active_record/connection_adapters/makara_jdbcmysql_adapter.rb +4 -18
- data/lib/active_record/connection_adapters/makara_jdbcpostgresql_adapter.rb +4 -18
- data/lib/active_record/connection_adapters/makara_mysql2_adapter.rb +4 -20
- data/lib/active_record/connection_adapters/makara_postgis_adapter.rb +4 -19
- data/lib/active_record/connection_adapters/makara_postgresql_adapter.rb +4 -20
- data/lib/active_record/connection_adapters/mysql2_makara_adapter.rb +4 -20
- data/lib/active_record/connection_adapters/postgresql_makara_adapter.rb +4 -20
- data/lib/makara/cache.rb +0 -2
- data/lib/makara/config_parser.rb +7 -15
- data/lib/makara/connection_wrapper.rb +43 -22
- data/lib/makara/context.rb +1 -0
- data/lib/makara/cookie.rb +1 -0
- data/lib/makara/error_handler.rb +0 -9
- data/lib/makara/errors/all_connections_blacklisted.rb +0 -2
- data/lib/makara/errors/blacklist_connection.rb +0 -2
- data/lib/makara/errors/blacklisted_while_in_transaction.rb +12 -0
- data/lib/makara/errors/invalid_shard.rb +14 -0
- data/lib/makara/errors/makara_error.rb +0 -1
- data/lib/makara/errors/no_connections_available.rb +0 -2
- data/lib/makara/logging/logger.rb +0 -4
- data/lib/makara/logging/subscriber.rb +0 -2
- data/lib/makara/middleware.rb +1 -2
- data/lib/makara/pool.rb +49 -31
- data/lib/makara/proxy.rb +56 -30
- data/lib/makara/railtie.rb +0 -2
- data/lib/makara/strategies/abstract.rb +1 -0
- data/lib/makara/strategies/priority_failover.rb +2 -0
- data/lib/makara/strategies/round_robin.rb +1 -3
- data/lib/makara/strategies/shard_aware.rb +45 -0
- data/lib/makara/version.rb +1 -3
- data/lib/makara.rb +7 -6
- data/makara.gemspec +26 -3
- data/spec/active_record/connection_adapters/makara_abstract_adapter_error_handling_spec.rb +1 -6
- data/spec/active_record/connection_adapters/makara_abstract_adapter_spec.rb +0 -9
- data/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb +9 -22
- data/spec/active_record/connection_adapters/makara_postgis_adapter_spec.rb +2 -10
- data/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb +62 -18
- data/spec/cache_spec.rb +0 -1
- data/spec/config_parser_spec.rb +54 -56
- data/spec/connection_wrapper_spec.rb +1 -2
- data/spec/cookie_spec.rb +4 -4
- data/spec/middleware_spec.rb +2 -2
- data/spec/pool_spec.rb +25 -14
- data/spec/proxy_spec.rb +0 -4
- data/spec/spec_helper.rb +6 -1
- data/spec/strategies/priority_failover_spec.rb +3 -4
- data/spec/strategies/round_robin_spec.rb +4 -8
- data/spec/strategies/shard_aware_spec.rb +218 -0
- data/spec/support/deep_dup.rb +1 -1
- data/spec/support/helpers.rb +5 -5
- data/spec/support/mock_objects.rb +5 -4
- data/spec/support/mysql2_database.yml +2 -2
- data/spec/support/mysql2_database_with_custom_errors.yml +2 -2
- data/spec/support/pool_extensions.rb +0 -3
- data/spec/support/postgis_schema.rb +1 -1
- data/spec/support/postgresql_database.yml +0 -2
- data/spec/support/proxy_extensions.rb +1 -3
- data/spec/support/schema.rb +1 -1
- data/spec/support/user.rb +1 -2
- metadata +165 -20
- data/.travis.yml +0 -105
- data/gemfiles/ar-head.gemfile +0 -24
- data/gemfiles/ar30.gemfile +0 -36
- data/gemfiles/ar31.gemfile +0 -36
- data/gemfiles/ar32.gemfile +0 -36
- data/gemfiles/ar40.gemfile +0 -24
- data/gemfiles/ar41.gemfile +0 -24
- data/gemfiles/ar42.gemfile +0 -24
- data/gemfiles/ar50.gemfile +0 -24
- data/gemfiles/ar51.gemfile +0 -24
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Makara::Strategies::RoundRobin do
|
4
|
-
let(:proxy){ FakeProxy.new({:
|
4
|
+
let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
|
5
5
|
let(:pool){ Makara::Pool.new('test', proxy) }
|
6
|
-
let(:pool_config){ {:
|
6
|
+
let(:pool_config){ {blacklist_duration: 5} }
|
7
7
|
let(:makara_config) { {} }
|
8
8
|
let(:strategy) { pool.strategy }
|
9
9
|
|
@@ -14,7 +14,7 @@ describe Makara::Strategies::RoundRobin do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
context 'bad config' do
|
17
|
-
let(:makara_config) { { :
|
17
|
+
let(:makara_config) { { test_strategy: 'SomethingElse::Here' } }
|
18
18
|
it 'should raise name error' do
|
19
19
|
expect {
|
20
20
|
pool
|
@@ -23,13 +23,12 @@ describe Makara::Strategies::RoundRobin do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
context 'given in config' do
|
26
|
-
let(:makara_config) { { :
|
26
|
+
let(:makara_config) { { test_strategy: 'round_robin' } }
|
27
27
|
it 'should use the strategy' do
|
28
28
|
expect(pool.strategy).to be_instance_of(Makara::Strategies::RoundRobin)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
32
|
it 'should loop through with weights' do
|
34
33
|
wrapper_a = pool.add(pool_config){ FakeConnection.new(something: 'a') }
|
35
34
|
wrapper_b = pool.add(pool_config){ FakeConnection.new(something: 'b') }
|
@@ -61,7 +60,4 @@ describe Makara::Strategies::RoundRobin do
|
|
61
60
|
expect(strategy.next.something).to eql('c')
|
62
61
|
expect(strategy.next.something).to eql('b')
|
63
62
|
end
|
64
|
-
|
65
|
-
|
66
|
-
|
67
63
|
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Makara::Strategies::ShardAware do
|
4
|
+
def with_shard(shard_id)
|
5
|
+
begin
|
6
|
+
Thread.current['makara_shard_id'] = shard_id
|
7
|
+
yield
|
8
|
+
ensure
|
9
|
+
Thread.current['makara_shard_id'] = nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "failover strategy with shard awareness," do
|
14
|
+
let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
|
15
|
+
let(:pool){ Makara::Pool.new('master', proxy) }
|
16
|
+
let(:pool_config){ { blacklist_duration: 5} }
|
17
|
+
let(:makara_config) { {
|
18
|
+
master_strategy: 'failover',
|
19
|
+
master_shard_aware: true,
|
20
|
+
master_default_shard: 'shard2'
|
21
|
+
} }
|
22
|
+
let(:strategy) { pool.strategy }
|
23
|
+
|
24
|
+
it 'should use the strategy' do
|
25
|
+
expect(pool.strategy).to be_instance_of(Makara::Strategies::ShardAware)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should take the top weight for a given shard' do
|
29
|
+
wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
|
30
|
+
wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1', weight: 2)){ FakeConnection.new(something: 'b') }
|
31
|
+
wrapper_c = pool.add(pool_config.merge(weight: 2, shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
|
32
|
+
|
33
|
+
# default shard
|
34
|
+
expect(strategy.current.something).to eql('c')
|
35
|
+
expect(strategy.next.something).to eql('c')
|
36
|
+
expect(strategy.next.something).to eql('c')
|
37
|
+
|
38
|
+
# shard1
|
39
|
+
with_shard('shard1') do
|
40
|
+
expect(strategy.current.something).to eql('b')
|
41
|
+
expect(strategy.next.something).to eql('b')
|
42
|
+
expect(strategy.next.something).to eql('b')
|
43
|
+
end
|
44
|
+
|
45
|
+
# shard2
|
46
|
+
with_shard('shard2') do
|
47
|
+
expect(strategy.current.something).to eql('c')
|
48
|
+
expect(strategy.next.something).to eql('c')
|
49
|
+
expect(strategy.next.something).to eql('c')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should take given order within shard if no weights' do
|
54
|
+
wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
|
55
|
+
wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'b') }
|
56
|
+
wrapper_c = pool.add(pool_config.merge(shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
|
57
|
+
|
58
|
+
# default shard
|
59
|
+
expect(strategy.current.something).to eql('c')
|
60
|
+
expect(strategy.next.something).to eql('c')
|
61
|
+
expect(strategy.next.something).to eql('c')
|
62
|
+
|
63
|
+
# shard1
|
64
|
+
with_shard('shard1') do
|
65
|
+
expect(strategy.current.something).to eql('a')
|
66
|
+
expect(strategy.next.something).to eql('a')
|
67
|
+
end
|
68
|
+
|
69
|
+
# shard2
|
70
|
+
with_shard('shard2') do
|
71
|
+
expect(strategy.current.something).to eql('c')
|
72
|
+
expect(strategy.next.something).to eql('c')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should handle failover to next one within shard' do
|
77
|
+
wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
|
78
|
+
wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'b') }
|
79
|
+
wrapper_c = pool.add(pool_config.merge(shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
|
80
|
+
|
81
|
+
# default shard
|
82
|
+
expect(strategy.current.something).to eql('c')
|
83
|
+
expect(strategy.next.something).to eql('c')
|
84
|
+
expect(strategy.next.something).to eql('c')
|
85
|
+
|
86
|
+
# skips a for shard1
|
87
|
+
with_shard('shard1') do
|
88
|
+
pool.provide do |connection|
|
89
|
+
if connection == wrapper_a
|
90
|
+
raise Makara::Errors::BlacklistConnection.new(wrapper_a, StandardError.new('failure'))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
expect(strategy.current.something).to eql('b')
|
94
|
+
expect(strategy.next.something).to eql('b')
|
95
|
+
end
|
96
|
+
|
97
|
+
# shard2
|
98
|
+
with_shard('shard2') do
|
99
|
+
expect(strategy.current.something).to eql('c')
|
100
|
+
expect(strategy.next.something).to eql('c')
|
101
|
+
end
|
102
|
+
end
|
103
|
+
it 'raises error for invalid shard' do
|
104
|
+
with_shard('shard3') do
|
105
|
+
expect{strategy.current.something }.to raise_error(Makara::Errors::InvalidShard)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "round_robin strategy with shard awareness," do
|
111
|
+
let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
|
112
|
+
let(:pool){ Makara::Pool.new('master', proxy) }
|
113
|
+
let(:pool_config){ { blacklist_duration: 5} }
|
114
|
+
let(:makara_config) { {
|
115
|
+
master_strategy: 'round_robin',
|
116
|
+
master_shard_aware: true,
|
117
|
+
master_default_shard: 'shard2'
|
118
|
+
} }
|
119
|
+
let(:strategy) { pool.strategy }
|
120
|
+
|
121
|
+
it 'should use the strategy' do
|
122
|
+
expect(pool.strategy).to be_instance_of(Makara::Strategies::ShardAware)
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should loop through with weights within shard' do
|
126
|
+
wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
|
127
|
+
wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1', weight: 2)){ FakeConnection.new(something: 'b') }
|
128
|
+
wrapper_c = pool.add(pool_config.merge(weight: 2, shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
|
129
|
+
|
130
|
+
# default shard
|
131
|
+
expect(strategy.current.something).to eql('c')
|
132
|
+
expect(strategy.next.something).to eql('c')
|
133
|
+
expect(strategy.next.something).to eql('c')
|
134
|
+
|
135
|
+
# shard1
|
136
|
+
with_shard('shard1') do
|
137
|
+
expect(strategy.current.something).to eql('a')
|
138
|
+
expect(strategy.next.something).to eql('b')
|
139
|
+
expect(strategy.next.something).to eql('b')
|
140
|
+
expect(strategy.next.something).to eql('a')
|
141
|
+
end
|
142
|
+
|
143
|
+
# shard2
|
144
|
+
with_shard('shard2') do
|
145
|
+
expect(strategy.current.something).to eql('c')
|
146
|
+
expect(strategy.next.something).to eql('c')
|
147
|
+
expect(strategy.next.something).to eql('c')
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should handle failover to next one within shard' do
|
152
|
+
wrapper_a = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'a') }
|
153
|
+
wrapper_b = pool.add(pool_config.merge(shard_id: 'shard1')){ FakeConnection.new(something: 'b') }
|
154
|
+
wrapper_c = pool.add(pool_config.merge(shard_id: 'shard2')){ FakeConnection.new(something: 'c') }
|
155
|
+
|
156
|
+
# default shard
|
157
|
+
expect(strategy.current.something).to eql('c')
|
158
|
+
expect(strategy.next.something).to eql('c')
|
159
|
+
expect(strategy.next.something).to eql('c')
|
160
|
+
|
161
|
+
# skips a for shard1
|
162
|
+
with_shard('shard1') do
|
163
|
+
pool.provide do |connection|
|
164
|
+
if connection == wrapper_a
|
165
|
+
raise Makara::Errors::BlacklistConnection.new(wrapper_a, StandardError.new('failure'))
|
166
|
+
end
|
167
|
+
end
|
168
|
+
expect(strategy.current.something).to eql('b')
|
169
|
+
expect(strategy.next.something).to eql('b')
|
170
|
+
expect(strategy.next.something).to eql('b')
|
171
|
+
end
|
172
|
+
|
173
|
+
# shard2
|
174
|
+
with_shard('shard2') do
|
175
|
+
expect(strategy.current.something).to eql('c')
|
176
|
+
expect(strategy.next.something).to eql('c')
|
177
|
+
expect(strategy.next.something).to eql('c')
|
178
|
+
end
|
179
|
+
end
|
180
|
+
it 'raises error for invalid shard' do
|
181
|
+
with_shard('shard3') do
|
182
|
+
expect{strategy.current.something }.to raise_error(Makara::Errors::InvalidShard)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "uses the configured failover strategy when shard_aware set to false," do
|
188
|
+
let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
|
189
|
+
let(:pool){ Makara::Pool.new('master', proxy) }
|
190
|
+
let(:pool_config){ { blacklist_duration: 5} }
|
191
|
+
let(:makara_config) { {
|
192
|
+
master_strategy: 'failover',
|
193
|
+
master_shard_aware: false,
|
194
|
+
master_default_shard: 'shard2'
|
195
|
+
} }
|
196
|
+
let(:strategy) { pool.strategy }
|
197
|
+
|
198
|
+
it 'should use the failover strategy' do
|
199
|
+
expect(pool.strategy).to be_instance_of(Makara::Strategies::PriorityFailover)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "uses the configured roundrobin strategy when shard_aware set to false," do
|
204
|
+
let(:proxy){ FakeProxy.new({makara: pool_config.merge(makara_config).merge(connections: [])}) }
|
205
|
+
let(:pool){ Makara::Pool.new('master', proxy) }
|
206
|
+
let(:pool_config){ { blacklist_duration: 5} }
|
207
|
+
let(:makara_config) { {
|
208
|
+
master_strategy: 'round_robin',
|
209
|
+
master_shard_aware: false,
|
210
|
+
master_default_shard: 'shard2'
|
211
|
+
} }
|
212
|
+
let(:strategy) { pool.strategy }
|
213
|
+
|
214
|
+
it 'should use the failover strategy' do
|
215
|
+
expect(pool.strategy).to be_instance_of(Makara::Strategies::RoundRobin)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
data/spec/support/deep_dup.rb
CHANGED
data/spec/support/helpers.rb
CHANGED
@@ -11,16 +11,16 @@ module SpecHelpers
|
|
11
11
|
|
12
12
|
def config(masters = 1, slaves = 2)
|
13
13
|
connections = []
|
14
|
-
masters.times{ connections << {:
|
15
|
-
slaves.times{ connections << {:
|
14
|
+
masters.times{ connections << {role: 'master'} }
|
15
|
+
slaves.times{ connections << {role: 'slave'} }
|
16
16
|
{
|
17
|
-
:
|
17
|
+
makara: {
|
18
18
|
# Defaults:
|
19
19
|
# :master_ttl => 5,
|
20
20
|
# :blacklist_duration => 30,
|
21
21
|
# :sticky => true
|
22
|
-
:
|
23
|
-
:
|
22
|
+
id: 'mock_mysql',
|
23
|
+
connections: connections
|
24
24
|
}
|
25
25
|
}
|
26
26
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'active_record/connection_adapters/makara_abstract_adapter'
|
2
2
|
|
3
3
|
class FakeConnection < Struct.new(:config)
|
4
|
-
|
5
4
|
def ping
|
6
5
|
'ping!'
|
7
6
|
end
|
@@ -18,6 +17,10 @@ class FakeConnection < Struct.new(:config)
|
|
18
17
|
true
|
19
18
|
end
|
20
19
|
|
20
|
+
def open_transactions
|
21
|
+
(config || {}).fetch(:open_transactions, 0)
|
22
|
+
end
|
23
|
+
|
21
24
|
def disconnect!
|
22
25
|
true
|
23
26
|
end
|
@@ -28,7 +31,6 @@ class FakeConnection < Struct.new(:config)
|
|
28
31
|
end
|
29
32
|
|
30
33
|
class FakeDatabaseAdapter < Struct.new(:config)
|
31
|
-
|
32
34
|
def execute(sql, name = nil)
|
33
35
|
[]
|
34
36
|
end
|
@@ -44,11 +46,9 @@ class FakeDatabaseAdapter < Struct.new(:config)
|
|
44
46
|
def active?
|
45
47
|
true
|
46
48
|
end
|
47
|
-
|
48
49
|
end
|
49
50
|
|
50
51
|
class FakeProxy < Makara::Proxy
|
51
|
-
|
52
52
|
send_to_all :ping
|
53
53
|
hijack_method :execute
|
54
54
|
|
@@ -58,6 +58,7 @@ class FakeProxy < Makara::Proxy
|
|
58
58
|
|
59
59
|
def needs_master?(method_name, args)
|
60
60
|
return false if args.first =~ /^select/
|
61
|
+
|
61
62
|
true
|
62
63
|
end
|
63
64
|
end
|
@@ -5,7 +5,7 @@ conn.execute "create extension if not exists postgis"
|
|
5
5
|
if conn.table_exists? "towns"
|
6
6
|
conn.execute("TRUNCATE TABLE towns")
|
7
7
|
else
|
8
|
-
conn.create_table "towns", :
|
8
|
+
conn.create_table "towns", force: true do |t|
|
9
9
|
t.st_point "location"
|
10
10
|
end
|
11
11
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module ProxyExtensions
|
2
|
-
|
3
2
|
attr_reader :master_pool, :slave_pool, :id
|
4
|
-
|
3
|
+
|
5
4
|
def master_for?(sql)
|
6
5
|
pool_for(sql) == master_pool
|
7
6
|
end
|
@@ -27,7 +26,6 @@ module ProxyExtensions
|
|
27
26
|
def sticky=(s)
|
28
27
|
@sticky = s
|
29
28
|
end
|
30
|
-
|
31
29
|
end
|
32
30
|
|
33
31
|
Makara::Proxy.send(:include, ProxyExtensions)
|
data/spec/support/schema.rb
CHANGED
data/spec/support/user.rb
CHANGED