diff --git a/lib/PerconaTest.pm b/lib/PerconaTest.pm index 96238960..a076005d 100644 --- a/lib/PerconaTest.pm +++ b/lib/PerconaTest.pm @@ -224,11 +224,11 @@ sub wait_until { my $slept = 0; while ( $slept <= $max_t ) { - return if $code->(); + return 1 if $code->(); sleep $t; $slept += $t; } - return; + return 0; } # Wait t seconds for code to return. @@ -558,6 +558,41 @@ sub test_bash_tool { return; } +my %checksum_result_col = ( + ts => 0, + errors => 1, + diffs => 2, + rows => 3, + chunks => 4, + skipped => 5, + time => 6, + table => 7, +); +sub count_checksum_results { + my ($output, $column, $table) = @_; + + my (@res) = map { + my $line = $_; + my (@cols) = $line =~ m/(\S+)/g; + \@cols; + } + grep { + my $line = $_; + if ( !$table ) { + $line; + } + else { + $line =~ m/$table$/m ? $line : ''; + } + } + grep { m/^\d+\-\d+T/ } split /\n/, $output; + my $colno = $checksum_result_col{lc $column}; + die "Invalid checksum result column: $column" unless defined $colno; + my $total = 0; + map { $total += $_->[$colno] } @res; + return $total; +} + 1; } # ########################################################################### diff --git a/sandbox/load-sakila-db b/sandbox/load-sakila-db index 82bae8d6..1fbb2ceb 100755 --- a/sandbox/load-sakila-db +++ b/sandbox/load-sakila-db @@ -46,7 +46,4 @@ exit_status=$((exit_status | $?)) /tmp/$PORT/use < sakila-db/sakila-data.sql exit_status=$((exit_status | $?)) -$PERCONA_TOOLKIT_BRANCH/sandbox/test-env reset -exit_status=$((exit_status | $?)) - exit $exit_status diff --git a/sandbox/test-env b/sandbox/test-env index 023d0427..c3e5b7fb 100755 --- a/sandbox/test-env +++ b/sandbox/test-env @@ -291,6 +291,7 @@ case $opt in exit_status=$? if [ $exit_status -eq 0 ]; then ./start-sandbox slave 12346 12345 + ./start-sandbox slave 12347 12346 exit_status=$? if [ "$version" != "4.0" ] && [ "$version" != "4.1" ]; then if [ $? -eq 0 ]; then @@ -353,11 +354,18 @@ case $opt in # don't replicate to new sandbox servers. This makes creating new # sandbox servers a lot faster. There's no check if this works or # not, so... yeah. - /tmp/12345/use -e "RESET MASTER" - /tmp/12346/use -e "RESET MASTER" + /tmp/12347/use -e "STOP SLAVE" /tmp/12346/use -e "STOP SLAVE" + + /tmp/12346/use -e "RESET MASTER" + /tmp/12345/use -e "RESET MASTER" + + /tmp/12347/use -e "change master to master_host='127.0.0.1', master_user='msandbox', master_password='msandbox', master_port=12346, master_log_file='mysql-bin.000001', master_log_pos=0" /tmp/12346/use -e "change master to master_host='127.0.0.1', master_user='msandbox', master_password='msandbox', master_port=12345, master_log_file='mysql-bin.000001', master_log_pos=0" + + /tmp/12347/use -e "START SLAVE" /tmp/12346/use -e "START SLAVE" + exit_status=0 ;; version) diff --git a/t/pt-table-checksum/ignore_columns.t b/t/pt-table-checksum/ignore_columns.t index a54c2586..eeed0eb6 100644 --- a/t/pt-table-checksum/ignore_columns.t +++ b/t/pt-table-checksum/ignore_columns.t @@ -42,21 +42,9 @@ $sb->load_file('master', 't/pt-table-checksum/samples/issue_94.sql'); PerconaTest::wait_for_table($slave_dbh, 'test.issue_94', 'a=11'); $slave_dbh->do("update test.issue_94 set c=''"); -sub get_diffs { - my ($output) = @_; - my (@diffs) = $output =~ m/ - ^\S+\s+ # TS - \d+\s+ # ERRORS - (\d+) # DIFFS - /gmx; - my $total_diffs = 0; - map { $total_diffs += $_ } @diffs; - return $total_diffs; -} - $output = output( sub { pt_table_checksum::main(@args, qw(-d test -t issue_94)) }, - trf => \&get_diffs, + trf => sub { return PerconaTest::count_checksum_results(@_, 'DIFFS') }, ); is( $output, @@ -67,7 +55,7 @@ is( $output = output( sub { pt_table_checksum::main(@args, qw(-d test -t issue_94), qw(--ignore-columns c)) }, - trf => \&get_diffs, + trf => sub { return PerconaTest::count_checksum_results(@_, 'DIFFS') }, ); is( $output, diff --git a/t/pt-table-checksum/option_sanity.t b/t/pt-table-checksum/option_sanity.t index 79dd9a09..51784893 100644 --- a/t/pt-table-checksum/option_sanity.t +++ b/t/pt-table-checksum/option_sanity.t @@ -9,26 +9,132 @@ BEGIN { use strict; use warnings FATAL => 'all'; use English qw(-no_match_vars); -use Test::More tests => 2; +use Test::More tests => 16; use PerconaTest; require "$trunk/bin/pt-table-checksum"; my $output; -# Test DSN value inheritance -$output = `$trunk/bin/pt-table-checksum h=127.1 --replicate table`; +# Calling the tool pt_table_checksum::main() doesn't work when we're +# dealing with cmd line option error and --help because OptionParser +# exits on such things which would cause this test to exit too. + +# ############################################################################ +# Check default values for some options to ensure something wasn't +# changed accidentally. A lot of tests rely on these defaults, so +# if they change, it can have weird side-effects. +# ############################################################################ +$output = `$trunk/bin/pt-table-checksum h=127.1 --help`; + +like( + $output, + qr/^ --check-replication-filters\s+TRUE$/m, + "Default --check-replication-filters=TRUE" +); + +like( + $output, + qr/^ --create-replicate-table\s+TRUE$/m, + "Default --create-replicate-table=TRUE" +); + +like( + $output, + qr/^ --empty-replicate-table\s+TRUE$/m, + "Default --empty-replicate-table=TRUE" +); + +like( + $output, + qr/^ --explain\s+0$/m, + "Default --explain=0" +); + +like( + $output, + qr/^ --host\s+localhost$/m, + "Default --host=localhost" +); + +like( + $output, + qr/^ --lock-wait-timeout\s+1$/m, + "Default --lock-wait-timeout=1" +); + +like( + $output, + qr/^ --max-lag\s+1$/m, + "Default --max-lag=1" +); + +like( + $output, + qr/^ --quiet\s+0$/m, + "Default --quiet=0" +); + +like( + $output, + qr/^ --recheck\s+TRUE$/m, + "Default --recheck=TRUE" +); + +like( + $output, + qr/^ --replicate\s+percona\.checksums$/m, + "Default --replicate=percona.checksums" +); + +like( + $output, + qr/^ --replicate-check\s+TRUE$/m, + "Default --replicate-check=TRUE" +); + +# ############################################################################ +# Check opts that disable other opts. +# ############################################################################ +$output = `$trunk/bin/pt-table-checksum h=127.1 --help --explain`; +like( + $output, + qr/^ --empty-replicate-table\s+FALSE$/m, + "--explain disables --empty-replicate-table" +); + +$output = `$trunk/bin/pt-table-checksum h=127.1 --help --resume`; +like( + $output, + qr/^ --empty-replicate-table\s+FALSE$/m, + "--resume disables --empty-replicate-table" +); + +$output = `$trunk/bin/pt-table-checksum h=127.1 --help --quiet`; +like( + $output, + qr/^ --progress\s+\(No value\)$/m, + "--quiet disables --progress" +); + +# ############################################################################ +# Only 1 DSN should be allowed on the command line; no extra args. +# ############################################################################ +$output = `$trunk/bin/pt-table-checksum h=127.1 h=host1 h=host2`; +like( + $output, + qr/More than one host specified; only one allowed/, + "Only one DSN allowed on the command line" +); + +# ############################################################################ +# --replicate table must be db-qualified. +# ############################################################################ +$output = `$trunk/bin/pt-table-checksum h=127.1 --replicate checksums`; like( $output, qr/--replicate table must be database-qualified/, - "--replicate table must be db-qualified" -); - -$output = `$trunk/bin/pt-table-checksum h=127.1 --replicate test.checksum --throttle-method foo`; -like( - $output, - qr/Invalid --throttle-method: foo/, - "Invalid --throttle-method" + "--replicate table must be database-qualified" ); # ############################################################################# diff --git a/t/pt-table-checksum/progress.t b/t/pt-table-checksum/progress.t index 10a122b6..c5d55ee6 100644 --- a/t/pt-table-checksum/progress.t +++ b/t/pt-table-checksum/progress.t @@ -15,10 +15,6 @@ use PerconaTest; use Sandbox; require "$trunk/bin/pt-table-checksum"; -diag(`$trunk/sandbox/test-env reset`); -diag(`$trunk/sandbox/stop-sandbox 12347 >/dev/null`); -diag(`$trunk/sandbox/start-sandbox slave 12347 12346 >/dev/null`); - my $dp = new DSNParser(opts=>$dsn_opts); my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); my $master_dbh = $sb->get_dbh_for('master'); @@ -38,17 +34,21 @@ else { plan tests => 3; } +# Must have empty checksums table for these tests. +$master_dbh->do('drop table if exists percona.checksums'); + +# 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), + '--progress', 'time,1'); my $output; my $row; -my $cnf ='/tmp/12345/my.sandbox.cnf'; -my @args = (qw(--replicate test.checksum --empty-replicate-table -d test -t resume -q), '-F', $cnf, 'h=127.1', '--progress', 'time,1'); - -$sb->create_dbs($master_dbh, [qw(test)]); -$sb->load_file('master', 't/pt-table-checksum/samples/checksum_tbl.sql'); -# We're not using resume table for anything specific, we just need -# any table to checksum. -$sb->load_file('master', 't/pt-table-checksum/samples/resume.sql'); +my $scripts = "$trunk/t/pt-table-checksum/scripts/"; +# ############################################################################ +# Tool should check all slaves' lag, so slave2, not just slave1. +# ############################################################################ wait_until( # slaves aren't lagging sub { $row = $slave1_dbh->selectrow_hashref('show slave status'); @@ -56,47 +56,40 @@ wait_until( # slaves aren't lagging $row = $slave2_dbh->selectrow_hashref('show slave status'); return 0 if $row->{Seconds_Behind_Master}; return 1; - }, 0.5, 10); + } +) or die "Slaves are still lagging"; -$slave1_dbh->do('stop slave sql_thread'); -$row = $slave1_dbh->selectrow_hashref('show slave status'); -is( - $row->{slave_sql_running}, - 'No', - 'Stopped slave SQL thread on slave1' -); +# This big fancy command waits until it sees the checksum for sakila.city +# in the repl table on the master, then it stops slave2 for 2 seconds, +# then starts it again. +system("$trunk/util/wait-to-exec '$scripts/wait-for-chunk.sh 12345 sakila city 1' '$scripts/exec-wait-exec.sh 12347 \"stop slave sql_thread\" 2 \"start slave sql_thread\"' 3 >/dev/null &"); -$slave2_dbh->do('stop slave sql_thread'); -$row = $slave2_dbh->selectrow_hashref('show slave status'); -is( - $row->{slave_sql_running}, - 'No', - 'Stopped slave SQL thread on slave2' -); - -system("sleep 2 && /tmp/12346/use -e 'start slave sql_thread' >/dev/null 2>/dev/null &"); -system("sleep 3 && /tmp/12347/use -e 'start slave sql_thread' >/dev/null 2>/dev/null &"); - -# This time we do not need to capture STDERR because mk-table-checksum -# should see slave2 come alive in 2 seconds then return before wait_for -# dies. $output = output( - sub { pt_table_checksum::main(@args); }, + sub { pt_table_checksum::main(@args, qw(-d sakila)); }, stderr => 1, ); like( $output, - qr/Waiting for slave.+?Still waiting/s, - "Progress reports while waiting for slaves" + qr/Replica h=127.0.0.1,P=12347 is stopped/, + "--progress for slave lag" +); + +like( + $output, + qr/sakila.store$/m, + "Checksumming continues after waiting for slave lag" +); + +is( + PerconaTest::count_checksum_results($output, 'errors'), + 0, + "No errors after waiting for slave lag" ); # ############################################################################# # Done. # ############################################################################# -diag(`$trunk/sandbox/stop-sandbox 12347 >/dev/null`); -diag(`/tmp/12346/stop >/dev/null`); # Start/stop clears SHOW SLAVE HOSTS. -diag(`/tmp/12346/start >/dev/null`); $sb->wipe_clean($master_dbh); diag(`$trunk/sandbox/test-env reset >/dev/null`); exit; diff --git a/t/pt-table-checksum/samples/no-recheck.txt b/t/pt-table-checksum/samples/no-recheck.txt index a28306e5..e03c1a8c 100644 --- a/t/pt-table-checksum/samples/no-recheck.txt +++ b/t/pt-table-checksum/samples/no-recheck.txt @@ -3,3 +3,8 @@ TABLE CHUNK CNT_DIFF CRC_DIFF CHUNK_INDEX LOWER_BOUNDARY UPPER_BOUNDARY sakila.city 1 0 1 PRIMARY 1 100 sakila.city 6 0 1 PRIMARY 501 600 +Differences on h=127.0.0.1,P=12347 +TABLE CHUNK CNT_DIFF CRC_DIFF CHUNK_INDEX LOWER_BOUNDARY UPPER_BOUNDARY +sakila.city 1 0 1 PRIMARY 1 100 +sakila.city 6 0 1 PRIMARY 501 600 + diff --git a/t/pt-table-checksum/scripts/exec-wait-exec.sh b/t/pt-table-checksum/scripts/exec-wait-exec.sh new file mode 100755 index 00000000..76835485 --- /dev/null +++ b/t/pt-table-checksum/scripts/exec-wait-exec.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +port=$1 +query1=$2 +t=$3 +query2=$4 + +/tmp/$port/use -e "$query1" +sleep $t +/tmp/$port/use -e "$query2" diff --git a/t/pt-table-checksum/scripts/wait-for-chunk.sh b/t/pt-table-checksum/scripts/wait-for-chunk.sh new file mode 100755 index 00000000..71f9cc38 --- /dev/null +++ b/t/pt-table-checksum/scripts/wait-for-chunk.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +port=$1 +db=$2 +tbl=$3 +chunk=$4 + +/tmp/$port/use -e "select 1 from percona.checksums where db='$db' and tbl='$tbl' and chunk=$chunk" 2>/dev/null | grep -q 1 2>/dev/null diff --git a/t/pt-table-checksum/standard_options.t b/t/pt-table-checksum/standard_options.t index e4a31806..59f3dc21 100644 --- a/t/pt-table-checksum/standard_options.t +++ b/t/pt-table-checksum/standard_options.t @@ -27,7 +27,7 @@ elsif ( !$slave_dbh ) { plan skip_all => 'Cannot connect to sandbox slave1'; } else { - plan tests => 4; + plan tests => 5; } # The sandbox servers run with lock_wait_timeout=3 and it's not dynamic @@ -36,6 +36,37 @@ my @args = (qw(--lock-wait-timeout 3 --explain --tables sakila.country)); my $cnf = "/tmp/12345/my.sandbox.cnf"; my $pid_file = "/tmp/mk-table-checksum-test.pid"; my $output; +my $exit_status; + +# ############################################################################ +# Tool should connect to localhost without any options. +# ############################################################################ + +# This may not work because the sandbox servers aren't on localhost, +# but if your box has MySQL running on localhost then maybe it will, +# so we'll account for both of these possibilities. + +eval { + $exit_status = pt_table_checksum::main(@args); +}; +if ( $EVAL_ERROR ) { + # It's ok that this fails. It means that your box, like mine, doesn't + # have MySQL on localhost:3306:/tmp/mysql.socket/etc. + like( + $EVAL_ERROR, + qr/connect\(';host=localhost;/, + 'Default DSN is h=localhost' + ); +} +else { + # Apparently, your box is running MySQL on default ports. That + # means the tool ran, so it should run without errors. + is( + $exit_status, + 0, + 'Default DSN is h=localhost' + ); +} # ############################################################################ # DSN should inherit connection options (--port, etc.) diff --git a/util/wait-to-exec b/util/wait-to-exec new file mode 100755 index 00000000..ad0a1232 --- /dev/null +++ b/util/wait-to-exec @@ -0,0 +1,40 @@ +#!/usr/bin/env perl + +use strict; +use warnings FATAL => 'all'; +use Time::HiRes qw(sleep time); + +if ( !@ARGV || @ARGV < 2 ) { + print STDERR "Usage: wait-to-exec WAIT EXEC [RUNTIME [INTERVAL]]\n", + "When the WAIT script exits 0, execute the EXEC script.\n"; + exit 1; +} + +my ($wait, $exec, $runtime, $interval) = @ARGV; +$runtime ||= 1.0; +$interval ||= 0.1; + +#warn "wait: $wait\n"; +#warn "exec: $exec\n"; + +my $t_start = time; +while ( time - $t_start < $runtime ) { + if ( exit_status($wait) ) { + sleep $interval; + } + else { + # exec does not return; this script becomes the exec process. + exec($exec); + } +} + +warn "Never observed wait condition"; +exit 1; + +sub exit_status { + my ($wait) = @_; + system($wait); + # Like grep, exit status 0 means "match found", so stop waiting. + my $exit_status = $? >> 8; + return $exit_status; +}