master_slave_adapter_soundcloud 0.1.3 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/VERSION +1 -1
- data/lib/master_slave_adapter.rb +29 -10
- data/master_slave_adapter.gemspec +1 -1
- data/specs/specs.rb +57 -51
- metadata +4 -22
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.6
|
data/lib/master_slave_adapter.rb
CHANGED
@@ -47,8 +47,9 @@ module ActiveRecord
|
|
47
47
|
def massage(config)
|
48
48
|
config = config.symbolize_keys
|
49
49
|
skip = [ :adapter, :connection_adapter, :master, :slaves ]
|
50
|
-
defaults = config.
|
51
|
-
|
50
|
+
defaults = config.
|
51
|
+
reject { |k,_| skip.include?(k) }.
|
52
|
+
merge(:adapter => config.fetch(:connection_adapter))
|
52
53
|
([config.fetch(:master)] + config.fetch(:slaves, [])).map do |cfg|
|
53
54
|
cfg.symbolize_keys!.reverse_merge!(defaults)
|
54
55
|
end
|
@@ -142,7 +143,7 @@ module ActiveRecord
|
|
142
143
|
# try random slave, else fall back to master
|
143
144
|
slave = slave_connection!
|
144
145
|
conn =
|
145
|
-
if !open_transaction? &&
|
146
|
+
if !open_transaction? && slave_consistent?(slave, clock)
|
146
147
|
[ slave, :slave ]
|
147
148
|
else
|
148
149
|
[ master_connection, :master ]
|
@@ -264,8 +265,8 @@ module ActiveRecord
|
|
264
265
|
:delete_sql,
|
265
266
|
:sanitize_limit,
|
266
267
|
:to => :master_connection
|
267
|
-
delegate *ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods
|
268
|
-
:to => :master_connection
|
268
|
+
delegate *(ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods + [{
|
269
|
+
:to => :master_connection }])
|
269
270
|
# no clear interface contract:
|
270
271
|
delegate :tables, # commented in SchemaStatements
|
271
272
|
:truncate_table, # monkeypatching database_cleaner gem
|
@@ -298,8 +299,10 @@ module ActiveRecord
|
|
298
299
|
private :connection_for_read
|
299
300
|
|
300
301
|
# === doesn't really matter, but must be handled by underlying adapter
|
301
|
-
delegate *ActiveRecord::ConnectionAdapters::Quoting.instance_methods
|
302
|
-
:to => :current_connection
|
302
|
+
delegate *(ActiveRecord::ConnectionAdapters::Quoting.instance_methods + [{
|
303
|
+
:to => :current_connection }])
|
304
|
+
# issue #4: current_database is not supported by all adapters, though
|
305
|
+
delegate :current_database, :to => :current_connection
|
303
306
|
|
304
307
|
# UTIL ==================================================================
|
305
308
|
|
@@ -340,13 +343,18 @@ module ActiveRecord
|
|
340
343
|
end
|
341
344
|
end
|
342
345
|
|
343
|
-
def slave_clock(
|
344
|
-
conn ||= slave_connection!
|
346
|
+
def slave_clock(conn)
|
345
347
|
if status = conn.uncached { conn.select_one("SHOW SLAVE STATUS") }
|
346
|
-
Clock.new(status['Relay_Master_Log_File'], status['Exec_Master_Log_Pos'])
|
348
|
+
Clock.new(status['Relay_Master_Log_File'], status['Exec_Master_Log_Pos']).tap do |c|
|
349
|
+
set_last_seen_slave_clock(conn, c)
|
350
|
+
end
|
347
351
|
end
|
348
352
|
end
|
349
353
|
|
354
|
+
def slave_consistent?(conn, clock)
|
355
|
+
(get_last_seen_slave_clock(conn) || slave_clock(conn)).try(:>=, clock)
|
356
|
+
end
|
357
|
+
|
350
358
|
protected
|
351
359
|
|
352
360
|
def on_write
|
@@ -406,6 +414,17 @@ module ActiveRecord
|
|
406
414
|
def on_rollback_callbacks
|
407
415
|
Thread.current[:on_rollback_callbacks] ||= []
|
408
416
|
end
|
417
|
+
|
418
|
+
def get_last_seen_slave_clock(conn)
|
419
|
+
conn.instance_variable_get(:@last_seen_slave_clock)
|
420
|
+
end
|
421
|
+
|
422
|
+
def set_last_seen_slave_clock(conn, clock)
|
423
|
+
last_seen = get_last_seen_slave_clock(conn)
|
424
|
+
if last_seen.nil? || last_seen < clock
|
425
|
+
conn.instance_variable_set(:@last_seen_slave_clock, clock)
|
426
|
+
end
|
427
|
+
end
|
409
428
|
end
|
410
429
|
end
|
411
430
|
end
|
@@ -4,7 +4,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = 'master_slave_adapter_soundcloud'
|
6
6
|
s.version = File.read('VERSION').to_s
|
7
|
-
s.date = '2011-
|
7
|
+
s.date = '2011-08-23'
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = [ 'Mauricio Linhares', 'Torsten Curdt', 'Kim Altintop', 'Omid Aladini', 'SoundCloud' ]
|
10
10
|
s.email = %q{kim@soundcloud.com tcurdt@soundcloud.com omid@soundcloud.com}
|
data/specs/specs.rb
CHANGED
@@ -211,17 +211,17 @@ describe ActiveRecord::ConnectionAdapters::MasterSlaveAdapter do
|
|
211
211
|
def slave_should_report_clock(pos)
|
212
212
|
pos = Array(pos)
|
213
213
|
values = pos.map { |p| { 'Relay_Master_Log_File' => '', 'Exec_Master_Log_Pos' => p } }
|
214
|
-
slave_connection
|
215
|
-
|
216
|
-
|
214
|
+
slave_connection.
|
215
|
+
should_receive('select_one').exactly(pos.length).with('SHOW SLAVE STATUS').
|
216
|
+
and_return(*values)
|
217
217
|
end
|
218
218
|
|
219
219
|
def master_should_report_clock(pos)
|
220
220
|
pos = Array(pos)
|
221
221
|
values = pos.map { |p| { 'File' => '', 'Position' => p } }
|
222
|
-
master_connection
|
223
|
-
|
224
|
-
|
222
|
+
master_connection.
|
223
|
+
should_receive('select_one').exactly(pos.length).with('SHOW MASTER STATUS').
|
224
|
+
and_return(*values)
|
225
225
|
end
|
226
226
|
|
227
227
|
SelectMethods.each do |method|
|
@@ -287,26 +287,27 @@ describe ActiveRecord::ConnectionAdapters::MasterSlaveAdapter do
|
|
287
287
|
slave_should_report_clock(0)
|
288
288
|
master_should_report_clock([0, 1, 1])
|
289
289
|
|
290
|
-
slave_connection
|
291
|
-
|
292
|
-
|
290
|
+
slave_connection.
|
291
|
+
should_receive('select_all').exactly(1).times.with('testing').
|
292
|
+
and_return(true)
|
293
293
|
|
294
|
-
master_connection
|
295
|
-
|
296
|
-
|
297
|
-
master_connection
|
298
|
-
|
299
|
-
|
294
|
+
master_connection.
|
295
|
+
should_receive('update').exactly(3).times.with('testing').
|
296
|
+
and_return(true)
|
297
|
+
master_connection.
|
298
|
+
should_receive('select_all').exactly(5).times.with('testing').
|
299
|
+
and_return(true)
|
300
300
|
%w(begin_db_transaction
|
301
301
|
commit_db_transaction
|
302
302
|
increment_open_transactions
|
303
|
-
decrement_open_transactions
|
303
|
+
decrement_open_transactions
|
304
|
+
outside_transaction?).each do |txstmt|
|
304
305
|
master_connection.should_receive(txstmt).exactly(1).times
|
305
306
|
end
|
306
307
|
|
307
|
-
master_connection
|
308
|
-
|
309
|
-
|
308
|
+
master_connection.
|
309
|
+
should_receive('open_transactions').exactly(13).times.
|
310
|
+
and_return(
|
310
311
|
# adapter: with_consistency, select_all, update, select_all
|
311
312
|
0, 0, 0, 0,
|
312
313
|
# connection: transaction
|
@@ -341,10 +342,10 @@ describe ActiveRecord::ConnectionAdapters::MasterSlaveAdapter do
|
|
341
342
|
|
342
343
|
context "with nested with_consistency" do
|
343
344
|
it "should return the same clock if not writing and no lag" do
|
344
|
-
slave_should_report_clock(
|
345
|
-
slave_connection
|
346
|
-
|
347
|
-
|
345
|
+
slave_should_report_clock(0) # note: tests memoizing slave clock
|
346
|
+
slave_connection.
|
347
|
+
should_receive('select_one').exactly(3).times.with('testing').
|
348
|
+
and_return(true)
|
348
349
|
|
349
350
|
old_clock = zero
|
350
351
|
new_clock = ActiveRecord::Base.with_consistency(old_clock) do
|
@@ -358,13 +359,15 @@ describe ActiveRecord::ConnectionAdapters::MasterSlaveAdapter do
|
|
358
359
|
end
|
359
360
|
|
360
361
|
it "requesting a newer clock should return a new clock" do
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
362
|
+
adapter_connection.
|
363
|
+
should_receive('slave_consistent?').exactly(2).times.
|
364
|
+
and_return(true, false)
|
365
|
+
slave_connection.
|
366
|
+
should_receive('select_all').exactly(2).times.with('testing').
|
367
|
+
and_return(true)
|
368
|
+
master_connection.
|
369
|
+
should_receive('select_all').exactly(1).times.with('testing').
|
370
|
+
and_return(true)
|
368
371
|
|
369
372
|
start_clock = zero
|
370
373
|
inner_clock = zero
|
@@ -439,23 +442,24 @@ describe ActiveRecord::ConnectionAdapters::MasterSlaveAdapter do
|
|
439
442
|
end
|
440
443
|
|
441
444
|
def run_tx
|
442
|
-
adapter_connection
|
443
|
-
|
444
|
-
|
445
|
+
adapter_connection.
|
446
|
+
should_receive('master_clock').
|
447
|
+
and_return(Clock.new('', 1))
|
445
448
|
%w(begin_db_transaction
|
446
449
|
commit_db_transaction
|
447
450
|
increment_open_transactions
|
448
|
-
decrement_open_transactions
|
449
|
-
|
450
|
-
|
451
|
+
decrement_open_transactions
|
452
|
+
outside_transaction?).each do |txstmt|
|
453
|
+
master_connection.
|
454
|
+
should_receive(txstmt).exactly(1).times
|
451
455
|
end
|
452
|
-
master_connection
|
453
|
-
|
454
|
-
|
456
|
+
master_connection.
|
457
|
+
should_receive('open_transactions').exactly(4).times.
|
458
|
+
and_return(0, 1, 0, 0)
|
455
459
|
|
456
|
-
master_connection
|
457
|
-
|
458
|
-
|
460
|
+
master_connection.
|
461
|
+
should_receive('update').with('testing').
|
462
|
+
and_return(true)
|
459
463
|
|
460
464
|
ActiveRecord::Base.transaction do
|
461
465
|
adapter_connection.send('update', 'testing')
|
@@ -467,15 +471,17 @@ describe ActiveRecord::ConnectionAdapters::MasterSlaveAdapter do
|
|
467
471
|
rollback_db_transaction
|
468
472
|
increment_open_transactions
|
469
473
|
decrement_open_transactions).each do |txstmt|
|
470
|
-
master_connection
|
471
|
-
|
472
|
-
end
|
473
|
-
master_connection
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
474
|
+
master_connection.
|
475
|
+
should_receive(txstmt).exactly(1).times
|
476
|
+
end
|
477
|
+
master_connection.
|
478
|
+
should_receive('outside_transaction?').exactly(2).times
|
479
|
+
master_connection.
|
480
|
+
should_receive('open_transactions').exactly(3).times.
|
481
|
+
and_return(0, 1, 0)
|
482
|
+
master_connection.
|
483
|
+
should_receive('update').with('testing').
|
484
|
+
and_return(true)
|
479
485
|
|
480
486
|
ActiveRecord::Base.transaction do
|
481
487
|
adapter_connection.send('update', 'testing')
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: master_slave_adapter_soundcloud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 1
|
8
|
-
- 3
|
9
|
-
version: 0.1.3
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.6
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- Mauricio Linhares
|
@@ -18,7 +14,7 @@ autorequire:
|
|
18
14
|
bindir: bin
|
19
15
|
cert_chain: []
|
20
16
|
|
21
|
-
date: 2011-
|
17
|
+
date: 2011-08-23 00:00:00 +02:00
|
22
18
|
default_executable:
|
23
19
|
dependencies:
|
24
20
|
- !ruby/object:Gem::Dependency
|
@@ -29,8 +25,6 @@ dependencies:
|
|
29
25
|
requirements:
|
30
26
|
- - ">="
|
31
27
|
- !ruby/object:Gem::Version
|
32
|
-
segments:
|
33
|
-
- 0
|
34
28
|
version: "0"
|
35
29
|
type: :development
|
36
30
|
version_requirements: *id001
|
@@ -42,10 +36,6 @@ dependencies:
|
|
42
36
|
requirements:
|
43
37
|
- - "="
|
44
38
|
- !ruby/object:Gem::Version
|
45
|
-
segments:
|
46
|
-
- 2
|
47
|
-
- 3
|
48
|
-
- 9
|
49
39
|
version: 2.3.9
|
50
40
|
type: :runtime
|
51
41
|
version_requirements: *id002
|
@@ -82,25 +72,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
82
72
|
requirements:
|
83
73
|
- - ">="
|
84
74
|
- !ruby/object:Gem::Version
|
85
|
-
segments:
|
86
|
-
- 1
|
87
|
-
- 9
|
88
|
-
- 2
|
89
75
|
version: 1.9.2
|
90
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
77
|
none: false
|
92
78
|
requirements:
|
93
79
|
- - ">="
|
94
80
|
- !ruby/object:Gem::Version
|
95
|
-
segments:
|
96
|
-
- 1
|
97
|
-
- 3
|
98
|
-
- 7
|
99
81
|
version: 1.3.7
|
100
82
|
requirements: []
|
101
83
|
|
102
84
|
rubyforge_project:
|
103
|
-
rubygems_version: 1.
|
85
|
+
rubygems_version: 1.5.0
|
104
86
|
signing_key:
|
105
87
|
specification_version: 3
|
106
88
|
summary: Replication Aware Master/Slave Database Adapter for Rails/ActiveRecord
|