mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-09 07:30:02 +00:00
Merged fix-1062563-1063912-ptc-pxc-bugs and resolved conflicts, added the missing Percona::XtraDB::Cluster file
This commit is contained in:
@@ -1466,6 +1466,7 @@ use strict;
|
||||
use warnings FATAL => 'all';
|
||||
use English qw(-no_match_vars);
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
use constant {
|
||||
PTDEBUG => $ENV{PTDEBUG} || 0,
|
||||
PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0,
|
||||
@@ -1572,21 +1573,6 @@ sub name {
|
||||
return $self->{hostname} || $self->{dsn_name} || 'unknown host';
|
||||
}
|
||||
|
||||
sub is_cluster_node {
|
||||
my ($self) = @_;
|
||||
return $self->{is_cluster_node} if defined $self->{is_cluster_node};
|
||||
|
||||
my $sql = "SHOW VARIABLES LIKE 'wsrep_on'";
|
||||
PTDEBUG && _d($sql);
|
||||
my $row = $self->{dbh}->selectrow_arrayref($sql);
|
||||
PTDEBUG && _d(defined $row ? @$row : 'undef');
|
||||
$self->{is_cluster_node} = $row && $row->[1]
|
||||
? ($row->[1] eq 'ON' || $row->[1] eq '1')
|
||||
: 0;
|
||||
|
||||
return $self->{is_cluster_node};
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
my ($self) = @_;
|
||||
if ( $self->{dbh}
|
||||
|
28
bin/pt-kill
28
bin/pt-kill
@@ -2478,16 +2478,20 @@ $Data::Dumper::Indent = 1;
|
||||
$Data::Dumper::Sortkeys = 1;
|
||||
$Data::Dumper::Quotekeys = 0;
|
||||
|
||||
local $EVAL_ERROR;
|
||||
eval {
|
||||
require Quoter;
|
||||
};
|
||||
|
||||
sub new {
|
||||
my ( $class, %args ) = @_;
|
||||
my @required_args = qw(Quoter);
|
||||
foreach my $arg ( @required_args ) {
|
||||
die "I need a $arg argument" unless $args{$arg};
|
||||
}
|
||||
my $self = { %args };
|
||||
$self->{Quoter} ||= Quoter->new();
|
||||
return bless $self, $class;
|
||||
}
|
||||
|
||||
sub Quoter { shift->{Quoter} }
|
||||
|
||||
sub get_create_table {
|
||||
my ( $self, $dbh, $db, $tbl ) = @_;
|
||||
die "I need a dbh parameter" unless $dbh;
|
||||
@@ -4711,6 +4715,7 @@ use strict;
|
||||
use warnings FATAL => 'all';
|
||||
use English qw(-no_match_vars);
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
use constant {
|
||||
PTDEBUG => $ENV{PTDEBUG} || 0,
|
||||
PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0,
|
||||
@@ -4817,21 +4822,6 @@ sub name {
|
||||
return $self->{hostname} || $self->{dsn_name} || 'unknown host';
|
||||
}
|
||||
|
||||
sub is_cluster_node {
|
||||
my ($self) = @_;
|
||||
return $self->{is_cluster_node} if defined $self->{is_cluster_node};
|
||||
|
||||
my $sql = "SHOW VARIABLES LIKE 'wsrep_on'";
|
||||
PTDEBUG && _d($sql);
|
||||
my $row = $self->{dbh}->selectrow_arrayref($sql);
|
||||
PTDEBUG && _d(defined $row ? @$row : 'undef');
|
||||
$self->{is_cluster_node} = $row && $row->[1]
|
||||
? ($row->[1] eq 'ON' || $row->[1] eq '1')
|
||||
: 0;
|
||||
|
||||
return $self->{is_cluster_node};
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
my ($self) = @_;
|
||||
if ( $self->{dbh}
|
||||
|
@@ -2712,16 +2712,20 @@ $Data::Dumper::Indent = 1;
|
||||
$Data::Dumper::Sortkeys = 1;
|
||||
$Data::Dumper::Quotekeys = 0;
|
||||
|
||||
local $EVAL_ERROR;
|
||||
eval {
|
||||
require Quoter;
|
||||
};
|
||||
|
||||
sub new {
|
||||
my ( $class, %args ) = @_;
|
||||
my @required_args = qw(Quoter);
|
||||
foreach my $arg ( @required_args ) {
|
||||
die "I need a $arg argument" unless $args{$arg};
|
||||
}
|
||||
my $self = { %args };
|
||||
$self->{Quoter} ||= Quoter->new();
|
||||
return bless $self, $class;
|
||||
}
|
||||
|
||||
sub Quoter { shift->{Quoter} }
|
||||
|
||||
sub get_create_table {
|
||||
my ( $self, $dbh, $db, $tbl ) = @_;
|
||||
die "I need a dbh parameter" unless $dbh;
|
||||
@@ -3341,6 +3345,7 @@ use strict;
|
||||
use warnings FATAL => 'all';
|
||||
use English qw(-no_match_vars);
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
use constant {
|
||||
PTDEBUG => $ENV{PTDEBUG} || 0,
|
||||
PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0,
|
||||
@@ -3447,21 +3452,6 @@ sub name {
|
||||
return $self->{hostname} || $self->{dsn_name} || 'unknown host';
|
||||
}
|
||||
|
||||
sub is_cluster_node {
|
||||
my ($self) = @_;
|
||||
return $self->{is_cluster_node} if defined $self->{is_cluster_node};
|
||||
|
||||
my $sql = "SHOW VARIABLES LIKE 'wsrep_on'";
|
||||
PTDEBUG && _d($sql);
|
||||
my $row = $self->{dbh}->selectrow_arrayref($sql);
|
||||
PTDEBUG && _d(defined $row ? @$row : 'undef');
|
||||
$self->{is_cluster_node} = $row && $row->[1]
|
||||
? ($row->[1] eq 'ON' || $row->[1] eq '1')
|
||||
: 0;
|
||||
|
||||
return $self->{is_cluster_node};
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
my ($self) = @_;
|
||||
if ( $self->{dbh}
|
||||
|
@@ -21,6 +21,7 @@ BEGIN {
|
||||
OptionParser
|
||||
Mo
|
||||
Cxn
|
||||
Percona::XtraDB::Cluster
|
||||
Quoter
|
||||
VersionParser
|
||||
TableParser
|
||||
@@ -3211,6 +3212,7 @@ use strict;
|
||||
use warnings FATAL => 'all';
|
||||
use English qw(-no_match_vars);
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
use constant {
|
||||
PTDEBUG => $ENV{PTDEBUG} || 0,
|
||||
PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0,
|
||||
@@ -3317,21 +3319,6 @@ sub name {
|
||||
return $self->{hostname} || $self->{dsn_name} || 'unknown host';
|
||||
}
|
||||
|
||||
sub is_cluster_node {
|
||||
my ($self) = @_;
|
||||
return $self->{is_cluster_node} if defined $self->{is_cluster_node};
|
||||
|
||||
my $sql = "SHOW VARIABLES LIKE 'wsrep_on'";
|
||||
PTDEBUG && _d($sql);
|
||||
my $row = $self->{dbh}->selectrow_arrayref($sql);
|
||||
PTDEBUG && _d(defined $row ? @$row : 'undef');
|
||||
$self->{is_cluster_node} = $row && $row->[1]
|
||||
? ($row->[1] eq 'ON' || $row->[1] eq '1')
|
||||
: 0;
|
||||
|
||||
return $self->{is_cluster_node};
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
my ($self) = @_;
|
||||
if ( $self->{dbh}
|
||||
@@ -3357,6 +3344,104 @@ sub _d {
|
||||
# End Cxn package
|
||||
# ###########################################################################
|
||||
|
||||
# ###########################################################################
|
||||
# Percona::XtraDB::Cluster package
|
||||
# This package is a copy without comments from the original. The original
|
||||
# with comments and its test file can be found in the Bazaar repository at,
|
||||
# lib/Percona/XtraDB/Cluster.pm
|
||||
# t/lib/Percona/XtraDB/Cluster.t
|
||||
# See https://launchpad.net/percona-toolkit for more information.
|
||||
# ###########################################################################
|
||||
{
|
||||
|
||||
package Percona::XtraDB::Cluster;
|
||||
use Mo;
|
||||
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
|
||||
|
||||
sub is_cluster_node {
|
||||
my ($self, $cxn) = @_;
|
||||
return $self->{is_cluster_node}->{$cxn} if defined $self->{is_cluster_node}->{$cxn};
|
||||
|
||||
my $sql = "SHOW VARIABLES LIKE 'wsrep_on'";
|
||||
PTDEBUG && _d($sql);
|
||||
my $row = $cxn->dbh->selectrow_arrayref($sql);
|
||||
PTDEBUG && _d(defined $row ? @$row : 'undef');
|
||||
$self->{is_cluster_node}->{$cxn} = $row && $row->[1]
|
||||
? ($row->[1] eq 'ON' || $row->[1] eq '1')
|
||||
: 0;
|
||||
|
||||
return $self->{is_cluster_node}->{$cxn};
|
||||
}
|
||||
|
||||
sub same_cluster {
|
||||
my ($self, $cxn1, $cxn2) = @_;
|
||||
return unless $self->is_cluster_node($cxn1) && $self->is_cluster_node($cxn2);
|
||||
return if $self->is_master_of($cxn1, $cxn2) || $self->is_master_of($cxn2, $cxn1);
|
||||
|
||||
my $sql = q{SHOW VARIABLES LIKE 'wsrep_cluster_name'};
|
||||
PTDEBUG && _d($sql);
|
||||
my (undef, $row) = $cxn1->dbh->selectrow_array($sql);
|
||||
my (undef, $cxn2_row) = $cxn2->dbh->selectrow_array($sql);
|
||||
|
||||
return unless $row eq $cxn2_row;
|
||||
|
||||
$sql = q{SHOW VARIABLES LIKE 'wsrep_cluster_address'};
|
||||
PTDEBUG && _d($sql);
|
||||
my (undef, $addr) = $cxn1->dbh->selectrow_array($sql);
|
||||
my (undef, $cxn2_addr) = $cxn2->dbh->selectrow_array($sql);
|
||||
|
||||
return if $addr eq 'gcomm://' && $cxn2_addr eq 'gcomm://';
|
||||
|
||||
if ( $addr eq 'gcomm://' ) {
|
||||
$addr = $self->_find_full_gcomm_addr($cxn1->dbh);
|
||||
}
|
||||
elsif ( $cxn2_addr eq 'gcomm://' ) {
|
||||
$cxn2_addr = $self->_find_full_gcomm_addr($cxn2->dbh);
|
||||
}
|
||||
|
||||
return 1 if lc($addr) eq lc($cxn2_addr);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub is_master_of {
|
||||
my ($self, $cxn1, $cxn2) = @_;
|
||||
|
||||
my $cxn2_dbh = $cxn2->dbh;
|
||||
my $sql = q{SHOW SLAVE STATUS};
|
||||
PTDEBUG && _d($sql);
|
||||
local $cxn2_dbh->{FetchHashKeyName} = 'NAME_lc';
|
||||
my $slave_status = $cxn2_dbh->selectrow_hashref($sql);
|
||||
return unless ref($slave_status) eq 'HASH';
|
||||
|
||||
my $port = $cxn1->dsn->{P};
|
||||
return unless $slave_status->{master_port} eq $port;
|
||||
return 1 if $cxn1->dsn->{h} eq $slave_status->{master_host};
|
||||
|
||||
my $host = scalar gethostbyname($cxn1->dsn->{h});
|
||||
my $master_host = scalar gethostbyname($slave_status->{master_host});
|
||||
return 1 if $master_host eq $host;
|
||||
return;
|
||||
}
|
||||
|
||||
sub _find_full_gcomm_addr {
|
||||
my ($self, $dbh) = @_;
|
||||
|
||||
my $sql = q{SHOW VARIABLES LIKE 'wsrep_provider_options'};
|
||||
PTDEBUG && _d($sql);
|
||||
my (undef, $provider_opts) = $dbh->selectrow_array($sql);
|
||||
my ($prov_addr) = $provider_opts =~ m{\Qgmcast.listen_addr\E\s*=\s*tcp://([^:]+:[0-9]+)\s*;}i;
|
||||
my $full_gcomm = "gcomm://$prov_addr";
|
||||
PTDEBUG && _d("gcomm address: ", $full_gcomm);
|
||||
return $full_gcomm;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
# End Percona::XtraDB::Cluster package
|
||||
# ###########################################################################
|
||||
|
||||
# ###########################################################################
|
||||
# Quoter package
|
||||
# This package is a copy without comments from the original. The original
|
||||
@@ -5281,7 +5366,7 @@ sub _get_hash_func {
|
||||
}
|
||||
my ($dbh) = @args{@required_args};
|
||||
my $o = $self->{OptionParser};
|
||||
my @funcs = qw(CRC32 FNV1A_64 FNV_64 MD5 SHA1);
|
||||
my @funcs = qw(CRC32 FNV1A_64 FNV_64 MURMUR_HASH MD5 SHA1);
|
||||
|
||||
if ( my $func = $o->get('function') ) {
|
||||
unshift @funcs, $func;
|
||||
@@ -5302,7 +5387,7 @@ sub _get_hash_func {
|
||||
PTDEBUG && _d('Chosen hash func:', $result);
|
||||
return $func;
|
||||
}
|
||||
die $error || 'No hash functions (CRC32, MD5, etc.) are available';
|
||||
die($error || 'No hash functions (CRC32, MD5, etc.) are available');
|
||||
}
|
||||
|
||||
sub _get_crc_width {
|
||||
@@ -8535,6 +8620,9 @@ sub main {
|
||||
$have_time = sub { return 1; };
|
||||
}
|
||||
|
||||
# PXC helper class
|
||||
my $cluster = Percona::XtraDB::Cluster->new();
|
||||
|
||||
# ########################################################################
|
||||
# If this is not a dry run (--explain was not specified), then we're
|
||||
# going to checksum the tables, so do the necessary preparations and
|
||||
@@ -8604,12 +8692,42 @@ sub main {
|
||||
die $err if $err;
|
||||
}
|
||||
|
||||
if ( $master_cxn->is_cluster_node() && !@$slaves ) {
|
||||
die $master_cxn->name() . " is a cluster node but no other nodes "
|
||||
. "or regular replicas were found. Use --recursion-method=dsn "
|
||||
. "to specify the other nodes in the cluster.\n";
|
||||
if ( $cluster->is_cluster_node($master_cxn) ) {
|
||||
if ( !@$slaves ) {
|
||||
die $master_cxn->name() . " is a cluster node but no other nodes "
|
||||
. "or regular replicas were found. Use --recursion-method=dsn "
|
||||
. "to specify the other nodes in the cluster.\n";
|
||||
}
|
||||
else {
|
||||
my $err = '';
|
||||
for my $slave (@$slaves) {
|
||||
if ( $cluster->is_cluster_node($slave)
|
||||
&& !$cluster->same_cluster($slave, $master_cxn) ) {
|
||||
$err .= $slave->name() . " is a cluster node, but doesn't "
|
||||
. "belong to the same cluster as " . $master_cxn->name()
|
||||
. ". This is not currently supported; You can try "
|
||||
. "using --recursion-method=dsn to specify all nodes "
|
||||
. "in the slave cluster.\n"
|
||||
}
|
||||
}
|
||||
warn $err if $err;
|
||||
}
|
||||
}
|
||||
|
||||
elsif ( @$slaves ) {
|
||||
my $err = '';
|
||||
for my $slave (@$slaves) {
|
||||
if ( $cluster->is_cluster_node($slave) ) {
|
||||
$err .= $slave->name() . " is a cluster node, but "
|
||||
. $master_cxn->name() . " is not. This is not currently "
|
||||
. "supported; You can try to specify "
|
||||
. "all nodes in the cluster with "
|
||||
. "--recursion-method=dsn if you want them checksummed.\n"
|
||||
}
|
||||
}
|
||||
warn $err if $err;
|
||||
}
|
||||
|
||||
|
||||
if ( $o->get('check-slave-lag') ) {
|
||||
PTDEBUG && _d('Will use --check-slave-lag to check for slave lag');
|
||||
my $cxn = $make_cxn->(
|
||||
@@ -8629,7 +8747,8 @@ sub main {
|
||||
# to appear should be sufficient.
|
||||
@$slave_lag_cxns = grep {
|
||||
my $slave_cxn = $_;
|
||||
if ( $slave_cxn->is_cluster_node() ) {
|
||||
if ( $cluster->is_cluster_node($slave_cxn)
|
||||
&& $cluster->same_cluster($master_cxn, $slave_cxn) ) {
|
||||
warn "Not checking replica lag on " . $slave_cxn->name()
|
||||
. " because it is a cluster node.\n";
|
||||
0;
|
||||
@@ -8844,7 +8963,7 @@ sub main {
|
||||
. "(db, tbl, chunk, chunk_index,"
|
||||
. " lower_boundary, upper_boundary, this_cnt, this_crc) "
|
||||
. "SELECT"
|
||||
. ($master_cxn->is_cluster_node() ? ' /*!99997*/' : '')
|
||||
. ($cluster->is_cluster_node($master_cxn) ? ' /*!99997*/' : '')
|
||||
. " ?, ?, ?, ?, ?, ?,";
|
||||
my $past_cols = " COUNT(*), '0'";
|
||||
|
||||
|
16
lib/Cxn.pm
16
lib/Cxn.pm
@@ -36,6 +36,7 @@ use strict;
|
||||
use warnings FATAL => 'all';
|
||||
use English qw(-no_match_vars);
|
||||
use Scalar::Util qw(blessed);
|
||||
|
||||
use constant {
|
||||
PTDEBUG => $ENV{PTDEBUG} || 0,
|
||||
# Hostnames make testing less accurate. Tests need to see
|
||||
@@ -195,21 +196,6 @@ sub name {
|
||||
return $self->{hostname} || $self->{dsn_name} || 'unknown host';
|
||||
}
|
||||
|
||||
sub is_cluster_node {
|
||||
my ($self) = @_;
|
||||
return $self->{is_cluster_node} if defined $self->{is_cluster_node};
|
||||
|
||||
my $sql = "SHOW VARIABLES LIKE 'wsrep_on'";
|
||||
PTDEBUG && _d($sql);
|
||||
my $row = $self->{dbh}->selectrow_arrayref($sql);
|
||||
PTDEBUG && _d(defined $row ? @$row : 'undef');
|
||||
$self->{is_cluster_node} = $row && $row->[1]
|
||||
? ($row->[1] eq 'ON' || $row->[1] eq '1')
|
||||
: 0;
|
||||
|
||||
return $self->{is_cluster_node};
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
my ($self) = @_;
|
||||
if ( $self->{dbh}
|
||||
|
123
lib/Percona/XtraDB/Cluster.pm
Normal file
123
lib/Percona/XtraDB/Cluster.pm
Normal file
@@ -0,0 +1,123 @@
|
||||
# This program is copyright 2011 Percona Inc.
|
||||
# Feedback and improvements are welcome.
|
||||
#
|
||||
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar
|
||||
# systems, you can issue `man perlgpl' or `man perlartistic' to read these
|
||||
# licenses.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
# Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
# ###########################################################################
|
||||
# Percona::XtraDB::Cluster package
|
||||
# ###########################################################################
|
||||
{
|
||||
# Package: Percona::XtraDB::Cluster
|
||||
# Percona::XtraDB::Cluster has helper methods to deal with Percona XtraDB Cluster
|
||||
# based servers
|
||||
|
||||
package Percona::XtraDB::Cluster;
|
||||
use Mo;
|
||||
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
|
||||
|
||||
sub is_cluster_node {
|
||||
my ($self, $cxn) = @_;
|
||||
return $self->{is_cluster_node}->{$cxn} if defined $self->{is_cluster_node}->{$cxn};
|
||||
|
||||
my $sql = "SHOW VARIABLES LIKE 'wsrep_on'";
|
||||
PTDEBUG && _d($sql);
|
||||
my $row = $cxn->dbh->selectrow_arrayref($sql);
|
||||
PTDEBUG && _d(defined $row ? @$row : 'undef');
|
||||
$self->{is_cluster_node}->{$cxn} = $row && $row->[1]
|
||||
? ($row->[1] eq 'ON' || $row->[1] eq '1')
|
||||
: 0;
|
||||
|
||||
return $self->{is_cluster_node}->{$cxn};
|
||||
}
|
||||
|
||||
sub same_cluster {
|
||||
my ($self, $cxn1, $cxn2) = @_;
|
||||
return unless $self->is_cluster_node($cxn1) && $self->is_cluster_node($cxn2);
|
||||
return if $self->is_master_of($cxn1, $cxn2) || $self->is_master_of($cxn2, $cxn1);
|
||||
|
||||
my $sql = q{SHOW VARIABLES LIKE 'wsrep_cluster_name'};
|
||||
PTDEBUG && _d($sql);
|
||||
my (undef, $row) = $cxn1->dbh->selectrow_array($sql);
|
||||
my (undef, $cxn2_row) = $cxn2->dbh->selectrow_array($sql);
|
||||
|
||||
return unless $row eq $cxn2_row;
|
||||
|
||||
# Now it becomes tricky. Ostensibly clusters shouldn't have the
|
||||
# same name, but tell that to the world.
|
||||
$sql = q{SHOW VARIABLES LIKE 'wsrep_cluster_address'};
|
||||
PTDEBUG && _d($sql);
|
||||
my (undef, $addr) = $cxn1->dbh->selectrow_array($sql);
|
||||
my (undef, $cxn2_addr) = $cxn2->dbh->selectrow_array($sql);
|
||||
|
||||
# If they both have gcomm://, then they are both the first
|
||||
# node of a cluster, so they can't be in the same one.
|
||||
return if $addr eq 'gcomm://' && $cxn2_addr eq 'gcomm://';
|
||||
|
||||
if ( $addr eq 'gcomm://' ) {
|
||||
$addr = $self->_find_full_gcomm_addr($cxn1->dbh);
|
||||
}
|
||||
elsif ( $cxn2_addr eq 'gcomm://' ) {
|
||||
$cxn2_addr = $self->_find_full_gcomm_addr($cxn2->dbh);
|
||||
}
|
||||
|
||||
# Meanwhile, if they have the same address, then
|
||||
# they are definitely part of the same cluster
|
||||
return 1 if lc($addr) eq lc($cxn2_addr);
|
||||
|
||||
# However, this still leaves us with the issue that
|
||||
# the cluster addresses could look like this:
|
||||
# node1 -> node2, node2 -> node1,
|
||||
# or
|
||||
# node1 -> node2 addr,
|
||||
# node2 -> node3 addr,
|
||||
# node3 -> node1 addr,
|
||||
# TODO No clue what to do here
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub is_master_of {
|
||||
my ($self, $cxn1, $cxn2) = @_;
|
||||
|
||||
my $cxn2_dbh = $cxn2->dbh;
|
||||
my $sql = q{SHOW SLAVE STATUS};
|
||||
PTDEBUG && _d($sql);
|
||||
local $cxn2_dbh->{FetchHashKeyName} = 'NAME_lc';
|
||||
my $slave_status = $cxn2_dbh->selectrow_hashref($sql);
|
||||
return unless ref($slave_status) eq 'HASH';
|
||||
|
||||
my $port = $cxn1->dsn->{P};
|
||||
return unless $slave_status->{master_port} eq $port;
|
||||
return 1 if $cxn1->dsn->{h} eq $slave_status->{master_host};
|
||||
|
||||
# They might be the same but in different format
|
||||
my $host = scalar gethostbyname($cxn1->dsn->{h});
|
||||
my $master_host = scalar gethostbyname($slave_status->{master_host});
|
||||
return 1 if $master_host eq $host;
|
||||
return;
|
||||
}
|
||||
|
||||
sub _find_full_gcomm_addr {
|
||||
my ($self, $dbh) = @_;
|
||||
|
||||
my $sql = q{SHOW VARIABLES LIKE 'wsrep_provider_options'};
|
||||
PTDEBUG && _d($sql);
|
||||
my (undef, $provider_opts) = $dbh->selectrow_array($sql);
|
||||
my ($prov_addr) = $provider_opts =~ m{\Qgmcast.listen_addr\E\s*=\s*tcp://([^:]+:[0-9]+)\s*;}i;
|
||||
my $full_gcomm = "gcomm://$prov_addr";
|
||||
PTDEBUG && _d("gcomm address: ", $full_gcomm);
|
||||
return $full_gcomm;
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
@@ -39,6 +39,8 @@ $Data::Dumper::Quotekeys = 0;
|
||||
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
|
||||
use constant PTDEVDEBUG => $ENV{PTDEVDEBUG} || 0;
|
||||
|
||||
use IO::Socket::INET;
|
||||
|
||||
my $trunk = $ENV{PERCONA_TOOLKIT_BRANCH};
|
||||
|
||||
my %port_for = (
|
||||
@@ -74,7 +76,7 @@ sub use {
|
||||
return if !defined $cmd || !$cmd;
|
||||
my $use = $self->_use_for($server) . " $cmd";
|
||||
PTDEBUG && _d('"Executing', $use, 'on', $server);
|
||||
my $out = `$use 2>&1`;
|
||||
my $out = `$use`;
|
||||
if ( $? >> 8 ) {
|
||||
die "Failed to execute $cmd on $server: $out";
|
||||
}
|
||||
@@ -130,7 +132,7 @@ sub get_dbh_for {
|
||||
}
|
||||
|
||||
sub load_file {
|
||||
my ( $self, $server, $file, $use_db ) = @_;
|
||||
my ( $self, $server, $file, $use_db, %args ) = @_;
|
||||
_check_server($server);
|
||||
$file = "$trunk/$file";
|
||||
if ( !-f $file ) {
|
||||
@@ -141,11 +143,11 @@ sub load_file {
|
||||
|
||||
my $use = $self->_use_for($server) . " $d < $file";
|
||||
PTDEBUG && _d('Loading', $file, 'on', $server, ':', $use);
|
||||
my $out = `$use 2>&1`;
|
||||
my $out = `$use`;
|
||||
if ( $? >> 8 ) {
|
||||
die "Failed to execute $file on $server: $out";
|
||||
}
|
||||
$self->wait_for_slaves();
|
||||
$self->wait_for_slaves() unless $args{no_wait};
|
||||
}
|
||||
|
||||
sub _use_for {
|
||||
@@ -420,6 +422,85 @@ sub can_load_data {
|
||||
return ($output || '') =~ /1/;
|
||||
}
|
||||
|
||||
sub set_as_slave {
|
||||
my ($self, $server, $master_server, @extras) = @_;
|
||||
PTDEBUG && _d("Setting $server as slave of $master_server");
|
||||
my $master_port = $port_for{$master_server};
|
||||
my $sql = join ", ", qq{change master to master_host='127.0.0.1'},
|
||||
qq{master_user='msandbox'},
|
||||
qq{master_password='msandbox'},
|
||||
qq{master_port=$master_port},
|
||||
@extras;
|
||||
for my $sql_to_run ($sql, "start slave") {
|
||||
my $out = $self->use($server, qq{-e "$sql_to_run"});
|
||||
PTDEBUG && _d($out);
|
||||
}
|
||||
}
|
||||
|
||||
sub start_sandbox {
|
||||
my ($self, $mode, $server, $master_server) = @_;
|
||||
my $port = $port_for{$server};
|
||||
my $master_port = $master_server ? $port_for{$master_server} : '';
|
||||
my $out = `$trunk/sandbox/start-sandbox $mode $port $master_port`;
|
||||
die $out if $CHILD_ERROR;
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub stop_sandbox {
|
||||
my ($self, @sandboxes) = @_;
|
||||
my @ports = @port_for{@sandboxes};
|
||||
my $out = `$trunk/sandbox/stop-sandbox @ports`;
|
||||
die $out if $CHILD_ERROR;
|
||||
return $out;
|
||||
}
|
||||
|
||||
sub start_cluster {
|
||||
my ($self, %args) = @_;
|
||||
my $cluster_size = $args{cluster_size} || 3;
|
||||
|
||||
my $out = '';
|
||||
|
||||
my ($node1, @nodes) = map {
|
||||
my $node_name = "node$_";
|
||||
$node_name = "_$node_name" while exists $port_for{$node_name};
|
||||
$port_for{$node_name} = $self->_get_unused_port();
|
||||
$node_name
|
||||
} 1..$cluster_size;
|
||||
|
||||
local $ENV{CLUSTER_NAME} = $args{cluster_name} if $args{cluster_name};
|
||||
$self->start_sandbox("cluster", $node1);
|
||||
for my $node ( @nodes ) {
|
||||
$self->start_sandbox("cluster", $node, $node1);
|
||||
}
|
||||
|
||||
return ($node1, @nodes);
|
||||
}
|
||||
|
||||
# Lifted from Nginx::Test on CPAN
|
||||
sub _get_unused_port {
|
||||
my $port = 50000 + int (rand() * 5000);
|
||||
|
||||
while ($port++ < 64000) {
|
||||
my $sock = IO::Socket::INET->new (
|
||||
Listen => 5,
|
||||
LocalAddr => '127.0.0.1',
|
||||
LocalPort => $port,
|
||||
Proto => 'tcp',
|
||||
ReuseAddr => 1
|
||||
) or next;
|
||||
|
||||
$sock->close;
|
||||
return $port;
|
||||
}
|
||||
|
||||
die "Cannot find an open port";
|
||||
}
|
||||
|
||||
sub port_for {
|
||||
my ($self, $server) = @_;
|
||||
return $port_for{$server};
|
||||
}
|
||||
|
||||
1;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
10
t/lib/Cxn.t
10
t/lib/Cxn.t
@@ -9,7 +9,8 @@ BEGIN {
|
||||
use strict;
|
||||
use warnings FATAL => 'all';
|
||||
use English qw(-no_match_vars);
|
||||
use Test::More tests => 19;
|
||||
use Test::More;
|
||||
use Data::Dumper;
|
||||
|
||||
use Sandbox;
|
||||
use OptionParser;
|
||||
@@ -17,8 +18,7 @@ use DSNParser;
|
||||
use Quoter;
|
||||
use PerconaTest;
|
||||
use Cxn;
|
||||
|
||||
use Data::Dumper;
|
||||
use VersionParser;
|
||||
|
||||
my $q = new Quoter();
|
||||
my $dp = new DSNParser(opts=>$dsn_opts);
|
||||
@@ -248,12 +248,10 @@ is_deeply(
|
||||
"Default cxn inherits default connection options"
|
||||
);
|
||||
|
||||
@ARGV = ();
|
||||
$o->get_opts();
|
||||
|
||||
# #############################################################################
|
||||
# Done.
|
||||
# #############################################################################
|
||||
$master_dbh->disconnect() if $master_dbh;
|
||||
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
|
||||
done_testing;
|
||||
exit;
|
||||
|
216
t/lib/Percona/XtraDB/Cluster.t
Normal file
216
t/lib/Percona/XtraDB/Cluster.t
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
BEGIN {
|
||||
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
|
||||
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
|
||||
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
|
||||
};
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => 'all';
|
||||
use English qw(-no_match_vars);
|
||||
use Test::More;
|
||||
use Data::Dumper;
|
||||
|
||||
# Hostnames make testing less accurate. Tests need to see
|
||||
# that such-and-such happened on specific slave hosts, but
|
||||
# the sandbox servers are all on one host so all slaves have
|
||||
# the same hostname.
|
||||
$ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} = 1;
|
||||
|
||||
use Sandbox;
|
||||
use OptionParser;
|
||||
use DSNParser;
|
||||
use Quoter;
|
||||
use PerconaTest;
|
||||
use Cxn;
|
||||
use VersionParser;
|
||||
|
||||
use Percona::XtraDB::Cluster;
|
||||
|
||||
my $q = new Quoter();
|
||||
my $dp = new DSNParser(opts=>$dsn_opts);
|
||||
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
|
||||
my $master_dbh = $sb->get_dbh_for('master');
|
||||
|
||||
my $cluster = Percona::XtraDB::Cluster->new();
|
||||
|
||||
if ( !$master_dbh ) {
|
||||
plan skip_all => 'Cannot connect to sandbox master';
|
||||
}
|
||||
|
||||
my $o = new OptionParser(description => 'Cxn');
|
||||
$o->get_specs("$trunk/bin/pt-table-checksum");
|
||||
$o->get_opts();
|
||||
$dp->prop('set-vars', $o->get('set-vars'));
|
||||
|
||||
sub make_cxn {
|
||||
my (%args) = @_;
|
||||
$o->get_opts();
|
||||
return new Cxn(
|
||||
OptionParser => $o,
|
||||
DSNParser => $dp,
|
||||
%args,
|
||||
);
|
||||
}
|
||||
|
||||
local @ARGV = ();
|
||||
$o->get_opts();
|
||||
|
||||
diag("Starting master1");
|
||||
$sb->start_sandbox("master", "master1");
|
||||
|
||||
my ($master_cxn, $slave1_cxn, $master1_cxn)
|
||||
= map {
|
||||
my $cxn = make_cxn( dsn_string => $sb->dsn_for($_) );
|
||||
$cxn->connect();
|
||||
$cxn;
|
||||
} qw( master slave1 master1 );
|
||||
|
||||
for my $cxn ( $master_cxn, $slave1_cxn, $master1_cxn ) {
|
||||
ok(
|
||||
!$cluster->is_cluster_node($cxn),
|
||||
"is_cluster_node works correctly for non-nodes " . $cxn->name
|
||||
);
|
||||
}
|
||||
|
||||
ok($cluster->is_master_of($master_cxn, $slave1_cxn), "is_master_of(master, slave1) is true");
|
||||
ok(!$cluster->is_master_of($slave1_cxn, $master_cxn), "is_master_of(slave1, master) is false");
|
||||
|
||||
my $db_flavor = VersionParser->new($master_dbh)->flavor();
|
||||
SKIP: {
|
||||
skip "PXC-only test", 17
|
||||
unless $db_flavor =~ /XtraDB Cluster/;
|
||||
|
||||
diag("Starting a 1-node PXC");
|
||||
my ($node) = $sb->start_cluster(cluster_size => 1);
|
||||
|
||||
my $cxn1 = make_cxn( dsn_string => $sb->dsn_for($node) );
|
||||
$cxn1->connect();
|
||||
ok(
|
||||
$cluster->is_cluster_node($cxn1),
|
||||
"is_cluster_node works correctly for cluster nodes"
|
||||
);
|
||||
|
||||
ok(
|
||||
!$cluster->is_master_of($master1_cxn, $cxn1),
|
||||
"->is_master_of works correctly for a server unrelated to a cluster"
|
||||
);
|
||||
|
||||
diag("Setting node as a slave of master1");
|
||||
$sb->set_as_slave($node, "master1");
|
||||
ok(
|
||||
$cluster->is_master_of($master1_cxn, $cxn1),
|
||||
"->is_master_of works correctly for master -> cluster"
|
||||
);
|
||||
ok(
|
||||
!$cluster->is_master_of($cxn1, $master1_cxn),
|
||||
"...and the inverse returns the expected result"
|
||||
);
|
||||
ok(
|
||||
!$cluster->same_cluster($master1_cxn, $cxn1),
|
||||
"->same_cluster works for master -> cluster"
|
||||
);
|
||||
diag("Restarting the cluster");
|
||||
diag($sb->stop_sandbox($node));
|
||||
($node) = $sb->start_cluster(cluster_size => 1);
|
||||
$cxn1 = make_cxn( dsn_string => $sb->dsn_for($node) );
|
||||
$cxn1->connect();
|
||||
|
||||
diag("Setting master1 as a slave of the node");
|
||||
$sb->set_as_slave("master1", $node);
|
||||
ok(
|
||||
$cluster->is_master_of($cxn1, $master1_cxn),
|
||||
"->is_master_of works correctly for cluster -> master"
|
||||
);
|
||||
ok(
|
||||
!$cluster->is_master_of($master1_cxn, $cxn1),
|
||||
"...and the inverse returns the expected result"
|
||||
);
|
||||
|
||||
ok(
|
||||
!$cluster->same_cluster($cxn1, $master1_cxn),
|
||||
"->same_cluster works for cluster -> master"
|
||||
);
|
||||
|
||||
diag("Starting a 2-node PXC");
|
||||
my ($node2, $node3) = $sb->start_cluster(cluster_size => 2);
|
||||
|
||||
my $cxn2 = make_cxn( dsn_string => $sb->dsn_for($node2) );
|
||||
$cxn2->connect();
|
||||
my $cxn3 = make_cxn( dsn_string => $sb->dsn_for($node3) );
|
||||
$cxn3->connect();
|
||||
ok(
|
||||
$cluster->is_cluster_node($cxn2),
|
||||
"is_cluster_node correctly finds that this node is part of a cluster"
|
||||
);
|
||||
|
||||
ok(
|
||||
!$cluster->same_cluster($cxn1, $cxn2),
|
||||
"and same_cluster correctly finds that they don't belong to the same cluster, even when they have the same cluster name"
|
||||
);
|
||||
|
||||
ok(
|
||||
$cluster->same_cluster($cxn2, $cxn3),
|
||||
"...but does find that they are in the same cluster, even if one is node1"
|
||||
);
|
||||
|
||||
TODO: {
|
||||
local $::TODO = "Should detected that (cluster1.node1) (cluster2.node2) come from different clusters, but doesn't";
|
||||
ok(
|
||||
!$cluster->same_cluster($cxn1, $cxn3),
|
||||
"...same_cluster works correctly when they have the same cluster names"
|
||||
);
|
||||
}
|
||||
|
||||
diag("Making the second cluster a slave of the first");
|
||||
$sb->set_as_slave($node2, $node);
|
||||
ok($cluster->is_master_of($cxn1, $cxn2), "is_master_of(cluster1, cluster2) works");
|
||||
|
||||
ok(
|
||||
!$cluster->same_cluster($cxn1, $cxn2),
|
||||
"...same_cluster works correctly when they are cluster1.node1.master -> cluster2.node1.slave"
|
||||
);
|
||||
|
||||
diag($sb->stop_sandbox($node2, $node3));
|
||||
diag("Starting a 3-node cluster");
|
||||
my $node4;
|
||||
($node2, $node3, $node4)
|
||||
= $sb->start_cluster(
|
||||
cluster_size => 3,
|
||||
cluster_name => "pt_cxn_test",
|
||||
);
|
||||
$cxn2 = make_cxn( dsn_string => $sb->dsn_for($node2) );
|
||||
$cxn2->connect();
|
||||
$cxn3 = make_cxn( dsn_string => $sb->dsn_for($node3) );
|
||||
$cxn3->connect();
|
||||
my $cxn4 = make_cxn( dsn_string => $sb->dsn_for($node4) );
|
||||
$cxn4->connect();
|
||||
|
||||
ok(
|
||||
!$cluster->same_cluster($cxn1, $cxn2),
|
||||
"...same_cluster works correctly when they have different cluster names & the are both gcomm"
|
||||
);
|
||||
|
||||
ok(
|
||||
!$cluster->same_cluster($cxn1, $cxn3),
|
||||
"same_cluster detects that (cluster1.node1) (cluster2.node2) come from different clusters if they have different cluster_names"
|
||||
);
|
||||
|
||||
ok(
|
||||
$cluster->same_cluster($cxn2, $cxn3),
|
||||
"sanity check: but still finds that nodes in the same cluster belong together"
|
||||
);
|
||||
|
||||
diag($sb->stop_sandbox($node, $node2, $node3, $node4));
|
||||
}
|
||||
|
||||
diag($sb->stop_sandbox("master1"));
|
||||
|
||||
# #############################################################################
|
||||
# Done.
|
||||
# #############################################################################
|
||||
$master_dbh->disconnect() if $master_dbh;
|
||||
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
|
||||
done_testing;
|
||||
exit;
|
242
t/pt-table-checksum/pxc.t
Normal file
242
t/pt-table-checksum/pxc.t
Normal file
@@ -0,0 +1,242 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
BEGIN {
|
||||
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
|
||||
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
|
||||
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
|
||||
};
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => 'all';
|
||||
use English qw(-no_match_vars);
|
||||
use Test::More;
|
||||
use Data::Dumper;
|
||||
use File::Spec::Functions;
|
||||
|
||||
# Hostnames make testing less accurate. Tests need to see
|
||||
# that such-and-such happened on specific slave hosts, but
|
||||
# the sandbox servers are all on one host so all slaves have
|
||||
# the same hostname.
|
||||
$ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} = 1;
|
||||
|
||||
use PerconaTest;
|
||||
use Sandbox;
|
||||
|
||||
require "$trunk/bin/pt-table-checksum";
|
||||
# Do this after requiring ptc, since it uses Mo
|
||||
require VersionParser;
|
||||
|
||||
my $dp = new DSNParser(opts=>$dsn_opts);
|
||||
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
|
||||
my $master_dbh = $sb->get_dbh_for('master');
|
||||
|
||||
my $db_flavor = VersionParser->new($master_dbh)->flavor();
|
||||
|
||||
if ( !$master_dbh ) {
|
||||
plan skip_all => 'Cannot connect to sandbox master';
|
||||
}
|
||||
elsif ( $db_flavor !~ /XtraDB Cluster/ ) {
|
||||
plan skip_all => "PXC tests";
|
||||
}
|
||||
|
||||
# The sandbox servers run with lock_wait_timeout=3 and it's not dynamic
|
||||
# so we need to specify --lock-wait-timeout=3 else the tool will die.
|
||||
my $master_dsn = 'h=127.1,P=12345,u=msandbox,p=msandbox';
|
||||
my @args = ($master_dsn, qw(--lock-wait-timeout 3));
|
||||
my $output;
|
||||
my $exit_status;
|
||||
my $sample = "t/pt-table-checksum/samples/";
|
||||
|
||||
# #############################################################################
|
||||
# pt-table-checksum v2.1.4 doesn't detect diffs on Percona XtraDB Cluster nodes
|
||||
# https://bugs.launchpad.net/percona-toolkit/+bug/1062563
|
||||
# #############################################################################
|
||||
|
||||
sub make_dbh_differ {
|
||||
my ($dbh, @vals) = @_;
|
||||
@vals = (@vals ? @vals : 1);
|
||||
# Make them differ...
|
||||
$dbh->do("DROP DATABASE IF EXISTS bug_1062563");
|
||||
$dbh->do("CREATE DATABASE bug_1062563");
|
||||
$dbh->do("CREATE TABLE bug_1062563.ptc_pxc (i int)");
|
||||
|
||||
# Now make this node different from the rest
|
||||
$dbh->do("set sql_log_bin=0");
|
||||
$dbh->do("INSERT INTO bug_1062563.ptc_pxc (i) VALUES ($_)") for @vals;
|
||||
$dbh->do("set sql_log_bin=1");
|
||||
}
|
||||
|
||||
diag("Creating a 5-node PXC cluster...");
|
||||
my @nodes = $sb->start_cluster(cluster_size => 5);
|
||||
diag("Nodes: ", Dumper( { map { $_ => $sb->port_for($_) } @nodes } ));
|
||||
|
||||
my $node2 = $nodes[1];
|
||||
my $node2_dbh = $sb->get_dbh_for($node2);
|
||||
|
||||
my $node2_slave = "master3";
|
||||
|
||||
diag("Creating a slave for $node2...");
|
||||
{
|
||||
local $ENV{BINLOG_FORMAT} = 'ROW';
|
||||
diag($sb->start_sandbox("slave", $node2_slave, $node2));
|
||||
}
|
||||
my $node_slave_dbh = $sb->get_dbh_for($node2_slave);
|
||||
|
||||
make_dbh_differ($node2_dbh);
|
||||
|
||||
# And make its slave differ as well
|
||||
PerconaTest::wait_for_table($sb->get_dbh_for($nodes[-1]), "bug_1062563.ptc_pxc");
|
||||
PerconaTest::wait_for_table($node_slave_dbh, "bug_1062563.ptc_pxc");
|
||||
$node_slave_dbh->do("INSERT INTO bug_1062563.ptc_pxc (i) VALUES ($_)") for 3, 4;
|
||||
|
||||
my $dsns_table_sql = catfile(qw(t lib samples MasterSlave dsn_table.sql));
|
||||
$sb->load_file($node2, $dsns_table_sql, undef, no_wait => 1);
|
||||
$node2_dbh->do("DELETE FROM dsn_t.dsns"); # Delete 12346
|
||||
my $sth = $node2_dbh->prepare("INSERT INTO dsn_t.dsns VALUES (null, null, ?)");
|
||||
for my $dsn ( map { $sb->dsn_for($_) } @nodes[0,2..$#nodes], $node2_slave ) {
|
||||
$sth->execute($dsn);
|
||||
}
|
||||
|
||||
my $node2_dsn = $sb->dsn_for($node2);
|
||||
$output = output(
|
||||
sub { pt_table_checksum::main(
|
||||
$node2_dsn, qw(--lock-wait-timeout 3),
|
||||
qw(-d bug_1062563),
|
||||
'--recursion-method', "dsn=D=dsn_t,t=dsns"
|
||||
) },
|
||||
stderr => 1,
|
||||
);
|
||||
|
||||
is(
|
||||
PerconaTest::count_checksum_results($output, 'diffs'),
|
||||
1,
|
||||
"Bug 1062563: Detects diffs between PXC nodes"
|
||||
) or diag($output);
|
||||
|
||||
my @cluster_nodes = $output =~ /(because it is a cluster node)/g;
|
||||
is(
|
||||
scalar(@cluster_nodes),
|
||||
4,
|
||||
"Skips all the cluster nodes in the dsns table"
|
||||
) or diag($output);
|
||||
|
||||
# Now try with just the slave
|
||||
|
||||
$node2_dbh->do("DELETE FROM dsn_t.dsns");
|
||||
$sth->execute($sb->dsn_for($node2_slave));
|
||||
|
||||
$output = output(
|
||||
sub { pt_table_checksum::main(
|
||||
$node2_dsn, qw(--lock-wait-timeout 3),
|
||||
qw(--chunk-size 1),
|
||||
qw(-d bug_1062563),
|
||||
'--recursion-method', "dsn=D=dsn_t,t=dsns"
|
||||
) },
|
||||
stderr => 1,
|
||||
);
|
||||
|
||||
is(
|
||||
PerconaTest::count_checksum_results($output, 'diffs'),
|
||||
1,
|
||||
"Bug 1062563: Detects diffs on slaves where the master is a PXC node"
|
||||
) or diag($output);
|
||||
|
||||
$sth->finish();
|
||||
diag("Stopping the PXC cluster and the slave...");
|
||||
$sb->stop_sandbox($node2_slave, @nodes);
|
||||
|
||||
# Now checking that cluster -> cluster works
|
||||
|
||||
diag("Creating two 3-node clusters...");
|
||||
my @cluster1 = $sb->start_cluster(cluster_size => 3, cluster_name => "pt_test_cluster_1");
|
||||
my @cluster2 = $sb->start_cluster(cluster_size => 3, cluster_name => "pt_test_cluster_2");
|
||||
diag("Cluster 1: ", Dumper( { map { $_ => $sb->port_for($_) } @cluster1 } ));
|
||||
diag("Cluster 2: ", Dumper( { map { $_ => $sb->port_for($_) } @cluster2 } ));
|
||||
|
||||
$sb->set_as_slave($cluster2[0], $cluster1[0]);
|
||||
|
||||
my $cluster1_dbh = $sb->get_dbh_for($cluster1[0]);
|
||||
my $cluster2_dbh = $sb->get_dbh_for($cluster2[0]);
|
||||
make_dbh_differ($cluster1_dbh);
|
||||
|
||||
# And make its slave differ as well
|
||||
PerconaTest::wait_for_table($sb->get_dbh_for($cluster2[-1]), "bug_1062563.ptc_pxc");
|
||||
PerconaTest::wait_for_table($sb->get_dbh_for($cluster1[-1]), "bug_1062563.ptc_pxc");
|
||||
PerconaTest::wait_for_table($cluster2_dbh, "bug_1062563.ptc_pxc");
|
||||
$cluster2_dbh->do("INSERT INTO bug_1062563.ptc_pxc (i) VALUES ($_)") for 3, 4;
|
||||
|
||||
$dsns_table_sql = catfile(qw(t lib samples MasterSlave dsn_table.sql));
|
||||
$sb->load_file($cluster1[0], $dsns_table_sql, undef, no_wait => 1);
|
||||
$cluster1_dbh->do("DELETE FROM dsn_t.dsns"); # Delete 12346
|
||||
$sth = $cluster1_dbh->prepare("INSERT INTO dsn_t.dsns VALUES (null, null, ?)");
|
||||
for my $dsn ( map { $sb->dsn_for($_) } @cluster1[1..$#cluster1], $cluster2[0] ) {
|
||||
$sth->execute($dsn);
|
||||
}
|
||||
$sth->finish();
|
||||
|
||||
my $cluster1_dsn = $sb->dsn_for($cluster1[0]);
|
||||
$output = output(
|
||||
sub { pt_table_checksum::main(
|
||||
$cluster1_dsn, qw(--lock-wait-timeout 3),
|
||||
qw(-d bug_1062563),
|
||||
'--recursion-method', "dsn=D=dsn_t,t=dsns"
|
||||
) },
|
||||
stderr => 1,
|
||||
);
|
||||
|
||||
is(
|
||||
PerconaTest::count_checksum_results($output, 'diffs'),
|
||||
1,
|
||||
"Bug 1062563: Detects diffs between PXC nodes when cluster -> cluster"
|
||||
) or diag($output);
|
||||
|
||||
like(
|
||||
$output,
|
||||
qr/is a cluster node, but doesn't belong to the same cluster as/, #'
|
||||
"Shows a warning when cluster -> cluster"
|
||||
) or diag($output);
|
||||
|
||||
diag("Starting master1...");
|
||||
$sb->start_sandbox("master", "master1");
|
||||
diag("Setting it as master of a node in the first cluster");
|
||||
$sb->set_as_slave($cluster1[0], "master1");
|
||||
|
||||
my $master1_dbh = $sb->get_dbh_for("master1");
|
||||
make_dbh_differ($master1_dbh, 10..50);
|
||||
|
||||
my $master1_dsn = $sb->dsn_for("master1");
|
||||
$output = output(
|
||||
sub { pt_table_checksum::main(
|
||||
$master1_dsn, qw(--lock-wait-timeout 3),
|
||||
qw(-d bug_1062563),
|
||||
) },
|
||||
stderr => 1,
|
||||
);
|
||||
|
||||
is(
|
||||
PerconaTest::count_checksum_results($output, 'diffs'),
|
||||
1,
|
||||
"Bug 1062563: Detects diffs when master -> cluster"
|
||||
) or diag($output);
|
||||
|
||||
is(
|
||||
PerconaTest::count_checksum_results($output, 'rows'),
|
||||
41,
|
||||
"Bug 1062563: Correct number of rows for master -> cluster"
|
||||
) or diag($output);
|
||||
|
||||
like(
|
||||
$output,
|
||||
qr/is a cluster node, but .*? is not. This is not currently supported/,
|
||||
"Shows a warning when master -> cluster"
|
||||
) or diag($output);
|
||||
|
||||
diag("Stopping both clusters and master1...");
|
||||
$sb->stop_sandbox(@cluster1, @cluster2, "master1");
|
||||
|
||||
# #############################################################################
|
||||
# Done.
|
||||
# #############################################################################
|
||||
$sb->wipe_clean($master_dbh);
|
||||
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
|
||||
done_testing;
|
Reference in New Issue
Block a user