master_slave_adapter 1.0.0.beta2 → 1.0.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.
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: master_slave_adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.beta2
5
- prerelease: 6
4
+ version: 1.0.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Mauricio Linhares
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2012-06-20 00:00:00.000000000 Z
18
+ date: 2012-07-24 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activerecord
@@ -25,7 +25,7 @@ dependencies:
25
25
  - - ! '>='
26
26
  - !ruby/object:Gem::Version
27
27
  version: 2.3.9
28
- - - <=
28
+ - - <
29
29
  - !ruby/object:Gem::Version
30
30
  version: '4.0'
31
31
  type: :runtime
@@ -36,7 +36,7 @@ dependencies:
36
36
  - - ! '>='
37
37
  - !ruby/object:Gem::Version
38
38
  version: 2.3.9
39
- - - <=
39
+ - - <
40
40
  - !ruby/object:Gem::Version
41
41
  version: '4.0'
42
42
  - !ruby/object:Gem::Dependency
@@ -72,7 +72,7 @@ dependencies:
72
72
  - !ruby/object:Gem::Version
73
73
  version: '0'
74
74
  description: (MySQL) Replication Aware Master/Slave Database Adapter for ActiveRecord
75
- email: kim@soundcloud.com tcurdt@soundcloud.com ts@soundcloud
75
+ email: tiago@soundcloud.com ts@soundcloud.com
76
76
  executables: []
77
77
  extensions: []
78
78
  extra_rdoc_files: []
@@ -94,17 +94,19 @@ files:
94
94
  - lib/master_slave_adapter.rb
95
95
  - master_slave_adapter.gemspec
96
96
  - spec/all.sh
97
- - spec/circuit_breaker_spec.rb
97
+ - spec/common/circuit_breaker_spec.rb
98
+ - spec/common/master_slave_adapter_spec.rb
99
+ - spec/common/mysql2_master_slave_adapter_spec.rb
100
+ - spec/common/mysql_master_slave_adapter_spec.rb
101
+ - spec/common/support/connection_setup_helper.rb
102
+ - spec/common/support/mysql_consistency_examples.rb
98
103
  - spec/gemfiles/activerecord2.3
99
104
  - spec/gemfiles/activerecord3.0
100
105
  - spec/gemfiles/activerecord3.2
101
- - spec/integration/helpers/mysql_helper.rb
102
- - spec/integration/helpers/shared_mysql_examples.rb
103
106
  - spec/integration/mysql2_master_slave_adapter_spec.rb
104
107
  - spec/integration/mysql_master_slave_adapter_spec.rb
105
- - spec/master_slave_adapter_spec.rb
106
- - spec/mysql2_master_slave_adapter_spec.rb
107
- - spec/mysql_master_slave_adapter_spec.rb
108
+ - spec/integration/support/mysql_setup_helper.rb
109
+ - spec/integration/support/shared_mysql_examples.rb
108
110
  homepage: http://github.com/soundcloud/master_slave_adapter
109
111
  licenses: []
110
112
  post_install_message:
@@ -131,14 +133,16 @@ specification_version: 3
131
133
  summary: Replication Aware Master/Slave Database Adapter for ActiveRecord
132
134
  test_files:
133
135
  - spec/all.sh
134
- - spec/circuit_breaker_spec.rb
136
+ - spec/common/circuit_breaker_spec.rb
137
+ - spec/common/master_slave_adapter_spec.rb
138
+ - spec/common/mysql2_master_slave_adapter_spec.rb
139
+ - spec/common/mysql_master_slave_adapter_spec.rb
140
+ - spec/common/support/connection_setup_helper.rb
141
+ - spec/common/support/mysql_consistency_examples.rb
135
142
  - spec/gemfiles/activerecord2.3
136
143
  - spec/gemfiles/activerecord3.0
137
144
  - spec/gemfiles/activerecord3.2
138
- - spec/integration/helpers/mysql_helper.rb
139
- - spec/integration/helpers/shared_mysql_examples.rb
140
145
  - spec/integration/mysql2_master_slave_adapter_spec.rb
141
146
  - spec/integration/mysql_master_slave_adapter_spec.rb
142
- - spec/master_slave_adapter_spec.rb
143
- - spec/mysql2_master_slave_adapter_spec.rb
144
- - spec/mysql_master_slave_adapter_spec.rb
147
+ - spec/integration/support/mysql_setup_helper.rb
148
+ - spec/integration/support/shared_mysql_examples.rb
@@ -1,372 +0,0 @@
1
- $: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
-
3
- require 'rspec'
4
- require 'logger'
5
- require 'active_record/connection_adapters/mysql2_master_slave_adapter'
6
-
7
- module ActiveRecord
8
- class Base
9
- cattr_accessor :master_mock, :slave_mock
10
- def self.mysql2_connection(config)
11
- config[:database] == 'slave' ? slave_mock : master_mock
12
- end
13
- end
14
- end
15
-
16
- describe ActiveRecord::ConnectionAdapters::Mysql2MasterSlaveAdapter do
17
- let(:database_setup) do
18
- {
19
- :adapter => 'master_slave',
20
- :username => 'root',
21
- :database => 'slave',
22
- :connection_adapter => 'mysql2',
23
- :master => { :username => 'root', :database => 'master' },
24
- :slaves => [{ :database => 'slave' }],
25
- }
26
- end
27
-
28
- let(:mocked_methods) do
29
- {
30
- :reconnect! => true,
31
- :disconnect! => true,
32
- :active? => true,
33
- }
34
- end
35
-
36
- let!(:master_connection) do
37
- mock(
38
- 'master connection',
39
- mocked_methods.merge(:open_transactions => 0)
40
- ).tap do |conn|
41
- conn.stub!(:uncached).and_yield
42
- ActiveRecord::Base.master_mock = conn
43
- end
44
- end
45
-
46
- let!(:slave_connection) do
47
- mock('slave connection', mocked_methods).tap do |conn|
48
- conn.stub!(:uncached).and_yield
49
- ActiveRecord::Base.slave_mock = conn
50
- end
51
- end
52
-
53
- def adapter_connection
54
- ActiveRecord::Base.connection
55
- end
56
-
57
- SelectMethods = [ :select_all, :select_one, :select_rows, :select_value, :select_values ] unless defined?(SelectMethods)
58
- Clock = ActiveRecord::ConnectionAdapters::MasterSlaveAdapter::Clock unless defined?(Clock)
59
-
60
- before do
61
- ActiveRecord::Base.establish_connection(database_setup)
62
- end
63
-
64
- after do
65
- ActiveRecord::Base.connection_handler.clear_all_connections!
66
- end
67
-
68
- describe 'consistency' do
69
- def zero
70
- Clock.zero
71
- end
72
-
73
- def master_position(pos)
74
- Clock.new('', pos)
75
- end
76
-
77
- def select_method
78
- :select_one
79
- end
80
-
81
- def should_report_clock(pos, connection, log_file, log_pos, sql)
82
- pos = Array(pos)
83
- values = pos.map { |p| { log_file => '', log_pos => p } }
84
-
85
- connection.
86
- should_receive(select_method).exactly(pos.length).times.
87
- with(sql).
88
- and_return(*values)
89
- end
90
-
91
- def slave_should_report_clock(pos)
92
- should_report_clock(pos, slave_connection, 'Relay_Master_Log_File', 'Exec_Master_Log_Pos', 'SHOW SLAVE STATUS')
93
- end
94
-
95
- def master_should_report_clock(pos)
96
- should_report_clock(pos, master_connection, 'File', 'Position', 'SHOW MASTER STATUS')
97
- end
98
-
99
- SelectMethods.each do |method|
100
- it "should send the method '#{method}' to the slave if nil is given" do
101
- slave_should_report_clock(0)
102
- slave_connection.should_receive(method).with('testing').and_return(true)
103
- new_clock = ActiveRecord::Base.with_consistency(nil) do
104
- adapter_connection.send(method, 'testing')
105
- end
106
- new_clock.should be_a(Clock)
107
- new_clock.should equal(zero)
108
- end
109
-
110
- it "should send the method '#{method}' to the slave if clock.zero is given" do
111
- slave_should_report_clock(0)
112
- slave_connection.should_receive(method).with('testing').and_return(true)
113
- old_clock = zero
114
- new_clock = ActiveRecord::Base.with_consistency(old_clock) do
115
- adapter_connection.send(method, 'testing')
116
- end
117
- new_clock.should be_a(Clock)
118
- new_clock.should equal(old_clock)
119
- end
120
-
121
- it "should send the method '#{method}' to the master if slave hasn't cought up to required clock yet" do
122
- slave_should_report_clock(0)
123
- master_connection.should_receive(method).with('testing').and_return(true)
124
- old_clock = master_position(1)
125
- new_clock = ActiveRecord::Base.with_consistency(old_clock) do
126
- adapter_connection.send(method, 'testing' )
127
- end
128
- new_clock.should be_a(Clock)
129
- new_clock.should equal(old_clock)
130
- end
131
-
132
- it "should send the method '#{method}' to the master connection if there are open transactions" do
133
- master_connection.stub!(:open_transactions).and_return(1)
134
- master_connection.should_receive(method).with('testing').and_return(true)
135
- old_clock = zero
136
- new_clock = ActiveRecord::Base.with_consistency(old_clock) do
137
- adapter_connection.send(method, 'testing')
138
- end
139
- new_clock.should be_a(Clock)
140
- new_clock.should equal(zero)
141
- end
142
-
143
- it "should send the method '#{method}' to the master after a write operation" do
144
- slave_should_report_clock(0)
145
- master_should_report_clock(2)
146
- slave_connection.should_receive(method).with('testing').and_return(true)
147
- master_connection.should_receive(:update).with('testing').and_return(true)
148
- master_connection.should_receive(method).with('testing').and_return(true)
149
- old_clock = zero
150
- new_clock = ActiveRecord::Base.with_consistency(old_clock) do
151
- adapter_connection.send(method, 'testing') # slave
152
- adapter_connection.send(:update, 'testing') # master
153
- adapter_connection.send(method, 'testing') # master
154
- end
155
- new_clock.should be_a(Clock)
156
- new_clock.should > old_clock
157
- end
158
- end
159
-
160
- it "should update the clock after a transaction" do
161
- slave_should_report_clock(0)
162
- master_should_report_clock([0, 1, 1])
163
-
164
- slave_connection.
165
- should_receive(:select_all).exactly(1).times.with('testing').
166
- and_return(true)
167
-
168
- master_connection.
169
- should_receive(:update).exactly(3).times.with('testing').
170
- and_return(true)
171
- master_connection.
172
- should_receive(:select_all).exactly(5).times.with('testing').
173
- and_return(true)
174
- %w(begin_db_transaction
175
- commit_db_transaction
176
- increment_open_transactions
177
- decrement_open_transactions
178
- outside_transaction?).each do |txstmt|
179
- master_connection.should_receive(txstmt).exactly(1).times
180
- end
181
-
182
- master_connection.
183
- should_receive('open_transactions').exactly(13).times.
184
- and_return(
185
- # adapter: with_consistency, select_all, update, select_all
186
- 0, 0, 0, 0,
187
- # connection: transaction
188
- 0,
189
- # adapter: select_all, update, select_all, commit_db_transaction
190
- 1, 1, 1, 0,
191
- # connection: transaction (ensure)
192
- 0,
193
- # adapter: select_all, update, select_all
194
- 0, 0, 0
195
- )
196
-
197
- old_clock = zero
198
- new_clock = ActiveRecord::Base.with_consistency(old_clock) do
199
- adapter_connection.send(:select_all, 'testing') # slave s=0 m=0
200
- adapter_connection.send(:update, 'testing') # master s=0 m=1
201
- adapter_connection.send(:select_all, 'testing') # master s=0 m=1
202
-
203
- ActiveRecord::Base.transaction do
204
- adapter_connection.send(:select_all, 'testing') # master s=0 m=1
205
- adapter_connection.send(:update, 'testing') # master s=0 m=1
206
- adapter_connection.send(:select_all, 'testing') # master s=0 m=1
207
- end
208
-
209
- adapter_connection.send(:select_all, 'testing') # master s=0 m=2
210
- adapter_connection.send(:update, 'testing') # master s=0 m=3
211
- adapter_connection.send(:select_all, 'testing') # master s=0 m=3
212
- end
213
-
214
- new_clock.should > old_clock
215
- end
216
-
217
- context "with nested with_consistency" do
218
- it "should return the same clock if not writing and no lag" do
219
- slave_should_report_clock(0)
220
- slave_connection.
221
- should_receive(:select_one).exactly(3).times.with('testing').
222
- and_return(true)
223
-
224
- old_clock = zero
225
- new_clock = ActiveRecord::Base.with_consistency(old_clock) do
226
- adapter_connection.send(:select_one, 'testing')
227
- ActiveRecord::Base.with_consistency(old_clock) do
228
- adapter_connection.send(:select_one, 'testing')
229
- end
230
- adapter_connection.send(:select_one, 'testing')
231
- end
232
- new_clock.should equal(old_clock)
233
- end
234
-
235
- it "requesting a newer clock should return a new clock" do
236
- adapter_connection.
237
- should_receive('slave_consistent?').exactly(2).times.
238
- and_return(true, false)
239
- slave_connection.
240
- should_receive(:select_all).exactly(2).times.with('testing').
241
- and_return(true)
242
- master_connection.
243
- should_receive(:select_all).exactly(1).times.with('testing').
244
- and_return(true)
245
-
246
- start_clock = zero
247
- inner_clock = zero
248
- outer_clock = ActiveRecord::Base.with_consistency(start_clock) do
249
- adapter_connection.send(:select_all, 'testing') # slave
250
- inner_clock = ActiveRecord::Base.with_consistency(master_position(1)) do
251
- adapter_connection.send(:select_all, 'testing') # master
252
- end
253
- adapter_connection.send(:select_all, 'testing') # slave
254
- end
255
-
256
- start_clock.should equal(outer_clock)
257
- inner_clock.should > start_clock
258
- end
259
- end
260
-
261
- it "should do the right thing when nested inside with_master" do
262
- slave_should_report_clock(0)
263
- slave_connection.should_receive(:select_all).exactly(1).times.with('testing').and_return(true)
264
- master_connection.should_receive(:select_all).exactly(2).times.with('testing').and_return(true)
265
- ActiveRecord::Base.with_master do
266
- adapter_connection.send(:select_all, 'testing') # master
267
- ActiveRecord::Base.with_consistency(zero) do
268
- adapter_connection.send(:select_all, 'testing') # slave
269
- end
270
- adapter_connection.send(:select_all, 'testing') # master
271
- end
272
- end
273
-
274
- it "should do the right thing when nested inside with_slave" do
275
- slave_should_report_clock(0)
276
- slave_connection.should_receive(:select_all).exactly(3).times.with('testing').and_return(true)
277
- ActiveRecord::Base.with_slave do
278
- adapter_connection.send(:select_all, 'testing') # slave
279
- ActiveRecord::Base.with_consistency(zero) do
280
- adapter_connection.send(:select_all, 'testing') # slave
281
- end
282
- adapter_connection.send(:select_all, 'testing') # slave
283
- end
284
- end
285
-
286
- it "should do the right thing when wrapping with_master" do
287
- slave_should_report_clock(0)
288
- slave_connection.should_receive(:select_all).exactly(2).times.with('testing').and_return(true)
289
- master_connection.should_receive(:select_all).exactly(1).times.with('testing').and_return(true)
290
- ActiveRecord::Base.with_consistency(zero) do
291
- adapter_connection.send(:select_all, 'testing') # slave
292
- ActiveRecord::Base.with_master do
293
- adapter_connection.send(:select_all, 'testing') # master
294
- end
295
- adapter_connection.send(:select_all, 'testing') # slave
296
- end
297
- end
298
-
299
- it "should do the right thing when wrapping with_slave" do
300
- slave_should_report_clock(0)
301
- slave_connection.should_receive(:select_all).exactly(1).times.with('testing').and_return(true)
302
- master_connection.should_receive(:select_all).exactly(2).times.with('testing').and_return(true)
303
- ActiveRecord::Base.with_consistency(master_position(1)) do
304
- adapter_connection.send(:select_all, 'testing') # master
305
- ActiveRecord::Base.with_slave do
306
- adapter_connection.send(:select_all, 'testing') # slave
307
- end
308
- adapter_connection.send(:select_all, 'testing') # master
309
- end
310
- end
311
-
312
- it "should accept clock as string" do
313
- slave_should_report_clock(0)
314
- slave_connection.should_receive(:select_all).with('testing')
315
-
316
- ActiveRecord::Base.with_consistency("@0") do
317
- adapter_connection.send(:select_all, 'testing')
318
- end
319
- end
320
- end # /with_consistency
321
-
322
- describe "connection error detection" do
323
- {
324
- 2002 => "query: not connected",
325
- 2003 => "Can't connect to MySQL server on 'localhost' (3306)",
326
- 2006 => "MySQL server has gone away",
327
- 2013 => "Lost connection to MySQL server during query",
328
- }.each do |errno, description|
329
- it "raises MasterUnavailable for '#{description}' during query execution" do
330
- master_connection.stub_chain(:raw_connection, :errno).and_return(errno)
331
- master_connection.should_receive(:insert).and_raise(ActiveRecord::StatementInvalid.new("Mysql2::Error: #{description}: INSERT 42"))
332
-
333
- expect do
334
- adapter_connection.insert("INSERT 42")
335
- end.to raise_error(ActiveRecord::MasterUnavailable)
336
- end
337
-
338
- it "doesn't raise anything for '#{description}' during connection" do
339
- error = Mysql2::Error.new(description)
340
- error.stub(:errno).and_return(errno)
341
- ActiveRecord::Base.should_receive(:mysql2_connection).twice do |config|
342
- if config[:name] == :master
343
- raise error
344
- else
345
- slave_connection
346
- end
347
- end
348
-
349
- expect do
350
- ActiveRecord::Base.connection_handler.clear_all_connections!
351
- ActiveRecord::Base.connection
352
- end.to_not raise_error
353
- end
354
- end
355
-
356
- it "raises MasterUnavailable for 'closed MySQL connection' during query execution" do
357
- master_connection.should_receive(:insert).and_raise(ActiveRecord::StatementInvalid.new("Mysql2::Error: closed MySQL connection: INSERT 42"))
358
-
359
- expect do
360
- adapter_connection.insert("INSERT 42")
361
- end.to raise_error(ActiveRecord::MasterUnavailable)
362
- end
363
-
364
- it "raises StatementInvalid for other errors" do
365
- master_connection.should_receive(:insert).and_raise(ActiveRecord::StatementInvalid.new("Mysql2::Error: Query execution was interrupted: INSERT 42"))
366
-
367
- expect do
368
- adapter_connection.insert("INSERT 42")
369
- end.to raise_error(ActiveRecord::StatementInvalid)
370
- end
371
- end
372
- end