repctl 0.0.3 → 0.0.4
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/TODO +9 -0
- data/bin/repctl +45 -17
- data/config/mysql.cnf +3 -0
- data/lib/repctl/mysql_admin.rb +42 -73
- data/lib/repctl/servers.rb +9 -0
- data/lib/repctl/version.rb +1 -1
- metadata +9 -8
data/TODO
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
1. Add a slave to an existing slave without
|
2
|
+
breaking the first slave's relationship to
|
3
|
+
its master.
|
4
|
+
|
5
|
+
Use an option to add_slave?
|
6
|
+
|
7
|
+
2. If you stop a slave and then restart it, it
|
8
|
+
becomes a slave of instance 1, even if it was
|
9
|
+
slaving off a different master before the shutdown.
|
data/bin/repctl
CHANGED
@@ -22,6 +22,10 @@ module Repctl
|
|
22
22
|
run_mysql_query(instance, "START SLAVE")
|
23
23
|
end
|
24
24
|
|
25
|
+
def do_stop_slave(instance)
|
26
|
+
run_mysql_query(instance, "STOP SLAVE")
|
27
|
+
end
|
28
|
+
|
25
29
|
def do_restart(instance)
|
26
30
|
do_admin(instance, "shutown")
|
27
31
|
do_start(instance)
|
@@ -141,7 +145,7 @@ class RepctlCmds < Thor
|
|
141
145
|
end
|
142
146
|
end
|
143
147
|
|
144
|
-
desc "restart INSTANCE"
|
148
|
+
desc "restart INSTANCE", "Stop and start a server instance."
|
145
149
|
def restart(instance)
|
146
150
|
say "Restarting instance #{instance}", :green
|
147
151
|
do_stop(instance)
|
@@ -153,6 +157,12 @@ class RepctlCmds < Thor
|
|
153
157
|
say "Starting slave #{slave}", :green
|
154
158
|
do_start_slave(slave)
|
155
159
|
end
|
160
|
+
|
161
|
+
desc "stop_slave SLAVE", "Issue STOP SLAVE on the SLAVE MySQL instance."
|
162
|
+
def stop_slave(slave)
|
163
|
+
say "Stopping slave #{slave}", :green
|
164
|
+
do_stop_slave(slave)
|
165
|
+
end
|
156
166
|
|
157
167
|
desc "change_master MASTER SLAVE FILE POSITION", "Execute CHANGE MASTER TO on the SLAVE."
|
158
168
|
def change_master(master, slave, file, position)
|
@@ -160,6 +170,12 @@ class RepctlCmds < Thor
|
|
160
170
|
do_change_master(master, slave, :file => file, :position => position)
|
161
171
|
end
|
162
172
|
|
173
|
+
desc "switch_master MASTER SLAVES", "Change the master of a running cluster."
|
174
|
+
def switch_master(master, *slaves)
|
175
|
+
do_switch_master(master, slaves)
|
176
|
+
say "Switching to master #{master}", :green
|
177
|
+
end
|
178
|
+
|
163
179
|
desc "crash INSTANCE", "Crash a running server."
|
164
180
|
def crash(instance)
|
165
181
|
say "Crashing instance #{instance}", :red
|
@@ -186,10 +202,10 @@ class RepctlCmds < Thor
|
|
186
202
|
def status
|
187
203
|
todos = options[:servers] || all_live_instances
|
188
204
|
unless todos.any?
|
189
|
-
say "No Running Servers.", :blue
|
205
|
+
say "No Running Servers. Are you running as the mysql user?", :blue
|
190
206
|
return
|
191
207
|
end
|
192
|
-
header = sprintf("%-5s%-
|
208
|
+
header = sprintf("%-5s%-27s%-27s%-27s%-8s\n",
|
193
209
|
"inst", "master", "received", "applied", "lag")
|
194
210
|
|
195
211
|
loop do
|
@@ -197,29 +213,32 @@ class RepctlCmds < Thor
|
|
197
213
|
|
198
214
|
todos.each do |i|
|
199
215
|
coordinates = get_coordinates(i)
|
200
|
-
slave_status = get_slave_status(i)
|
201
|
-
is_slave = !(slave_status["Error"] == "MySQL server is not a slave.")
|
202
216
|
master_file = coordinates[:file]
|
203
217
|
master_pos = coordinates[:position]
|
204
|
-
|
218
|
+
slave = is_slave?(i)
|
219
|
+
if slave
|
220
|
+
slave_status = get_slave_status(i)
|
205
221
|
recv_file = slave_status["Master_Log_File"]
|
206
222
|
recv_pos = slave_status["Read_Master_Log_Pos"]
|
207
223
|
apply_file = slave_status["Relay_Master_Log_File"]
|
208
224
|
apply_pos = slave_status["Exec_Master_Log_Pos"]
|
209
225
|
lag = slave_status["Seconds_Behind_Master"]
|
226
|
+
master_host = slave_status["Master_Host"]
|
227
|
+
master_port = slave_status["Master_Port"]
|
228
|
+
master_instance = instance_for(master_host, master_port)
|
210
229
|
end
|
211
|
-
|
212
|
-
|
213
|
-
if is_slave
|
230
|
+
|
231
|
+
if slave
|
214
232
|
if lag
|
215
233
|
lag = lag.to_s
|
216
234
|
else
|
217
235
|
lag = "-"
|
218
236
|
end
|
219
|
-
format
|
220
|
-
str = sprintf(format, i,
|
221
|
-
apply_file, apply_pos, lag)
|
237
|
+
format = "%1d%-4s%16s:%-10d%16s:%-10d%16s:%-10d%-8s"
|
238
|
+
str = sprintf(format, i, "(#{master_instance.to_s})", master_file,
|
239
|
+
master_pos, recv_file, recv_pos, apply_file, apply_pos, lag)
|
222
240
|
else
|
241
|
+
format = "%-5d%16s:%-10d"
|
223
242
|
str = sprintf(format, i, master_file, master_pos)
|
224
243
|
end
|
225
244
|
|
@@ -257,14 +276,18 @@ class RepctlCmds < Thor
|
|
257
276
|
if options[:populate]
|
258
277
|
invoke "utils:bench", [master, "/opt/MySQL/b2.properties"]
|
259
278
|
end
|
260
|
-
file, position = invoke "
|
279
|
+
file, position = invoke "dump", [master]
|
261
280
|
|
262
281
|
# Slave is running, but it is not yet configured as a slave.
|
263
282
|
# Load slave from the dump file...
|
264
|
-
invoke "
|
283
|
+
invoke "restore", [slave]
|
265
284
|
|
266
285
|
do_change_master(master, slave, :file => file, :position => position)
|
267
286
|
do_start_slave(slave)
|
287
|
+
|
288
|
+
do_cluster_user(slave)
|
289
|
+
do_repl_user(slave)
|
290
|
+
|
268
291
|
end
|
269
292
|
|
270
293
|
DEFAULT_MASTER = 1
|
@@ -383,13 +406,18 @@ class RepctlCmds < Thor
|
|
383
406
|
do_reset(master)
|
384
407
|
do_reset(slave1)
|
385
408
|
do_reset(slave2)
|
386
|
-
do_cluster_user(master)
|
387
|
-
do_repl_user(master)
|
388
409
|
else
|
389
410
|
do_restart(master)
|
390
411
|
do_restart(slave1)
|
391
412
|
do_restart(slave2)
|
392
413
|
end
|
414
|
+
# Set up the replication accounts for all servers, in case we
|
415
|
+
# decide to switch masters later.
|
416
|
+
[master, slave1, slave2].each do |s|
|
417
|
+
do_cluster_user(s)
|
418
|
+
do_repl_user(s)
|
419
|
+
end
|
420
|
+
|
393
421
|
coordinates = get_coordinates(master)
|
394
422
|
file = coordinates[:file]
|
395
423
|
position = coordinates[:position]
|
@@ -400,7 +428,7 @@ class RepctlCmds < Thor
|
|
400
428
|
do_start_slave(slave2)
|
401
429
|
end
|
402
430
|
|
403
|
-
desc "
|
431
|
+
desc "install_sample_configs DIRECTORY", "After initial install, use these "
|
404
432
|
"as templates for configuration. The target directory must exist and be empty."
|
405
433
|
def install_sample_configs(dest_dir)
|
406
434
|
source_dir = File.join(Repctl::Config.const_get(:GEM_HOME), "config")
|
data/config/mysql.cnf
CHANGED
@@ -39,6 +39,8 @@ query_cache_size= 16M
|
|
39
39
|
# Try number of CPU's*2 for thread_concurrency
|
40
40
|
thread_concurrency = 8
|
41
41
|
plugin-dir=/usr/local/mysql/lib/plugin
|
42
|
+
general-log=1
|
43
|
+
log-output=FILE # or TABLE
|
42
44
|
|
43
45
|
# Don't listen on a TCP/IP port at all. This can be a security enhancement,
|
44
46
|
# if all processes that need to connect to mysqld run on the same host.
|
@@ -52,6 +54,7 @@ plugin-dir=/usr/local/mysql/lib/plugin
|
|
52
54
|
# binary logging is required for replication
|
53
55
|
log-bin=mysql-bin
|
54
56
|
log-slave-updates
|
57
|
+
replicate-ignore-db = china
|
55
58
|
|
56
59
|
# binary logging format - mixed recommended
|
57
60
|
binlog_format=mixed
|
data/lib/repctl/mysql_admin.rb
CHANGED
@@ -82,6 +82,8 @@ module Repctl
|
|
82
82
|
# do_repl_user
|
83
83
|
# do_cluster_user
|
84
84
|
# do_create_widgets
|
85
|
+
# do_switch_master
|
86
|
+
# do_stop_slave
|
85
87
|
# get_coordinates
|
86
88
|
# get_mysqld_pid
|
87
89
|
# get_slave_status
|
@@ -186,7 +188,9 @@ module Repctl
|
|
186
188
|
"Relay_Master_Log_File",
|
187
189
|
"Exec_Master_Log_Pos",
|
188
190
|
"Relay_Log_File",
|
189
|
-
"Relay_Log_Pos"
|
191
|
+
"Relay_Log_Pos",
|
192
|
+
"Master_Host",
|
193
|
+
"Master_Port"
|
190
194
|
]
|
191
195
|
results = {}
|
192
196
|
status = do_slave_status(instance)
|
@@ -232,18 +236,18 @@ module Repctl
|
|
232
236
|
# set global innodb_max_dirty_pages_pct = 75;
|
233
237
|
#
|
234
238
|
|
235
|
-
def do_change_master(master, slave, coordinates)
|
239
|
+
def do_change_master(master, slave, coordinates, opts = {})
|
236
240
|
master_server = server_for_instance(master)
|
241
|
+
raise "master_server is nil" unless master_server
|
242
|
+
|
237
243
|
begin
|
238
244
|
slave_connection = Client.open(slave)
|
239
245
|
if slave_connection
|
240
246
|
|
241
|
-
# Replication on the slave can't be running if we want to
|
242
|
-
# CHANGE MASTER TO.
|
247
|
+
# Replication on the slave can't be running if we want to
|
248
|
+
# execute CHANGE MASTER TO.
|
243
249
|
slave_connection.query("STOP SLAVE") rescue Mysql2::Error
|
244
250
|
|
245
|
-
raise "master_server is nil" unless master_server
|
246
|
-
|
247
251
|
cmd = <<-EOT
|
248
252
|
CHANGE MASTER TO
|
249
253
|
MASTER_HOST = \'#{master_server['hostname']}\',
|
@@ -261,7 +265,10 @@ EOT
|
|
261
265
|
rescue Mysql2::Error => e
|
262
266
|
puts e.message
|
263
267
|
ensure
|
264
|
-
|
268
|
+
if slave_connection
|
269
|
+
slave_connection.query("START SLAVE") if opts[:restart]
|
270
|
+
slave_connection.close
|
271
|
+
end
|
265
272
|
end
|
266
273
|
end
|
267
274
|
|
@@ -416,6 +423,31 @@ EOT
|
|
416
423
|
Integer(File.open(pidfile, &:readline).strip)
|
417
424
|
end
|
418
425
|
|
426
|
+
# 'master' is currently a slave that is to be the new master.
|
427
|
+
# 'slaves' contains the list of slaves, one of these may be the
|
428
|
+
# current master.
|
429
|
+
def do_switch_master(master, slaves)
|
430
|
+
master = master.to_i
|
431
|
+
slaves = slaves.map(&:to_i)
|
432
|
+
|
433
|
+
# Step 1. Make sure all slaves have completely processed their
|
434
|
+
# Relay Log.
|
435
|
+
slaves.each do |s|
|
436
|
+
# This will also stop the slave threads.
|
437
|
+
drain_relay_log(s) if is_slave?(s)
|
438
|
+
end
|
439
|
+
|
440
|
+
# Step 2. For the slave being promoted to master, issue STOP SLAVE
|
441
|
+
# and RESET MASTER. Get the coordinates of its binlog.
|
442
|
+
promote_slave_to_master(master)
|
443
|
+
coordinates = get_coordinates(master)
|
444
|
+
|
445
|
+
# Step 3. Change the master for the other slaves.
|
446
|
+
slaves.each do |s|
|
447
|
+
do_change_master(master, s, coordinates, :restart => true)
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
419
451
|
private
|
420
452
|
|
421
453
|
# This is an example template to create commands to issue queries.
|
@@ -433,54 +465,13 @@ EOT
|
|
433
465
|
client.close if client
|
434
466
|
end
|
435
467
|
|
436
|
-
# 'master' is the new master
|
437
|
-
# 'slaves' is contains the list of slaves, one of these may be the current master.
|
438
|
-
def switch_master_to(master, slaves)
|
439
|
-
|
440
|
-
slaves = Array(slaves)
|
441
|
-
|
442
|
-
# Step 1. Make sure all slaves have completely processed their
|
443
|
-
# Relay Log.
|
444
|
-
slaves.each do |s|
|
445
|
-
puts "Draining relay log for slave instance #{s}"
|
446
|
-
drain_relay_log(s) if is_slave?(s)
|
447
|
-
end
|
448
|
-
|
449
|
-
# Step 2. For the slave being promoted to master, issue STOP SLAVE
|
450
|
-
# and RESET MASTER.
|
451
|
-
client = Client.open(master)
|
452
|
-
client.query("STOP SLAVE")
|
453
|
-
client.query("RESET MASTER")
|
454
|
-
client.close
|
455
|
-
|
456
|
-
# Step 3. Change the master for the other slaves (and the former master?)
|
457
|
-
# XXX this is not complete -- what about the coordinates?
|
458
|
-
master_server = server_for_instance(master)
|
459
|
-
master_host = 'localhost' # should be master_server[:hostname]
|
460
|
-
master_user = 'repl'
|
461
|
-
master_password = 'har526'
|
462
|
-
master_port = master_server[:port]
|
463
|
-
slaves.each do |s|
|
464
|
-
client = Client.open(s)
|
465
|
-
cmd = <<-EOT
|
466
|
-
CHANGE MASTER TO
|
467
|
-
MASTER_HOST=\'#{master_host}\',
|
468
|
-
MASTER_PORT=#{master_port},
|
469
|
-
MASTER_USER=\'#{master_user}\',
|
470
|
-
MASTER_PASSWORD=\'#{master_password}\'
|
471
|
-
EOT
|
472
|
-
client.query(cmd)
|
473
|
-
client.query("START SLAVE")
|
474
|
-
client.close
|
475
|
-
end
|
476
|
-
end
|
477
468
|
|
478
469
|
def is_master?(instance)
|
479
470
|
get_slave_coordinates(instance).empty?
|
480
471
|
end
|
481
472
|
|
482
473
|
def is_slave?(instance)
|
483
|
-
|
474
|
+
get_slave_status(instance).has_key?("Slave_IO_State")
|
484
475
|
end
|
485
476
|
|
486
477
|
def find_masters()
|
@@ -513,7 +504,6 @@ EOT
|
|
513
504
|
client.close if client
|
514
505
|
end
|
515
506
|
|
516
|
-
# unused
|
517
507
|
def promote_slave_to_master(instance)
|
518
508
|
client = Client.open(instance)
|
519
509
|
if client
|
@@ -566,7 +556,8 @@ EOT
|
|
566
556
|
end
|
567
557
|
end
|
568
558
|
puts "Waiting for slave to read relay log." unless done
|
569
|
-
end
|
559
|
+
end
|
560
|
+
client.query("STOP SLAVE")
|
570
561
|
else
|
571
562
|
puts "Could not open connection to instance #{instance}."
|
572
563
|
end
|
@@ -574,24 +565,6 @@ EOT
|
|
574
565
|
client.close if client
|
575
566
|
end
|
576
567
|
|
577
|
-
=begin
|
578
|
-
# This is an example template to create commands to issue queries.
|
579
|
-
def do_slave_status(instance)
|
580
|
-
client = Client.open(instance)
|
581
|
-
if client
|
582
|
-
results = client.query("SHOW SLAVE STATUS")
|
583
|
-
results.each_with_index do |index, line|
|
584
|
-
puts "#{index}: #{line}"
|
585
|
-
end
|
586
|
-
else
|
587
|
-
puts "Could not open connection to MySQL instance #{instance}."
|
588
|
-
end
|
589
|
-
rescue Mysql2::Error => e
|
590
|
-
puts e.message
|
591
|
-
ensure
|
592
|
-
client.close if client
|
593
|
-
end
|
594
|
-
=end
|
595
568
|
#
|
596
569
|
# Get the status of replication for the master and all slaves.
|
597
570
|
# Return an array of hashes, each hash has the form:
|
@@ -629,10 +602,6 @@ EOT
|
|
629
602
|
end
|
630
603
|
end
|
631
604
|
|
632
|
-
# STOP SLAVE
|
633
|
-
# RESET MASTER
|
634
|
-
# CHANGE MASTER TO
|
635
|
-
|
636
605
|
class RunExamples
|
637
606
|
include Repctl::Commands
|
638
607
|
|
data/lib/repctl/servers.rb
CHANGED
@@ -34,6 +34,15 @@ module Repctl
|
|
34
34
|
get_mysqld_pid(instance)
|
35
35
|
end
|
36
36
|
|
37
|
+
def instance_for(host, port)
|
38
|
+
@servers.each do |s|
|
39
|
+
if s["hostname"] == host && s["port"].to_i == port.to_i
|
40
|
+
return s["instance"].to_i
|
41
|
+
end
|
42
|
+
end
|
43
|
+
return nil
|
44
|
+
end
|
45
|
+
|
37
46
|
private
|
38
47
|
|
39
48
|
# Return the process ID (pid) for an instance.
|
data/lib/repctl/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: repctl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-02-17 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
16
|
-
requirement: &
|
16
|
+
requirement: &76697300 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *76697300
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: thor
|
27
|
-
requirement: &
|
27
|
+
requirement: &76696880 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *76696880
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: mysql2
|
38
|
-
requirement: &
|
38
|
+
requirement: &76696460 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *76696460
|
47
47
|
description: Ruby gem with Thor script to manage MySQL and PostgreSQL replication
|
48
48
|
email:
|
49
49
|
- lydianblues@gmail.com
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- Gemfile
|
57
57
|
- README.md
|
58
58
|
- Rakefile
|
59
|
+
- TODO
|
59
60
|
- bin/repctl
|
60
61
|
- config/bristlecone.properties
|
61
62
|
- config/config.rb
|