Merged fix-1062563-1063912-ptc-pxc-bugs and resolved conflicts, added the missing Percona::XtraDB::Cluster file

This commit is contained in:
Brian Fraser
2012-11-08 17:38:04 -03:00
10 changed files with 833 additions and 102 deletions

View File

@@ -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}

View 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;
}

View File

@@ -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;
}
# ###########################################################################