repctl 0.0.1
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/.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
|