repctl 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +80 -0
- data/Rakefile +1 -0
- data/bin/mysql.thor +431 -0
- data/config/bristlecone.properties +29 -0
- data/config/config.rb +50 -0
- data/config/mysql.cnf +149 -0
- data/config/servers.yml +45 -0
- data/lib/repctl/mysql_admin.rb +638 -0
- data/lib/repctl/servers.rb +48 -0
- data/lib/repctl/version.rb +3 -0
- data/lib/repctl.rb +9 -0
- data/repctl.gemspec +25 -0
- metadata +94 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Repctl - Manage Replication among a set of SQL Servers
|
2
|
+
|
3
|
+
`repctl` is a utility to configure, reconfigure, start, stop, crash, generate
|
4
|
+
workloads, dump, restore, benchmark, and monitor a set of SQL servers for
|
5
|
+
development environments. Replication relationships can be set up among server
|
6
|
+
instances with a single command. While running a load generator or benchmark,
|
7
|
+
the replication status, including current lag, can be seen in a continuously
|
8
|
+
updated display. A slave can be added to an existing server that already has
|
9
|
+
data.
|
10
|
+
|
11
|
+
The `repctl` gem includes a _Thor_ script that makes all the `repctl`
|
12
|
+
functionality available at the command line.
|
13
|
+
|
14
|
+
## Limitations
|
15
|
+
|
16
|
+
Currently only MySQL is supported but PostgresSQL will soon be added. All the
|
17
|
+
server instances must run on a single host. This restriction may be soon lifted
|
18
|
+
as well.
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
You will need to have a local installation installation of MySQL. You do not
|
23
|
+
need to do anything to configure the installation. For example, if you compile
|
24
|
+
MySQL from source, then do `make install`, then you are done! No MySQL
|
25
|
+
post-installation steps are necessary. All post-install configuration is
|
26
|
+
handled by `repctl`.
|
27
|
+
|
28
|
+
The top
|
29
|
+
|
30
|
+
== Available Commands
|
31
|
+
|
32
|
+
tethys:repctl mbs$ thor list
|
33
|
+
mysql
|
34
|
+
-----
|
35
|
+
thor mysql:change_master MASTER SLAVE FILE POSITION # Execute CHANGE MASTER TO on the SLAVE.
|
36
|
+
thor mysql:cluster_user INSTANCE # Create the cluster user account on a MySQL instance.
|
37
|
+
thor mysql:config INSTANCE # Initialize the data directory for a new instance.
|
38
|
+
thor mysql:config_all # Initialize the data directories for all instances.
|
39
|
+
thor mysql:crash INSTANCE # Crash a running MySQL server.
|
40
|
+
thor mysql:dump INSTANCE [DUMPFILE] # Dump all databases after FLUSH TABLES WITH READ LOCK
|
41
|
+
thor mysql:repl_user INSTANCE # Create the replication user account on a MySQL insta...
|
42
|
+
thor mysql:reset INSTANCE # Remove database and restart MySQL server.
|
43
|
+
thor mysql:reset_all # Remove all databases and restart MySQL instances.
|
44
|
+
thor mysql:restore INSTANCE [DUMPFILE] # Restore INSTANCE from a 'mysqldump' file DUMPFILE.
|
45
|
+
thor mysql:start_slave SLAVE # Issue START SLAVE on the SLAVE MySQL instance.
|
46
|
+
thor mysql:status # Show the status of replication.
|
47
|
+
thor mysql:stop INSTANCE # Stop a running MySQL server instance.
|
48
|
+
thor mysql:stop_all # Stop all the MySQL servers.
|
49
|
+
|
50
|
+
setup
|
51
|
+
-----
|
52
|
+
thor setup:add_slave MASTER SLAVE # Master has some data that is used to initialize the slave.
|
53
|
+
thor setup:repl_pair MASTER SLAVE # Set up a single master/slave replication pair from the very beginning.
|
54
|
+
|
55
|
+
utils
|
56
|
+
-----
|
57
|
+
thor utils:bench [INSTANCE] [PROPS] # Run the Tungsten Bristlecone benchmarker. The INSTAN...
|
58
|
+
thor utils:create_db [INSTANCE] [DBNAME] # "Create a database on a MySQL instance. INSTANCE de...
|
59
|
+
thor utils:create_tbl [INSTANCE] [DBNAME] [TBLNAME] # Create a database table. INSTANCE defaults to DEFAU...
|
60
|
+
thor utils:gen_rows [INSTANCE], [DBNAME], [TBLNAME] # Add rows to a table that was created by "utils:crea...
|
61
|
+
|
62
|
+
== Configuring Simple Repctl
|
63
|
+
|
64
|
+
This tool needs some configuration before you can use it.
|
65
|
+
|
66
|
+
You should have an valid MySQL installation and the Thor gem installed. Your existing MySQL server will not be affected by the +repctl+ script. However, binaries from this installation will be reused. In the +config.rb+ file set the constants:
|
67
|
+
|
68
|
+
* MYSQL_HOME -- the location of the local MySQL installation
|
69
|
+
* DATA_HOME -- the location of the directory where per-MySQL server data directories are created.
|
70
|
+
* DUMP_DIR -- the location where you want dump files to be stored
|
71
|
+
* RELAY_LOG -- adjust this according to your hostname
|
72
|
+
|
73
|
+
Next, define the potential instances you want to create. Edit the <tt>servers.yml</tt> file as appropriate.
|
74
|
+
|
75
|
+
Finally, edit the existing <tt>my*.cnf*</tt> files to at least have the correct <tt>datadir</tt> defined.
|
76
|
+
|
77
|
+
== Using Simple Repctl
|
78
|
+
|
79
|
+
You are now ready to rock. Run <tt>thor mysql:start_all</tt> to start up all the servers listed in your +servers.yml+ file. Instead or subsequently, you can run <tt>thor setup:repl_pair 1 2</tt> to reset everything and create a master/slave replication pair. Start up some load to the MySQL master (at socket +/tmp/mysql1.sock+, by default), then watch the replication status change by running <tt>thor mysql:status -s 1 2 -c 5</tt> to see a continuous update of the status, updated every 5 seconds. Add a new slave using instance +3+, which may or may not be running and may or may not have its data directory initialized, by running <tt>thor setup:add_slave 1 3</tt>. This does a dump on the master and a restore on the slave and restarts replication using the proper replication coordinates.
|
80
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/mysql.thor
ADDED
@@ -0,0 +1,431 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'repctl'
|
3
|
+
|
4
|
+
# Thor has the 'helpful' property that any Thor task gets executed only
|
5
|
+
# once. The 'Helpers' module makes many Thor tasks available as ordinary
|
6
|
+
# Ruby functions for internal use. The corresponding Thor tasks usually
|
7
|
+
# delegate to the corresponding helper function with 'super'.
|
8
|
+
module Helpers
|
9
|
+
|
10
|
+
include Repctl::Config
|
11
|
+
include Repctl::Servers
|
12
|
+
|
13
|
+
def start(instance)
|
14
|
+
say "Starting instance #{instance}.", :green
|
15
|
+
do_start(instance)
|
16
|
+
end
|
17
|
+
|
18
|
+
def start_all
|
19
|
+
all_instances.each do |instance|
|
20
|
+
start(instance)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def stop(instance)
|
25
|
+
say "Stopping instance #{instance}.", :green
|
26
|
+
do_admin(instance, "shutdown")
|
27
|
+
end
|
28
|
+
|
29
|
+
def stop_all
|
30
|
+
all_live_instances.each do |instance|
|
31
|
+
stop(instance)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def config(instance)
|
36
|
+
say "Initializing new data directory for instance #{instance}."
|
37
|
+
do_config(instance)
|
38
|
+
end
|
39
|
+
|
40
|
+
def config_all
|
41
|
+
all_instances.each do |instance|
|
42
|
+
config(instance)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def secure_accounts(instance)
|
47
|
+
do_secure_accounts(instance)
|
48
|
+
end
|
49
|
+
|
50
|
+
def secure_accounts_all
|
51
|
+
Repctl::Servers.all_live_instances.each do |instance|
|
52
|
+
secure_accounts(instance)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def reset(instance)
|
57
|
+
stop(instance)
|
58
|
+
config(instance)
|
59
|
+
start(instance)
|
60
|
+
secure_accounts(instance)
|
61
|
+
end
|
62
|
+
|
63
|
+
def restart(instance)
|
64
|
+
stop(instance)
|
65
|
+
start(instance)
|
66
|
+
end
|
67
|
+
|
68
|
+
def reset_all
|
69
|
+
all_instances.each do |instance|
|
70
|
+
reset(instance)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def change_master(master, slave, file, position)
|
75
|
+
say "Changing master: master = #{master}, slave = #{slave}, file = #{file}, position = #{position}"
|
76
|
+
do_change_master(master, slave, :file => file, :position => position)
|
77
|
+
end
|
78
|
+
|
79
|
+
def start_slave(slave)
|
80
|
+
say "Starting slave #{slave}", :green
|
81
|
+
run_mysql_query(slave, "START SLAVE")
|
82
|
+
end
|
83
|
+
|
84
|
+
def crash(instance)
|
85
|
+
say "Crashing instance #{instance}", :red
|
86
|
+
do_crash(instance)
|
87
|
+
end
|
88
|
+
|
89
|
+
def repl_user(instance)
|
90
|
+
say "Creating replication account on instance #{instance}.", :green
|
91
|
+
do_repl_user(instance)
|
92
|
+
end
|
93
|
+
|
94
|
+
def cluster_user(instance = 1)
|
95
|
+
say "Installing cluster user for instance #{instance}.", :green
|
96
|
+
do_cluster_user(instance)
|
97
|
+
end
|
98
|
+
|
99
|
+
end # Module helpers
|
100
|
+
|
101
|
+
class Mysql < Thor
|
102
|
+
|
103
|
+
include Thor::Actions
|
104
|
+
include Repctl::Config
|
105
|
+
include Repctl::Commands
|
106
|
+
include Repctl::Servers
|
107
|
+
include Helpers
|
108
|
+
|
109
|
+
desc "start INSTANCE", "Ensure that the given MySQL server instance is running."
|
110
|
+
def start(instance)
|
111
|
+
super
|
112
|
+
end
|
113
|
+
|
114
|
+
desc "start_all", "Start all the MySQL instances."
|
115
|
+
def start_all
|
116
|
+
super
|
117
|
+
end
|
118
|
+
|
119
|
+
desc "stop INSTANCE", "Stop a running MySQL server instance."
|
120
|
+
def stop(instance)
|
121
|
+
super
|
122
|
+
end
|
123
|
+
|
124
|
+
desc "stop_all", "Stop all the MySQL servers."
|
125
|
+
def stop_all
|
126
|
+
super
|
127
|
+
end
|
128
|
+
|
129
|
+
desc "config INSTANCE", "Initialize the data directory for a new instance."
|
130
|
+
def config(instance)
|
131
|
+
super
|
132
|
+
end
|
133
|
+
|
134
|
+
desc "config_all", "Initialize the data directories for all instances."
|
135
|
+
def config_all
|
136
|
+
super
|
137
|
+
end
|
138
|
+
|
139
|
+
desc "secure_accounts INSTANCE", "Add passwords for root and anonymous accounts."
|
140
|
+
def secure_accounts(instance)
|
141
|
+
super
|
142
|
+
end
|
143
|
+
|
144
|
+
desc "secure_accounts_all", "Add passwords for root and anonymous accounts for all instances."
|
145
|
+
def secure_accounts_all
|
146
|
+
super
|
147
|
+
end
|
148
|
+
|
149
|
+
desc "reset", "Remove the database data directory, reconfigure and restart the instance."
|
150
|
+
def reset(instance)
|
151
|
+
super
|
152
|
+
end
|
153
|
+
|
154
|
+
desc "reset_all", "Remove all databases and restart MySQL instances."
|
155
|
+
def reset_all
|
156
|
+
super
|
157
|
+
end
|
158
|
+
|
159
|
+
desc "start_slave SLAVE", "Issue START SLAVE on the SLAVE MySQL instance."
|
160
|
+
def start_slave(slave)
|
161
|
+
super
|
162
|
+
end
|
163
|
+
|
164
|
+
desc "change_master MASTER SLAVE FILE POSITION", "Execute CHANGE MASTER TO on the SLAVE."
|
165
|
+
def change_master(master, slave, file, position)
|
166
|
+
super
|
167
|
+
end
|
168
|
+
|
169
|
+
desc "crash INSTANCE", "Crash a running MySQL server."
|
170
|
+
def crash(instance)
|
171
|
+
super
|
172
|
+
end
|
173
|
+
|
174
|
+
desc "repl_user INSTANCE", "Create the replication user account on a MySQL instance."
|
175
|
+
def repl_user(instance)
|
176
|
+
super
|
177
|
+
end
|
178
|
+
|
179
|
+
desc "cluster_user INSTANCE", "Create the cluster user account on a MySQL instance."
|
180
|
+
def cluster_user(instance)
|
181
|
+
super
|
182
|
+
end
|
183
|
+
|
184
|
+
desc "repl_status", "Show the status of replication."
|
185
|
+
method_option :continuous, :aliases => "-c", :type => :numeric,
|
186
|
+
:desc => "Continuous output at specified interval (in seconds)."
|
187
|
+
method_option :servers, :aliases => "-s", :type => :array,
|
188
|
+
:desc => "Only check the status of given servers."
|
189
|
+
def repl_status
|
190
|
+
todos = options[:servers] || all_live_instances
|
191
|
+
unless todos.any?
|
192
|
+
say "No Running Servers.", :blue
|
193
|
+
return
|
194
|
+
end
|
195
|
+
header = sprintf("%-5s%-25s%-25s%-25s%-8s\n",
|
196
|
+
"inst", "master", "received", "applied", "lag")
|
197
|
+
|
198
|
+
loop do
|
199
|
+
say header, :blue
|
200
|
+
|
201
|
+
todos.each do |i|
|
202
|
+
coordinates = get_coordinates(i)
|
203
|
+
slave_status = get_slave_status(i)
|
204
|
+
is_slave = !(slave_status["Error"] == "MySQL server is not a slave.")
|
205
|
+
master_file = coordinates[:file]
|
206
|
+
master_pos = coordinates[:position]
|
207
|
+
if is_slave
|
208
|
+
recv_file = slave_status["Master_Log_File"]
|
209
|
+
recv_pos = slave_status["Read_Master_Log_Pos"]
|
210
|
+
apply_file = slave_status["Relay_Master_Log_File"]
|
211
|
+
apply_pos = slave_status["Exec_Master_Log_Pos"]
|
212
|
+
lag = slave_status["Seconds_Behind_Master"]
|
213
|
+
end
|
214
|
+
|
215
|
+
format = "%-5d%16s:%-8d"
|
216
|
+
if is_slave
|
217
|
+
if lag
|
218
|
+
lag = lag.to_s
|
219
|
+
else
|
220
|
+
lag = "-"
|
221
|
+
end
|
222
|
+
format += "%16s:%-8d%16s:%-8d%-8s"
|
223
|
+
str = sprintf(format, i, master_file, master_pos, recv_file, recv_pos,
|
224
|
+
apply_file, apply_pos, lag)
|
225
|
+
else
|
226
|
+
str = sprintf(format, i, master_file, master_pos)
|
227
|
+
end
|
228
|
+
|
229
|
+
say str + "\n", :yellow
|
230
|
+
end
|
231
|
+
break unless options[:continuous]
|
232
|
+
sleep options[:continuous]
|
233
|
+
say ""
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
desc "dump INSTANCE [DUMPFILE]", "Dump all databases after FLUSH TABLES WITH READ LOCK"
|
238
|
+
def dump(instance, dumpfile = DEFAULT_DUMPFILE)
|
239
|
+
coordinates = do_dump(instance, dumpfile)
|
240
|
+
file = coordinates[:file]
|
241
|
+
position = coordinates[:position]
|
242
|
+
puts "(#{file}, #{position})"
|
243
|
+
[file, String(position)]
|
244
|
+
end
|
245
|
+
|
246
|
+
desc "restore INSTANCE [DUMPFILE]", "Restore INSTANCE from a \'mysqldump\' file DUMPFILE."
|
247
|
+
def restore(slave, dumpfile = DEFAULT_DUMPFILE)
|
248
|
+
do_restore(slave, dumpfile)
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
class Utils < Thor
|
254
|
+
|
255
|
+
include Thor::Actions
|
256
|
+
include Repctl::Config
|
257
|
+
include Repctl::Commands
|
258
|
+
include Repctl::Servers
|
259
|
+
include Helpers
|
260
|
+
|
261
|
+
DEFAULT_MASTER = 1
|
262
|
+
|
263
|
+
desc "bench [INSTANCE] [PROPS]", "Run the Tungsten Bristlecone benchmarker.
|
264
|
+
The INSTANCE specifies the instance to perform all operations to, and PROPS
|
265
|
+
is the properties file to use. The INSTANCE defaults to #{DEFAULT_MASTER} and
|
266
|
+
the properties file defaults to #{BENCHMARK_PROPERTIES}."
|
267
|
+
def bench(instance = DEFAULT_MASTER, props = nil)
|
268
|
+
props ||= BENCHMARK_PROPERTIES
|
269
|
+
invoke :create_db, [instance, "widgets"]
|
270
|
+
run("#{BENCHMARK} -props #{props}", :verbose => true, :capture => false)
|
271
|
+
end
|
272
|
+
|
273
|
+
desc "create_db [INSTANCE] [DBNAME]", <<-EOS
|
274
|
+
"Create a database on a MySQL instance. INSTANCE defaults to DEFAULT_MASTER,
|
275
|
+
and DBNAME defaults to "widgets".
|
276
|
+
EOS
|
277
|
+
method_option :replace, :type => :boolean, :aliases => "-r",
|
278
|
+
:desc => "drop and recreate the database"
|
279
|
+
def create_db(instance = DEFAULT_MASTER, dbname = "widgets")
|
280
|
+
run_mysql_query(instance, "DROP DATABASE IF EXISTS #{dbname}") if options[:replace]
|
281
|
+
run_mysql_query(instance, "CREATE DATABASE IF NOT EXISTS #{dbname}")
|
282
|
+
end
|
283
|
+
|
284
|
+
desc "create_tbl [INSTANCE] [DBNAME] [TBLNAME]", <<-EOS
|
285
|
+
Create a database table. INSTANCE defaults to DEFAULT_MASTER, DBNAME defaults
|
286
|
+
to "widgets" and TBLNAME defaults to "users". The table schema is fixed.
|
287
|
+
EOS
|
288
|
+
method_option :replace, :type => :boolean, :aliases => "-r",
|
289
|
+
:desc => "drop and recreate the table"
|
290
|
+
def create_tbl(instance = DEFAULT_MASTER, dbname = "widgets", tblname = "users")
|
291
|
+
invoke :create_db, [instance, dbname], :replace => false
|
292
|
+
run_mysql_query(instance,
|
293
|
+
"DROP TABLE IF EXISTS #{dbname}.#{tblname}") if options[:replace]
|
294
|
+
cmd = <<-EOS
|
295
|
+
CREATE TABLE #{dbname}.#{tblname} (
|
296
|
+
id INT NOT NULL,
|
297
|
+
last_name CHAR(30) NOT NULL,
|
298
|
+
first_name CHAR(30) NOT NULL,
|
299
|
+
credentials VARCHAR(32768) NOT NULL,
|
300
|
+
PRIMARY KEY (id),
|
301
|
+
INDEX name (last_name,first_name)
|
302
|
+
)
|
303
|
+
EOS
|
304
|
+
run_mysql_query(instance, cmd)
|
305
|
+
end
|
306
|
+
|
307
|
+
desc "gen_rows [INSTANCE], [DBNAME], [TBLNAME]", <<-EOS
|
308
|
+
Add rows to a table that was created by "utils:create_tbl". INSTANCE defaults
|
309
|
+
to DEFAULT_MASTER, DBNAME defaults to "widgets", and TBLNAME defaults to "users".
|
310
|
+
EOS
|
311
|
+
method_option :delay, :type => :numeric, :aliases => "-d", :default => 0,
|
312
|
+
:desc => "sleep for the specified number of milliseconds between row inserts."
|
313
|
+
method_option :count, :type => :numeric, :aliases => "-c", :default => 1000,
|
314
|
+
:desc => "number of rows to insert"
|
315
|
+
method_option :size, :type => :numeric, :aliases => "-s", :default => 100,
|
316
|
+
:desc => "the approximate size of the record to insert (in bytes)."
|
317
|
+
method_option :forever, :type => :boolean, :aliases => "-f",
|
318
|
+
:desc => "run forever, ignoring COUNT option."
|
319
|
+
method_option :verbose, :type => :boolean, :aliases => "-v",
|
320
|
+
:desc => "print a '.' for each row inserted."
|
321
|
+
def gen_rows(instance = DEFAULT_MASTER, dbname = "widgets", tblname = "users")
|
322
|
+
invoke :create_tbl, [instance, dbname], :replace => true
|
323
|
+
size = options[:size]
|
324
|
+
size ||= 100
|
325
|
+
size = [size, 32768].min
|
326
|
+
data = IO.read("#{Mysql::DATA_HOME}/words.txt", size)
|
327
|
+
id = 1
|
328
|
+
count = 0
|
329
|
+
|
330
|
+
loop do
|
331
|
+
cmd = <<-EOS
|
332
|
+
INSERT INTO #{dbname}.#{tblname} VALUES (
|
333
|
+
#{id},
|
334
|
+
'Fillmore',
|
335
|
+
'Millard',
|
336
|
+
'#{data}'
|
337
|
+
)
|
338
|
+
EOS
|
339
|
+
run_mysql_query(instance, cmd)
|
340
|
+
putc "." if options[:verbose]
|
341
|
+
id += 1
|
342
|
+
count += 1
|
343
|
+
break if (count >= options[:count] and (not options[:forever]))
|
344
|
+
msecs = options[:delay]
|
345
|
+
sleep(msecs / 1000.0) if msecs > 0
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|
351
|
+
|
352
|
+
class Setup < Thor
|
353
|
+
|
354
|
+
include ::Thor::Actions
|
355
|
+
include Repctl::Config
|
356
|
+
include Repctl::Commands
|
357
|
+
include Repctl::Servers
|
358
|
+
include Helpers
|
359
|
+
|
360
|
+
#
|
361
|
+
# Setting Up Replication with New Master and Slaves.
|
362
|
+
# Here, we stop all MySQL servers, remove the data directories, reinitialize
|
363
|
+
# the data directories, restart the servers, and set up a master/slave
|
364
|
+
# relationship.
|
365
|
+
#
|
366
|
+
desc "repl_pair MASTER SLAVE",
|
367
|
+
"Set up a single master/slave replication pair from the very beginning."
|
368
|
+
def repl_pair(master, slave)
|
369
|
+
say "master is #{master}, slave is #{slave}", :green
|
370
|
+
reset(master)
|
371
|
+
reset(slave)
|
372
|
+
cluster_user(master)
|
373
|
+
repl_user(master)
|
374
|
+
coordinates = get_coordinates(master)
|
375
|
+
file = coordinates[:file]
|
376
|
+
position = coordinates[:position]
|
377
|
+
say "File is #{file}, Position is #{position}", :green
|
378
|
+
change_master(master, slave, file, position)
|
379
|
+
start_slave(slave)
|
380
|
+
end
|
381
|
+
|
382
|
+
desc "repl_trio MASTER SLAVE1 SLAVE2",
|
383
|
+
"Set up a single master and two slave replication cluster."
|
384
|
+
method_option :reset, :type => :boolean, :default => true
|
385
|
+
def repl_trio(master, slave1, slave2)
|
386
|
+
say "master is #{master}, slaves are #{slave1} and #{slave2}", :green
|
387
|
+
if options[:reset]
|
388
|
+
reset(master)
|
389
|
+
reset(slave1)
|
390
|
+
reset(slave2)
|
391
|
+
cluster_user(master)
|
392
|
+
repl_user(master)
|
393
|
+
else
|
394
|
+
restart(master)
|
395
|
+
restart(slave1)
|
396
|
+
restart(slave2)
|
397
|
+
end
|
398
|
+
coordinates = get_coordinates(master)
|
399
|
+
file = coordinates[:file]
|
400
|
+
position = coordinates[:position]
|
401
|
+
say "File is #{file}, Position is #{position}", :green
|
402
|
+
change_master(master, slave1, file, position)
|
403
|
+
start_slave(slave1)
|
404
|
+
change_master(master, slave2, file, position)
|
405
|
+
start_slave(slave2)
|
406
|
+
end
|
407
|
+
|
408
|
+
#
|
409
|
+
# Setting Up Replication with Existing Data using the 'mysqldump' utility. The
|
410
|
+
# master has existing data.
|
411
|
+
#
|
412
|
+
desc "add_slave MASTER SLAVE", "Master has some data that is used to initialize the slave."
|
413
|
+
method_option :populate, :type => :boolean, :default => false
|
414
|
+
def add_slave(master, slave)
|
415
|
+
reset(slave)
|
416
|
+
|
417
|
+
if options[:populate]
|
418
|
+
invoke "utils:bench", [master, "/opt/MySQL/b2.properties"]
|
419
|
+
end
|
420
|
+
file, position = invoke "mysql:dump", [master]
|
421
|
+
|
422
|
+
# Slave is running, but it is not yet configured as a slave.
|
423
|
+
# Load slave from the dump file...
|
424
|
+
invoke "mysql:restore", [slave]
|
425
|
+
|
426
|
+
change_master(master, slave, file, position)
|
427
|
+
start_slave(slave)
|
428
|
+
end
|
429
|
+
|
430
|
+
end
|
431
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
url=jdbc:mysql://localhost:3307/widgets
|
2
|
+
user=cluster
|
3
|
+
password=secret
|
4
|
+
type=MySQL 5.6.2-m5
|
5
|
+
analyzeCmd=select 1
|
6
|
+
|
7
|
+
# Benchmark test to compare clustered and non-clustered write performance.
|
8
|
+
#
|
9
|
+
# We execute INSERT statements with varying numbers of clients and tables.
|
10
|
+
#
|
11
|
+
# To invoke this test try the following command.
|
12
|
+
# $benchmark.sh -props WriteSimpleScenario.properties
|
13
|
+
|
14
|
+
# Scenario name.
|
15
|
+
scenario=com.continuent.bristlecone.benchmark.scenarios.WriteSimpleScenario
|
16
|
+
|
17
|
+
# Database connection information.
|
18
|
+
# include=connection_postgresql.properties|connection_pcluster.properties
|
19
|
+
|
20
|
+
# Test duration and number of threads.
|
21
|
+
bound=duration
|
22
|
+
duration=60
|
23
|
+
threads=1|10|20
|
24
|
+
|
25
|
+
# Database table information.
|
26
|
+
tables=1|16
|
27
|
+
datatype=varchar
|
28
|
+
datawidth=100
|
29
|
+
datarows=100
|
data/config/config.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Repctl
|
2
|
+
|
3
|
+
module Config
|
4
|
+
|
5
|
+
# The location of the local MySQL installation.
|
6
|
+
MYSQL_HOME = "/usr/local/mysql"
|
7
|
+
|
8
|
+
ROOT_PASSWORD = "har526"
|
9
|
+
|
10
|
+
SERVER_CONFIG = File.expand_path("../servers.yml", __FILE__)
|
11
|
+
|
12
|
+
# Define the directory where subdirectores for data will be created
|
13
|
+
# for each MySQL server instance. This should agree with the
|
14
|
+
# per server'data_dir' property in the servers.yml file, and it should
|
15
|
+
# also agree with the 'datadir' property in the server's configuration
|
16
|
+
# file (my*.cnf).
|
17
|
+
DATA_HOME = "/opt/MySQL/instances"
|
18
|
+
|
19
|
+
# The home directory of Continuent's open source replicator. Eventually,
|
20
|
+
# a command will be added to this script to switch between MySQL native
|
21
|
+
# replication and Continuent's open-source Tungsten replicator.
|
22
|
+
REPLICATOR_HOME = "/opt/continuent/replicator"
|
23
|
+
|
24
|
+
# For simplicity, we're using the load-generator/benchmarker that comes
|
25
|
+
# in the Continuent source package. This may be replaced with sql-bench
|
26
|
+
# from the MySQL source distribution.
|
27
|
+
BENCHMARK = "#{REPLICATOR_HOME}/bristlecone/bin/benchmark.sh"
|
28
|
+
BENCHMARK_PROPERTIES = File.expand_path("../bristlecone.properties", __FILE__)
|
29
|
+
|
30
|
+
# Set this to the directory where you want dump files to be stored.
|
31
|
+
DUMP_DIR = "#{DATA_HOME}/dump"
|
32
|
+
|
33
|
+
# The default name of the dump file in the DUMP_DIR directory.
|
34
|
+
DEFAULT_DUMPFILE = "dbdump.db"
|
35
|
+
|
36
|
+
# User name and password for the replication account, used only internally by
|
37
|
+
# the replication processes.
|
38
|
+
REPLICATION_USER = "repl"
|
39
|
+
REPLICATION_PASSWORD = "secret"
|
40
|
+
|
41
|
+
# Replication account is set up as %.#{REPLICATION_DOMAIN}.
|
42
|
+
REPLICATION_DOMAIN = "thirdmode.com"
|
43
|
+
|
44
|
+
# A minor convenience.
|
45
|
+
DEFAULT_MASTER = 1
|
46
|
+
|
47
|
+
# Typically, this is of the form #{HOSTNAME}-relay-bin'.
|
48
|
+
RELAY_LOG = "deimos-relay-bin"
|
49
|
+
end
|
50
|
+
end
|