master_slave_adapter 1.0.0.beta1 → 1.0.0.beta2

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.
@@ -1,21 +1,9 @@
1
1
  $: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
2
 
3
3
  require 'rspec'
4
-
5
- class Mysql
6
- class Error
7
- CR_CONNECTION_ERROR = 1
8
- CR_CONN_HOST_ERROR = 2
9
- CR_SERVER_GONE_ERROR = 4
10
- CR_SERVER_LOST = 8
11
- end
12
- end
13
-
4
+ require 'logger'
14
5
  require 'active_record/connection_adapters/mysql_master_slave_adapter'
15
6
 
16
- ActiveRecord::Base.logger =
17
- Logger.new($stdout).tap { |l| l.level = Logger::DEBUG }
18
-
19
7
  module ActiveRecord
20
8
  class Base
21
9
  cattr_accessor :master_mock, :slave_mock
@@ -26,7 +14,7 @@ module ActiveRecord
26
14
  end
27
15
 
28
16
  describe ActiveRecord::ConnectionAdapters::MysqlMasterSlaveAdapter do
29
- let(:default_database_setup) do
17
+ let(:database_setup) do
30
18
  {
31
19
  :adapter => 'master_slave',
32
20
  :username => 'root',
@@ -37,13 +25,11 @@ describe ActiveRecord::ConnectionAdapters::MysqlMasterSlaveAdapter do
37
25
  }
38
26
  end
39
27
 
40
- let(:database_setup) { default_database_setup }
41
-
42
28
  let(:mocked_methods) do
43
29
  {
44
- :reconnect! => true,
30
+ :reconnect! => true,
45
31
  :disconnect! => true,
46
- :active? => true,
32
+ :active? => true,
47
33
  }
48
34
  end
49
35
 
@@ -68,8 +54,8 @@ describe ActiveRecord::ConnectionAdapters::MysqlMasterSlaveAdapter do
68
54
  ActiveRecord::Base.connection
69
55
  end
70
56
 
71
- SelectMethods = [ :select_all, :select_one, :select_rows, :select_value, :select_values ]
72
- Clock = ActiveRecord::ConnectionAdapters::MasterSlaveAdapter::Clock
57
+ SelectMethods = [ :select_all, :select_one, :select_rows, :select_value, :select_values ] unless defined?(SelectMethods)
58
+ Clock = ActiveRecord::ConnectionAdapters::MasterSlaveAdapter::Clock unless defined?(Clock)
73
59
 
74
60
  before do
75
61
  ActiveRecord::Base.establish_connection(database_setup)
@@ -88,20 +74,31 @@ describe ActiveRecord::ConnectionAdapters::MysqlMasterSlaveAdapter do
88
74
  Clock.new('', pos)
89
75
  end
90
76
 
91
- def slave_should_report_clock(pos)
77
+ def supports_prepared_statements?
78
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.instance_methods.map(&:to_sym).include?(:exec_without_stmt)
79
+ end
80
+
81
+ def select_method
82
+ supports_prepared_statements? ? :exec_without_stmt : :select_one
83
+ end
84
+
85
+ def should_report_clock(pos, connection, log_file, log_pos, sql)
92
86
  pos = Array(pos)
93
- values = pos.map { |p| { 'Relay_Master_Log_File' => '', 'Exec_Master_Log_Pos' => p } }
94
- slave_connection.
95
- should_receive('select_one').exactly(pos.length).with('SHOW SLAVE STATUS').
87
+ values = pos.map { |p| { log_file => '', log_pos => p } }
88
+ values.map! { |result| [ result ] } if supports_prepared_statements?
89
+
90
+ connection.
91
+ should_receive(select_method).exactly(pos.length).times.
92
+ with(sql).
96
93
  and_return(*values)
97
94
  end
98
95
 
96
+ def slave_should_report_clock(pos)
97
+ should_report_clock(pos, slave_connection, 'Relay_Master_Log_File', 'Exec_Master_Log_Pos', 'SHOW SLAVE STATUS')
98
+ end
99
+
99
100
  def master_should_report_clock(pos)
100
- pos = Array(pos)
101
- values = pos.map { |p| { 'File' => '', 'Position' => p } }
102
- master_connection.
103
- should_receive('select_one').exactly(pos.length).with('SHOW MASTER STATUS').
104
- and_return(*values)
101
+ should_report_clock(pos, master_connection, 'File', 'Position', 'SHOW MASTER STATUS')
105
102
  end
106
103
 
107
104
  SelectMethods.each do |method|
@@ -152,12 +149,12 @@ describe ActiveRecord::ConnectionAdapters::MysqlMasterSlaveAdapter do
152
149
  slave_should_report_clock(0)
153
150
  master_should_report_clock(2)
154
151
  slave_connection.should_receive(method).with('testing').and_return(true)
155
- master_connection.should_receive('update').with('testing').and_return(true)
152
+ master_connection.should_receive(:update).with('testing').and_return(true)
156
153
  master_connection.should_receive(method).with('testing').and_return(true)
157
154
  old_clock = zero
158
155
  new_clock = ActiveRecord::Base.with_consistency(old_clock) do
159
156
  adapter_connection.send(method, 'testing') # slave
160
- adapter_connection.send('update', 'testing') # master
157
+ adapter_connection.send(:update, 'testing') # master
161
158
  adapter_connection.send(method, 'testing') # master
162
159
  end
163
160
  new_clock.should be_a(Clock)
@@ -170,14 +167,14 @@ describe ActiveRecord::ConnectionAdapters::MysqlMasterSlaveAdapter do
170
167
  master_should_report_clock([0, 1, 1])
171
168
 
172
169
  slave_connection.
173
- should_receive('select_all').exactly(1).times.with('testing').
170
+ should_receive(:select_all).exactly(1).times.with('testing').
174
171
  and_return(true)
175
172
 
176
173
  master_connection.
177
- should_receive('update').exactly(3).times.with('testing').
174
+ should_receive(:update).exactly(3).times.with('testing').
178
175
  and_return(true)
179
176
  master_connection.
180
- should_receive('select_all').exactly(5).times.with('testing').
177
+ should_receive(:select_all).exactly(5).times.with('testing').
181
178
  and_return(true)
182
179
  %w(begin_db_transaction
183
180
  commit_db_transaction
@@ -204,19 +201,19 @@ describe ActiveRecord::ConnectionAdapters::MysqlMasterSlaveAdapter do
204
201
 
205
202
  old_clock = zero
206
203
  new_clock = ActiveRecord::Base.with_consistency(old_clock) do
207
- adapter_connection.send('select_all', 'testing') # slave s=0 m=0
208
- adapter_connection.send('update', 'testing') # master s=0 m=1
209
- adapter_connection.send('select_all', 'testing') # master s=0 m=1
204
+ adapter_connection.send(:select_all, 'testing') # slave s=0 m=0
205
+ adapter_connection.send(:update, 'testing') # master s=0 m=1
206
+ adapter_connection.send(:select_all, 'testing') # master s=0 m=1
210
207
 
211
208
  ActiveRecord::Base.transaction do
212
- adapter_connection.send('select_all', 'testing') # master s=0 m=1
213
- adapter_connection.send('update', 'testing') # master s=0 m=1
214
- adapter_connection.send('select_all', 'testing') # master s=0 m=1
209
+ adapter_connection.send(:select_all, 'testing') # master s=0 m=1
210
+ adapter_connection.send(:update, 'testing') # master s=0 m=1
211
+ adapter_connection.send(:select_all, 'testing') # master s=0 m=1
215
212
  end
216
213
 
217
- adapter_connection.send('select_all', 'testing') # master s=0 m=2
218
- adapter_connection.send('update', 'testing') # master s=0 m=3
219
- adapter_connection.send('select_all', 'testing') # master s=0 m=3
214
+ adapter_connection.send(:select_all, 'testing') # master s=0 m=2
215
+ adapter_connection.send(:update, 'testing') # master s=0 m=3
216
+ adapter_connection.send(:select_all, 'testing') # master s=0 m=3
220
217
  end
221
218
 
222
219
  new_clock.should > old_clock
@@ -224,18 +221,18 @@ describe ActiveRecord::ConnectionAdapters::MysqlMasterSlaveAdapter do
224
221
 
225
222
  context "with nested with_consistency" do
226
223
  it "should return the same clock if not writing and no lag" do
227
- slave_should_report_clock(0) # note: tests memoizing slave clock
224
+ slave_should_report_clock(0)
228
225
  slave_connection.
229
- should_receive('select_one').exactly(3).times.with('testing').
226
+ should_receive(:select_one).exactly(3).times.with('testing').
230
227
  and_return(true)
231
228
 
232
229
  old_clock = zero
233
230
  new_clock = ActiveRecord::Base.with_consistency(old_clock) do
234
- adapter_connection.send('select_one', 'testing')
231
+ adapter_connection.send(:select_one, 'testing')
235
232
  ActiveRecord::Base.with_consistency(old_clock) do
236
- adapter_connection.send('select_one', 'testing')
233
+ adapter_connection.send(:select_one, 'testing')
237
234
  end
238
- adapter_connection.send('select_one', 'testing')
235
+ adapter_connection.send(:select_one, 'testing')
239
236
  end
240
237
  new_clock.should equal(old_clock)
241
238
  end
@@ -245,20 +242,20 @@ describe ActiveRecord::ConnectionAdapters::MysqlMasterSlaveAdapter do
245
242
  should_receive('slave_consistent?').exactly(2).times.
246
243
  and_return(true, false)
247
244
  slave_connection.
248
- should_receive('select_all').exactly(2).times.with('testing').
245
+ should_receive(:select_all).exactly(2).times.with('testing').
249
246
  and_return(true)
250
247
  master_connection.
251
- should_receive('select_all').exactly(1).times.with('testing').
248
+ should_receive(:select_all).exactly(1).times.with('testing').
252
249
  and_return(true)
253
250
 
254
251
  start_clock = zero
255
252
  inner_clock = zero
256
253
  outer_clock = ActiveRecord::Base.with_consistency(start_clock) do
257
- adapter_connection.send('select_all', 'testing') # slave
254
+ adapter_connection.send(:select_all, 'testing') # slave
258
255
  inner_clock = ActiveRecord::Base.with_consistency(master_position(1)) do
259
- adapter_connection.send('select_all', 'testing') # master
256
+ adapter_connection.send(:select_all, 'testing') # master
260
257
  end
261
- adapter_connection.send('select_all', 'testing') # slave
258
+ adapter_connection.send(:select_all, 'testing') # slave
262
259
  end
263
260
 
264
261
  start_clock.should equal(outer_clock)
@@ -268,53 +265,100 @@ describe ActiveRecord::ConnectionAdapters::MysqlMasterSlaveAdapter do
268
265
 
269
266
  it "should do the right thing when nested inside with_master" do
270
267
  slave_should_report_clock(0)
271
- slave_connection.should_receive('select_all').exactly(1).times.with('testing').and_return(true)
272
- master_connection.should_receive('select_all').exactly(2).times.with('testing').and_return(true)
268
+ slave_connection.should_receive(:select_all).exactly(1).times.with('testing').and_return(true)
269
+ master_connection.should_receive(:select_all).exactly(2).times.with('testing').and_return(true)
273
270
  ActiveRecord::Base.with_master do
274
- adapter_connection.send('select_all', 'testing') # master
271
+ adapter_connection.send(:select_all, 'testing') # master
275
272
  ActiveRecord::Base.with_consistency(zero) do
276
- adapter_connection.send('select_all', 'testing') # slave
273
+ adapter_connection.send(:select_all, 'testing') # slave
277
274
  end
278
- adapter_connection.send('select_all', 'testing') # master
275
+ adapter_connection.send(:select_all, 'testing') # master
279
276
  end
280
277
  end
281
278
 
282
279
  it "should do the right thing when nested inside with_slave" do
283
280
  slave_should_report_clock(0)
284
- slave_connection.should_receive('select_all').exactly(3).times.with('testing').and_return(true)
281
+ slave_connection.should_receive(:select_all).exactly(3).times.with('testing').and_return(true)
285
282
  ActiveRecord::Base.with_slave do
286
- adapter_connection.send('select_all', 'testing') # slave
283
+ adapter_connection.send(:select_all, 'testing') # slave
287
284
  ActiveRecord::Base.with_consistency(zero) do
288
- adapter_connection.send('select_all', 'testing') # slave
285
+ adapter_connection.send(:select_all, 'testing') # slave
289
286
  end
290
- adapter_connection.send('select_all', 'testing') # slave
287
+ adapter_connection.send(:select_all, 'testing') # slave
291
288
  end
292
289
  end
293
290
 
294
291
  it "should do the right thing when wrapping with_master" do
295
292
  slave_should_report_clock(0)
296
- slave_connection.should_receive('select_all').exactly(2).times.with('testing').and_return(true)
297
- master_connection.should_receive('select_all').exactly(1).times.with('testing').and_return(true)
293
+ slave_connection.should_receive(:select_all).exactly(2).times.with('testing').and_return(true)
294
+ master_connection.should_receive(:select_all).exactly(1).times.with('testing').and_return(true)
298
295
  ActiveRecord::Base.with_consistency(zero) do
299
- adapter_connection.send('select_all', 'testing') # slave
296
+ adapter_connection.send(:select_all, 'testing') # slave
300
297
  ActiveRecord::Base.with_master do
301
- adapter_connection.send('select_all', 'testing') # master
298
+ adapter_connection.send(:select_all, 'testing') # master
302
299
  end
303
- adapter_connection.send('select_all', 'testing') # slave
300
+ adapter_connection.send(:select_all, 'testing') # slave
304
301
  end
305
302
  end
306
303
 
307
304
  it "should do the right thing when wrapping with_slave" do
308
305
  slave_should_report_clock(0)
309
- slave_connection.should_receive('select_all').exactly(1).times.with('testing').and_return(true)
310
- master_connection.should_receive('select_all').exactly(2).times.with('testing').and_return(true)
306
+ slave_connection.should_receive(:select_all).exactly(1).times.with('testing').and_return(true)
307
+ master_connection.should_receive(:select_all).exactly(2).times.with('testing').and_return(true)
311
308
  ActiveRecord::Base.with_consistency(master_position(1)) do
312
- adapter_connection.send('select_all', 'testing') # master
309
+ adapter_connection.send(:select_all, 'testing') # master
313
310
  ActiveRecord::Base.with_slave do
314
- adapter_connection.send('select_all', 'testing') # slave
311
+ adapter_connection.send(:select_all, 'testing') # slave
315
312
  end
316
- adapter_connection.send('select_all', 'testing') # master
313
+ adapter_connection.send(:select_all, 'testing') # master
314
+ end
315
+ end
316
+
317
+ it "should accept clock as string" do
318
+ slave_should_report_clock(0)
319
+ slave_connection.should_receive(:select_all).with('testing')
320
+
321
+ ActiveRecord::Base.with_consistency("@0") do
322
+ adapter_connection.send(:select_all, 'testing')
317
323
  end
318
324
  end
319
325
  end # /with_consistency
326
+
327
+ describe "connection error detection" do
328
+ {
329
+ Mysql::Error::CR_CONNECTION_ERROR => "query: not connected",
330
+ Mysql::Error::CR_CONN_HOST_ERROR => "Can't connect to MySQL server on 'localhost' (3306)",
331
+ Mysql::Error::CR_SERVER_GONE_ERROR => "MySQL server has gone away",
332
+ Mysql::Error::CR_SERVER_LOST => "Lost connection to MySQL server during query",
333
+ }.each do |errno, description|
334
+ it "raises MasterUnavailable for '#{description}' during query execution" do
335
+ master_connection.stub_chain(:raw_connection, :errno).and_return(errno)
336
+ master_connection.should_receive(:insert).and_raise(ActiveRecord::StatementInvalid.new("Mysql::Error: #{description}: INSERT 42"))
337
+
338
+ expect do
339
+ adapter_connection.insert("INSERT 42")
340
+ end.to raise_error(ActiveRecord::MasterUnavailable)
341
+ end
342
+
343
+ it "doesn't raise anything for '#{description}' during connection" do
344
+ error = Mysql::Error.new(description)
345
+ error.stub(:errno).and_return(errno)
346
+ ActiveRecord::Base.should_receive(:master_mock).and_raise(error)
347
+
348
+ expect do
349
+ ActiveRecord::Base.connection_handler.clear_all_connections!
350
+ ActiveRecord::Base.connection
351
+ end.to_not raise_error
352
+ end
353
+ end
354
+
355
+ it "raises StatementInvalid for other errors" do
356
+ master_connection.stub_chain(:raw_connection, :errno).and_return(Mysql::Error::ER_QUERY_INTERRUPTED)
357
+ master_connection.should_receive(:insert).and_raise(ActiveRecord::StatementInvalid.new("Mysql::Error: Query execution was interrupted: INSERT 42"))
358
+
359
+ expect do
360
+ adapter_connection.insert("INSERT 42")
361
+ end.to raise_error(ActiveRecord::StatementInvalid)
362
+ end
363
+ end
320
364
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: master_slave_adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.beta1
4
+ version: 1.0.0.beta2
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -15,24 +15,30 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2012-05-24 00:00:00.000000000 Z
18
+ date: 2012-06-20 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- name: rspec
21
+ name: activerecord
22
22
  requirement: !ruby/object:Gem::Requirement
23
23
  none: false
24
24
  requirements:
25
25
  - - ! '>='
26
26
  - !ruby/object:Gem::Version
27
- version: '0'
28
- type: :development
27
+ version: 2.3.9
28
+ - - <=
29
+ - !ruby/object:Gem::Version
30
+ version: '4.0'
31
+ type: :runtime
29
32
  prerelease: false
30
33
  version_requirements: !ruby/object:Gem::Requirement
31
34
  none: false
32
35
  requirements:
33
36
  - - ! '>='
34
37
  - !ruby/object:Gem::Version
35
- version: '0'
38
+ version: 2.3.9
39
+ - - <=
40
+ - !ruby/object:Gem::Version
41
+ version: '4.0'
36
42
  - !ruby/object:Gem::Dependency
37
43
  name: rake
38
44
  requirement: !ruby/object:Gem::Requirement
@@ -50,21 +56,21 @@ dependencies:
50
56
  - !ruby/object:Gem::Version
51
57
  version: '0'
52
58
  - !ruby/object:Gem::Dependency
53
- name: activerecord
59
+ name: rspec
54
60
  requirement: !ruby/object:Gem::Requirement
55
61
  none: false
56
62
  requirements:
57
- - - ~>
63
+ - - ! '>='
58
64
  - !ruby/object:Gem::Version
59
- version: 2.3.9
60
- type: :runtime
65
+ version: '0'
66
+ type: :development
61
67
  prerelease: false
62
68
  version_requirements: !ruby/object:Gem::Requirement
63
69
  none: false
64
70
  requirements:
65
- - - ~>
71
+ - - ! '>='
66
72
  - !ruby/object:Gem::Version
67
- version: 2.3.9
73
+ version: '0'
68
74
  description: (MySQL) Replication Aware Master/Slave Database Adapter for ActiveRecord
69
75
  email: kim@soundcloud.com tcurdt@soundcloud.com ts@soundcloud
70
76
  executables: []
@@ -72,22 +78,32 @@ extensions: []
72
78
  extra_rdoc_files: []
73
79
  files:
74
80
  - .gitignore
81
+ - .rspec
75
82
  - .travis.yml
76
83
  - CHANGELOG.md
77
- - Gemfile
78
84
  - LICENSE
79
85
  - Rakefile
80
86
  - Readme.md
81
- - TODO.txt
82
87
  - lib/active_record/connection_adapters/master_slave_adapter.rb
83
88
  - lib/active_record/connection_adapters/master_slave_adapter/circuit_breaker.rb
84
89
  - lib/active_record/connection_adapters/master_slave_adapter/clock.rb
90
+ - lib/active_record/connection_adapters/master_slave_adapter/shared_mysql_adapter_behavior.rb
85
91
  - lib/active_record/connection_adapters/master_slave_adapter/version.rb
92
+ - lib/active_record/connection_adapters/mysql2_master_slave_adapter.rb
86
93
  - lib/active_record/connection_adapters/mysql_master_slave_adapter.rb
87
94
  - lib/master_slave_adapter.rb
88
95
  - master_slave_adapter.gemspec
96
+ - spec/all.sh
89
97
  - spec/circuit_breaker_spec.rb
98
+ - spec/gemfiles/activerecord2.3
99
+ - spec/gemfiles/activerecord3.0
100
+ - spec/gemfiles/activerecord3.2
101
+ - spec/integration/helpers/mysql_helper.rb
102
+ - spec/integration/helpers/shared_mysql_examples.rb
103
+ - spec/integration/mysql2_master_slave_adapter_spec.rb
104
+ - spec/integration/mysql_master_slave_adapter_spec.rb
90
105
  - spec/master_slave_adapter_spec.rb
106
+ - spec/mysql2_master_slave_adapter_spec.rb
91
107
  - spec/mysql_master_slave_adapter_spec.rb
92
108
  homepage: http://github.com/soundcloud/master_slave_adapter
93
109
  licenses: []
@@ -109,11 +125,20 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
125
  version: 1.3.7
110
126
  requirements: []
111
127
  rubyforge_project:
112
- rubygems_version: 1.8.21
128
+ rubygems_version: 1.8.24
113
129
  signing_key:
114
130
  specification_version: 3
115
131
  summary: Replication Aware Master/Slave Database Adapter for ActiveRecord
116
132
  test_files:
133
+ - spec/all.sh
117
134
  - spec/circuit_breaker_spec.rb
135
+ - spec/gemfiles/activerecord2.3
136
+ - spec/gemfiles/activerecord3.0
137
+ - spec/gemfiles/activerecord3.2
138
+ - spec/integration/helpers/mysql_helper.rb
139
+ - spec/integration/helpers/shared_mysql_examples.rb
140
+ - spec/integration/mysql2_master_slave_adapter_spec.rb
141
+ - spec/integration/mysql_master_slave_adapter_spec.rb
118
142
  - spec/master_slave_adapter_spec.rb
143
+ - spec/mysql2_master_slave_adapter_spec.rb
119
144
  - spec/mysql_master_slave_adapter_spec.rb
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- gemspec
data/TODO.txt DELETED
@@ -1,18 +0,0 @@
1
- Read only mode
2
- --------------
3
-
4
- - Write tests
5
- - Clock.parse
6
- - connection_error (integration test)
7
- - integration tests
8
- - with_consistency accepts string clock
9
- - raise MasterUnavailable in all cases
10
- - connection stack usage
11
-
12
- - Check
13
- - thread safety
14
- - AR reconnect behavior / connection check
15
- - replication in other databases
16
-
17
- - restructure MasterSlaveAdapter with proper namespaces
18
- - make clock private