Merge lp:~akopytov/percona-xtrabackup/use-dbd-mysql-in-innobackupex into lp:percona-xtrabackup/2.1

Proposed by Alexey Kopytov
Status: Merged
Approved by: Alexey Kopytov
Approved revision: 487
Merged at revision: 494
Proposed branch: lp:~akopytov/percona-xtrabackup/use-dbd-mysql-in-innobackupex
Merge into: lp:percona-xtrabackup/2.1
Diff against target: 1067 lines (+346/-381)
4 files modified
doc/source/innobackupex/privileges.rst (+0/-4)
innobackupex (+340/-371)
test/t/bug729843.sh (+2/-3)
test/t/bug977101.sh (+4/-3)
To merge this branch: bzr merge lp:~akopytov/percona-xtrabackup/use-dbd-mysql-in-innobackupex
Reviewer Review Type Date Requested Status
Stewart Smith (community) Approve
Daniel Nichter (community) Needs Fixing
Laurynas Biveinis (community) Approve
Review via email: mp+143127@code.launchpad.net

Description of the change

    Blueprint "Use Perl DBD::MySQL for server communication in
    innobackupex".

    https://blueprints.launchpad.net/percona-xtrabackup/+spec/use-dbd-mysql-in-innobackupex

To post a comment you must log in.
Revision history for this message
Alexey Kopytov (akopytov) wrote :
Revision history for this message
Alexey Kopytov (akopytov) wrote :

Had to rework keepalives as the signal-based implementation was conflicting with DBD due to a Perl bug resulting in sporadic failures in the previous Jenkins build. Jenkins build with changed keepalive logic: http://jenkins.percona.com/view/XtraBackup/job/percona-xtrabackup-2.1-param/153/

Revision history for this message
Alexey Kopytov (akopytov) wrote :

Setting to Work In Progress, because I'd like to include fix for bug #1092235. That, however, would require reverting the documentation fix for bug #1092235 in this branch. Will implement a proper fix and update docs once the limitation is documented and merged to main branches.

Revision history for this message
Alexey Kopytov (akopytov) wrote :
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) wrote :

I have reviewed this, but this MP about exceeds my Perl skills. Please get a quick glance from Daniel or Brian, or some other Perl expert. Thus approving but leaving at "Needs Review."

review: Approve
Revision history for this message
Daniel Nichter (daniel-nichter) wrote :

I don't know if the script declares this in its main package, but if not:

use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);

No production Perl program should run without those. Then:

s/$@/$EVAL_ERROR/g
s/$!/$CHILD_ERROR/g

etc. See http://perldoc.perl.org/perlvar.html

---

This

20 +# check existence of DBD::mysql module
21 +my $dbd_mysql_installed;
22 +
23 +BEGIN {
24 + eval "use DBD::mysql";
25 + $dbd_mysql_installed=$@ ? 0 : 1;
26 +}
27 +

is better written like:

eval {
   require DBD::mysql;
};
my $have_dbd_mysql = $EVAL_ERROR ? 0 : 1;

---

Typo:

322 + Die "Keep-alive process has already been started for this connection."
395 + Die "Keep-alive process has never been started for this connection."
etc.

s/Die/die/

---

With hashes like

45 +my %mysql;
97 + %mysql = mysql_connect({
152 + wait_for_safe_slave(\%mysql);

it's best to return and pass hashrefs (and arrayrefs). So instead of

return %hash

a sub should

my %hash = ( ... );
return \%hash;

or

my $hash = { ... };
return $hash;

I almost never use "regular" arrays (@foo) or hashes (%foo) unless there's a good reason.

And sometimes using a hash like this is a sign that the hash should be turned into a class/object.

---

For sub args like

97 + %mysql = mysql_connect({
98 + abort_on_error => 0,
99 + keepalives => 0
100 + });

the general standard is

hello(arg1 => "foo", arg2 => $bar); # caller

sub hello {
   my (%args) = @_;
   my $args1 = $args{args1};
   my $args2 = $args{args2};
   ...

Then you can do things like check for required args, have optional args (e.g. $optional_arg = $args{whatever} || 42;).

---

For eval like

347 + eval {
348 + $con->{dbh}->selectrow_array("select '$hello_message'");
349 + };
350 +
351 + $rc = 255 if $@;

it's good to adopt this style:

eval {
   ...
};
if ( $EVAL_ERROR ) {
   ...
}

So always put an explicit check of $EVAL_ERROR after and eval {} block. This is core Perl's equivalent to try {} catch {}. (There's a real try-catch syntax with a non-core module, but it has serious limitations imho.)

---

Using 'and' as in

846 -if($var_version =~ m/5\.1\.\d/ and $var_innodb_version =~ m/.*/){

isn't preferred because it has different precedence than '&&', so '&&' is used in most cases. 'or' is the common exception in Perl, used very often like:

open my $fh, '>', $file or die "Error opening $file: $OS_ERROR";

===

Setting review to "Needs Fixing" because of "Die" (see above); that will cause the tool to die with a syntax error rather than the intended error message.

review: Needs Fixing
Revision history for this message
Alexey Kopytov (akopytov) wrote :
Download full text (4.3 KiB)

Hi Daniel,

Thanks for the review.

On Mon, 04 Feb 2013 21:56:20 -0000, Daniel Nichter wrote:
> Review: Needs Fixing
>
> I don't know if the script declares this in its main package, but if not:
>
> use strict;
> use warnings FATAL => 'all';
> use English qw(-no_match_vars);
>

We have:

use strict;
use English qw(-no_match_vars);

> No production Perl program should run without those. Then:
>
> s/$@/$EVAL_ERROR/g
> s/$!/$CHILD_ERROR/g
>
> etc. See http://perldoc.perl.org/perlvar.html
>

The above page describes "$@" as a synonym for "$EVAL_ERROR", and "$?"
as a synonym for "$CHILD_ERROR". It doesn't explain why one form would
be preferable over the other one. In fact, examples use the short form
"@" and "$?".

> ---
>
> This
>
> 20 +# check existence of DBD::mysql module
> 21 +my $dbd_mysql_installed;
> 22 +
> 23 +BEGIN {
> 24 + eval "use DBD::mysql";
> 25 + $dbd_mysql_installed=$@ ? 0 : 1;
> 26 +}
> 27 +
>
> is better written like:
>
> eval {
> require DBD::mysql;
> };
> my $have_dbd_mysql = $EVAL_ERROR ? 0 : 1;
>

OK.

> ---
>
> Typo:
>
> 322 + Die "Keep-alive process has already been started for this connection."
> 395 + Die "Keep-alive process has never been started for this connection."
> etc.
>
> s/Die/die/
>

No, we do have the Die subroutine in innobackupex. Which also makes sure
we call all child processes before calling real "die".

> ---
>
> With hashes like
>
> 45 +my %mysql;
> 97 + %mysql = mysql_connect({
> 152 + wait_for_safe_slave(\%mysql);
>
> it's best to return and pass hashrefs (and arrayrefs). So instead of
>
> return %hash
>
> a sub should
>
> my %hash = ( ... );
> return \%hash;
>
> or
>
> my $hash = { ... };
> return $hash;
>
> I almost never use "regular" arrays (@foo) or hashes (%foo) unless there's a good reason.
>

Yes, my take on this is that if hash is supposed to be small, it's
acceptable to return the hash itself rather than a reference, as that
provides cleaner syntax. There are other implications of returning
hashes, but none of them seem to apply to our case which is very very
simple.

> And sometimes using a hash like this is a sign that the hash should be turned into a class/object.
>
> ---
>
> For sub args like
>
> 97 + %mysql = mysql_connect({
> 98 + abort_on_error => 0,
> 99 + keepalives => 0
> 100 + });
>
> the general standard is
>
> hello(arg1 => "foo", arg2 => $bar); # caller
>
> sub hello {
> my (%args) = @_;
> my $args1 = $args{args1};
> my $args2 = $args{args2};
> ...
>
> Then you can do things like check for required args, have optional args (e.g. $optional_arg = $args{whatever} || 42;).
>

Sure, but that's exactly what I use. I.e. pass a hash of arguments, have
optional args, etc.?

> ---
>
> For eval like
>
> 347 + eval {
> 348 + $con->{dbh}->selectrow_array("select '$hello_message'");
> 349 + };
> 350 +
> 351 + $rc = 255 if $@;
>
> it's good to adopt this style:
>
> eval {
> ...
> };
> if ( $EVAL_ERROR ) {
> ...
> ...

Read more...

487. By Alexey Kopytov

Blueprint "Use Perl DBD::MySQL for server communication in innobackupex".

https://blueprints.launchpad.net/percona-xtrabackup/+spec/use-dbd-mysql-in-innobackupex

Revision history for this message
Alexey Kopytov (akopytov) wrote :

Changes made:

- s/$@/$EVAL_ERROR/g. I didn't do the same for CHILD_ERROR, as we have plenty of existing code using $!, and that would blow the diff for this change unnecessarily.

- rewrote the DBD::mysql detection code as suggested

- rewrote "$rc = 255 if $@;" constructs as suggested ("if ($EVAL_ERROR) {...}")

- replaced "and" with "&&" in the existing code touched by this patch.

I have also reported bug #1116177 as a followup to the "use warnings FATAL => 'all'" suggestion, as that seems to reveal a couple of bugs in the existing code which should be fixed in all branches.

Jenkins build with the updated branch: http://jenkins.percona.com/view/XtraBackup/job/percona-xtrabackup-2.1-param/157/

Revision history for this message
Alexey Kopytov (akopytov) wrote :

No response from Danial, assuming approved.

Revision history for this message
Stewart Smith (stewart) wrote :

I'm in full support of this patch even if it breaks something, maintenance will be improved by several orders of magnitude.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'doc/source/innobackupex/privileges.rst'
2--- doc/source/innobackupex/privileges.rst 2013-01-17 15:08:37 +0000
3+++ doc/source/innobackupex/privileges.rst 2013-02-05 11:56:22 +0000
4@@ -66,7 +66,3 @@
5 mysql> CREATE USER 'bkpuser'@'localhost' IDENTIFIED BY 's3cret';
6 mysql> GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'bkpuser'@'localhost';
7 mysql> FLUSH PRIVILEGES;
8-
9-.. note::
10-
11- Connection-related parameters are only recognized in the [client] and [mysql] groups in configuration files. Adding custom groups like [xtrabackup] will work only if XtraBackup binary is used, it will not work with the innobackupex.
12
13=== modified file 'innobackupex'
14--- innobackupex 2013-01-22 01:23:45 +0000
15+++ innobackupex 2013-02-05 11:56:22 +0000
16@@ -39,6 +39,12 @@
17 my @required_perl_version = (5, 0, 5);
18 my $required_perl_version_old_style = 5.005;
19
20+# check existence of DBD::mysql module
21+eval {
22+ require DBD::mysql;
23+};
24+my $dbd_mysql_installed = $EVAL_ERROR ? 0 : 1;
25+
26 # force flush after every write and print
27 $| = 1;
28
29@@ -53,9 +59,6 @@
30 # separately printed when a backup is made
31 my $backup_file_print_limit = 9;
32
33-# timeout in seconds for a reply from mysql
34-my $mysql_response_timeout = 900;
35-
36 # default compression level (this is an argument to ibbackup)
37 my $default_compression_level = 1;
38
39@@ -121,6 +124,9 @@
40 my $option_compact = '';
41 my $option_rebuild_indexes = '';
42
43+my %mysql;
44+my $option_backup = '';
45+
46 # name of the my.cnf configuration file
47 #my $config_file = '';
48
49@@ -160,20 +166,9 @@
50 # prefix for output lines
51 my $prefix = "$innobackup_script:";
52
53-# process id of mysql client program (runs as a child process of this script)
54-my $mysql_pid = '';
55-
56 # mysql server version string
57 my $mysql_server_version = '';
58
59-# name of the file where stderr of mysql process is directed
60-my $mysql_stderr_fh = File::Temp->new();
61-my $mysql_stderr = $mysql_stderr_fh->filename;
62-
63-# name of the file where stdout of mysql process is directed
64-my $mysql_stdout_fh = File::Temp->new();
65-my $mysql_stdout = $mysql_stdout_fh->filename;
66-
67 # name of the file where binlog position info is written
68 my $binlog_info;
69
70@@ -196,12 +191,6 @@
71 # a counter for numbering mysql connection checks
72 my $hello_id = 0;
73
74-# the request which has been sent to mysqld, but to which
75-# mysqld has not yet replied. Empty string denotes that no
76-# request has been sent to mysqld or that mysqld has replied
77-# to all requests.
78-my $current_mysql_request = '';
79-
80 # escape sequences for options files
81 my %option_value_escapes = ('b' => "\b",
82 't' => "\t",
83@@ -255,29 +244,29 @@
84 close XTRABACKUP_BINARY;
85 }
86
87- else {
88+ else {
89 if( $option_ibbackup_binary eq "autodetect" ){
90 # Try to connect MySQL and get the version
91- print STDERR "option_ibbackup_binary is autodetect, trying to connect to MySQL\n";
92- my $options = get_mysql_options();
93- $mysql_pid = open(*MYSQL_WRITER, "| mysql $options >$mysql_stdout 2>$mysql_stderr ");
94- print STDERR "Connected to MySQL with pid $mysql_pid\n";
95- sleep 1;
96- if ($mysql_pid && $mysql_pid == waitpid($mysql_pid, &WNOHANG)) {
97- my $reason = `cat $mysql_stderr`;
98- $mysql_pid = '';
99- # Failed to connect to MySQL
100- die "Failed to connect to MySQL server to detect version.\nYou must set xtrabackup version to use with --ibbackup option.\nPossible values are xtrabackup_55 (for MySQL 5.5) or xtrabackup (for MySQL 5.1 with InnoDB plugin or Percona Server)\n";
101- }
102- else{
103- mysql_close();
104- print STDERR "Connected successfully\n";
105- $option_ibbackup_binary = set_xtrabackup_version();
106- }
107- }
108+ %mysql = mysql_connect(
109+ abort_on_error => 0,
110+ keepalives => 0
111+ );
112+ if ($mysql{dbh}) {
113+ print STDERR "Connected successfully\n";
114+ $option_ibbackup_binary = set_xtrabackup_version();
115+ mysql_close();
116+ } else {
117+ die "Failed to connect to MySQL server to detect version.\nYou must set xtrabackup version to use with --ibbackup option.\nPossible values are xtrabackup_55 (for MySQL 5.5) or xtrabackup (for MySQL 5.1 with InnoDB plugin or Percona Server)\n";
118+ }
119 }
120-} elsif ($option_ibbackup_binary eq 'autodetect') {
121- $option_ibbackup_binary = set_xtrabackup_version();
122+ }
123+} elsif ($option_backup) {
124+ # backup
125+ %mysql = mysql_connect(abort_on_error => 1, keepalives => 1);
126+
127+ if ($option_ibbackup_binary eq 'autodetect') {
128+ $option_ibbackup_binary = set_xtrabackup_version();
129+ }
130 }
131 init();
132
133@@ -295,6 +284,9 @@
134 } else {
135 # make a backup of InnoDB and MyISAM tables, indexes and .frm files.
136 $ibbackup_exit_code = backup();
137+
138+ mysql_close(\%mysql);
139+
140 if ($option_stream) {
141 open XTRABACKUP_BINARY, "> $option_tmpdir/$xtrabackup_binary_file"
142 || die "Cannot open file $option_tmpdir/$xtrabackup_binary_file: $!\n";
143@@ -367,16 +359,11 @@
144 #
145 sub Die {
146 my $message = shift;
147- my $extra_info = '';
148
149 # kill all child processes of this process
150 kill_child_processes();
151
152- if ($current_mysql_request) {
153- $extra_info = " while waiting for reply to MySQL request:" .
154- " '$current_mysql_request'";
155- }
156- die "$prefix Error: $message$extra_info";
157+ die "$prefix Error: $message";
158 }
159
160
161@@ -388,22 +375,14 @@
162 sub backup {
163 my $orig_datadir = get_option(\%config, $option_defaults_group, 'datadir');
164
165- # check that we can connect to the database. This done by
166- # connecting, issuing a query, and closing the connection.
167- mysql_open();
168- mysql_close();
169-
170 # start ibbackup as a child process
171 start_ibbackup();
172
173 # wait for ibbackup to suspend itself
174 wait_for_ibbackup_suspend();
175
176- # connect to database
177- mysql_open();
178-
179 if ($option_safe_slave_backup) {
180- wait_for_safe_slave();
181+ wait_for_safe_slave(\%mysql);
182 }
183
184 # flush tables with read lock
185@@ -412,11 +391,11 @@
186 backup_files(1);
187
188 # flush tables with read lock
189- mysql_lockall();
190+ mysql_lockall(\%mysql);
191 }
192
193 if ($option_slave_info) {
194- write_slave_info();
195+ write_slave_info(\%mysql);
196 }
197
198
199@@ -428,18 +407,15 @@
200 resume_ibbackup();
201
202 # release read locks on all tables
203- mysql_unlockall() if !$option_no_lock;
204+ mysql_unlockall(\%mysql) if !$option_no_lock;
205
206 my $ibbackup_exit_code = wait_for_ibbackup_finish();
207
208 if ( $option_safe_slave_backup && $sql_thread_started) {
209 print STDERR "$prefix: Starting slave SQL thread\n";
210- mysql_send('START SLAVE SQL_THREAD;');
211+ mysql_query(\%mysql, 'START SLAVE SQL_THREAD;');
212 }
213
214- # Close the DB connection
215- mysql_close();
216-
217 # copy ib_lru_dump
218 if (-e "$orig_datadir/ib_lru_dump") {
219 if ($option_stream) {
220@@ -1084,194 +1060,249 @@
221
222
223 #
224-# get_mysql_options subroutine returns the options to mysql client program
225-# as a string. The options are determined from the options given by the
226-# user to innobackup.
227+# parse_connection_options() subroutine parses connection-related command line
228+# options
229 #
230-sub get_mysql_options {
231- my $options = '';
232+sub parse_connection_options {
233+ my $con = shift;
234+
235+ $con->{dsn} = 'dbi:mysql:';
236
237 # this option has to be first
238 if ($option_defaults_file) {
239- $options = "$options --defaults-file='$option_defaults_file'";
240+ $con->{dsn} .= ";mysql_read_default_file=$option_defaults_file";
241 }
242
243 if ($option_defaults_extra_file) {
244- $options = $options . " --defaults-extra-file=\"$option_defaults_extra_file\" ";
245+ $con->{dsn} .= ";mysql_read_default_file=$option_defaults_extra_file";
246 }
247
248+ $con->{dsn} .= ";mysql_read_default_group=xtrabackup";
249+
250 if ($option_mysql_password) {
251- $options = "$options --password='$option_mysql_password'";
252+ $con->{dsn_password} = "$option_mysql_password";
253 }
254 if ($option_mysql_user) {
255- $options = "$options --user='$option_mysql_user'";
256+ $con->{dsn_user} = "$option_mysql_user";
257 }
258 if ($option_mysql_host) {
259- $options = "$options --host='$option_mysql_host'";
260+ $con->{dsn} .= ";host=$option_mysql_host";
261 }
262 if ($option_mysql_port) {
263- $options = "$options --port='$option_mysql_port'";
264+ $con->{dsn} .= ";port=$option_mysql_port";
265 }
266 if ($option_mysql_socket) {
267- $options = "$options --socket='$option_mysql_socket'";
268- }
269- $options = "$options --unbuffered --";
270- return $options;
271-}
272-
273-
274-#
275-# Check that the server is responding to queries
276-#
277-sub mysql_ping {
278- my $alarm_handler = shift;
279- my $mysql_pid_copy = $mysql_pid;
280-
281- # send a dummy query to mysql child process
282- $hello_id++;
283-
284- my $hello_message = "xtrabackup ping $hello_id";
285- print MYSQL_WRITER "select '$hello_message';\n"
286- or Die "Connection to mysql child process failed: $!";
287-
288- # Don't check server's response if running in a signal handler
289- # because that breaks system() calls
290- if ($alarm_handler)
291- {
292- return 0;
293- }
294- # wait for reply
295- eval {
296- local $SIG{ALRM} = sub { die "alarm clock restart" };
297- my $stdout = '';
298- my $stderr = '';
299- alarm $mysql_response_timeout;
300- while (index($stdout, $hello_message) < 0) {
301- sleep 2;
302- if ($mysql_pid && $mysql_pid == waitpid($mysql_pid, &WNOHANG)) {
303- my $reason = `cat $mysql_stderr`;
304- $mysql_pid = '';
305- die "mysql child process has died: $reason";
306- }
307- $stdout = `cat $mysql_stdout`;
308- $stderr = `cat $mysql_stderr | grep -v ^Warning`;
309- if ($stderr) {
310- # mysql has reported an error, do exit
311- die "mysql error: $stderr";
312- }
313- }
314- alarm 0;
315- };
316- if ($@ =~ /alarm clock restart/) {
317- Die "Connection to mysql child process (pid=$mysql_pid_copy) timedout."
318- . " (Time limit of $mysql_response_timeout seconds exceeded."
319- . " You may adjust time limit by editing the value of parameter"
320- . " \"\$mysql_response_timeout\" in this script.)";
321- } elsif ($@) { Die $@; }
322-}
323-
324-
325-#
326-# SIGALRM handler which sends a keepalive query to the server
327-#
328-sub catch_sigalrm {
329- mysql_ping(1);
330- # Reschedule SIGALRM
331- alarm $mysql_keep_alive;
332-}
333-
334-
335-#
336-# Schedule periodic server pings
337+ $con->{dsn} .= ";mysql_socket=$option_mysql_socket";
338+ }
339+}
340+
341+#
342+# Start a background process that will send keep-alive queries periodically
343+# using the connection passed as a 'mysql' hash reference
344 #
345 sub start_keepalives {
346- $SIG{ALRM} = \&catch_sigalrm;
347- alarm $mysql_keep_alive;
348+ my $con = shift;
349+
350+ if (defined($con->{keep_alive_pid})) {
351+ Die "Keep-alive process has already been started for this connection."
352+ }
353+
354+ my $keep_alive_pid = fork();
355+
356+ if ($keep_alive_pid) {
357+ # parent process
358+ $con->{keep_alive_pid} = $keep_alive_pid;
359+
360+ return;
361+ }
362+
363+ # child process
364+ $con->{dbh}->{InactiveDestroy} = 1;
365+
366+ my $rc = 0;
367+ my $hello_id = 0;
368+
369+ # send dummy queries to connection until interrupted with SIGINT or a
370+ # connection error
371+ while (!$rc) {
372+ sleep $mysql_keep_alive;
373+
374+ my $hello_message = "xtrabackup ping " . $hello_id++;
375+
376+ eval {
377+ $con->{dbh}->selectrow_array("select '$hello_message'");
378+ };
379+
380+ if ($EVAL_ERROR) {
381+ $rc = 255;
382+ }
383+ }
384+
385+ exit $rc;
386 }
387
388 #
389-# Cancel periodic server pings
390+# Stop the background keep-alive process
391 #
392 sub stop_keepalives {
393- alarm 0;
394- $SIG{ALRM} = "DEFAULT";
395-}
396-
397-#
398-# mysql_open subroutine starts mysql as a child process with
399-# a pipe connection.
400-#
401-sub mysql_open {
402- my $options = get_mysql_options();
403- # run mysql as a child process with a pipe connection
404- $now = current_time();
405- (my $prt_options = $options) =~ s/--password=[^ ]+ /--password=xxxxxxxx /g;
406- print STDERR "$now $prefix Starting mysql with options: $prt_options\n";
407- $mysql_pid = open(*MYSQL_WRITER, "| mysql $options >$mysql_stdout 2>$mysql_stderr ") or Die "Failed to spawn mysql child process: $!";
408- MYSQL_WRITER->autoflush(1);
409- $now = current_time();
410- print STDERR "$now $prefix Connected to database with mysql child process (pid=$mysql_pid)\n";
411- print MYSQL_WRITER "SET SESSION wait_timeout = 2147000;\n" or die "Connection to mysql child process failed: $!";
412-
413- mysql_ping(0);
414-
415- start_keepalives();
416-}
417-
418-
419-#
420-# mysql_send subroutine send a request string to mysql child process.
421-# This subroutine appends a newline character to the request and checks
422-# that mysqld receives the query.
423+ my $con = shift;
424+
425+ if (!defined($con->{keep_alive_pid})) {
426+ Die "Keep-alive process has never been started for this connection."
427+ }
428+
429+ kill 'INT', $con->{keep_alive_pid};
430+ waitpid($con->{keep_alive_pid}, 0);
431+ my $rc = $? >> 8;
432+
433+ if ($rc != 0) {
434+ Die "Keep-alive process died with exit code " . $rc;
435+ }
436+ undef $con->{keep_alive_pid};
437+}
438+
439+#
440+# mysql_connect subroutine connects to MySQL server
441+#
442+sub mysql_connect {
443+ my %con;
444+ my %args = (
445+ # Defaults
446+ abort_on_error => 1,
447+ keepalives => 0,
448+ @_
449+ );
450+
451+ $con{abort_on_error} = $args{abort_on_error};
452+ $con{keepalives} = $args{keepalives};
453+
454+ parse_connection_options(\%con);
455+
456+ $now = current_time();
457+ print STDERR "$now $prefix Connecting to MySQL server with DSN '" .
458+ $con{dsn} . "' as '" . $con{dsn_user} . "' (using password: ";
459+ if (defined($con{dsn_password})) {
460+ print STDERR "YES).\n";
461+ } else {
462+ print STDERR "NO).\n";
463+ }
464+
465+ eval {
466+ $con{dbh}=DBI->connect($con{dsn}, $con{dsn_user},
467+ $con{dsn_password}, { RaiseError => 1 });
468+ };
469+
470+ if ($EVAL_ERROR) {
471+ $con{connect_error}=$EVAL_ERROR;
472+ } else {
473+ $now = current_time();
474+ print STDERR "$now $prefix Connected to MySQL server\n";
475+ }
476+
477+ if ($args{abort_on_error}) {
478+ if (!$dbd_mysql_installed) {
479+ die "ERROR: Failed to connect to MySQL server as " .
480+ "DBD::mysql module is not installed";
481+ } else {
482+ if (!$con{dbh}) {
483+ die "ERROR: Failed to connect to MySQL server: " .
484+ $con{connect_error};
485+ }
486+ }
487+ }
488+
489+ if ($con{dbh} && $con{keepalives}) {
490+ start_keepalives(\%con);
491+ }
492+
493+ return %con;
494+}
495+
496+#
497+# mysql_query subroutine send a query to MySQL server child process.
498 # Parameters:
499-# request request string
500-#
501-sub mysql_send {
502- my $request = shift;
503-
504- stop_keepalives();
505-
506- $current_mysql_request = $request;
507- print MYSQL_WRITER "$request\n";
508- mysql_ping(0);
509- $current_mysql_request = '';
510-
511- start_keepalives();
512-}
513-
514-
515-#
516-# mysql_close subroutine terminates mysql child process gracefully.
517+# query query to execute
518+#
519+sub mysql_query {
520+ my ($con, $query) = @_;
521+
522+ eval {
523+ if ($con->{keepalives}) {
524+ stop_keepalives($con);
525+ }
526+
527+ if ($query eq 'SHOW VARIABLES') {
528+ $con->{vars} = $con->{dbh}->selectall_hashref('SHOW VARIABLES',
529+ 'Variable_name');
530+ } elsif ($query eq 'SHOW STATUS') {
531+ $con->{status} = $con->{dbh}->selectall_hashref('SHOW STATUS',
532+ 'Variable_name');
533+ } elsif ($query eq 'SHOW MASTER STATUS') {
534+ $con->{master_status} =
535+ $con->{dbh}->selectrow_hashref("SHOW MASTER STATUS");
536+ } elsif ($query eq 'SHOW SLAVE STATUS') {
537+ $con->{slave_status} =
538+ $con->{dbh}->selectrow_hashref("SHOW SLAVE STATUS");
539+ } else {
540+ $con->{dbh}->do($query);
541+ }
542+
543+ if ($con->{keepalives}) {
544+ start_keepalives($con);
545+ }
546+ };
547+ if ($EVAL_ERROR) {
548+ Die "\nError executing '$query': $EVAL_ERROR\n";
549+ }
550+}
551+
552+
553+sub get_mysql_vars {
554+ mysql_query($_[0], 'SHOW VARIABLES')
555+}
556+
557+sub get_mysql_status {
558+ mysql_query($_[0], 'SHOW STATUS');
559+}
560+
561+sub get_mysql_master_status {
562+ mysql_query($_[0], "SHOW MASTER STATUS");
563+}
564+
565+sub get_mysql_slave_status {
566+ mysql_query($_[0], "SHOW SLAVE STATUS");
567+}
568+
569+#
570+# mysql_close subroutine closes connection to MySQL server gracefully.
571 #
572 sub mysql_close {
573- stop_keepalives();
574-
575- print MYSQL_WRITER "quit\n";
576+ my $con = shift;
577+
578+ if ($con->{keepalives}) {
579+ stop_keepalives($con);
580+ };
581+
582+ $con->{dbh}->disconnect();
583+
584 $now = current_time();
585 print STDERR "$now $prefix Connection to database server closed\n";
586- $mysql_pid = '';
587 }
588
589-
590 #
591 # write_binlog_info subroutine retrieves MySQL binlog position and
592 # saves it in a file. It also prints it to stdout.
593 #
594 sub write_binlog_info {
595- my @lines;
596- my $position = '';
597- my $filename = '';
598+ my $con = shift;
599
600 # get binlog position
601- mysql_send 'SHOW MASTER STATUS\G';
602+ get_mysql_master_status($con);
603
604 # get the name of the last binlog file and position in it
605 # from the SHOW MASTER STATUS output
606- file_to_array($mysql_stdout, \@lines);
607- foreach (@lines) {
608- $filename = $1 if /^\s+File:\s(\S+)\s*$/;
609- $position = $1 if /^\s+Position:\s(\d+)\s*$/;
610- }
611+ my $filename = $con->{master_status}->{File};
612+ my $position = $con->{master_status}->{Position};
613
614 # write binlog info file
615 open(FILE, ">$binlog_info") || Die "Failed to open file '$binlog_info': $!";
616@@ -1292,34 +1323,20 @@
617 # saves it in a file. It also prints it to stdout.
618 #
619 sub write_galera_info {
620+ my $con = shift;
621 my @lines;
622 my @info_lines = ();
623 my $position = '';
624 my $filename = '';
625
626 # get binlog position
627- mysql_send "SHOW STATUS LIKE 'wsrep_local_state_uuid';";
628- mysql_send "SHOW STATUS LIKE 'wsrep_last_committed';";
629-
630- # get "show master status" output lines (2) from mysql output
631- file_to_array($mysql_stdout, \@lines);
632+ get_mysql_status($con);
633
634 open(FILE, ">$galera_info") ||
635 Die "Failed to open file '$galera_info': $!";
636
637- foreach my $line (@lines) {
638- if ($line =~ m/wsrep_local_state_uuid/) {
639- $line =~ s/wsrep_local_state_uuid\s*//g;
640- print FILE "$line:";
641- }
642- }
643-
644- foreach my $line (@lines) {
645- if ($line =~ m/wsrep_last_committed/) {
646- $line =~ s/wsrep_last_committed\s*//g;
647- print FILE "$line\n";
648- }
649- }
650+ print FILE $con->{status}->{wsrep_local_state_uuid}->{Value};
651+ print FILE $con->{status}->{wsrep_last_committed}->{Value};
652
653 close(FILE);
654
655@@ -1338,23 +1355,25 @@
656 # also saves it in $msql_slave_position variable.
657 #
658 sub write_slave_info {
659+ my $con = shift;
660+
661 my @lines;
662 my @info_lines;
663 my $position = '';
664 my $filename = '';
665 my $master= '';
666-
667- # get slave status. Use single quotes here, otherwise
668- # \G is evaluated as a control character.
669- mysql_send 'SHOW SLAVE STATUS\G';
670+
671+ # get slave status
672+ get_mysql_slave_status($con);
673
674 # get output of the "show slave status" command from mysql output
675 # and extract binlog position of the master server
676- file_to_array($mysql_stdout, \@lines);
677- for (@lines) {
678- $master = $1 if /Master_Host:\s*(\S*)\s*$/;
679- $filename = $1 if /Master_Log_File:\s*(\S*)\s*$/;
680- $position = $1 if /Master_Log_Pos:\s*(\S*)\s*$/;
681+ $master = $con->{slave_status}->{Master_Host};
682+ $filename = $con->{slave_status}->{Relay_Master_Log_File};
683+ $position = $con->{slave_status}->{Exec_Master_Log_Pos};
684+
685+ if (!defined($master) || !defined($filename) || !defined($position)) {
686+ Die "Failed to get master binlog coordinates from SHOW SLAVE STATUS";
687 }
688
689 # print slave status to a file
690@@ -1376,31 +1395,22 @@
691 # mysql_lockall subroutine puts a read lock on all tables in all databases.
692 #
693 sub mysql_lockall {
694+ my $con = shift;
695+
696 $now = current_time();
697 print STDERR "$now $prefix Starting to lock all tables...\n";
698
699- mysql_send "USE mysql;";
700-# mysql_send "DROP TABLE IF EXISTS ibbackup_binlog_marker;";
701-# if (compare_versions($mysql_server_version, '4.1.0') == -1) {
702-# # MySQL server version is 4.0 or older, ENGINE keyword not supported
703-# mysql_send "CREATE TABLE ibbackup_binlog_marker(a INT) TYPE=INNODB;";
704-# } else {
705-# # MySQL server version is 4.1 or newer, use ENGINE keyword
706-# mysql_send "CREATE TABLE ibbackup_binlog_marker(a INT) ENGINE=INNODB;";
707-# }
708- mysql_send "SET AUTOCOMMIT=0;";
709-# mysql_send "INSERT INTO ibbackup_binlog_marker VALUES (1);";
710 if (compare_versions($mysql_server_version, '4.0.22') == 0
711 || compare_versions($mysql_server_version, '4.1.7') == 0) {
712 # MySQL server version is 4.0.22 or 4.1.7
713- mysql_send "COMMIT;";
714- mysql_send "FLUSH TABLES WITH READ LOCK;";
715+ mysql_query($con, "COMMIT");
716+ mysql_query($con, "FLUSH TABLES WITH READ LOCK");
717 } else {
718 # MySQL server version is other than 4.0.22 or 4.1.7
719- mysql_send "FLUSH TABLES WITH READ LOCK;";
720- mysql_send "COMMIT;";
721+ mysql_query($con, "FLUSH TABLES WITH READ LOCK");
722+ mysql_query($con, "COMMIT");
723 }
724- write_binlog_info;
725+ write_binlog_info($con);
726 write_galera_info if $option_galera_info;
727
728 $now = current_time();
729@@ -1413,32 +1423,14 @@
730 # databases.
731 #
732 sub mysql_unlockall {
733- mysql_send "UNLOCK TABLES;";
734-# mysql_send "DROP TABLE IF EXISTS ibbackup_binlog_marker;";
735+ my $con = shift;
736+
737+ mysql_query ($con, "UNLOCK TABLES");
738
739 $now = current_time();
740 print STDERR "$now $prefix All tables unlocked\n";
741 }
742
743-
744-#
745-# catch_sigpipe subroutine is a signal handler for SIGPIPE.
746-#
747-sub catch_sigpipe {
748- my $rcode;
749-
750- if ($mysql_pid && (-1 == ($rcode = waitpid($mysql_pid, &WNOHANG))
751- || $rcode == $mysql_pid)) {
752- my $reason = `cat $mysql_stderr`;
753- print STDERR "Pipe to mysql child process broken: $reason at";
754- system("date +'%H:%M:%S'");
755- exit(1);
756- } else {
757- Die "Broken pipe";
758- }
759-}
760-
761-
762 #
763 # kill_child_processes subroutine kills all child processes of this process.
764 #
765@@ -1447,11 +1439,6 @@
766 kill($kill_signal, $ibbackup_pid);
767 $ibbackup_pid = '';
768 }
769-
770- if ($mysql_pid) {
771- kill($kill_signal, $mysql_pid);
772- $mysql_pid = '';
773- }
774 }
775
776
777@@ -1580,24 +1567,12 @@
778 print STDERR " At the end of a successful $run run $innobackup_script\n";
779 print STDERR " prints \"completed OK!\".\n\n";
780
781- # check that MySQL client program and InnoDB Hot Backup program
782- # are runnable via shell
783 if (!$option_copy_back && !$option_move_back) {
784 # we are making a backup or applying log to backup
785 if (!$option_apply_log) {
786 # we are making a backup, we need mysql server
787- my $output = '';
788- my @lines = ();
789-
790- # check that we have mysql client program
791- require_external('mysql', '--version', 'Ver ([^,]+)',
792- \$mysql_version);
793-
794- # get mysql server version
795- my $options = get_mysql_options();
796- @lines = split('\n',
797- `mysql $options -e "select \@\@version"`);
798- $mysql_server_version = $lines[1];
799+ get_mysql_vars(\%mysql);
800+ $mysql_server_version = $mysql{vars}->{version}->{Value};
801 print STDERR "$prefix Using mysql server version $mysql_server_version\n";
802 }
803 #require_external($option_ibbackup_binary, '--license',
804@@ -1614,9 +1589,6 @@
805 . " v2.0 in order to use --include option.\n";
806 }
807 }
808-
809- # set signal handlers
810- $SIG{PIPE} = \&catch_sigpipe;
811
812 # read MySQL options file
813 #read_config_file($config_file, \%config);
814@@ -1837,11 +1809,9 @@
815 exit(1);
816 }
817
818- # get options file name
819- #$config_file = $ARGV[0];
820-
821 if (!$option_apply_log && !$option_copy_back && !$option_move_back) {
822 # we are making a backup, get backup root directory
823+ $option_backup = "1";
824 $backup_root = $ARGV[0];
825 if ($option_incremental && !$option_incremental_lsn) {
826 my @dirs = `ls -1 -t $backup_root`;
827@@ -2540,86 +2510,89 @@
828 }
829
830 sub set_xtrabackup_version {
831-# Based on MySQL version choose correct binary
832-# MySQL 5.1.* with InnoDB plugin - xtrabackup
833-# MariaDB 5.1.* - xtrabackup
834-# MariaDB 5.2.* - xtrabackup
835-# MariaDB 5.3.* - xtrabackup
836-# Percona Server 5.0 - xtrabackup_51
837-# Percona Server 5.1 - xtrabackup
838-# Percona Server 5.5 - xtrabackup_55
839-# MySQL 5.5.* - xtrabackup_55
840-# MariaDB 5.5.* - xtrabackup_55
841-
842-my @lines;
843-my $var_version = '';
844-my $var_innodb_version = '';
845-my $ibbackup_binary;
846-mysql_open();
847-mysql_send "SHOW VARIABLES LIKE 'version'\\G";
848-file_to_array($mysql_stdout, \@lines);
849-for (@lines) {
850- $var_version = $1 if /Value:\s+(\S+)/;
851- }
852-mysql_send "SHOW VARIABLES LIKE 'innodb_version'\\G";
853-file_to_array($mysql_stdout, \@lines);
854-for (@lines) {
855- $var_innodb_version = $1 if /Value:\s+(\S+)/;
856- }
857-if($var_version =~ m/5\.0\.\d/){
858+ # Based on MySQL version choose correct binary
859+ # MySQL 5.1.* with InnoDB plugin - xtrabackup
860+ # MariaDB 5.1.* - xtrabackup
861+ # MariaDB 5.2.* - xtrabackup
862+ # MariaDB 5.3.* - xtrabackup
863+ # Percona Server 5.0 - xtrabackup_51
864+ # Percona Server 5.1 - xtrabackup
865+ # Percona Server 5.5 - xtrabackup_55
866+ # MySQL 5.5.* - xtrabackup_55
867+ # MariaDB 5.5.* - xtrabackup_55
868+
869+ my $var_version = '';
870+ my $var_innodb_version = '';
871+ my $ibbackup_binary;
872+
873+ get_mysql_vars(\%mysql);
874+
875+ $var_version = $mysql{vars}->{version}->{Value};
876+ $var_innodb_version = $mysql{vars}->{innodb_version}->{Value};
877+
878+
879+ if($var_version =~ m/5\.0\.\d/) {
880 Die "MySQL 5.0 support was removed in Percona XtraBackup 2.1. The last version to support MySQL 5.0 was Percona XtraBackup 2.0.";
881-}
882-if($var_version =~ m/5\.1\.\d/ and $var_innodb_version =~ m/.*/){
883+ }
884+
885+ if($var_version =~ m/5\.1\.\d/ && $var_innodb_version =~ m/.*/) {
886 Die "Support for MySQL 5.1 with builtin InnoDB (not the plugin) was removed in Percona XtraBackup 2.1. The last version to support MySQL 5.1 with builtin InnoDB was Percona XtraBackup 2.0.";
887-}
888-if($var_version =~ m/5\.1\.\d/ and $var_innodb_version =~ m/1\.0\.\d+$/){
889- $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup');
890-}
891-if($var_version =~ m/5\.1\.\d/ and $var_innodb_version =~ m/1\.0\.\d+-(rel)?\d/){
892- $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup');
893-}
894-if($var_version =~ m/5\.2\.\d/){
895- $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup');
896-}
897-if($var_version =~ m/5\.3\.\d/){
898- $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup');
899-}
900-if($var_version =~ m/5\.5\.\d/){
901+ }
902+
903+ if($var_version =~ m/5\.1\.\d/ && $var_innodb_version =~ m/1\.0\.\d+$/) {
904+ $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup');
905+ }
906+
907+ if($var_version =~ m/5\.1\.\d/ && $var_innodb_version =~ m/1\.0\.\d+-(rel)?\d/){
908+ $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup');
909+ }
910+
911+ if($var_version =~ m/5\.2\.\d/){
912+ $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup');
913+ }
914+
915+ if($var_version =~ m/5\.3\.\d/){
916+ $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup');
917+ }
918+
919+ if($var_version =~ m/5\.5\.\d/){
920 $ibbackup_binary = ($win eq 1 ? 'xtrabackup.exe' : 'xtrabackup_55');
921-}
922-mysql_close();
923-return $ibbackup_binary;
924+ }
925+
926+ return $ibbackup_binary;
927 }
928
929 # Wait until it's safe to backup a slave. Returns immediately if
930 # the host isn't a slave. Currently there's only one check:
931 # Slave_open_temp_tables has to be zero. Dies on timeout.
932 sub wait_for_safe_slave {
933+ my $con = shift;
934+
935 my @lines;
936 # whether host is detected as slave in safe slave backup mode
937 my $host_is_slave = 0;
938
939 $sql_thread_started = 0;
940+ get_mysql_slave_status($con);
941
942- mysql_send 'SHOW SLAVE STATUS\G;';
943- file_to_array($mysql_stdout, \@lines);
944- foreach my $line ( @lines ) {
945- if ( $line =~ m/Read_Master_Log_Pos/ ) {
946+ if (defined($con->{slave_status}->{Read_Master_Log_Pos})) {
947 $host_is_slave = 1;
948- } elsif ( $line =~ m/Slave_SQL_Running:.*Yes/ ) {
949+ }
950+
951+ if ($con->{slave_status}->{Slave_SQL_Running} =~ m/Yes/ ) {
952 $sql_thread_started = 1;
953- }
954 }
955+
956 if ( !$host_is_slave ) {
957 print STDERR "$prefix: Not checking slave open temp tables for --safe-slave-backup because host is not a slave\n";
958 return;
959 }
960
961 if ($sql_thread_started) {
962- mysql_send 'STOP SLAVE SQL_THREAD;';
963+ mysql_query($con, 'STOP SLAVE SQL_THREAD');
964 }
965
966- my $open_temp_tables = get_slave_open_temp_tables();
967+ my $open_temp_tables = get_slave_open_temp_tables($con);
968 print STDERR "$prefix: Slave open temp tables: $open_temp_tables\n";
969
970 return if $open_temp_tables == 0;
971@@ -2629,11 +2602,11 @@
972 while ( $n_attempts-- ) {
973 print STDERR "$prefix: Starting slave SQL thread, waiting $sleep_time seconds, then checking Slave_open_temp_tables again ($n_attempts attempts remaining)...\n";
974
975- mysql_send 'START SLAVE SQL_THREAD;';
976+ mysql_query($con, 'START SLAVE SQL_THREAD');
977 sleep $sleep_time;
978- mysql_send 'STOP SLAVE SQL_THREAD;';
979+ mysql_query($con, 'STOP SLAVE SQL_THREAD');
980
981- $open_temp_tables = get_slave_open_temp_tables();
982+ $open_temp_tables = get_slave_open_temp_tables($con);
983 print STDERR "$prefix: Slave open temp tables: $open_temp_tables\n";
984 if ( !$open_temp_tables ) {
985 print STDERR "$prefix: Slave is safe to backup\n";
986@@ -2644,29 +2617,25 @@
987 # Restart the slave if it was running at start
988 if ($sql_thread_started) {
989 print STDERR "Restarting slave SQL thread.\n";
990- mysql_send 'START SLAVE SQL_THREAD;';
991+ mysql_query($con, 'START SLAVE SQL_THREAD');
992 }
993
994 Die "Slave_open_temp_tables did not become zero after waiting $option_safe_slave_backup_timeout seconds";
995 }
996
997 sub get_slave_open_temp_tables {
998- my @lines;
999- mysql_send 'SHOW STATUS LIKE "slave_open_temp_tables"\G;';
1000- file_to_array($mysql_stdout, \@lines);
1001- my $last_value;
1002- for my $i ( 0..$#lines ) {
1003- $last_value = $i + 1
1004- if $lines[$i] =~ m/Variable_name: Slave_open_temp_tables/i;
1005- }
1006- Die "SHOW STATUS LIKE 'slave_open_temp_tables' did not return anything"
1007- unless $last_value;
1008-
1009- Die "Failed to get Slave_open_temp_tables from SHOW STATUS"
1010- unless defined $lines[$last_value];
1011-
1012- my ($n) = $lines[$last_value] =~ m/(\d+)/;
1013- return $n;
1014+ my $con = shift;
1015+
1016+ get_mysql_status($con);
1017+
1018+ if (!defined($con->{status}->{Slave_open_temp_tables})) {
1019+ Die "Failed to get Slave_open_temp_tables from SHOW STATUS"
1020+ }
1021+ if (!defined($con->{status}->{Slave_open_temp_tables}->{Value})) {
1022+ Die "SHOW STATUS LIKE 'slave_open_temp_tables' did not return anything"
1023+ }
1024+
1025+ return $con->{status}->{Slave_open_temp_tables}->{Value};
1026 }
1027
1028 =pod
1029
1030=== modified file 'test/t/bug729843.sh'
1031--- test/t/bug729843.sh 2012-06-04 18:29:48 +0000
1032+++ test/t/bug729843.sh 2013-02-05 11:56:22 +0000
1033@@ -12,9 +12,8 @@
1034 # Don't use run_cmd_* or innobackupex functions here to avoid logging
1035 # the full command line (including the password in plaintext)
1036 set +e
1037-$IB_BIN $IB_ARGS --password=secret $topdir/backup 2>&1 | tee $logfile
1038+$IB_BIN $IB_ARGS --password=secretpassword $topdir/backup 2>&1 | tee $logfile
1039 set -e
1040
1041 # Check that the password was not logged in plaintext
1042-run_cmd grep -- "--password=xxxxxxxx" $logfile
1043-run_cmd_expect_failure grep -- "--password=secret" $logfile
1044+run_cmd_expect_failure grep -- "secretpassword" $logfile
1045
1046=== modified file 'test/t/bug977101.sh'
1047--- test/t/bug977101.sh 2012-06-08 18:46:19 +0000
1048+++ test/t/bug977101.sh 2013-02-05 11:56:22 +0000
1049@@ -17,15 +17,16 @@
1050
1051 # Check that binlog info is correct with --safe-slave-backup
1052 innobackupex --no-timestamp --safe-slave-backup $topdir/backup
1053-egrep -q '^mysql-bin.000001[[:space:]]+[0-9]+[[:space:]]+$' \
1054+run_cmd egrep -q '^mysql-bin.000001[[:space:]]+[0-9]+[[:space:]]+$' \
1055 $topdir/backup/xtrabackup_binlog_info
1056
1057 # Check that both binlog info and slave info are correct with
1058 # --safe-slave-backup
1059 rm -rf $topdir/backup
1060 innobackupex --no-timestamp --slave-info --safe-slave-backup $topdir/backup
1061-egrep -q '^mysql-bin.000001[[:space:]]+[0-9]+[[:space:]]+$' \
1062+run_cmd egrep -q '^mysql-bin.000001[[:space:]]+[0-9]+[[:space:]]+$' \
1063 $topdir/backup/xtrabackup_binlog_info
1064-egrep -q '^CHANGE MASTER TO MASTER_LOG_FILE='\''mysql-bin.000001'\'', MASTER_LOG_POS=[0-9]+$' \
1065+run_cmd egrep -q \
1066+ '^CHANGE MASTER TO MASTER_LOG_FILE='\''mysql-bin.000001'\'', MASTER_LOG_POS=[0-9]+$' \
1067 $topdir/backup/xtrabackup_slave_info
1068

Subscribers

People subscribed via source and target branches

to all changes: