makara 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 854be0f8b5615812158cd37511db68cc47310f75
4
- data.tar.gz: 8cc3d705dd2d83415a8d4348d9ce2b949d0eb954
3
+ metadata.gz: d07deed98a5cc668ea6621fe741b4fbbcb210e9b
4
+ data.tar.gz: 1f849bf4f2eb6a38c5a6253ed64348e9d60e76cd
5
5
  SHA512:
6
- metadata.gz: 8669c261ead2cce40c1ffa8f319f975d162623071f54290fbed54e1a65159b2698c9eab709159cb6f71858b2d32b20ad9996b8472502c91a8086fdb89c08d31a
7
- data.tar.gz: 2089f3e36f9add64c5ecdef2634665fde5aa3a2cc94ab4230f9f8099d9c438f7da057b73406b8c8f9abab8c596424ad4cd0deef627fff7831f0c0ca9f9004233
6
+ metadata.gz: 837a1140206708c09ca4ba72f3febe76d00ebc9e10161c114f7ca80d6f749476d9b8bd0b5e4505394c9229d80a12c7c973967e1430dd42c2038b4b236c21d531
7
+ data.tar.gz: 21dca3b441fae6bb6f438566e3e2d5d2fa0acfa7e0637475f26215486277b225805206200b5c3150f6ac79cc17ae8f0bcfd379feb1e090ec25f3d6aee996d1a9
data/CHANGELOG.md CHANGED
@@ -1,6 +1,27 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## v0.3.7.rc - 2016-09-16
5
+
6
+ [Full Changelog](https://github.com/taskrabbit/makara/compare/v0.3.6...v0.3.7.rc)
7
+
8
+ Changed
9
+
10
+ - Fix the hierarchy of the config file [#116](https://github.com/taskrabbit/makara/pull/116) Kevin Bacha
11
+ - "Disable blacklist" parameter [#134](https://github.com/taskrabbit/makara/pull/134) Alex Tonkonozhenko
12
+ - Fixes bug in `without_sticking` [#96](https://github.com/taskrabbit/makara/pull/96) Brian Leonard
13
+ - Always stick inside transactions [#96](https://github.com/taskrabbit/makara/pull/96) Brian Leonard
14
+ - Rails 5 support [#122](https://github.com/taskrabbit/makara/pull/122) Jonny McAllister
15
+
16
+ ## v0.3.6 - 2016-04-21
17
+
18
+ [Full Changelog](https://github.com/taskrabbit/makara/compare/v0.3.5...v0.3.6)
19
+
20
+ Changed
21
+
22
+ - Allow different strategies such as `priority` and `round_robin` for pools [#105](https://github.com/taskrabbit/makara/pull/105) Brian Leonard
23
+
24
+
4
25
  ## v0.3.5 - 2016-01-08
5
26
 
6
27
  [Full Changelog](https://github.com/taskrabbit/makara/compare/v0.3.4.rc1...v0.3.5)
data/README.md CHANGED
@@ -16,8 +16,10 @@ There is a potential performance issue when used alongside certain versions of
16
16
 
17
17
  ## Installation
18
18
 
19
+ Use the current version of the gem from [rubygems](https://rubygems.org/gems/makara) in your `Gemfile`.
20
+
19
21
  ```ruby
20
- gem 'makara', github: 'taskrabbit/makara', tag: 'v0.3.x'
22
+ gem 'makara'
21
23
  ```
22
24
 
23
25
  ## Basic Usage
@@ -160,6 +162,7 @@ Following the adapter choice is all the standard configurations (host, port, ret
160
162
 
161
163
  The makara subconfig sets up the proxy with a few of its own options, then provides the connection list. The makara options are:
162
164
  * blacklist_duration - the number of seconds a node is blacklisted when a connection failure occurs
165
+ * disable_blacklist - do not blacklist node at any error, useful in case of one master
163
166
  * sticky - if a node should be stuck to once it's used during a specific context
164
167
  * master_ttl - how long the master context is persisted. generally, this needs to be longer than any replication lag
165
168
  * master_strategy - use a different strategy for picking the "current" node: `failover` will try to keep the same one until it is blacklisted. The default is `round_robin` which will cycle through available ones.
@@ -157,16 +157,16 @@ module Makara
157
157
 
158
158
 
159
159
  def master_configs
160
- all_configs.
161
- select{|config| config[:role] == 'master' }.
162
- map{|config| config.except(:role) }
160
+ all_configs
161
+ .select { |config| config[:role] == 'master' }
162
+ .map { |config| config.except(:role) }
163
163
  end
164
164
 
165
165
 
166
166
  def slave_configs
167
- all_configs.
168
- reject{|config| config[:role] == 'master' }.
169
- map{|config| config.except(:role) }
167
+ all_configs
168
+ .reject { |config| config[:role] == 'master' }
169
+ .map { |config| config.except(:role) }
170
170
  end
171
171
 
172
172
 
@@ -175,7 +175,8 @@ module Makara
175
175
 
176
176
  def all_configs
177
177
  @makara_config[:connections].map do |connection|
178
- base_config.merge(connection.symbolize_keys)
178
+ base_config.merge(makara_config.except(:connections))
179
+ .merge(connection.symbolize_keys)
179
180
  end
180
181
  end
181
182
 
@@ -44,7 +44,7 @@ module Makara
44
44
  def _makara_blacklist!
45
45
  @connection.disconnect! if @connection
46
46
  @connection = nil
47
- @blacklisted_until = Time.now.to_i + @config[:blacklist_duration]
47
+ @blacklisted_until = Time.now.to_i + @config[:blacklist_duration] unless @config[:disable_blacklist]
48
48
  end
49
49
 
50
50
  # release the blacklist
data/lib/makara/proxy.rb CHANGED
@@ -59,10 +59,13 @@ module Makara
59
59
  end
60
60
 
61
61
  def without_sticking
62
+ before_context = @master_context
63
+ @master_context = nil
62
64
  @skip_sticking = true
63
65
  yield
64
66
  ensure
65
67
  @skip_sticking = false
68
+ @master_context ||= before_context
66
69
  end
67
70
 
68
71
  def hijacked?
@@ -141,7 +144,7 @@ module Makara
141
144
  @master_pool.provide do |con|
142
145
  yield con
143
146
  end
144
- rescue ::Makara::Errors::AllConnectionsBlacklisted, ::Makara::Errors::NoConnectionsAvailable => e
147
+ rescue ::Makara::Errors::AllConnectionsBlacklisted, ::Makara::Errors::NoConnectionsAvailable
145
148
  begin
146
149
  @master_pool.disabled = true
147
150
  @slave_pool.provide do |con|
@@ -208,6 +211,9 @@ module Makara
208
211
  stick_to_master(method_name, args)
209
212
  @master_pool
210
213
 
214
+ elsif in_transaction?
215
+ @master_pool
216
+
211
217
  # yay! use a slave
212
218
  else
213
219
  @slave_pool
@@ -219,6 +225,13 @@ module Makara
219
225
  true
220
226
  end
221
227
 
228
+ def in_transaction?
229
+ if respond_to?(:open_transactions)
230
+ self.open_transactions > 0
231
+ else
232
+ false
233
+ end
234
+ end
222
235
 
223
236
  def hijacked
224
237
  @hijacked = true
@@ -256,14 +269,14 @@ module Makara
256
269
  def instantiate_connections
257
270
  @master_pool = Makara::Pool.new('master', self)
258
271
  @config_parser.master_configs.each do |master_config|
259
- @master_pool.add master_config.merge(@config_parser.makara_config) do
272
+ @master_pool.add master_config do
260
273
  graceful_connection_for(master_config)
261
274
  end
262
275
  end
263
276
 
264
277
  @slave_pool = Makara::Pool.new('slave', self)
265
278
  @config_parser.slave_configs.each do |slave_config|
266
- @slave_pool.add slave_config.merge(@config_parser.makara_config) do
279
+ @slave_pool.add slave_config do
267
280
  graceful_connection_for(slave_config)
268
281
  end
269
282
  end
@@ -1,8 +1,9 @@
1
1
  module Makara
2
2
  class Railtie < ::Rails::Railtie
3
3
 
4
- config.app_middleware.use 'Makara::Middleware'
5
-
4
+ initializer "makara.configure_rails_initialization" do |app|
5
+ app.middleware.use Makara::Middleware
6
+ end
6
7
 
7
8
  initializer "makara.initialize_logger" do |app|
8
9
  ActiveRecord::LogSubscriber.log_subscribers.each do |subscriber|
@@ -3,7 +3,7 @@ module Makara
3
3
 
4
4
  MAJOR = 0
5
5
  MINOR = 3
6
- PATCH = 6
6
+ PATCH = 7
7
7
  PRE = nil
8
8
 
9
9
  def self.to_s
@@ -10,6 +10,8 @@ describe 'MakaraMysql2Adapter' do
10
10
  base
11
11
  }
12
12
 
13
+ let(:connection) { ActiveRecord::Base.connection }
14
+
13
15
  before :each do
14
16
  ActiveRecord::Base.clear_all_connections!
15
17
  change_context
@@ -99,8 +101,6 @@ describe 'MakaraMysql2Adapter' do
99
101
 
100
102
  context 'with the connection established and schema loaded' do
101
103
 
102
- let(:connection) { ActiveRecord::Base.connection }
103
-
104
104
  before do
105
105
  ActiveRecord::Base.establish_connection(config)
106
106
  load(File.dirname(__FILE__) + '/../../support/schema.rb')
@@ -180,4 +180,60 @@ describe 'MakaraMysql2Adapter' do
180
180
 
181
181
  end
182
182
 
183
+ describe 'transaction support' do
184
+ shared_examples 'a transaction supporter' do
185
+ before do
186
+ ActiveRecord::Base.establish_connection(config)
187
+ load(File.dirname(__FILE__) + '/../../support/schema.rb')
188
+ change_context
189
+
190
+ connection.slave_pool.connections.each do |slave|
191
+ # Using method missing to help with back trace, literally
192
+ # no query should be executed on slave once a transaction is opened
193
+ expect(slave).to receive(:method_missing).never
194
+ expect(slave).to receive(:execute).never
195
+ end
196
+ end
197
+
198
+ context 'when querying through a polymorphic relation' do
199
+ it 'should respect the transaction' do
200
+ ActiveRecord::Base.transaction do
201
+ connection.execute("INSERT INTO users (name) VALUES ('John')")
202
+ connection.execute('SELECT * FROM users')
203
+ end
204
+ end
205
+ end
206
+
207
+ context 'when querying an aggregate' do
208
+ it 'should respect the transaction' do
209
+ ActiveRecord::Base.transaction { connection.execute('SELECT COUNT(*) FROM users') }
210
+ end
211
+ end
212
+
213
+ context 'when querying for a specific record' do
214
+ it 'should respect the transaction' do
215
+ ActiveRecord::Base.transaction { connection.execute('SELECT * FROM users WHERE id = 1') }
216
+ end
217
+ end
218
+
219
+ context 'when executing a query' do
220
+ it 'should respect the transaction' do
221
+ ActiveRecord::Base.transaction { connection.execute('SELECT 1') }
222
+ end
223
+ end
224
+ end
225
+
226
+ context 'when sticky is true' do
227
+ before { config['makara']['sticky'] = true }
228
+
229
+ it_behaves_like 'a transaction supporter'
230
+ end
231
+
232
+ context 'when sticky is false' do
233
+ before { config['makara']['sticky'] = false }
234
+
235
+ it_behaves_like 'a transaction supporter'
236
+ end
237
+ end
238
+
183
239
  end
@@ -27,6 +27,7 @@ describe 'MakaraPostgreSQLAdapter' do
27
27
  context 'with the connection established and schema loaded' do
28
28
 
29
29
  before do
30
+ puts config
30
31
  ActiveRecord::Base.establish_connection(config)
31
32
  load(File.dirname(__FILE__) + '/../../support/schema.rb')
32
33
  change_context
@@ -77,45 +78,102 @@ describe 'MakaraPostgreSQLAdapter' do
77
78
  expect(con).to receive(:execute).with('UPDATE users SET name = "bob" WHERE id = 1')
78
79
  connection.execute('UPDATE users SET name = "bob" WHERE id = 1')
79
80
  end
80
-
81
81
  end
82
82
 
83
83
  context 'without live connections' do
84
- it 'should raise errors on read or write' do
85
- allow(ActiveRecord::Base).to receive(:postgresql_connection).and_raise(StandardError.new('could not connect to server: Connection refused'))
84
+ it 'should raise errors on read or write' do
85
+ allow(ActiveRecord::Base).to receive(:postgresql_connection).and_raise(StandardError.new('could not connect to server: Connection refused'))
86
86
 
87
- ActiveRecord::Base.establish_connection(config)
88
- expect { connection.execute('SELECT * FROM users') }.to raise_error(Makara::Errors::NoConnectionsAvailable)
89
- expect { connection.execute('INSERT INTO users (name) VALUES (\'John\')') }.to raise_error(Makara::Errors::NoConnectionsAvailable)
90
- end
87
+ ActiveRecord::Base.establish_connection(config)
88
+ expect { connection.execute('SELECT * FROM users') }.to raise_error(Makara::Errors::NoConnectionsAvailable)
89
+ expect { connection.execute('INSERT INTO users (name) VALUES (\'John\')') }.to raise_error(Makara::Errors::NoConnectionsAvailable)
91
90
  end
91
+ end
92
92
 
93
- context 'with only master connection' do
94
- it 'should not raise errors on read and write' do
95
- custom_config = config.deep_dup
96
- custom_config['makara']['connections'].select{|h| h['role'] == 'slave' }.each{|h| h['port'] = '1'}
93
+ context 'with only master connection' do
94
+ it 'should not raise errors on read and write' do
95
+ custom_config = config.deep_dup
96
+ custom_config['makara']['connections'].select{|h| h['role'] == 'slave' }.each{|h| h['port'] = '1'}
97
97
 
98
- ActiveRecord::Base.establish_connection(custom_config)
99
- load(File.dirname(__FILE__) + '/../../support/schema.rb')
98
+ ActiveRecord::Base.establish_connection(custom_config)
99
+ load(File.dirname(__FILE__) + '/../../support/schema.rb')
100
100
 
101
- connection.execute('SELECT * FROM users')
102
- connection.execute('INSERT INTO users (name) VALUES (\'John\')')
103
- end
101
+ connection.execute('SELECT * FROM users')
102
+ connection.execute('INSERT INTO users (name) VALUES (\'John\')')
103
+ end
104
+ end
105
+
106
+ context 'with only slave connection' do
107
+ it 'should raise error only on write' do
108
+ ActiveRecord::Base.establish_connection(config)
109
+ load(File.dirname(__FILE__) + '/../../support/schema.rb')
110
+ ActiveRecord::Base.clear_all_connections!
111
+
112
+ custom_config = config.deep_dup
113
+ custom_config['makara']['connections'].select{|h| h['role'] == 'master' }.each{|h| h['port'] = '1'}
114
+
115
+ ActiveRecord::Base.establish_connection(custom_config)
116
+
117
+ connection.execute('SELECT * FROM users')
118
+ expect { connection.execute('INSERT INTO users (name) VALUES (\'John\')') }.to raise_error(Makara::Errors::NoConnectionsAvailable)
104
119
  end
120
+ end
105
121
 
106
- context 'with only slave connection' do
107
- it 'should raise error only on write' do
122
+ describe 'transaction support' do
123
+ shared_examples 'a transaction supporter' do
124
+ before do
108
125
  ActiveRecord::Base.establish_connection(config)
109
126
  load(File.dirname(__FILE__) + '/../../support/schema.rb')
110
- ActiveRecord::Base.clear_all_connections!
127
+ change_context
128
+
129
+ # Pre-loads the attributes so that schema queries don't hit slave
130
+ # user = User.create(name: 'hello')
131
+ connection.slave_pool.connections.each do |slave|
132
+ # Using method missing to help with back trace, literally
133
+ # no query should be executed on slave once a transaction is opened
134
+ expect(slave).to receive(:method_missing).never
135
+ expect(slave).to receive(:execute).never
136
+ end
137
+ end
138
+
139
+ context 'when querying through a polymorphic relation' do
140
+ it 'should respect the transaction' do
141
+ ActiveRecord::Base.transaction do
142
+ connection.execute("INSERT INTO users (name) VALUES ('John')")
143
+ connection.execute('SELECT * FROM users')
144
+ end
145
+ end
146
+ end
111
147
 
112
- custom_config = config.deep_dup
113
- custom_config['makara']['connections'].select{|h| h['role'] == 'master' }.each{|h| h['port'] = '1'}
148
+ context 'when querying an aggregate' do
149
+ it 'should respect the transaction' do
150
+ ActiveRecord::Base.transaction { connection.execute('SELECT COUNT(*) FROM users') }
151
+ end
152
+ end
114
153
 
115
- ActiveRecord::Base.establish_connection(custom_config)
154
+ context 'when querying for a specific record' do
155
+ it 'should respect the transaction' do
156
+ ActiveRecord::Base.transaction { connection.execute('SELECT * FROM users WHERE id = 1') }
157
+ end
158
+ end
116
159
 
117
- connection.execute('SELECT * FROM users')
118
- expect { connection.execute('INSERT INTO users (name) VALUES (\'John\')') }.to raise_error(Makara::Errors::NoConnectionsAvailable)
160
+ context 'when executing a query' do
161
+ it 'should respect the transaction' do
162
+ ActiveRecord::Base.transaction { connection.execute('SELECT 1') }
163
+ end
119
164
  end
120
165
  end
166
+
167
+ context 'when sticky is true' do
168
+ before { config['makara']['sticky'] = true }
169
+
170
+ it_behaves_like 'a transaction supporter'
171
+ end
172
+
173
+ context 'when sticky is false' do
174
+ before { config['makara']['sticky'] = false }
175
+
176
+ it_behaves_like 'a transaction supporter'
177
+ end
178
+ end
121
179
  end
@@ -88,15 +88,73 @@ describe Makara::ConfigParser do
88
88
  expect(parsera.id).to eq(parserc.id)
89
89
  end
90
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
91
+ context 'master and slave configs' do
92
+ it 'should provide master and slave configs' do
93
+ parser = described_class.new(config)
94
+ expect(parser.master_configs).to eq([
95
+ {
96
+ :name => 'themaster',
97
+ :top_level => 'value',
98
+ :sticky => true,
99
+ :blacklist_duration => 30,
100
+ :master_ttl => 5,
101
+ :sticky => true
102
+ }
103
+ ])
104
+ expect(parser.slave_configs).to eq([
105
+ {
106
+ :name => 'slave1',
107
+ :top_level => 'value',
108
+ :sticky => true,
109
+ :blacklist_duration => 30,
110
+ :master_ttl => 5,
111
+ :sticky => true
112
+ },
113
+ {
114
+ :name => 'slave2',
115
+ :top_level => 'value',
116
+ :sticky => true,
117
+ :blacklist_duration => 30,
118
+ :master_ttl => 5,
119
+ :sticky => true
120
+ }
121
+ ])
122
+ end
123
+
124
+ it 'connection configuration should override makara config' do
125
+ config[:makara][:blacklist_duration] = 123
126
+ config[:makara][:connections][0][:blacklist_duration] = 456
127
+ config[:makara][:connections][1][:top_level] = 'slave value'
101
128
 
129
+ parser = described_class.new(config)
130
+ expect(parser.master_configs).to eq([
131
+ {
132
+ :name => 'themaster',
133
+ :top_level => 'value',
134
+ :sticky => true,
135
+ :blacklist_duration => 456,
136
+ :master_ttl => 5,
137
+ :sticky => true
138
+ }
139
+ ])
140
+ expect(parser.slave_configs).to eq([
141
+ {
142
+ :name => 'slave1',
143
+ :top_level => 'slave value',
144
+ :sticky => true,
145
+ :blacklist_duration => 123,
146
+ :master_ttl => 5,
147
+ :sticky => true
148
+ },
149
+ {
150
+ :name => 'slave2',
151
+ :top_level => 'value',
152
+ :sticky => true,
153
+ :blacklist_duration => 123,
154
+ :master_ttl => 5,
155
+ :sticky => true
156
+ }
157
+ ])
158
+ end
159
+ end
102
160
  end
data/spec/proxy_spec.rb CHANGED
@@ -81,15 +81,29 @@ describe Makara::Proxy do
81
81
  end
82
82
 
83
83
  it 'should not stick to master if we are in a without_sticking block' do
84
+ expect(proxy.master_for?('insert into users values (a,b,c)')).to eq(true)
85
+
84
86
  proxy.without_sticking do
85
87
  expect(proxy.master_for?('insert into users values (a,b,c)')).to eq(true)
86
88
  expect(proxy.master_for?('select * from users')).to eq(false)
87
89
  end
88
90
 
91
+ expect(proxy.master_for?('select * from users')).to eq(true)
89
92
  expect(proxy.master_for?('insert into users values (a,b,c)')).to eq(true)
90
93
  expect(proxy.master_for?('select * from users')).to eq(true)
91
94
  end
92
95
 
96
+ it 'should not stick to master after without_sticking block if there is a write in it' do
97
+ expect(proxy.master_for?('select * from users')).to eq(false)
98
+
99
+ proxy.without_sticking do
100
+ expect(proxy.master_for?('insert into users values (a,b,c)')).to eq(true)
101
+ expect(proxy.master_for?('select * from users')).to eq(false)
102
+ end
103
+
104
+ expect(proxy.master_for?('select * from users')).to eq(false)
105
+ end
106
+
93
107
  # if the context changes we should still use master until the previous context is no longer relevant
94
108
  it 'should release master if the context changes and enough time passes' do
95
109
  expect(proxy.master_for?('insert into users values (a,b,c)')).to eq(true)
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.3.6
4
+ version: 0.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Nelson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-21 00:00:00.000000000 Z
11
+ date: 2016-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord