From 187506686869c8230f5489008ec505006b756413 Mon Sep 17 00:00:00 2001 From: "Brian Fraser fraserb@gmail.com" <> Date: Wed, 17 Oct 2012 12:58:48 -0300 Subject: [PATCH 1/6] Fix and tests for 1062563 and 1063912: ptc+PXC bugs --- bin/pt-table-checksum | 40 +++++++- lib/Cxn.pm | 71 ++++++++++++++ lib/Sandbox.pm | 84 +++++++++++++++- t/lib/Cxn.t | 138 ++++++++++++++++++++++++++- t/pt-table-checksum/bugs.t | 191 +++++++++++++++++++++++++++++++++++++ 5 files changed, 516 insertions(+), 8 deletions(-) diff --git a/bin/pt-table-checksum b/bin/pt-table-checksum index c783e2bb..992023fd 100755 --- a/bin/pt-table-checksum +++ b/bin/pt-table-checksum @@ -8586,10 +8586,39 @@ sub main { ); PTDEBUG && _d(scalar @$slaves, 'slaves found'); - 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 ( $master_cxn->is_cluster_node() ) { + 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 ( $slave->is_cluster_node() + && !$slave->same_cluster($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 ( $slave->is_cluster_node() ) { + $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') ) { @@ -8611,7 +8640,8 @@ sub main { # to appear should be sufficient. @$slave_lag_cxns = grep { my $slave_cxn = $_; - if ( $slave_cxn->is_cluster_node() ) { + if ( $slave_cxn->is_cluster_node() + && $master_cxn->same_cluster($slave_cxn) ) { warn "Not checking replica lag on " . $slave_cxn->name() . " because it is a cluster node.\n"; 0; diff --git a/lib/Cxn.pm b/lib/Cxn.pm index 5efca626..eeef3006 100644 --- a/lib/Cxn.pm +++ b/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} diff --git a/lib/Sandbox.pm b/lib/Sandbox.pm index a00a0a86..eccc9651 100644 --- a/lib/Sandbox.pm +++ b/lib/Sandbox.pm @@ -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; } # ########################################################################### diff --git a/t/lib/Cxn.t b/t/lib/Cxn.t index 75355b76..c644919e 100644 --- a/t/lib/Cxn.t +++ b/t/lib/Cxn.t @@ -9,7 +9,7 @@ BEGIN { use strict; use warnings FATAL => 'all'; use English qw(-no_match_vars); -use Test::More tests => 19; +use Test::More; use Sandbox; use OptionParser; @@ -251,9 +251,145 @@ is_deeply( @ARGV = (); $o->get_opts(); +diag("Starting master1"); +$sb->start_sandbox("master", "master1"); + +$cxn = make_cxn( dsn_string => $sb->dsn_for("master1") ); +$cxn->connect(); +ok( + !$cxn->is_cluster_node(), + "is_cluster_node works correctly for non-nodes" +); + +use VersionParser; +my $db_flavor = VersionParser->new($master_dbh)->flavor(); +SKIP: { + skip "PXC-only test", 1 + 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( + $cxn1->is_cluster_node(), + "is_cluster_node works correctly for cluster nodes" + ); + + diag("Setting node as a slave of master1"); + $sb->set_as_slave($node, "master1"); + ok( + $cxn->is_master_of($cxn1), + "->is_master_of works correctly for master -> cluster" + ); + ok( + !$cxn1->is_master_of($cxn), + "...and the inverse returns the expected result" + ); + ok( + !$cxn->same_cluster($cxn1), + "->same_cluster works for master -> cluster" + ); + diag("Restarting the cluster"); + $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( + $cxn1->is_master_of($cxn), + "->is_master_of works correctly for cluster -> master" + ); + ok( + !$cxn->is_master_of($cxn1), + "...and the inverse returns the expected result" + ); + + ok( + !$cxn1->same_cluster($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( + $cxn2->is_cluster_node(), + "is_cluster_node correctly finds that this node is part of a cluster" + ); + + ok( + !$cxn1->same_cluster($cxn2), + "and same_cluster correctly finds that they don't belong to the same cluster, even when they have the same cluster name" + ); + + ok( + $cxn2->same_cluster($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( + !$cxn1->same_cluster($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($cxn1->is_master_of($cxn2), "is_master_of works"); + + ok( + !$cxn1->same_cluster($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( + !$cxn1->same_cluster($cxn2), + "...same_cluster works correctly when they have different cluster names & the are both gcomm" + ); + + ok( + !$cxn1->same_cluster($cxn3), + "same_cluster detects that (cluster1.node1) (cluster2.node2) come from different clusters if they have different cluster_names" + ); + + ok( + $cxn2->same_cluster($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; diff --git a/t/pt-table-checksum/bugs.t b/t/pt-table-checksum/bugs.t index 7262cdff..635c1365 100644 --- a/t/pt-table-checksum/bugs.t +++ b/t/pt-table-checksum/bugs.t @@ -191,6 +191,197 @@ like( "Bug 1016131: ptc should skip tables where all columns are excluded" ); +# ############################################################################# +# pt-table-checksum v2.1.4 doesn't detect diffs on Percona XtraDB Cluster nodes +# https://bugs.launchpad.net/percona-toolkit/+bug/1062563 +# ############################################################################# +use File::Spec::Functions; + +my $db_flavor = VersionParser->new($master_dbh)->flavor(); +SKIP: { + skip "PXC-only tests", 8 + unless $db_flavor =~ /XtraDB Cluster/; + + 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'; + $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($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); + 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" + ); + + 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($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); + $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" + ); + + 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"); + +} + +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"); +} + # ############################################################################# # Done. # ############################################################################# From 5a9cc13d6fffcfba9dab5ec8c96dc0d15d87802b Mon Sep 17 00:00:00 2001 From: Brian Fraser Date: Wed, 17 Oct 2012 15:39:06 -0300 Subject: [PATCH 2/6] Fixes for the previous commit --- bin/pt-table-checksum | 73 ++++++++++++++++++++++++++++++++++++++ lib/Cxn.pm | 27 +++++++++++--- t/lib/Cxn.t | 9 +++-- t/pt-table-checksum/bugs.t | 16 +++++---- 4 files changed, 112 insertions(+), 13 deletions(-) diff --git a/bin/pt-table-checksum b/bin/pt-table-checksum index 992023fd..8c257922 100755 --- a/bin/pt-table-checksum +++ b/bin/pt-table-checksum @@ -3206,11 +3206,21 @@ 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, }; +use Data::Dumper (); +sub Dumper { + local $Data::Dumper::Indent = 1; + local $Data::Dumper::Sortkeys = 1; + local $Data::Dumper::Quotekeys = 0; + + Data::Dumper::Dumper(@_); +} + sub new { my ( $class, %args ) = @_; my @required_args = qw(DSNParser OptionParser); @@ -3327,6 +3337,69 @@ 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; + + $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); + + 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); + } + + return 1 if $addr eq $cxn_addr; + + return 1; +} + +sub is_master_of { + my ($self, $cxn) = @_; + + my $cxn_dbh = $cxn->dbh; + local $cxn_dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = q{SHOW SLAVE STATUS}; + PTDEBUG && _d($sql); + my $slave_status = $cxn_dbh->selectrow_hashref($sql); + return unless ref($slave_status) eq 'HASH'; + + my $port = $self->dsn->{P}; + return unless $slave_status->{master_port} eq $port; + return 1 if $self->dsn->{h} eq $slave_status->{master_host}; + + my $host = scalar gethostbyname($self->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*;}; + my $full_gcomm = "gcomm://$prov_addr"; + PTDEBUG && _d("gcomm address: ", $full_gcomm); + return $full_gcomm; +} + sub DESTROY { my ($self) = @_; if ( $self->{dbh} diff --git a/lib/Cxn.pm b/lib/Cxn.pm index eeef3006..0ab8b1d3 100644 --- a/lib/Cxn.pm +++ b/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 @@ -45,6 +46,15 @@ use constant { PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0, }; +use Data::Dumper (); +sub Dumper { + local $Data::Dumper::Indent = 1; + local $Data::Dumper::Sortkeys = 1; + local $Data::Dumper::Quotekeys = 0; + + Data::Dumper::Dumper(@_); +} + # Sub: new # # Required Arguments: @@ -260,13 +270,20 @@ sub is_master_of { my $cxn_dbh = $cxn->dbh; local $cxn_dbh->{FetchHashKeyName} = 'NAME_lc'; - my $slave_status = $cxn_dbh->selectrow_hashref(q{SHOW SLAVE STATUS}); + my $sql = q{SHOW SLAVE STATUS}; + PTDEBUG && _d($sql); + my $slave_status = $cxn_dbh->selectrow_hashref($sql); 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; + my $port = $self->dsn->{P}; + return unless $slave_status->{master_port} eq $port; + return 1 if $self->dsn->{h} eq $slave_status->{master_host}; + + # They might be the same but in different format + my $host = scalar gethostbyname($self->dsn->{h}); + my $master_host = scalar gethostbyname($slave_status->{master_host}); + return 1 if $master_host eq $host; + return; } sub _find_full_gcomm_addr { diff --git a/t/lib/Cxn.t b/t/lib/Cxn.t index c644919e..eae15ac6 100644 --- a/t/lib/Cxn.t +++ b/t/lib/Cxn.t @@ -276,6 +276,11 @@ SKIP: { $cxn1->is_cluster_node(), "is_cluster_node works correctly for cluster nodes" ); + + ok( + !$cxn->is_master_of($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"); @@ -292,7 +297,7 @@ SKIP: { "->same_cluster works for master -> cluster" ); diag("Restarting the cluster"); - $sb->stop_sandbox($node); + diag($sb->stop_sandbox($node)); ($node) = $sb->start_cluster(cluster_size => 1); $cxn1 = make_cxn( dsn_string => $sb->dsn_for($node) ); $cxn1->connect(); @@ -391,5 +396,5 @@ diag($sb->stop_sandbox("master1")); # ############################################################################# $master_dbh->disconnect() if $master_dbh; ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); -done_testing +done_testing; exit; diff --git a/t/pt-table-checksum/bugs.t b/t/pt-table-checksum/bugs.t index 635c1365..0dcac017 100644 --- a/t/pt-table-checksum/bugs.t +++ b/t/pt-table-checksum/bugs.t @@ -214,18 +214,20 @@ SKIP: { diag("Creating a slave for $node2..."); { local $ENV{BINLOG_FORMAT} = 'ROW'; - $sb->start_sandbox("slave", $node2_slave, $node2); + 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); @@ -245,16 +247,15 @@ SKIP: { 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"); @@ -295,11 +296,14 @@ SKIP: { 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); @@ -320,7 +324,7 @@ SKIP: { PerconaTest::count_checksum_results($output, 'diffs'), 1, "Bug 1062563: Detects diffs between PXC nodes when cluster -> cluster" - ); + ) or diag($output); like( $output, From e8cfb37341f33896c2d1a981366117f7ee2e43e2 Mon Sep 17 00:00:00 2001 From: Daniel Nichter Date: Tue, 6 Nov 2012 16:10:01 -0700 Subject: [PATCH 3/6] Move ptc PXC tests from bugs.t to pxc.t. Remove 2>&1 from Sandbox.pm shell-outs: we should let errors go to stderr so we can trap them. Fix skip count in Cxn.t for PXC tests. --- lib/Sandbox.pm | 8 +- t/lib/Cxn.t | 22 ++-- t/pt-table-checksum/bugs.t | 195 ----------------------------- t/pt-table-checksum/pxc.t | 244 +++++++++++++++++++++++++++++++++++++ 4 files changed, 259 insertions(+), 210 deletions(-) create mode 100644 t/pt-table-checksum/pxc.t diff --git a/lib/Sandbox.pm b/lib/Sandbox.pm index eccc9651..79011079 100644 --- a/lib/Sandbox.pm +++ b/lib/Sandbox.pm @@ -77,7 +77,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"; } @@ -141,7 +141,7 @@ 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"; } @@ -432,7 +432,7 @@ 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`; + my $out = `$trunk/sandbox/start-sandbox $mode $port $master_port`; die $out if $CHILD_ERROR; return $out; } @@ -440,7 +440,7 @@ sub start_sandbox { sub stop_sandbox { my ($self, @sandboxes) = @_; my @ports = @port_for{@sandboxes}; - my $out = `$trunk/sandbox/stop-sandbox @ports 2>&1`; + my $out = `$trunk/sandbox/stop-sandbox @ports`; die $out if $CHILD_ERROR; return $out; } diff --git a/t/lib/Cxn.t b/t/lib/Cxn.t index eae15ac6..a9517b5b 100644 --- a/t/lib/Cxn.t +++ b/t/lib/Cxn.t @@ -10,6 +10,7 @@ use strict; use warnings FATAL => 'all'; use English qw(-no_match_vars); 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); @@ -261,15 +261,14 @@ ok( "is_cluster_node works correctly for non-nodes" ); -use VersionParser; my $db_flavor = VersionParser->new($master_dbh)->flavor(); SKIP: { - skip "PXC-only test", 1 + 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( @@ -281,7 +280,7 @@ SKIP: { !$cxn->is_master_of($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( @@ -360,10 +359,11 @@ SKIP: { 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", - ); + ($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) ); diff --git a/t/pt-table-checksum/bugs.t b/t/pt-table-checksum/bugs.t index 0dcac017..7262cdff 100644 --- a/t/pt-table-checksum/bugs.t +++ b/t/pt-table-checksum/bugs.t @@ -191,201 +191,6 @@ like( "Bug 1016131: ptc should skip tables where all columns are excluded" ); -# ############################################################################# -# pt-table-checksum v2.1.4 doesn't detect diffs on Percona XtraDB Cluster nodes -# https://bugs.launchpad.net/percona-toolkit/+bug/1062563 -# ############################################################################# -use File::Spec::Functions; - -my $db_flavor = VersionParser->new($master_dbh)->flavor(); -SKIP: { - skip "PXC-only tests", 8 - unless $db_flavor =~ /XtraDB Cluster/; - - 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"); - -} - -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"); -} - # ############################################################################# # Done. # ############################################################################# diff --git a/t/pt-table-checksum/pxc.t b/t/pt-table-checksum/pxc.t new file mode 100644 index 00000000..edf24a85 --- /dev/null +++ b/t/pt-table-checksum/pxc.t @@ -0,0 +1,244 @@ +#!/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; +use VersionParser; + +# Fix @INC because pt-table-checksum uses subclass OobNibbleIterator. +shift @INC; # our unshift (above) +shift @INC; # PerconaTest's unshift +require "$trunk/bin/pt-table-checksum"; + +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; From aa79c9df57e7a606995888011aa6ed18e5b35209 Mon Sep 17 00:00:00 2001 From: Brian Fraser Date: Wed, 7 Nov 2012 04:06:50 -0300 Subject: [PATCH 4/6] Moved the PXC-dealing parts of Cxn.pm into Percona::XtraDB::Cluster --- bin/pt-config-diff | 13 --- bin/pt-kill | 13 --- bin/pt-online-schema-change | 13 --- bin/pt-table-checksum | 194 ++++++++++++++++++++---------------- lib/Cxn.pm | 102 ------------------- t/lib/Cxn.t | 143 -------------------------- t/pt-table-checksum/pxc.t | 6 +- 7 files changed, 111 insertions(+), 373 deletions(-) diff --git a/bin/pt-config-diff b/bin/pt-config-diff index 719df9c4..4b2ca663 100755 --- a/bin/pt-config-diff +++ b/bin/pt-config-diff @@ -1568,19 +1568,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->[0] ? 1 : 0; - - return $self->{is_cluster_node}; -} - sub DESTROY { my ($self) = @_; if ( $self->{dbh} diff --git a/bin/pt-kill b/bin/pt-kill index 5c77c08a..567c7e81 100755 --- a/bin/pt-kill +++ b/bin/pt-kill @@ -4832,19 +4832,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->[0] ? 1 : 0; - - return $self->{is_cluster_node}; -} - sub DESTROY { my ($self) = @_; if ( $self->{dbh} diff --git a/bin/pt-online-schema-change b/bin/pt-online-schema-change index 466fae3f..a94ce9a2 100755 --- a/bin/pt-online-schema-change +++ b/bin/pt-online-schema-change @@ -3462,19 +3462,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->[0] ? 1 : 0; - - return $self->{is_cluster_node}; -} - sub DESTROY { my ($self) = @_; if ( $self->{dbh} diff --git a/bin/pt-table-checksum b/bin/pt-table-checksum index 8c257922..55ed329b 100755 --- a/bin/pt-table-checksum +++ b/bin/pt-table-checksum @@ -21,6 +21,7 @@ BEGIN { OptionParser Mo Cxn + Percona::XtraDB::Cluster Quoter VersionParser TableParser @@ -3322,84 +3323,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 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; - - $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); - - 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); - } - - return 1 if $addr eq $cxn_addr; - - return 1; -} - -sub is_master_of { - my ($self, $cxn) = @_; - - my $cxn_dbh = $cxn->dbh; - local $cxn_dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = q{SHOW SLAVE STATUS}; - PTDEBUG && _d($sql); - my $slave_status = $cxn_dbh->selectrow_hashref($sql); - return unless ref($slave_status) eq 'HASH'; - - my $port = $self->dsn->{P}; - return unless $slave_status->{master_port} eq $port; - return 1 if $self->dsn->{h} eq $slave_status->{master_host}; - - my $host = scalar gethostbyname($self->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*;}; - my $full_gcomm = "gcomm://$prov_addr"; - PTDEBUG && _d("gcomm address: ", $full_gcomm); - return $full_gcomm; -} - sub DESTROY { my ($self) = @_; if ( $self->{dbh} @@ -3425,6 +3348,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 @@ -8618,6 +8639,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 @@ -8659,7 +8683,7 @@ sub main { ); PTDEBUG && _d(scalar @$slaves, 'slaves found'); - if ( $master_cxn->is_cluster_node() ) { + 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 " @@ -8668,8 +8692,8 @@ sub main { else { my $err = ''; for my $slave (@$slaves) { - if ( $slave->is_cluster_node() - && !$slave->same_cluster($master_cxn) ) { + 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 " @@ -8683,7 +8707,7 @@ sub main { elsif ( @$slaves ) { my $err = ''; for my $slave (@$slaves) { - if ( $slave->is_cluster_node() ) { + 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 " @@ -8713,8 +8737,8 @@ sub main { # to appear should be sufficient. @$slave_lag_cxns = grep { my $slave_cxn = $_; - if ( $slave_cxn->is_cluster_node() - && $master_cxn->same_cluster($slave_cxn) ) { + 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; @@ -8929,7 +8953,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'"; diff --git a/lib/Cxn.pm b/lib/Cxn.pm index 0ab8b1d3..8b54e726 100644 --- a/lib/Cxn.pm +++ b/lib/Cxn.pm @@ -46,15 +46,6 @@ use constant { PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0, }; -use Data::Dumper (); -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - # Sub: new # # Required Arguments: @@ -205,99 +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 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 $sql = q{SHOW SLAVE STATUS}; - PTDEBUG && _d($sql); - my $slave_status = $cxn_dbh->selectrow_hashref($sql); - return unless ref($slave_status) eq 'HASH'; - - my $port = $self->dsn->{P}; - return unless $slave_status->{master_port} eq $port; - return 1 if $self->dsn->{h} eq $slave_status->{master_host}; - - # They might be the same but in different format - my $host = scalar gethostbyname($self->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*;}; - my $full_gcomm = "gcomm://$prov_addr"; - PTDEBUG && _d("gcomm address: ", $full_gcomm); - return $full_gcomm; -} - sub DESTROY { my ($self) = @_; if ( $self->{dbh} diff --git a/t/lib/Cxn.t b/t/lib/Cxn.t index a9517b5b..42933ae7 100644 --- a/t/lib/Cxn.t +++ b/t/lib/Cxn.t @@ -248,149 +248,6 @@ is_deeply( "Default cxn inherits default connection options" ); -@ARGV = (); -$o->get_opts(); - -diag("Starting master1"); -$sb->start_sandbox("master", "master1"); - -$cxn = make_cxn( dsn_string => $sb->dsn_for("master1") ); -$cxn->connect(); -ok( - !$cxn->is_cluster_node(), - "is_cluster_node works correctly for non-nodes" -); - -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( - $cxn1->is_cluster_node(), - "is_cluster_node works correctly for cluster nodes" - ); - - ok( - !$cxn->is_master_of($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( - $cxn->is_master_of($cxn1), - "->is_master_of works correctly for master -> cluster" - ); - ok( - !$cxn1->is_master_of($cxn), - "...and the inverse returns the expected result" - ); - ok( - !$cxn->same_cluster($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( - $cxn1->is_master_of($cxn), - "->is_master_of works correctly for cluster -> master" - ); - ok( - !$cxn->is_master_of($cxn1), - "...and the inverse returns the expected result" - ); - - ok( - !$cxn1->same_cluster($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( - $cxn2->is_cluster_node(), - "is_cluster_node correctly finds that this node is part of a cluster" - ); - - ok( - !$cxn1->same_cluster($cxn2), - "and same_cluster correctly finds that they don't belong to the same cluster, even when they have the same cluster name" - ); - - ok( - $cxn2->same_cluster($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( - !$cxn1->same_cluster($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($cxn1->is_master_of($cxn2), "is_master_of works"); - - ok( - !$cxn1->same_cluster($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( - !$cxn1->same_cluster($cxn2), - "...same_cluster works correctly when they have different cluster names & the are both gcomm" - ); - - ok( - !$cxn1->same_cluster($cxn3), - "same_cluster detects that (cluster1.node1) (cluster2.node2) come from different clusters if they have different cluster_names" - ); - - ok( - $cxn2->same_cluster($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. # ############################################################################# diff --git a/t/pt-table-checksum/pxc.t b/t/pt-table-checksum/pxc.t index edf24a85..50dad28a 100644 --- a/t/pt-table-checksum/pxc.t +++ b/t/pt-table-checksum/pxc.t @@ -21,12 +21,10 @@ $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} = 1; use PerconaTest; use Sandbox; -use VersionParser; -# Fix @INC because pt-table-checksum uses subclass OobNibbleIterator. -shift @INC; # our unshift (above) -shift @INC; # PerconaTest's unshift 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); From 7beb0b518e2cca28187b23798ff64e10f62eecaf Mon Sep 17 00:00:00 2001 From: Brian Fraser Date: Thu, 8 Nov 2012 17:47:00 -0300 Subject: [PATCH 5/6] Reverted a few unnecessary changes --- bin/pt-config-diff | 1 - bin/pt-kill | 1 - bin/pt-online-schema-change | 1 - lib/Cxn.pm | 1 - lib/Sandbox.pm | 4 ++-- t/lib/Cxn.t | 10 ++++++---- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/bin/pt-config-diff b/bin/pt-config-diff index 8ebd2a10..13135be2 100755 --- a/bin/pt-config-diff +++ b/bin/pt-config-diff @@ -1466,7 +1466,6 @@ 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, diff --git a/bin/pt-kill b/bin/pt-kill index 7af09248..bd4ad680 100755 --- a/bin/pt-kill +++ b/bin/pt-kill @@ -4715,7 +4715,6 @@ 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, diff --git a/bin/pt-online-schema-change b/bin/pt-online-schema-change index 5829bfc7..dd60f2cc 100755 --- a/bin/pt-online-schema-change +++ b/bin/pt-online-schema-change @@ -3345,7 +3345,6 @@ 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, diff --git a/lib/Cxn.pm b/lib/Cxn.pm index 8b54e726..df7f6ce4 100644 --- a/lib/Cxn.pm +++ b/lib/Cxn.pm @@ -36,7 +36,6 @@ 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 diff --git a/lib/Sandbox.pm b/lib/Sandbox.pm index a2f9a5f0..3a3f4d40 100644 --- a/lib/Sandbox.pm +++ b/lib/Sandbox.pm @@ -76,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`; + my $out = `$use 2>&1`; if ( $? >> 8 ) { die "Failed to execute $cmd on $server: $out"; } @@ -143,7 +143,7 @@ sub load_file { my $use = $self->_use_for($server) . " $d < $file"; PTDEBUG && _d('Loading', $file, 'on', $server, ':', $use); - my $out = `$use`; + my $out = `$use 2>&1`; if ( $? >> 8 ) { die "Failed to execute $file on $server: $out"; } diff --git a/t/lib/Cxn.t b/t/lib/Cxn.t index 42933ae7..75355b76 100644 --- a/t/lib/Cxn.t +++ b/t/lib/Cxn.t @@ -9,8 +9,7 @@ BEGIN { use strict; use warnings FATAL => 'all'; use English qw(-no_match_vars); -use Test::More; -use Data::Dumper; +use Test::More tests => 19; use Sandbox; use OptionParser; @@ -18,7 +17,8 @@ use DSNParser; use Quoter; use PerconaTest; use Cxn; -use VersionParser; + +use Data::Dumper; my $q = new Quoter(); my $dp = new DSNParser(opts=>$dsn_opts); @@ -248,10 +248,12 @@ 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; From ab65692708757d092956f49ce615c5422d236944 Mon Sep 17 00:00:00 2001 From: Brian Fraser Date: Fri, 9 Nov 2012 13:07:08 -0300 Subject: [PATCH 6/6] Split the Percona::XtraDB::Cluster tests in two files: One that requires a PXC sandbox, and a general tests file --- t/lib/Percona/XtraDB/Cluster-no-PXC.t | 88 ++++++++++ t/lib/Percona/XtraDB/Cluster.t | 243 ++++++++++++-------------- 2 files changed, 202 insertions(+), 129 deletions(-) create mode 100644 t/lib/Percona/XtraDB/Cluster-no-PXC.t diff --git a/t/lib/Percona/XtraDB/Cluster-no-PXC.t b/t/lib/Percona/XtraDB/Cluster-no-PXC.t new file mode 100644 index 00000000..335d9352 --- /dev/null +++ b/t/lib/Percona/XtraDB/Cluster-no-PXC.t @@ -0,0 +1,88 @@ +#!/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"); + +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; diff --git a/t/lib/Percona/XtraDB/Cluster.t b/t/lib/Percona/XtraDB/Cluster.t index 005085fc..c2382231 100644 --- a/t/lib/Percona/XtraDB/Cluster.t +++ b/t/lib/Percona/XtraDB/Cluster.t @@ -35,9 +35,14 @@ my $master_dbh = $sb->get_dbh_for('master'); my $cluster = Percona::XtraDB::Cluster->new(); +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-only test"; +} my $o = new OptionParser(description => 'Cxn'); $o->get_specs("$trunk/bin/pt-table-checksum"); @@ -60,151 +65,131 @@ $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 ); +my $master1_cxn = make_cxn( dsn_string => $sb->dsn_for("master1") ); +$master1_cxn->connect(); -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 - ); -} +diag("Starting a 1-node PXC"); +my ($node) = $sb->start_cluster(cluster_size => 1); -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 $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" +); -my $db_flavor = VersionParser->new($master_dbh)->flavor(); -SKIP: { - skip "PXC-only test", 17 - unless $db_flavor =~ /XtraDB Cluster/; +ok( + !$cluster->is_master_of($master1_cxn, $cxn1), + "->is_master_of works correctly for a server unrelated to a cluster" +); - diag("Starting a 1-node PXC"); - my ($node) = $sb->start_cluster(cluster_size => 1); +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(); - 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" - ); +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->is_master_of($master1_cxn, $cxn1), - "->is_master_of works correctly for a server unrelated to a cluster" - ); +ok( + !$cluster->same_cluster($cxn1, $master1_cxn), + "->same_cluster works for cluster -> master" +); - 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("Starting a 2-node cluster"); +my ($node2, $node3) = $sb->start_cluster(cluster_size => 2); - 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" - ); +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, $master1_cxn), - "->same_cluster works for cluster -> master" - ); +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" +); - 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($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 detects that (cluster1.node1) (cluster2.node2) come from different clusters if they have different cluster_names" + "...same_cluster works correctly when they have the same 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("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")); # #############################################################################