ar_mysql_flexmaster 0.0.8 → 0.1.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/ar_mysql_flexmaster.gemspec +1 -1
- data/bin/master_cut +54 -3
- data/gemfiles/rails2.gemfile.lock +1 -1
- data/gemfiles/rails3.2.gemfile.lock +1 -1
- data/gemfiles/rails3.gemfile.lock +1 -1
- data/test/boot_mysql_env.rb +1 -0
- data/test/integration/no_traffic_test.rb +1 -1
- data/test/integration/there_and_back_again_test.rb +27 -0
- data/test/integration/with_queries_to_be_killed_test.rb +1 -1
- data/test/integration/wrong_setup_test.rb +1 -1
- data/test/mysql_isolated_server.rb +1 -0
- data/test/user.MYD +0 -0
- data/test/user.MYI +0 -0
- data/test/user.frm +0 -0
- metadata +12 -4
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.0
|
15
|
+
gem.version = "0.1.0"
|
16
16
|
|
17
17
|
gem.add_runtime_dependency("mysql2")
|
18
18
|
gem.add_runtime_dependency("activerecord")
|
data/bin/master_cut
CHANGED
@@ -4,11 +4,29 @@ require 'bundler/setup'
|
|
4
4
|
require 'mysql2'
|
5
5
|
require 'socket'
|
6
6
|
require 'pp'
|
7
|
+
require "getoptlong"
|
7
8
|
|
8
9
|
Thread.abort_on_exception = false
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
|
11
|
+
opts = GetoptLong.new(
|
12
|
+
["--password", "-p", GetoptLong::REQUIRED_ARGUMENT],
|
13
|
+
["--rehome-master", "-r", GetoptLong::NO_ARGUMENT],
|
14
|
+
)
|
15
|
+
|
16
|
+
opts.each do |opt, arg|
|
17
|
+
case opt
|
18
|
+
when '--password'
|
19
|
+
$password = arg
|
20
|
+
when '--rehome-master'
|
21
|
+
$rehome_master = true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
$old_master, $new_master, $username = *ARGV
|
26
|
+
unless $old_master && $new_master && $username
|
27
|
+
puts "Usage: master_cut OLD_MASTER NEW_MASTER ADMIN_USERNAME"
|
28
|
+
puts " [-p,--password PASSWORD]"
|
29
|
+
puts " [-r,--reset-slave]"
|
12
30
|
exit
|
13
31
|
end
|
14
32
|
|
@@ -33,6 +51,18 @@ def fail(reason)
|
|
33
51
|
exit false
|
34
52
|
end
|
35
53
|
|
54
|
+
def ask_for_password
|
55
|
+
return unless $password.nil?
|
56
|
+
|
57
|
+
$stdout.write("Password for #{$username}: ")
|
58
|
+
begin
|
59
|
+
system "stty -echo"
|
60
|
+
$password = $stdin.gets.chomp
|
61
|
+
ensure
|
62
|
+
system "stty echo"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
36
66
|
def preflight_check
|
37
67
|
cx = open_cx($old_master)
|
38
68
|
rw = cx.query("select @@read_only as read_only").first['read_only']
|
@@ -43,8 +73,14 @@ def preflight_check
|
|
43
73
|
fail("new-master #{$old_master} is read-write!") if rw != 1
|
44
74
|
|
45
75
|
slave_info = slave_cx.query("show slave status").first
|
76
|
+
fail("no slave configured!") if slave_info.nil?
|
46
77
|
fail("slave is stopped!") unless slave_info['Slave_IO_Running'] == 'Yes' && slave_info['Slave_SQL_Running'] == 'Yes'
|
47
78
|
fail("slave is delayed") if slave_info['Seconds_Behind_Master'].nil? || slave_info['Seconds_Behind_Master'] > 0
|
79
|
+
|
80
|
+
masters_slave_info = cx.query("show slave status").first
|
81
|
+
if $rehome_master && masters_slave_info.nil? || masters_slave_info['Master_User'] == 'test'
|
82
|
+
fail("I can't rehome the original master -- it has no slave user or password.")
|
83
|
+
end
|
48
84
|
|
49
85
|
master_ip, slave_master_ip = [$old_master, slave_info['Master_Host']].map do |h|
|
50
86
|
h = h.split(':').first
|
@@ -86,10 +122,23 @@ def swap_thread
|
|
86
122
|
puts "Swapped #{$old_master} and #{$new_master}"
|
87
123
|
puts "New master information at time of swap: "
|
88
124
|
pp new_master_info
|
125
|
+
if $rehome_master
|
126
|
+
rehome_master(new_master_info)
|
127
|
+
end
|
89
128
|
exit
|
90
129
|
end
|
91
130
|
end
|
92
131
|
|
132
|
+
def rehome_master(info)
|
133
|
+
puts "Reconfiguring #{$old_master} to be a slave of #{$new_master}..."
|
134
|
+
host, port = $new_master.split(":")
|
135
|
+
port_clause = port ? "master_port = #{port}," : ""
|
136
|
+
cx = open_cx($old_master)
|
137
|
+
cx.query("change master to master_host='#{host}', #{port_clause} master_log_file = '#{info['File']}', master_log_pos=#{info['Position']}")
|
138
|
+
cx.query("slave start")
|
139
|
+
end
|
140
|
+
|
141
|
+
ask_for_password
|
93
142
|
preflight_check
|
94
143
|
|
95
144
|
threads = []
|
@@ -97,5 +146,7 @@ threads << process_kill_thread
|
|
97
146
|
threads << swap_thread
|
98
147
|
threads.each(&:join)
|
99
148
|
|
149
|
+
rehome_master
|
150
|
+
|
100
151
|
|
101
152
|
|
data/test/boot_mysql_env.rb
CHANGED
@@ -21,6 +21,7 @@ mysql_slave_2.connection.query("set global server_id=3")
|
|
21
21
|
|
22
22
|
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"
|
23
23
|
|
24
|
+
mysql_master.connection.query("CHANGE MASTER TO master_host='127.0.0.1', master_user='root', master_password=''")
|
24
25
|
mysql_slave.make_slave_of(mysql_master)
|
25
26
|
mysql_slave_2.make_slave_of(mysql_slave)
|
26
27
|
|
@@ -8,7 +8,7 @@ $mysql_slave.connection.query("set GLOBAL READ_ONLY=1")
|
|
8
8
|
|
9
9
|
puts "testing basic cutover..."
|
10
10
|
|
11
|
-
system "#{master_cut_script} 127.0.0.1:#{$mysql_master.port} 127.0.0.1:#{$mysql_slave.port} root ''"
|
11
|
+
system "#{master_cut_script} 127.0.0.1:#{$mysql_master.port} 127.0.0.1:#{$mysql_slave.port} root -p ''"
|
12
12
|
if $mysql_master.connection.query("select @@read_only as ro").first['ro'] != 1
|
13
13
|
puts "Master is not readonly!"
|
14
14
|
exit 1
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'mysql2'
|
3
|
+
require_relative '../boot_mysql_env'
|
4
|
+
master_cut_script = File.expand_path(File.dirname(__FILE__)) + "/../../bin/master_cut"
|
5
|
+
|
6
|
+
$mysql_master.connection.query("set GLOBAL READ_ONLY=0")
|
7
|
+
$mysql_slave.connection.query("set GLOBAL READ_ONLY=1")
|
8
|
+
|
9
|
+
def assert_ro(cx, str, bool)
|
10
|
+
expected = bool ? 1 : 0
|
11
|
+
if expected != cx.query("select @@read_only as ro").first['ro']
|
12
|
+
$stderr.puts("#{str} is #{bool ? 'read-write' : 'read-only'} but I expected otherwise!")
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
puts "testing first cutover..."
|
17
|
+
|
18
|
+
system "#{master_cut_script} 127.0.0.1:#{$mysql_master.port} 127.0.0.1:#{$mysql_slave.port} root -p '' -r"
|
19
|
+
assert_ro($mysql_master.connection, 'original master', true)
|
20
|
+
assert_ro($mysql_slave.connection, 'original slave', false)
|
21
|
+
|
22
|
+
system "#{master_cut_script} 127.0.0.1:#{$mysql_slave.port} 127.0.0.1:#{$mysql_master.port} root -p '' -r"
|
23
|
+
assert_ro($mysql_master.connection, 'original master', false)
|
24
|
+
assert_ro($mysql_slave.connection, 'original slave', true)
|
25
|
+
|
26
|
+
puts "everything went real nice."
|
27
|
+
|
@@ -20,7 +20,7 @@ thread = Thread.new {
|
|
20
20
|
end
|
21
21
|
}
|
22
22
|
|
23
|
-
system "#{master_cut_script} 127.0.0.1:#{$mysql_master.port} 127.0.0.1:#{$mysql_slave.port} root ''"
|
23
|
+
system "#{master_cut_script} 127.0.0.1:#{$mysql_master.port} 127.0.0.1:#{$mysql_slave.port} root -p ''"
|
24
24
|
|
25
25
|
thread.join
|
26
26
|
|
@@ -5,7 +5,7 @@ require_relative '../boot_mysql_env'
|
|
5
5
|
|
6
6
|
def assert_script_failed
|
7
7
|
master_cut_script = File.expand_path(File.dirname(__FILE__)) + "/../../bin/master_cut"
|
8
|
-
if system "#{master_cut_script} 127.0.0.1:#{$mysql_master.port} 127.0.0.1:#{$mysql_slave.port} root ''"
|
8
|
+
if system "#{master_cut_script} 127.0.0.1:#{$mysql_master.port} 127.0.0.1:#{$mysql_slave.port} root -p ''"
|
9
9
|
puts "Script returned ok instead of false!"
|
10
10
|
exit 1
|
11
11
|
end
|
@@ -50,6 +50,7 @@ class MysqlIsolatedServer
|
|
50
50
|
mysql_install_db = `which mysql_install_db`
|
51
51
|
idb_path = File.dirname(mysql_install_db)
|
52
52
|
system("(cd #{idb_path}/..; mysql_install_db --datadir=#{@mysql_data_dir}) >/dev/null 2>&1")
|
53
|
+
system("cp #{File.expand_path(File.dirname(__FILE__))}/user.MY* #{@mysql_data_dir}/mysql")
|
53
54
|
end
|
54
55
|
|
55
56
|
exec_server <<-EOL
|
data/test/user.MYD
ADDED
Binary file
|
data/test/user.MYI
ADDED
Binary file
|
data/test/user.frm
ADDED
Binary file
|
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.0
|
4
|
+
version: 0.1.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-02-
|
12
|
+
date: 2013-02-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mysql2
|
@@ -120,9 +120,13 @@ files:
|
|
120
120
|
- test/boot_mysql_env.rb
|
121
121
|
- test/boot_slave
|
122
122
|
- test/integration/no_traffic_test.rb
|
123
|
+
- test/integration/there_and_back_again_test.rb
|
123
124
|
- test/integration/with_queries_to_be_killed_test.rb
|
124
125
|
- test/integration/wrong_setup_test.rb
|
125
126
|
- test/mysql_isolated_server.rb
|
127
|
+
- test/user.MYD
|
128
|
+
- test/user.MYI
|
129
|
+
- test/user.frm
|
126
130
|
homepage: ''
|
127
131
|
licenses: []
|
128
132
|
post_install_message:
|
@@ -137,7 +141,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
137
141
|
version: '0'
|
138
142
|
segments:
|
139
143
|
- 0
|
140
|
-
hash:
|
144
|
+
hash: -2431803354911510217
|
141
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
146
|
none: false
|
143
147
|
requirements:
|
@@ -146,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
150
|
version: '0'
|
147
151
|
segments:
|
148
152
|
- 0
|
149
|
-
hash:
|
153
|
+
hash: -2431803354911510217
|
150
154
|
requirements: []
|
151
155
|
rubyforge_project:
|
152
156
|
rubygems_version: 1.8.24
|
@@ -158,6 +162,10 @@ test_files:
|
|
158
162
|
- test/boot_mysql_env.rb
|
159
163
|
- test/boot_slave
|
160
164
|
- test/integration/no_traffic_test.rb
|
165
|
+
- test/integration/there_and_back_again_test.rb
|
161
166
|
- test/integration/with_queries_to_be_killed_test.rb
|
162
167
|
- test/integration/wrong_setup_test.rb
|
163
168
|
- test/mysql_isolated_server.rb
|
169
|
+
- test/user.MYD
|
170
|
+
- test/user.MYI
|
171
|
+
- test/user.frm
|