1
# Copyright (C) 2008-2009 Sun Microsystems, Inc. All rights reserved.
2
# Use is subject to license terms.
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; version 2 of the License.
8
# This program is distributed in the hope that it will be useful, but
9
# WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
# General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
18
package GenTest::Reporter::CloneSlaveXtrabackup;
21
@ISA = qw(GenTest::Reporter);
26
use GenTest::Constants;
27
use GenTest::Reporter;
28
use GenTest::Comparator;
38
# We abort the test in case not enough binlog events have been generated
39
# since the test can contain FLUSH , we check for either enough binlogs
40
# OR enough events in last binlog .
43
use constant MINIMUM_BINLOG_COUNT => 10;
44
use constant MINIMUM_BINLOG_SIZE => 1000000;
49
# In case of two servers, we will be called twice.
50
# Only clone a slave when called for the master
52
$first_reporter = $reporter if not defined $first_reporter;
53
return STATUS_OK if $reporter ne $first_reporter;
55
return STATUS_OK if time() < ($reporter->testStart() + ($reporter->testDuration() / 2)) ;
56
return STATUS_OK if $clone_done == 1;
60
my $basedir = $reporter->serverVariable('basedir');
62
foreach my $path ("$basedir/../client", "$basedir/../bin", "$basedir/client/RelWithDebInfo", "$basedir/client/Debug", "$basedir/client", "$basedir/bin") {
64
$client_basedir = $path;
69
die "can't determine client_basedir; basedir = $basedir" if not defined $client_basedir;
71
my $binary = $reporter->serverInfo('binary');
72
my $language = $reporter->serverVariable('language');
73
my $lc_messages_dir = $reporter->serverVariable('lc_messages_dir');
74
my $datadir = $reporter->serverVariable('datadir');
75
$datadir =~ s{[\\/]$}{}sgio;
76
my $slave_datadir = $datadir.'_clonedslave_xtrabackup';
77
mkdir($slave_datadir);
78
my $master_port = $reporter->serverVariable('port');
79
my $slave_port = $master_port + 4;
80
my $plugin_dir = $reporter->serverVariable('plugin_dir');
81
my $plugins = $reporter->serverPlugins();
83
my $master_dbh = DBI->connect($reporter->dsn());
85
my $xtrabackup_backup_command = "xtrabackup --backup --datadir=$datadir --target-dir=$slave_datadir";
86
say("Executing backup: $xtrabackup_backup_command");
87
system($xtrabackup_backup_command);
88
return STATUS_ENVIRONMENT_FAILURE if $! != 0;
90
my $xtrabackup_prepare_command = "xtrabackup --prepare --target-dir=$slave_datadir";
91
say("Executing first prepare: $xtrabackup_prepare_command");
92
system($xtrabackup_prepare_command);
93
return STATUS_ENVIRONMENT_FAILURE if $! != 0;
95
say("Executing second prepare: $xtrabackup_prepare_command");
96
system($xtrabackup_prepare_command);
97
return STATUS_ENVIRONMENT_FAILURE if $! != 0;
99
my $binlog_pos_file = $slave_datadir.'/xtrabackup_binlog_pos_innodb';
100
open(BINLOG_POS_FILE, $binlog_pos_file);
101
read(BINLOG_POS_FILE, my $binlog_pos_text, -s $binlog_pos_file);
102
chomp($binlog_pos_text);
103
my ($binlog_file, $binlog_pos) = $binlog_pos_text =~ m{.*/(.*?)\t(.*?)$}sgio;
105
if (($binlog_file eq '') || ($binlog_pos eq '')) {
106
say("Xtrabackup did not provide master_log_file and master_log_pos.");
107
return STATUS_ENVIRONMENT_FAILURE;
109
say("Xtrabackup reports: master_log_file: $binlog_file; master_log_pos: $binlog_pos.");
112
system("cp -r $datadir/mysql $slave_datadir/");
114
my @all_databases = @{$master_dbh->selectcol_arrayref("SHOW DATABASES")};
115
foreach my $database (grep { $_ !~ m{^(information_schema|performance_schema)$}sgio } @all_databases ) {
116
system("Copying .frm files for database $database...");
117
mkdir("$slave_datadir/$database");
118
system("cp -r $datadir/$database/*.frm $slave_datadir/$database");
121
my @mysqld_options = (
126
'--language='.$language,
127
'--loose-lc-messages-dir='.$lc_messages_dir,
128
'--datadir="'.$slave_datadir.'"',
130
'--skip-grant-tables',
132
'--relay-log=clonedslave-relay',
133
'--general_log_file="'.$slave_datadir.'/clonedslave.log"',
134
'--log_error="'.$slave_datadir.'/clonedslave.err"',
135
'--datadir="'.$slave_datadir.'"',
136
'--port='.$slave_port,
137
'--loose-plugin-dir='.$plugin_dir,
138
'--max-allowed-packet=20M',
140
'--sql_mode="NO_ENGINE_SUBSTITUTION"',
141
'--skip-grant-tables'
144
foreach my $plugin (@$plugins) {
145
push @mysqld_options, '--plugin-load='.$plugin->[0].'='.$plugin->[1];
148
my $mysqld_command = $binary.' '.join(' ', @mysqld_options).' 2>&1';
149
say("Starting a new mysqld for the cloned slave.");
150
say("$mysqld_command.");
151
my $mysqld_pid = open2(\*RDRFH, \*WTRFH, $mysqld_command);
155
foreach my $try (1..120) {
157
$slave_dbh = DBI->connect("dbi:mysql:user=root:host=127.0.0.1:port=".$slave_port, undef, undef, { RaiseError => 0 , PrintError => 0 } );
158
next if not defined $slave_dbh;
159
last if $slave_dbh->ping();
162
return STATUS_ENVIRONMENT_FAILURE if not defined $slave_dbh;
164
say("Cloned slave has started.");
166
say("Issuing START SLAVE on the cloned slave...");
170
MASTER_PORT = ".$master_port.",
171
MASTER_HOST = '127.0.0.1',
172
MASTER_LOG_FILE = '$binlog_file',
173
MASTER_LOG_POS = $binlog_pos,
174
MASTER_USER = 'root',
175
MASTER_CONNECT_RETRY = 1
178
$slave_dbh->do("START SLAVE");
180
say("START SLAVE was issued. The main test/workload will now continue.");
186
my $reporter = shift;
188
my $basedir = $reporter->serverVariable('basedir');
190
foreach my $path ("$basedir/../client", "$basedir/../bin", "$basedir/client/RelWithDebInfo", "$basedir/client/Debug", "$basedir/client", "$basedir/bin") {
192
$client_basedir = $path;
197
die "can't determine client_basedir; basedir = $basedir" if not defined $client_basedir;
199
my $master_port = $reporter->serverVariable('port');
200
my $slave_port = $master_port + 4;
201
my $master_dbh = DBI->connect($reporter->dsn());
202
my $slave_dbh = DBI->connect("dbi:mysql:user=root:host=127.0.0.1:port=".$slave_port, undef, undef, { RaiseError => 1 } );
204
say("Issuing START SLAVE on the cloned slave.");
205
$slave_dbh->do("START SLAVE");
207
my ($final_binlog_file, $final_binlog_pos) = $master_dbh->selectrow_array("SHOW MASTER STATUS");
208
exit_test(STATUS_UNKNOWN_ERROR) if !defined $final_binlog_file;
210
say("The cloned slave must catch up to $final_binlog_file:$final_binlog_pos.");
213
# We call MASTER_POS_WAIT at 100K increments in order to avoid buildbot timeout in case
214
# one big MASTER_POS_WAIT would take more than 20 minutes.
217
my $sth_binlogs = $master_dbh->prepare("SHOW BINARY LOGS");
218
$sth_binlogs->execute();
219
while (my ($intermediate_binlog_file, $intermediate_binlog_size) = $sth_binlogs->fetchrow_array()) {
220
my $intermediate_binlog_pos = $intermediate_binlog_size < 10000000 ? $intermediate_binlog_size : 10000000;
222
say("Executing intermediate MASTER_POS_WAIT('$intermediate_binlog_file', $intermediate_binlog_pos) on cloned slave.");
223
my $intermediate_wait_result = $slave_dbh->selectrow_array("SELECT MASTER_POS_WAIT('$intermediate_binlog_file', $intermediate_binlog_pos)");
224
if (not defined $intermediate_wait_result) {
225
say("Intermediate MASTER_POS_WAIT('$intermediate_binlog_file', $intermediate_binlog_pos) failed on cloned slave on port $slave_port.");
226
return STATUS_REPLICATION_FAILURE;
228
$intermediate_binlog_pos += 10000000;
229
} while ( $intermediate_binlog_pos <= $intermediate_binlog_size );
232
say("Waiting for cloned slave to catch up..., file $final_binlog_file, pos $final_binlog_pos .");
233
my $final_wait_result = $slave_dbh->selectrow_array("SELECT MASTER_POS_WAIT('$final_binlog_file', $final_binlog_pos)");
235
if (not defined $final_wait_result) {
236
say("MASTER_POS_WAIT() failed. Cloned slave replication thread not running.");
237
return STATUS_REPLICATION_FAILURE; }
239
say("Cloned slave caught up.");
241
my @all_databases = @{$master_dbh->selectcol_arrayref("SHOW DATABASES")};
242
my $databases_string = join(' ', grep { $_ !~ m{^(mysql|information_schema|performance_schema)$}sgio } @all_databases );
244
my @dump_ports = ($master_port, $slave_port);
247
foreach my $i (0..$#dump_ports) {
248
say("Dumping server on port $dump_ports[$i]...");
249
$dump_files[$i] = tmpdir()."/server_".$$."_".$i.".dump";
251
my $dump_result = system("\"$client_basedir/mysqldump\" --hex-blob --no-tablespaces --skip-triggers --compact --order-by-primary --skip-extended-insert --no-create-info --host=127.0.0.1 --port=$dump_ports[$i] --user=root --databases $databases_string | sort > $dump_files[$i]") >> 8;
252
return STATUS_ENVIRONMENT_FAILURE if $dump_result > 0;
255
say("Comparing SQL dumps...");
256
my $diff_result = system("diff -u $dump_files[0] $dump_files[1]") >> 8;
258
if ($diff_result == 0) {
259
say("No differences were found between master and cloned slave.");
262
foreach my $dump_file (@dump_files) {
266
return $diff_result == 0 ? STATUS_OK : STATUS_REPLICATION_FAILURE;
270
return REPORTER_TYPE_PERIODIC | REPORTER_TYPE_SUCCESS;