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.
@@ -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