makara 0.4.1 → 0.5.0

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