mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-29 09:32:47 +00:00
Fix and tests for 1062563 and 1063912: ptc+PXC bugs
This commit is contained in:
71
lib/Cxn.pm
71
lib/Cxn.pm
@@ -210,6 +210,77 @@ sub is_cluster_node {
|
||||
return $self->{is_cluster_node};
|
||||
}
|
||||
|
||||
sub same_cluster {
|
||||
my ($self, $cxn) = @_;
|
||||
return unless $self->is_cluster_node() && $cxn->is_cluster_node();
|
||||
return if $self->is_master_of($cxn) || $cxn->is_master_of($self);
|
||||
|
||||
my $sql = q{SHOW VARIABLES LIKE 'wsrep_cluster_name'};
|
||||
PTDEBUG && _d($sql);
|
||||
my (undef, $row) = $self->dbh->selectrow_array($sql);
|
||||
my (undef, $cxn_row) = $cxn->dbh->selectrow_array($sql);
|
||||
|
||||
return unless $row eq $cxn_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) = $self->dbh->selectrow_array($sql);
|
||||
my (undef, $cxn_addr) = $cxn->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://' && $cxn_addr eq 'gcomm://';
|
||||
|
||||
if ( $addr eq 'gcomm://' ) {
|
||||
$addr = $self->_find_full_gcomm_addr($self->dbh);
|
||||
}
|
||||
elsif ( $cxn_addr eq 'gcomm://' ) {
|
||||
$cxn_addr = $self->_find_full_gcomm_addr($cxn->dbh);
|
||||
}
|
||||
|
||||
# Meanwhile, if they have the same address, then
|
||||
# they are definitely part of the same cluster
|
||||
return 1 if $addr eq $cxn_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, $cxn) = @_;
|
||||
|
||||
my $cxn_dbh = $cxn->dbh;
|
||||
local $cxn_dbh->{FetchHashKeyName} = 'NAME_lc';
|
||||
my $slave_status = $cxn_dbh->selectrow_hashref(q{SHOW SLAVE STATUS});
|
||||
return unless ref($slave_status) eq 'HASH';
|
||||
my $port = $self->dsn->{P};
|
||||
my $host = $self->dsn->{h};
|
||||
|
||||
return 1 if $slave_status->{master_host} eq $host
|
||||
&& $slave_status->{master_port} eq $port;
|
||||
}
|
||||
|
||||
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*;};
|
||||
my $full_gcomm = "gcomm://$prov_addr";
|
||||
PTDEBUG && _d("gcomm address: ", $full_gcomm);
|
||||
return $full_gcomm;
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
my ($self) = @_;
|
||||
if ( $self->{dbh}
|
||||
|
@@ -39,6 +39,7 @@ $Data::Dumper::Quotekeys = 0;
|
||||
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
|
||||
use constant PTDEVDEBUG => $ENV{PTDEVDEBUG} || 0;
|
||||
|
||||
use IO::Socket::INET;
|
||||
$Sandbox::Percona::Toolkit::VERSION = "2.1.4";
|
||||
|
||||
my $trunk = $ENV{PERCONA_TOOLKIT_BRANCH};
|
||||
@@ -129,7 +130,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 ) {
|
||||
@@ -144,7 +145,7 @@ sub load_file {
|
||||
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 {
|
||||
@@ -412,6 +413,85 @@ sub is_cluster_node {
|
||||
: 0;
|
||||
}
|
||||
|
||||
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 2>&1`;
|
||||
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 2>&1`;
|
||||
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;
|
||||
}
|
||||
# ###########################################################################
|
||||
|
Reference in New Issue
Block a user