ar_mysql_flexmaster 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.8"
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
- $old_master, $new_master, $username, $password = *ARGV
10
- unless $old_master && $new_master && $username && $password
11
- puts "Usage: master_cut OLD_MASTER NEW_MASTER USERNAME PASSWORD"
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
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/ben/src/ar_mysql_flexmaster
3
3
  specs:
4
- ar_mysql_flexmaster (0.0.5)
4
+ ar_mysql_flexmaster (0.0.8)
5
5
  activerecord
6
6
  activesupport
7
7
  mysql2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/ben/src/ar_mysql_flexmaster
3
3
  specs:
4
- ar_mysql_flexmaster (0.0.5)
4
+ ar_mysql_flexmaster (0.0.8)
5
5
  activerecord
6
6
  activesupport
7
7
  mysql2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/ben/src/ar_mysql_flexmaster
3
3
  specs:
4
- ar_mysql_flexmaster (0.0.5)
4
+ ar_mysql_flexmaster (0.0.8)
5
5
  activerecord
6
6
  activesupport
7
7
  mysql2
@@ -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.8
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-06 00:00:00.000000000 Z
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: 1068549902361046667
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: 1068549902361046667
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