ar_mysql_flexmaster 0.1.3 → 0.2.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.
data/Rakefile
CHANGED
@@ -6,11 +6,16 @@ require 'yaggy'
|
|
6
6
|
|
7
7
|
Yaggy.gem(File.expand_path("ar_mysql_flexmaster.gemspec", File.dirname(__FILE__)), :push_gem => true)
|
8
8
|
|
9
|
-
Rake::TestTask.new(:
|
9
|
+
Rake::TestTask.new(:test_units) do |test|
|
10
10
|
test.libs << 'lib' << 'test'
|
11
11
|
test.pattern = 'test/*_test.rb'
|
12
|
-
#test.test_files = ['test/integration/run_integration_tests']
|
13
12
|
test.verbose = true
|
14
13
|
end
|
15
14
|
|
15
|
+
task :test do
|
16
|
+
retval = true
|
17
|
+
retval &= Rake::Task[:test_units].invoke
|
18
|
+
retval &= system(File.dirname(__FILE__) + "/test/integration/run_integration_tests")
|
19
|
+
end
|
20
|
+
|
16
21
|
task :default => :test
|
data/ar_mysql_flexmaster.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |gem|
|
|
12
12
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
13
|
gem.name = "ar_mysql_flexmaster"
|
14
14
|
gem.require_paths = ["lib"]
|
15
|
-
gem.version = "0.
|
15
|
+
gem.version = "0.2.0"
|
16
16
|
|
17
17
|
gem.add_runtime_dependency("mysql2")
|
18
18
|
gem.add_runtime_dependency("activerecord")
|
@@ -94,23 +94,30 @@ module ActiveRecord
|
|
94
94
|
def find_correct_host
|
95
95
|
cxs = hosts_and_ports.map do |host, port|
|
96
96
|
initialize_connection(host, port)
|
97
|
-
end
|
97
|
+
end.compact
|
98
98
|
|
99
|
-
correct_cxs = cxs.select { |cx|
|
99
|
+
correct_cxs = cxs.select { |cx| cx_correct?(cx) }
|
100
100
|
|
101
|
+
chosen_cx = nil
|
101
102
|
if @is_master
|
102
103
|
# for master connections, we make damn sure that we have just one master
|
103
104
|
if correct_cxs.size == 1
|
104
|
-
|
105
|
+
chosen_cx = correct_cxs.first
|
105
106
|
else
|
106
107
|
# nothing read-write, or too many read-write
|
107
108
|
# (should we manually close the connections?)
|
108
|
-
|
109
|
+
chosen_cx = nil
|
109
110
|
end
|
110
111
|
else
|
111
|
-
# for slave connections, we just return a random RO candidate
|
112
|
-
|
112
|
+
# for slave connections, we just return a random RO candidate or the master if none are available
|
113
|
+
if correct_cxs.empty?
|
114
|
+
chosen_cx = cxs.first
|
115
|
+
else
|
116
|
+
chosen_cx = correct_cxs.shuffle.first
|
117
|
+
end
|
113
118
|
end
|
119
|
+
cxs.each { |cx| cx.close unless chosen_cx == cx }
|
120
|
+
chosen_cx
|
114
121
|
end
|
115
122
|
|
116
123
|
def initialize_connection(host, port)
|
data/test/ar_flexmaster_test.rb
CHANGED
@@ -3,6 +3,7 @@ require 'ar_mysql_flexmaster'
|
|
3
3
|
require 'active_record'
|
4
4
|
require_relative 'boot_mysql_env'
|
5
5
|
require 'test/unit'
|
6
|
+
require 'debugger'
|
6
7
|
|
7
8
|
File.open(File.dirname(File.expand_path(__FILE__)) + "/database.yml", "w+") do |f|
|
8
9
|
f.write <<-EOL
|
@@ -17,7 +18,7 @@ test_slave:
|
|
17
18
|
adapter: mysql_flexmaster
|
18
19
|
username: flex
|
19
20
|
slave: true
|
20
|
-
hosts: ["127.0.0.1:#{$mysql_slave.port}", "127.0.0.1:#{$mysql_slave_2.port}"]
|
21
|
+
hosts: ["127.0.0.1:#{$mysql_master.port}", "127.0.0.1:#{$mysql_slave.port}", "127.0.0.1:#{$mysql_slave_2.port}"]
|
21
22
|
password:
|
22
23
|
database: flexmaster_test
|
23
24
|
EOL
|
@@ -57,7 +58,7 @@ class TestArFlexmaster < Test::Unit::TestCase
|
|
57
58
|
end
|
58
59
|
|
59
60
|
def test_should_select_the_master_on_boot
|
60
|
-
assert
|
61
|
+
assert main_connection_is_original_master?
|
61
62
|
end
|
62
63
|
|
63
64
|
def test_should_hold_txs_until_timeout_then_abort
|
@@ -80,7 +81,7 @@ class TestArFlexmaster < Test::Unit::TestCase
|
|
80
81
|
$mysql_slave.set_rw(true)
|
81
82
|
end
|
82
83
|
User.create(:name => "foo")
|
83
|
-
assert !
|
84
|
+
assert !main_connection_is_original_master?
|
84
85
|
assert User.first(:conditions => {:name => "foo"})
|
85
86
|
end
|
86
87
|
|
@@ -92,7 +93,7 @@ class TestArFlexmaster < Test::Unit::TestCase
|
|
92
93
|
$mysql_slave.set_rw(true)
|
93
94
|
end
|
94
95
|
User.update_all(:name => "bar")
|
95
|
-
assert !
|
96
|
+
assert !main_connection_is_original_master?
|
96
97
|
assert_equal "bar", User.first.name
|
97
98
|
end
|
98
99
|
|
@@ -109,11 +110,11 @@ class TestArFlexmaster < Test::Unit::TestCase
|
|
109
110
|
ActiveRecord::Base.connection
|
110
111
|
$mysql_master.set_rw(false)
|
111
112
|
$mysql_slave.set_rw(true)
|
112
|
-
assert
|
113
|
+
assert main_connection_is_original_master?
|
113
114
|
100.times do
|
114
115
|
u = User.first
|
115
116
|
end
|
116
|
-
assert !
|
117
|
+
assert !main_connection_is_original_master?
|
117
118
|
end
|
118
119
|
|
119
120
|
def test_should_choose_a_random_slave_connection
|
@@ -131,10 +132,11 @@ class TestArFlexmaster < Test::Unit::TestCase
|
|
131
132
|
User.create!
|
132
133
|
$mysql_master.set_rw(false)
|
133
134
|
$mysql_slave.set_rw(true)
|
134
|
-
|
135
|
-
UserSlave.
|
135
|
+
20.times do
|
136
|
+
UserSlave.connection.execute("select 1")
|
136
137
|
end
|
137
|
-
|
138
|
+
connected_port = port_for_class(UserSlave)
|
139
|
+
assert [$mysql_slave_2.port, $mysql_master.port].include?(connected_port)
|
138
140
|
end
|
139
141
|
|
140
142
|
def test_xxx_non_responsive_master
|
@@ -149,20 +151,31 @@ class TestArFlexmaster < Test::Unit::TestCase
|
|
149
151
|
def test_yyy_shooting_the_master_in_the_head
|
150
152
|
User.create!
|
151
153
|
Process.kill("TERM", $mysql_master.pid)
|
154
|
+
sleep 1
|
152
155
|
$mysql_slave.set_rw(true)
|
153
156
|
User.connection.reconnect!
|
154
157
|
User.create!
|
155
158
|
UserSlave.first
|
156
|
-
assert !
|
159
|
+
assert !main_connection_is_original_master?
|
157
160
|
end
|
158
161
|
|
162
|
+
# test that when nothing else is available we can fall back to the master in a slave role
|
163
|
+
# note that by the time this test runs, the 'yyy' test has already killed the master
|
164
|
+
def test_zzz_shooting_the_other_slave_in_the_head
|
165
|
+
$mysql_slave.set_rw(true)
|
166
|
+
$mysql_slave_2.kill!
|
167
|
+
UserSlave.connection.reconnect!
|
168
|
+
assert port_for_class(UserSlave) == $mysql_slave.port
|
169
|
+
end
|
170
|
+
|
171
|
+
|
159
172
|
private
|
160
173
|
|
161
174
|
def port_for_class(klass)
|
162
175
|
klass.connection.execute("show global variables like 'port'").first.last.to_i
|
163
176
|
end
|
164
177
|
|
165
|
-
def
|
178
|
+
def main_connection_is_original_master?
|
166
179
|
port = port_for_class(ActiveRecord::Base)
|
167
180
|
port == $mysql_master.port
|
168
181
|
end
|
data/test/boot_mysql_env.rb
CHANGED
@@ -2,33 +2,39 @@
|
|
2
2
|
|
3
3
|
require_relative "mysql_isolated_server"
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
mysql_master.
|
5
|
+
threads = []
|
6
|
+
threads << Thread.new do
|
7
|
+
$mysql_master = MysqlIsolatedServer.new(allow_output: false)
|
8
|
+
$mysql_master.boot!
|
9
|
+
$mysql_master.connection.query("set global server_id=1")
|
8
10
|
|
9
|
-
puts "mysql master booted on port #{mysql_master.port} -- access with mysql -uroot -h127.0.0.1 --port=#{mysql_master.port} mysql"
|
11
|
+
puts "mysql master booted on port #{$mysql_master.port} -- access with mysql -uroot -h127.0.0.1 --port=#{$mysql_master.port} mysql"
|
12
|
+
end
|
10
13
|
|
11
|
-
|
12
|
-
mysql_slave.
|
13
|
-
mysql_slave.
|
14
|
+
threads << Thread.new do
|
15
|
+
$mysql_slave = MysqlIsolatedServer.new
|
16
|
+
$mysql_slave.boot!
|
17
|
+
$mysql_slave.connection.query("set global server_id=2")
|
14
18
|
|
15
|
-
puts "mysql slave booted on port #{mysql_slave.port} -- access with mysql -uroot -h127.0.0.1 --port=#{mysql_slave.port} mysql"
|
19
|
+
puts "mysql slave booted on port #{$mysql_slave.port} -- access with mysql -uroot -h127.0.0.1 --port=#{$mysql_slave.port} mysql"
|
20
|
+
end
|
16
21
|
|
17
|
-
|
18
|
-
mysql_slave_2.
|
19
|
-
mysql_slave_2.
|
22
|
+
threads << Thread.new do
|
23
|
+
$mysql_slave_2 = MysqlIsolatedServer.new
|
24
|
+
$mysql_slave_2.boot!
|
25
|
+
$mysql_slave_2.connection.query("set global server_id=3")
|
20
26
|
|
21
|
-
puts "mysql chained slave booted on port #{mysql_slave_2.port} -- access with mysql -uroot -h127.0.0.1 --port=#{mysql_slave_2.port} mysql"
|
27
|
+
puts "mysql chained slave booted on port #{$mysql_slave_2.port} -- access with mysql -uroot -h127.0.0.1 --port=#{$mysql_slave_2.port} mysql"
|
28
|
+
end
|
22
29
|
|
23
|
-
|
24
|
-
mysql_slave.make_slave_of(mysql_master)
|
25
|
-
mysql_slave_2.make_slave_of(mysql_slave)
|
30
|
+
threads.each(&:join)
|
26
31
|
|
27
|
-
mysql_master.connection.query("
|
28
|
-
|
29
|
-
|
30
|
-
|
32
|
+
$mysql_master.connection.query("CHANGE MASTER TO master_host='127.0.0.1', master_user='root', master_password=''")
|
33
|
+
$mysql_slave.make_slave_of($mysql_master)
|
34
|
+
$mysql_slave_2.make_slave_of($mysql_slave)
|
35
|
+
|
36
|
+
$mysql_master.connection.query("GRANT ALL ON flexmaster_test.* to flex@localhost")
|
37
|
+
$mysql_master.connection.query("CREATE DATABASE flexmaster_test")
|
38
|
+
$mysql_master.connection.query("CREATE TABLE flexmaster_test.users (id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY, name varchar(20))")
|
39
|
+
$mysql_master.connection.query("INSERT INTO flexmaster_test.users set name='foo'")
|
31
40
|
|
32
|
-
$mysql_master = mysql_master
|
33
|
-
$mysql_slave = mysql_slave
|
34
|
-
$mysql_slave_2 = mysql_slave_2
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ar_mysql_flexmaster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-03-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mysql2
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- test/boot_mysql_env.rb
|
121
121
|
- test/boot_slave
|
122
122
|
- test/integration/no_traffic_test.rb
|
123
|
+
- test/integration/run_integration_tests
|
123
124
|
- test/integration/there_and_back_again_test.rb
|
124
125
|
- test/integration/with_queries_to_be_killed_test.rb
|
125
126
|
- test/integration/wrong_setup_test.rb
|
@@ -141,7 +142,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
141
142
|
version: '0'
|
142
143
|
segments:
|
143
144
|
- 0
|
144
|
-
hash:
|
145
|
+
hash: 3291849220981304751
|
145
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
147
|
none: false
|
147
148
|
requirements:
|
@@ -150,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
150
151
|
version: '0'
|
151
152
|
segments:
|
152
153
|
- 0
|
153
|
-
hash:
|
154
|
+
hash: 3291849220981304751
|
154
155
|
requirements: []
|
155
156
|
rubyforge_project:
|
156
157
|
rubygems_version: 1.8.24
|
@@ -162,6 +163,7 @@ test_files:
|
|
162
163
|
- test/boot_mysql_env.rb
|
163
164
|
- test/boot_slave
|
164
165
|
- test/integration/no_traffic_test.rb
|
166
|
+
- test/integration/run_integration_tests
|
165
167
|
- test/integration/there_and_back_again_test.rb
|
166
168
|
- test/integration/with_queries_to_be_killed_test.rb
|
167
169
|
- test/integration/wrong_setup_test.rb
|