Merge 2.1.9 fixes.

This commit is contained in:
Daniel Nichter
2013-02-05 09:47:17 -07:00
33 changed files with 1130 additions and 130 deletions

View File

@@ -4807,6 +4807,8 @@ sub main {
# ########################################################################
# Create the heartbeat table if --create-table was given.
# ########################################################################
my $utc = $o->get('utc');
my $now_func = $utc ? 'UTC_TIMESTAMP()' : 'NOW()';
my $db_tbl = $q->quote($db, $tbl);
my $server_id = $dbh->selectrow_array('SELECT @@server_id');
if ( $o->get('create-table') ) {
@@ -4816,7 +4818,7 @@ sub main {
$dbh->do($sql);
$sql = ($o->get('replace') ? "REPLACE" : "INSERT")
. qq/ INTO $db_tbl (ts, server_id) VALUES (UTC_TIMESTAMP(), $server_id)/;
. qq/ INTO $db_tbl (ts, server_id) VALUES ($now_func, $server_id)/;
PTDEBUG && _d($sql);
# This may fail if the table already existed and already had this row.
# We eval to ignore this possibility.
@@ -4913,7 +4915,7 @@ sub main {
PTDEBUG && _d('No heartbeat row in table');
if ( $o->get('insert-heartbeat-row') ) {
my $sql = "INSERT INTO $db_tbl ($pk_col, ts) "
. "VALUES ('$pk_val', UTC_TIMESTAMP())";
. "VALUES ('$pk_val', $now_func)";
PTDEBUG && _d($sql);
$dbh->do($sql);
}
@@ -5007,8 +5009,7 @@ sub main {
tries => 3,
wait => sub { sleep 0.25; return; },
try => sub {
my ($now) = $dbh->selectrow_array(q{SELECT UTC_TIMESTAMP()});
$sth->execute($now, @vals);
$sth->execute(ts(time, $utc), @vals);
PTDEBUG && _d($sth->{Statement});
$sth->finish();
},
@@ -5039,7 +5040,7 @@ sub main {
# UNIX_TIMESTAMP(ts) replaces unix_timestamp($ts) -- MySQL is the
# authority here, so let it calculate everything.
$heartbeat_sql
= "SELECT UNIX_TIMESTAMP(UTC_TIMESTAMP()), UNIX_TIMESTAMP(ts)"
= "SELECT " . ($utc ? 'UNIX_TIMESTAMP(ts)' : 'ts')
. ($dbi_driver eq 'mysql' ? '/*!50038, @@hostname AS host*/' : '')
. ($id ? "" : ", server_id")
. " FROM $db_tbl "
@@ -5053,12 +5054,13 @@ sub main {
my ($sth) = @_;
$sth->execute();
PTDEBUG && _d($sth->{Statement});
my ($now, $ts, $hostname, $server_id) = $sth->fetchrow_array();
my ($ts, $hostname, $server_id) = $sth->fetchrow_array();
my $now = time;
PTDEBUG && _d("Heartbeat from server", $server_id, "\n",
" now:", $now, "\n",
" now:", ts($now, $utc), "\n",
" ts:", $ts, "\n",
"skew:", $skew);
my $delay = $now - $ts - $skew;
my $delay = $now - unix_timestamp($ts, $utc) - $skew;
PTDEBUG && _d('Delay', sprintf('%.6f', $delay), 'on', $hostname);
# Because we adjust for skew, if the ts are less than skew seconds
@@ -5863,6 +5865,16 @@ short form: -u; type: string
User for login if not current user.
=item --utc
Ignore system time zones and use only UTC. By default pt-heartbeat does
not check or adjust for different system or MySQL time zones which can
cause the tool to compute the lag incorrectly. Specifying this option is
a good idea because it ensures that the tool works correctly regardless of
time zones, but it also makes the tool backwards-incompatible with
pt-heartbeat 2.1.7 and older (unless the older version of pt-heartbeat
is running on a system that uses UTC).
=item --version
Show version and exit.

View File

@@ -20,23 +20,32 @@ PTFUNCNAME=""
PTDEBUG="${PTDEBUG:-""}"
EXIT_STATUS=0
log() {
TS=$(date +%F-%T | tr ':-' '_');
ts() {
TS=$(date +%F-%T | tr ':-' '_')
echo "$TS $*"
}
info() {
[ ${OPT_VERBOSE:-3} -ge 3 ] && ts "$*"
}
log() {
[ ${OPT_VERBOSE:-3} -ge 2 ] && ts "$*"
}
warn() {
log "$*" >&2
[ ${OPT_VERBOSE:-3} -ge 1 ] && ts "$*" >&2
EXIT_STATUS=1
}
die() {
warn "$*"
ts "$*" >&2
EXIT_STATUS=1
exit 1
}
_d () {
[ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(log "$*")" >&2
[ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(ts "$*")" >&2
}
# ###########################################################################
@@ -861,7 +870,7 @@ main() {
# Execute the program if it was not included from another file.
# This makes it possible to include without executing, and thus test.
if [ "${0##*/}" = "$TOOL" ] \
|| [ "${0##*/}" = "bash" -a "$_" = "$0" ]; then
|| [ "${0##*/}" = "bash" -a "${_:-""}" = "$0" ]; then
# Parse command line options. We must do this first so we can
# see if --daemonize was specified.

View File

@@ -22,23 +22,32 @@ PTFUNCNAME=""
PTDEBUG="${PTDEBUG:-""}"
EXIT_STATUS=0
log() {
TS=$(date +%F-%T | tr ':-' '_');
ts() {
TS=$(date +%F-%T | tr ':-' '_')
echo "$TS $*"
}
info() {
[ ${OPT_VERBOSE:-3} -ge 3 ] && ts "$*"
}
log() {
[ ${OPT_VERBOSE:-3} -ge 2 ] && ts "$*"
}
warn() {
log "$*" >&2
[ ${OPT_VERBOSE:-3} -ge 1 ] && ts "$*" >&2
EXIT_STATUS=1
}
die() {
warn "$*"
ts "$*" >&2
EXIT_STATUS=1
exit 1
}
_d () {
[ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(log "$*")" >&2
[ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(ts "$*")" >&2
}
# ###########################################################################
@@ -2412,7 +2421,7 @@ main() {
# Execute the program if it was not included from another file.
# This makes it possible to include without executing, and thus test.
if [ "${0##*/}" = "$TOOL" ] \
|| [ "${0##*/}" = "bash" -a "$_" = "$0" ]; then
|| [ "${0##*/}" = "bash" -a "${_:-""}" = "$0" ]; then
# Set up temporary dir.
mk_tmpdir

View File

@@ -3348,7 +3348,7 @@ sub check_table {
die "I need a $arg argument" unless $args{$arg};
}
my ($dbh, $db, $tbl) = @args{@required_args};
my $q = $self->{Quoter};
my $q = $self->{Quoter} || 'Quoter';
my $db_tbl = $q->quote($db, $tbl);
PTDEBUG && _d('Checking', $db_tbl);
@@ -8450,19 +8450,74 @@ sub main {
# Find a pk or unique index to use for the delete trigger. can_nibble()
# above returns an index, but NibbleIterator will use non-unique indexes,
# so we have to do this again here.
my $indexes = $new_tbl->{tbl_struct}->{keys}; # brevity
foreach my $index ( $tp->sort_indexes($new_tbl->{tbl_struct}) ) {
if ( $index eq 'PRIMARY' || $indexes->{$index}->{is_unique} ) {
PTDEBUG && _d('Delete trigger index:', Dumper($index));
$new_tbl->{del_index} = $index;
last;
{
my $indexes = $new_tbl->{tbl_struct}->{keys}; # brevity
foreach my $index ( $tp->sort_indexes($new_tbl->{tbl_struct}) ) {
if ( $index eq 'PRIMARY' || $indexes->{$index}->{is_unique} ) {
PTDEBUG && _d('Delete trigger new index:', Dumper($index));
$new_tbl->{del_index} = $index;
last;
}
}
PTDEBUG && _d('New table delete index:', $new_tbl->{del_index});
}
{
my $indexes = $orig_tbl->{tbl_struct}->{keys}; # brevity
foreach my $index ( $tp->sort_indexes($orig_tbl->{tbl_struct}) ) {
if ( $index eq 'PRIMARY' || $indexes->{$index}->{is_unique} ) {
PTDEBUG && _d('Delete trigger orig index:', Dumper($index));
$orig_tbl->{del_index} = $index;
last;
}
}
PTDEBUG && _d('Orig table delete index:', $orig_tbl->{del_index});
}
if ( !$new_tbl->{del_index} ) {
die "The new table $new_tbl->{name} does not have a PRIMARY KEY "
. "or a unique index which is required for the DELETE trigger.\n";
}
# Determine whether to use the new or orig table delete index.
# The new table del index is preferred due to
# https://bugs.launchpad.net/percona-toolkit/+bug/1062324
# In short, if the chosen del index is re-created with new columns,
# its original columns may be dropped, so just use its new columns.
# But, due to https://bugs.launchpad.net/percona-toolkit/+bug/1103672,
# the chosen del index on the new table may reference columns which
# do not/no longer exist in the orig table, so we check for this
# and, if it's the case, we fall back to using the del index from
# the orig table.
my $del_tbl = $new_tbl; # preferred
my $new_del_index_cols # brevity
= $new_tbl->{tbl_struct}->{keys}->{ $new_tbl->{del_index} }->{cols};
foreach my $new_del_index_col ( @$new_del_index_cols ) {
if ( !exists $orig_cols->{$new_del_index_col} ) {
if ( !$orig_tbl->{del_index} ) {
die "The new table index $new_tbl->{del_index} would be used "
. "for the DELETE trigger, but it uses column "
. "$new_del_index_col which does not exist in the original "
. "table and the original table does not have a PRIMARY KEY "
. "or a unique index to use for the DELETE trigger.\n";
}
print "Using original table index $orig_tbl->{del_index} for the "
. "DELETE trigger instead of new table index $new_tbl->{del_index} "
. "because the new table index uses column $new_del_index_col "
. "which does not exist in the original table.\n";
$del_tbl = $orig_tbl;
last;
}
}
{
my $del_cols
= $del_tbl->{tbl_struct}->{keys}->{ $del_tbl->{del_index} }->{cols};
PTDEBUG && _d('Index for delete trigger: table', $del_tbl->{name},
'index', $del_tbl->{del_index},
'columns', @$del_cols);
}
# ########################################################################
# Step 3: Create the triggers to capture changes on the original table and
# apply them to the new table.
@@ -8492,6 +8547,7 @@ sub main {
create_triggers(
orig_tbl => $orig_tbl,
new_tbl => $new_tbl,
del_tbl => $del_tbl,
columns => \@common_cols,
Cxn => $cxn,
Quoter => $q,
@@ -9010,6 +9066,28 @@ sub check_alter {
my $ok = 1;
# ########################################################################
# Check for DROP PRIMARY KEY.
# ########################################################################
if ( $alter =~ m/DROP\s+PRIMARY\s+KEY/i ) {
my $msg = "--alter contains 'DROP PRIMARY KEY'. Dropping and "
. "altering the primary key can be dangerous, "
. "especially if the original table does not have other "
. "unique indexes.\n";
if ( $dry_run ) {
print $msg;
}
else {
$ok = 0;
warn $msg
. "The tool should handle this correctly, but you should "
. "test it first and carefully examine the triggers which "
. "rely on the PRIMARY KEY or a unique index. Specify "
. "--no-check-alter to disable this check and perform the "
. "--alter.\n";
}
}
# ########################################################################
# Check for renamed columns.
# https://bugs.launchpad.net/percona-toolkit/+bug/1068562
@@ -9056,6 +9134,7 @@ sub check_alter {
}
if ( !$ok ) {
# check_alter.t relies on this output.
die "--check-alter failed.\n";
}
@@ -9616,11 +9695,11 @@ sub drop_swap {
sub create_triggers {
my ( %args ) = @_;
my @required_args = qw(orig_tbl new_tbl columns Cxn Quoter OptionParser);
my @required_args = qw(orig_tbl new_tbl del_tbl columns Cxn Quoter OptionParser);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
}
my ($orig_tbl, $new_tbl, $cols, $cxn, $q, $o) = @args{@required_args};
my ($orig_tbl, $new_tbl, $del_tbl, $cols, $cxn, $q, $o) = @args{@required_args};
# This sub works for --dry-run and --execute. With --dry-run it's
# only interesting if --print is specified, too; then the user can
@@ -9649,8 +9728,8 @@ sub create_triggers {
# unique indexes can be nullable. Cols are from the new table and
# they may have been renamed
my %old_col_for = map { $_->{new} => $_->{old} } @$cols;
my $tbl_struct = $new_tbl->{tbl_struct};
my $del_index = $new_tbl->{del_index};
my $tbl_struct = $del_tbl->{tbl_struct};
my $del_index = $del_tbl->{del_index};
my $del_index_cols = join(" AND ", map {
my $new_col = $_;
my $old_col = $old_col_for{$new_col} || $new_col;
@@ -9778,6 +9857,10 @@ sub exec_nibble {
# Ignore this warning because we have purposely set statement-based
# replication.
1592 => 1,
# Error: 1062 SQLSTATE: 23000 ( ER_DUP_ENTRY )
# Message: Duplicate entry '%ld' for key '%s'
# MariaDB 5.5.28+ has this as a warning; See https://bugs.launchpad.net/percona-toolkit/+bug/1099836
1062 => 1,
);
# Warn once per-table for these error codes if the error message
@@ -9852,6 +9935,7 @@ sub exec_nibble {
. "Specify --statistics to see a count of all "
. "suppressed warnings and errors.\n";
}
warn $err;
}
}
else {
@@ -10306,9 +10390,19 @@ In previous versions of the tool, renaming a column with
C<CHANGE COLUMN name new_name> would lead to that column's data being lost.
The tool now parses the alter statement and tries to catch these cases, so
the renamed columns should have the same data as the originals. However, the
code that does this is not a full-blown SQL parser, so we recommend that users
run the tool with L<"--dry-run"> and check if it's detecting the renames
correctly.
code that does this is not a full-blown SQL parser, so you should first
run the tool with L<"--dry-run"> and L<"--print"> and verify that it detects
the renamed columns correctly.
=item DROP PRIMARY KEY
If L<"--alter"> contain C<DROP PRIMARY KEY> (case- and space-insensitive),
a warning is printed and the tool exits unless L<"--dry-run"> is specified.
Altering the primary key can be dangerous, but the tool can handle it.
The tool's triggers, particularly the DELETE trigger, are most affected by
altering the primary key because the tool prefers to use the primary key
for its triggers. You should first run the tool with L<"--dry-run"> and
L<"--print"> and verify that the triggers are correct.
=back

View File

@@ -215,7 +215,7 @@ main() {
# Execute the program if it was not included from another file. This makes it
# possible to include without executing, and thus test.
if [ "${0##*/}" = "$TOOL" ] \
|| [ "${0##*/}" = "bash" -a "$_" = "$0" ]; then
|| [ "${0##*/}" = "bash" -a "${_:-""}" = "$0" ]; then
mk_tmpdir
main "$@"
rm_tmpdir

View File

@@ -587,7 +587,7 @@ main() {
# Execute the program if it was not included from another file. This makes it
# possible to include without executing, and thus test.
if [ "${0##*/}" = "$TOOL" ] \
|| [ "${0##*/}" = "bash" -a "$_" = "$0" ]; then
|| [ "${0##*/}" = "bash" -a "${_:-""}" = "$0" ]; then
main "${@:-""}"
fi

View File

@@ -22,23 +22,32 @@ PTFUNCNAME=""
PTDEBUG="${PTDEBUG:-""}"
EXIT_STATUS=0
log() {
TS=$(date +%F-%T | tr ':-' '_');
ts() {
TS=$(date +%F-%T | tr ':-' '_')
echo "$TS $*"
}
info() {
[ ${OPT_VERBOSE:-3} -ge 3 ] && ts "$*"
}
log() {
[ ${OPT_VERBOSE:-3} -ge 2 ] && ts "$*"
}
warn() {
log "$*" >&2
[ ${OPT_VERBOSE:-3} -ge 1 ] && ts "$*" >&2
EXIT_STATUS=1
}
die() {
warn "$*"
ts "$*" >&2
EXIT_STATUS=1
exit 1
}
_d () {
[ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(log "$*")" >&2
[ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(ts "$*")" >&2
}
# ###########################################################################
@@ -962,6 +971,10 @@ after_collect_sleep() {
:
}
after_interval_sleep() {
:
}
after_stalk() {
:
}
@@ -1066,9 +1079,7 @@ sleep_ok() {
local seconds="$1"
local msg="${2:-""}"
if oktorun; then
if [ -n "$msg" ]; then
log "$msg"
fi
[ "$msg" ] && info "$msg"
sleep $seconds
fi
}
@@ -1126,7 +1137,11 @@ stalk() {
fi
local msg="Check results: $OPT_VARIABLE=$value, matched=${matched:-no}, cycles_true=$cycles_true"
log "$msg"
if [ "$matched" ]; then
log "$msg"
else
info "$msg"
fi
elif [ "$OPT_COLLECT" ]; then
# Make the next if condition true.
matched=1
@@ -1140,7 +1155,7 @@ stalk() {
# ##################################################################
# Start collecting, maybe.
# ##################################################################
log "Collect triggered"
log "Collect $ITER triggered"
# Send email to whomever that collect has been triggered.
if [ "$OPT_NOTIFY_BY_EMAIL" ]; then
@@ -1166,8 +1181,8 @@ stalk() {
"$margin"
if [ $? -eq 0 ]; then
# There should be enough disk space, so collect.
log "$msg" >> "$OPT_DEST/$prefix-trigger"
log "pt-stalk ran with $RAN_WITH" >> "$OPT_DEST/$prefix-trigger"
ts "$msg" >> "$OPT_DEST/$prefix-trigger"
ts "pt-stalk ran with $RAN_WITH" >> "$OPT_DEST/$prefix-trigger"
last_prefix="$prefix"
# Plugin hook:
@@ -1181,7 +1196,7 @@ stalk() {
collect "$OPT_DEST" "$prefix"
) >> "$OPT_DEST/$prefix-output" 2>&1 &
local collector_pid=$!
log "Collector PID $collector_pid"
log "Collect $ITER PID $collector_pid"
# Plugin hook:
after_collect $collector_pid
@@ -1199,6 +1214,7 @@ stalk() {
# ##################################################################
# Done collecting.
# ##################################################################
log "Collect $ITER done"
ITER=$((ITER + 1))
cycles_true=0
sleep_ok "$OPT_SLEEP" "Sleeping $OPT_SLEEP seconds after collect"
@@ -1208,6 +1224,9 @@ stalk() {
else
# Trigger/check/value is ok, sleep until next check.
sleep_ok "$OPT_INTERVAL"
# Plugin hook:
after_interval_sleep
fi
done
@@ -1291,7 +1310,7 @@ main() {
# Execute the program if it was not included from another file.
# This makes it possible to include without executing, and thus test.
if [ "${0##*/}" = "$TOOL" ] \
|| [ "${0##*/}" = "bash" -a "$_" = "$0" ]; then
|| [ "${0##*/}" = "bash" -a "${_:-""}" = "$0" ]; then
# Parse command line options. We must do this first so we can
# see if --daemonize was specified.
@@ -1737,6 +1756,10 @@ C<after_collect_sleep>.
Called after sleeping L<"--sleep"> seconds for the collector process to finish.
This hook is called after C<after_collect>.
=item after_interval_sleep
Called after sleeping L<"--interval"> seconds after each trigger check.
=item after_stalk
Called after stalking. Since pt-stalk stalks forever by default,
@@ -1840,6 +1863,22 @@ type: string; default: Threads_running
The variable to compare against the threshold. See L<"--function"> for details.
=item --verbose
type: int; default: 2
Print more or less information while running. Since the tool is designed
to be a long-running daemon, the default verbosity level only prints the
most important information. If you run the tool interactively, you may
want to use a higher verbosity level.
LEVEL PRINTS
===== =====================================
0 Errors
1 Warnings
2 Matching triggers and collection info
3 Non-matching triggers
=item --version
Print tool's version and exit.

View File

@@ -29,23 +29,32 @@ PTFUNCNAME=""
PTDEBUG="${PTDEBUG:-""}"
EXIT_STATUS=0
log() {
TS=$(date +%F-%T | tr ':-' '_');
ts() {
TS=$(date +%F-%T | tr ':-' '_')
echo "$TS $*"
}
info() {
[ ${OPT_VERBOSE:-3} -ge 3 ] && ts "$*"
}
log() {
[ ${OPT_VERBOSE:-3} -ge 2 ] && ts "$*"
}
warn() {
log "$*" >&2
[ ${OPT_VERBOSE:-3} -ge 1 ] && ts "$*" >&2
EXIT_STATUS=1
}
die() {
warn "$*"
ts "$*" >&2
EXIT_STATUS=1
exit 1
}
_d () {
[ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(log "$*")" >&2
[ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(ts "$*")" >&2
}
# ###########################################################################
@@ -2280,7 +2289,7 @@ sigtrap() { local PTFUNCNAME=sigtrap;
# Execute the program if it was not included from another file. This makes it
# possible to include without executing, and thus test.
if [ "${0##*/}" = "$TOOL" ] \
|| [ "${0##*/}" = "bash" -a "$_" = "$0" ]; then
|| [ "${0##*/}" = "bash" -a "${_:-""}" = "$0" ]; then
# Set up temporary dir.
mk_tmpdir

View File

@@ -8774,12 +8774,13 @@ sub main {
}
if ( $master_binlog ne $slave_binlog ) {
$err .= $master_cxn->name() . " has binlog_format "
. $master_binlog . " but replica " . $slave_cxn->name()
. " has binlog_format $slave_binlog. This could cause "
. "pt-table-checksum to break replication. "
. "If you understand the risks, specify "
. "--no-check-binlog-format to disable this check.\n";
$err .= "Replica " . $slave_cxn->name()
. qq{ has binlog_format $slave_binlog which could cause }
. qq{pt-table-checksum to break replication. Please read }
. qq{"Replicas using row-based replication" in the }
. qq{LIMITATIONS section of the tool's documentation. }
. qq{If you understand the risks, specify }
. qq{--no-check-binlog-format to disable this check.\n};
}
}
die $err if $err;

View File

@@ -10582,6 +10582,7 @@ sub sync_a_table {
trim => $o->get('trim'),
wait => $o->get('wait'),
function => $o->get('function'),
trace => !$ENV{PT_TEST_NO_TRACE},
);
if ( sum(@status{@ChangeHandler::ACTIONS}) ) {
@@ -11281,19 +11282,30 @@ sub diff_where {
PTDEBUG && _d('Ascend params:', Dumper($asc));
}
my $lb_sql = $asc->{boundaries}->{'>='};
foreach my $val ( $q->deserialize_list($diff->{lower_boundary}) ) {
my $quoted_val = $q->quote_val($val);
$lb_sql =~ s/\?/$quoted_val/;
my ($lb_sql, $ub_sql);
if ( defined $diff->{lower_boundary} ) {
$lb_sql = $asc->{boundaries}->{'>='};
foreach my $val ( $q->deserialize_list($diff->{lower_boundary}) ) {
my $quoted_val = $q->quote_val($val);
$lb_sql =~ s/\?/$quoted_val/;
}
}
my $ub_sql = $asc->{boundaries}->{'<='};
foreach my $val ( $q->deserialize_list($diff->{upper_boundary}) ) {
my $quoted_val = $q->quote_val($val);
$ub_sql =~ s/\?/$quoted_val/;
if ( defined $diff->{upper_boundary} ) {
$ub_sql = $asc->{boundaries}->{'<='};
foreach my $val ( $q->deserialize_list($diff->{upper_boundary}) ) {
my $quoted_val = $q->quote_val($val);
$ub_sql =~ s/\?/$quoted_val/;
}
}
return "$lb_sql AND $ub_sql";
die "Invalid checksum diff: " . Dumper($diff)
unless $lb_sql || $ub_sql;
return $lb_sql && $ub_sql ? "$lb_sql AND $ub_sql"
: $lb_sql ? $lb_sql
: $ub_sql;
}
}

View File

@@ -58,7 +58,6 @@ our @EXPORT = qw(
wait_until
wait_for
wait_until_slave_running
wait_until_no_lag
test_log_parser
test_protocol_parser
test_packet_parser
@@ -325,6 +324,39 @@ sub wait_for_sh {
);
};
sub kill_program {
my (%args) = @_;
my $pid_file = $args{pid_file};
my $pid = $args{pid};
if ( $pid_file ) {
chomp($pid = `cat $pid_file 2>/dev/null`);
}
if ( $pid ) {
PTDEVDEBUG && _d('Killing PID', $pid);
kill(15, $pid);
wait_until(
sub { my $is_alive = kill(0, $pid); return !$is_alive; },
1.5, # sleep between tries
15, # max time to try
);
if ( kill(0, $pid) ) {
warn "PID $pid did not die; using kill -9\n";
kill(9, $pid);
}
}
else {
PTDEVDEBUG && _d('No PID to kill');
}
if ( $pid_file && -f $pid_file ) {
PTDEVDEBUG && _d('Removing PID file', $pid_file);
unlink $pid_file;
}
}
sub not_running {
my ($cmd) = @_;
PTDEVDEBUG && _d('Wait until not running:', $cmd);

View File

@@ -302,7 +302,7 @@ sub check_table {
die "I need a $arg argument" unless $args{$arg};
}
my ($dbh, $db, $tbl) = @args{@required_args};
my $q = $self->{Quoter};
my $q = $self->{Quoter} || 'Quoter';
my $db_tbl = $q->quote($db, $tbl);
PTDEBUG && _d('Checking', $db_tbl);

View File

@@ -28,23 +28,32 @@ PTFUNCNAME=""
PTDEBUG="${PTDEBUG:-""}"
EXIT_STATUS=0
log() {
TS=$(date +%F-%T | tr ':-' '_');
ts() {
TS=$(date +%F-%T | tr ':-' '_')
echo "$TS $*"
}
info() {
[ ${OPT_VERBOSE:-3} -ge 3 ] && ts "$*"
}
log() {
[ ${OPT_VERBOSE:-3} -ge 2 ] && ts "$*"
}
warn() {
log "$*" >&2
[ ${OPT_VERBOSE:-3} -ge 1 ] && ts "$*" >&2
EXIT_STATUS=1
}
die() {
warn "$*"
ts "$*" >&2
EXIT_STATUS=1
exit 1
}
_d () {
[ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(log "$*")" >&2
[ "$PTDEBUG" ] && echo "# $PTFUNCNAME: $(ts "$*")" >&2
}
# ###########################################################################

View File

@@ -27,6 +27,13 @@ debug_sandbox() {
fi
}
mysql_upgrade_on() {
local cnf_file="$1"
local upgrade="$(which mysql_upgrade)"
$upgrade --defaults-file=$cnf_file --skip-write-binlog
}
make_sandbox() {
# Make the sandbox dir and extract the base files.
rm -rf /tmp/$port || die "Failed to rm /tmp/$port"
@@ -138,6 +145,10 @@ make_sandbox() {
/tmp/$port/use -e "CREATE TABLE IF NOT EXISTS percona_test.sentinel (id INT PRIMARY KEY, ping VARCHAR(64) NOT NULL DEFAULT '')";
/tmp/$port/use -e "REPLACE INTO percona_test.sentinel (id, ping) VALUES (1, '')";
if [ -n "${MYSQL_UPGRADE:-""}" ]; then
mysql_upgrade_on /tmp/$port/my.sandbox.cnf
fi
# If the sandbox is a slave, start the slave.
if [ "$type" = "slave" ]; then
/tmp/$port/use -e "change master to master_host='127.0.0.1', master_user='msandbox', master_password='msandbox', master_port=$master_port"

View File

@@ -1,17 +1,15 @@
#!/usr/bin/env bash
plan 6
source "$LIB_DIR/log_warn_die.sh"
log "Hello world!" > $TEST_PT_TMPDIR/log
log "Hello world A!" > $TEST_PT_TMPDIR/log
cmd_ok \
"grep -q 'Hello world!' $TEST_PT_TMPDIR/log" \
"grep -q 'Hello world A!' $TEST_PT_TMPDIR/log" \
"log msg"
log "Hello" "world!" > $TEST_PT_TMPDIR/log
log "Hello" "world B!" > $TEST_PT_TMPDIR/log
cmd_ok \
"grep -q 'Hello world!' $TEST_PT_TMPDIR/log" \
"grep -q 'Hello world B!' $TEST_PT_TMPDIR/log" \
"log msg msg"
is \
@@ -19,14 +17,14 @@ is \
"0" \
"Exit status 0"
warn "Hello world!" 2> $TEST_PT_TMPDIR/log
warn "Hello world C!" 2> $TEST_PT_TMPDIR/log
cmd_ok \
"grep -q 'Hello world!' $TEST_PT_TMPDIR/log" \
"grep -q 'Hello world C!' $TEST_PT_TMPDIR/log" \
"warn msg"
warn "Hello" "world!" 2> $TEST_PT_TMPDIR/log
warn "Hello" "world D!" 2> $TEST_PT_TMPDIR/log
cmd_ok \
"grep -q 'Hello world!' $TEST_PT_TMPDIR/log" \
"grep -q 'Hello world D!' $TEST_PT_TMPDIR/log" \
"warn msg msg"
is \
@@ -34,6 +32,81 @@ is \
"1" \
"Exit status 1"
OPT_VERBOSE=1
info "Hello world 1!" > $TEST_PT_TMPDIR/log
file_is_empty \
$TEST_PT_TMPDIR/log \
"verbose=1 info"
log "Hello world 2!" > $TEST_PT_TMPDIR/log
file_is_empty \
$TEST_PT_TMPDIR/log \
"verbose=1 log"
warn "Hello world 3!" > $TEST_PT_TMPDIR/log 2>&1
file_contains \
$TEST_PT_TMPDIR/log \
"Hello world 3!" \
"verbose=1 warn"
OPT_VERBOSE=2
info "Hello world 4!" > $TEST_PT_TMPDIR/log
file_is_empty \
$TEST_PT_TMPDIR/log \
"verbose=2 info"
log "Hello world 5!" > $TEST_PT_TMPDIR/log
file_contains \
$TEST_PT_TMPDIR/log \
"Hello world 5!" \
"verbose=2 log"
warn "Hello world 6!" > $TEST_PT_TMPDIR/log 2>&1
file_contains \
$TEST_PT_TMPDIR/log \
"Hello world 6!" \
"verbose=2 warn"
OPT_VERBOSE=3
info "Hello world 7!" > $TEST_PT_TMPDIR/log
file_contains \
$TEST_PT_TMPDIR/log \
"Hello world 7!" \
"verbose=3 info"
log "Hello world 8!" > $TEST_PT_TMPDIR/log
file_contains \
$TEST_PT_TMPDIR/log \
"Hello world 8!" \
"verbose=3 log"
warn "Hello world 9!" > $TEST_PT_TMPDIR/log 2>&1
file_contains \
$TEST_PT_TMPDIR/log \
"Hello world 9!" \
"verbose=3 warn"
OPT_VERBOSE=0
info "Hello world 10!" > $TEST_PT_TMPDIR/log
file_is_empty \
$TEST_PT_TMPDIR/log \
"verbose=0 info"
log "Hello world 11!" > $TEST_PT_TMPDIR/log
file_is_empty \
$TEST_PT_TMPDIR/log \
"verbose=0 log"
warn "Hello world 12!" > $TEST_PT_TMPDIR/log 2>&1
file_is_empty \
$TEST_PT_TMPDIR/log \
"verbose=0 warn"
# ###########################################################################
# Done
# ###########################################################################
done_testing

View File

@@ -93,7 +93,7 @@ my $slave1_dsn = $sb->dsn_for('slave1');
local $ENV{TZ} = '-09:00';
tzset();
pt_heartbeat::main($slave1_dsn, qw(--database test --table heartbeat),
qw(--check --master-server-id), $master_port)
qw(--utc --check --master-server-id), $master_port)
});
# If the servers use UTC then the lag should be 0.00, or at least
@@ -102,11 +102,35 @@ my $slave1_dsn = $sb->dsn_for('slave1');
like(
$output,
qr/\A\d.\d{2}$/,
"Bug 886059: pt-heartbeat doesn't get confused with differing timezones"
"--utc bypasses time zone differences (bug 886059, bug 1099665)"
);
stop_all_instances();
# #############################################################################
# pt-heartbeat 2.1.8 doesn't use precision/sub-second timestamps
# https://bugs.launchpad.net/percona-toolkit/+bug/1103221
# #############################################################################
$master_dbh->do('truncate table test.heartbeat');
$sb->wait_for_slaves;
my $master_dsn = $sb->dsn_for('master');
($output) = output(
sub {
pt_heartbeat::main($master_dsn, qw(--database test --update),
qw(--run-time 1))
},
);
my ($row) = $master_dbh->selectrow_hashref('select * from test.heartbeat');
like(
$row->{ts},
qr/\d{4}-\d\d-\d\dT\d+:\d+:\d+\.\d+/,
"Hi-res timestamp (bug 1103221)"
);
# ############################################################################
# Done.
# ############################################################################

View File

@@ -210,7 +210,7 @@ $sb->load_file('master', "$sample/del-trg-bug-1062324.sql");
sub { pt_online_schema_change::main(@args,
"$master_dsn,D=test,t=t1",
"--alter", "drop key 2bpk, drop key c3, drop primary key, drop c1, add primary key (c2, c3(4)), add key (c3(4))",
qw(--execute --no-drop-new-table --no-swap-tables)) },
qw(--no-check-alter --execute --no-drop-new-table --no-swap-tables)) },
);
# Since _t1_new no longer has the c1 column, the bug caused this
@@ -233,6 +233,34 @@ $sb->load_file('master', "$sample/del-trg-bug-1062324.sql");
undef,
"Delete trigger works after altering PK (bug 1062324)"
);
# Another instance of this bug:
# https://bugs.launchpad.net/percona-toolkit/+bug/1103672
$sb->load_file('master', "$sample/del-trg-bug-1103672.sql");
($output, $exit_status) = full_output(
sub { pt_online_schema_change::main(@args,
"$master_dsn,D=test,t=t1",
"--alter", "drop primary key, add column _id int unsigned not null primary key auto_increment FIRST",
qw(--no-check-alter --execute --no-drop-new-table --no-swap-tables)) },
);
eval {
$master_dbh->do("DELETE FROM test.t1 WHERE id=1");
};
is(
$EVAL_ERROR,
"",
"No delete trigger error after altering PK (bug 1103672)"
) or diag($output);
$row = $master_dbh->selectrow_arrayref("SELECT * FROM test._t1_new WHERE id=1");
is(
$row,
undef,
"Delete trigger works after altering PK (bug 1103672)"
);
}
# #############################################################################

View File

@@ -0,0 +1,65 @@
#!/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 PerconaTest;
use Sandbox;
require "$trunk/bin/pt-online-schema-change";
my $dp = new DSNParser(opts=>$dsn_opts);
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $master_dbh = $sb->get_dbh_for('master');
my $slave_dbh = $sb->get_dbh_for('slave1');
if ( !$master_dbh ) {
plan skip_all => 'Cannot connect to sandbox master';
}
elsif ( !$slave_dbh ) {
plan skip_all => 'Cannot connect to sandbox slave1';
}
# 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 = (qw(--lock-wait-timeout 3));
my $output;
my $exit_status;
my $sample = "t/pt-online-schema-change/samples/";
# #############################################################################
# DROP PRIMARY KEY
# #############################################################################
$sb->load_file('master', "$sample/del-trg-bug-1103672.sql");
($output, $exit_status) = full_output(
sub { pt_online_schema_change::main(@args,
"$master_dsn,D=test,t=t1",
"--alter", "drop primary key, add column _id int unsigned not null primary key auto_increment FIRST",
qw(--execute)),
},
);
like(
$output,
qr/--check-alter failed/,
"DROP PRIMARY KEY"
);
# #############################################################################
# Done.
# #############################################################################
$sb->wipe_clean($master_dbh);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
done_testing;

View File

@@ -0,0 +1,20 @@
drop database if exists test;
create database test;
use test;
CREATE TABLE `t1` (
`id` int(10) unsigned NOT NULL,
`x` char(3) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
INSERT INTO t1 VALUES
(1, 'a'),
(2, 'b'),
(3, 'c'),
(4, 'd'),
(5, 'f'),
(6, 'g'),
(7, 'h'),
(8, 'i'),
(9, 'j');

View File

@@ -0,0 +1,24 @@
#!/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 PerconaTest;
my $output = `$trunk/bin/pt-stalk --help`;
like(
$output,
qr/^\s+--verbose\s+2/m,
"Default --verbose=2"
);
done_testing;

View File

@@ -28,11 +28,28 @@ my $cnf = "/tmp/12345/my.sandbox.cnf";
my $pid_file = "/tmp/pt-stalk.pid.$PID";
my $log_file = "/tmp/pt-stalk.log.$PID";
my $dest = "/tmp/pt-stalk.collect.$PID";
my $int_file = "/tmp/pt-stalk-after-interval-sleep";
my $pid;
diag(`rm $pid_file 2>/dev/null`);
diag(`rm $log_file 2>/dev/null`);
diag(`rm -rf $dest 2>/dev/null`);
sub cleanup {
diag(`rm $pid_file $log_file $int_file 2>/dev/null`);
diag(`rm -rf $dest 2>/dev/null`);
}
sub wait_n_cycles {
my ($n) = @_;
PerconaTest::wait_until(
sub {
return 0 unless -f "$dest/after_interval_sleep";
my $n_cycles = `wc -l "$dest/after_interval_sleep" | awk '{print \$1}'`;
$n_cycles ||= '';
chomp($n_cycles);
return ($n_cycles || 0) >= $n;
},
1.5,
15
);
}
# ###########################################################################
# Test that it won't run if can't connect to MySQL.
@@ -56,11 +73,14 @@ is(
# ###########################################################################
# Test that it runs and dies normally.
# ###########################################################################
diag(`rm $pid_file 2>/dev/null`);
diag(`rm $log_file 2>/dev/null`);
diag(`rm -rf $dest 2>/dev/null`);
$retval = system("$trunk/bin/pt-stalk --daemonize --pid $pid_file --log $log_file --dest $dest -- --defaults-file=$cnf");
cleanup();
# As of v2.1.9 when --verbose was added, non-matching checks are not
# printed by default. So we use the --plugin to tell us when the tool
# has completed a cycle.
$retval = system("$trunk/bin/pt-stalk --daemonize --pid $pid_file --log $log_file --dest $dest --plugin $trunk/t/pt-stalk/samples/plugin002.sh -- --defaults-file=$cnf");
is(
$retval >> 8,
@@ -94,21 +114,16 @@ is(
"pt-stalk is running"
);
PerconaTest::wait_for_sh("grep -q 'Check results' $log_file >/dev/null");
wait_n_cycles(2);
PerconaTest::kill_program(pid_file => $pid_file);
$output = `cat $log_file 2>/dev/null`;
like(
unlike(
$output,
qr/Check results: Threads_running=\d+, matched=no, cycles_true=0/,
"Check results logged"
"Non-matching results not logged because --verbose=2"
) or diag(`cat $log_file 2>/dev/null`, `cat $dest/*-output 2>/dev/null`);
$retval = system("kill $pid 2>/dev/null");
is(
$retval >> 0,
0,
"Killed pt-stalk"
);
PerconaTest::wait_until(sub { !-f $pid_file });
ok(
@@ -123,12 +138,56 @@ like(
"Caught signal logged"
) or diag(`cat $log_file 2>/dev/null`, `cat $dest/*-output 2>/dev/null`);
# #############################################################################
# --verbose 3 (non-matching results)
# #############################################################################
cleanup();
$retval = system("$trunk/bin/pt-stalk --daemonize --pid $pid_file --log $log_file --dest $dest --verbose 3 -- --defaults-file=$cnf");
PerconaTest::wait_for_files($pid_file, $log_file);
PerconaTest::wait_for_sh("grep -q 'Check results' $log_file >/dev/null");
PerconaTest::kill_program(pid_file => $pid_file);
$output = `cat $log_file 2>/dev/null`;
like(
$output,
qr/Check results: Threads_running=\d+, matched=no, cycles_true=0/,
"Matching results logged with --verbose 3"
) or diag(`cat $log_file 2>/dev/null`, `cat $dest/*-output 2>/dev/null`);
# #############################################################################
# --verbose 1 (just errors and warnings)
# #############################################################################
cleanup();
$retval = system("$trunk/bin/pt-stalk --daemonize --pid $pid_file --log $log_file --dest $dest --verbose 1 --plugin $trunk/t/pt-stalk/samples/plugin002.sh -- --defaults-file=$cnf");
PerconaTest::wait_for_files($pid_file, $log_file);
wait_n_cycles(2);
PerconaTest::kill_program(pid_file => $pid_file);
$output = `cat $log_file 2>/dev/null`;
like(
$output,
qr/Caught signal, exiting/,
"Warning logged (--verbose 1)"
);
unlike(
$output,
qr/Start|Collect|Check/i,
"No run info log (--verbose 1)"
);
# ###########################################################################
# Test collect.
# ###########################################################################
diag(`rm $pid_file 2>/dev/null`);
diag(`rm $log_file 2>/dev/null`);
diag(`rm $dest/* 2>/dev/null`);
cleanup();
# We'll have to watch Uptime since it's the only status var that's going
# to be predictable.
@@ -180,10 +239,10 @@ like(
"Trigger file logs how pt-stalk was ran"
);
chomp($output = `cat $log_file 2>/dev/null | grep 'Collector PID'`);
chomp($output = `cat $log_file 2>/dev/null | grep 'Collect [0-9] PID'`);
like(
$output,
qr/Collector PID \d+/,
qr/Collect 1 PID \d+/,
"Collector PID logged"
)
or diag(
@@ -195,9 +254,8 @@ or diag(
# ###########################################################################
# Triggered but --no-collect.
# ###########################################################################
diag(`rm $pid_file 2>/dev/null`);
diag(`rm $log_file 2>/dev/null`);
diag(`rm $dest/* 2>/dev/null`);
cleanup();
(undef, $uptime) = $dbh->selectrow_array("SHOW STATUS LIKE 'Uptime'");
$threshold = $uptime + 2;
@@ -209,7 +267,7 @@ PerconaTest::wait_until(sub { !-f $pid_file });
$output = `cat $log_file 2>/dev/null`;
like(
$output,
qr/Collect triggered/,
qr/Collect 1 triggered/,
"Collect triggered"
);
@@ -227,6 +285,8 @@ ok(
# --config
# #############################################################################
cleanup();
diag(`cp $ENV{HOME}/.pt-stalk.conf $ENV{HOME}/.pt-stalk.conf.original 2>/dev/null`);
diag(`cp $trunk/t/pt-stalk/samples/config001.conf $ENV{HOME}/.pt-stalk.conf`);
@@ -254,9 +314,8 @@ diag(`cp $ENV{HOME}/.pt-stalk.conf.original $ENV{HOME}/.pt-stalk.conf 2>/dev/nul
# #############################################################################
# Don't stalk, just collect.
# #############################################################################
diag(`rm $pid_file 2>/dev/null`);
diag(`rm $log_file 2>/dev/null`);
diag(`rm $dest/* 2>/dev/null`);
cleanup();
$retval = system("$trunk/bin/pt-stalk --no-stalk --run-time 2 --dest $dest --prefix nostalk --pid $pid_file -- --defaults-file=$cnf >$log_file 2>&1");
@@ -343,9 +402,7 @@ unlike(
# #############################################################################
# Done.
# #############################################################################
diag(`rm $pid_file 2>/dev/null`);
diag(`rm $log_file 2>/dev/null`);
cleanup();
diag(`rm -rf $dest 2>/dev/null`);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
done_testing;

View File

@@ -0,0 +1,5 @@
#!/bin/sh
after_interval_sleep() {
date >> "$OPT_DEST/after_interval_sleep"
}

View File

@@ -256,7 +256,7 @@ SKIP: {
$output = output( sub { pt_table_checksum::main(@args) }, stderr => 1 );
my $re = qr/ has binlog_format .*? has binlog_format (\S+)\./msi;
my $re = qr/Replica .+? has binlog_format (\S+)/msi;
like(
$output,
$re,

View File

@@ -9,7 +9,7 @@ BEGIN {
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Test::More tests => 1;
use Test::More;
use PerconaTest;
use Sandbox;
@@ -57,7 +57,28 @@ test_diff_where(
where => "((`id` >= '7')) AND ((`id` <= '9'))",
);
test_diff_where(
name => "Lower oob chunk (bug 918056)",
file => "$sample/bug-918056-ddl.sql",
diff => {
chunk => '3',
chunk_index => 'PRIMARY',
cnt_diff => '49',
crc_diff => '0',
db => 'test',
lower_boundary => undef,
master_cnt => '0',
master_crc => '0',
table => 'test.history',
tbl => 'history',
this_cnt => '49',
this_crc => '0',
upper_boundary => '21,21,1045'
},
where => "((`uid` < '21') OR (`uid` = '21' AND `nid` <= '1045'))",
);
# #############################################################################
# Done.
# #############################################################################
exit;
done_testing;

View File

@@ -0,0 +1,90 @@
#!/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";
# Don't add /* trace */ messages to --print queries becuase they
# contain non-determinstic info like user, etc.
$ENV{PT_TEST_NO_TRACE} = 1;
};
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Test::More;
use PerconaTest;
use Sandbox;
require "$trunk/bin/pt-table-sync";
my $dp = new DSNParser(opts=>$dsn_opts);
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $master_dbh = $sb->get_dbh_for('master');
my $slave1_dbh = $sb->get_dbh_for('slave1');
if ( !$master_dbh ) {
plan skip_all => 'Cannot connect to sandbox master';
}
elsif ( !$slave1_dbh ) {
plan skip_all => 'Cannot connect to sandbox slave1';
}
my $master_dsn = $sb->dsn_for('master');
my $slave1_dsn = $sb->dsn_for('slave1');
my $output;
my $sample = "t/pt-table-sync/samples";
# #############################################################################
# --replicate tests
# #############################################################################
# #############################################################################
# Bug 918056: pt-table-sync false-positive error "Cannot nibble table because
# MySQL chose no index instead of the PRIMARY index"
# https://bugs.launchpad.net/percona-toolkit/+bug/918056
# #############################################################################
# The slave has 49 extra rows on the low end, e.g. master has rows 50+
# but slave has rows 1-49 and 50+. This tests syncing the lower oob chunk.
$sb->create_dbs($master_dbh, [qw(bug918056)]);
$sb->load_file('master', "$sample/bug-918056-master.sql", "bug918056");
$sb->load_file('slave1', "$sample/bug-918056-slave.sql", "bug918056");
ok(
no_diff(
sub {
pt_table_sync::main($master_dsn, qw(--replicate percona.checksums),
qw(--print))
},
"$sample/bug-918056-print.txt",
stderr => 1,
),
"Sync lower oob (bug 918056)"
);
# Test syncing the upper oob chunk.
$sb->load_file('master', "$sample/upper-oob-master.sql");
$sb->load_file('slave1', "$sample/upper-oob-slave.sql");
ok(
no_diff(
sub {
pt_table_sync::main($master_dsn, qw(--replicate percona.checksums),
qw(--print))
},
"$sample/upper-oob-print.txt",
stderr => 1,
),
"Sync upper oob (bug 918056)"
);
# #############################################################################
# Done.
# #############################################################################
$sb->wipe_clean($master_dbh);
$sb->wipe_clean($slave1_dbh);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
done_testing;

View File

@@ -0,0 +1,6 @@
CREATE TABLE `history` (
`uid` int(11) NOT NULL DEFAULT '0',
`nid` int(11) NOT NULL DEFAULT '0',
`timestamp` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`uid`,`nid`)
) ENGINE=InnoDB;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,49 @@
DELETE FROM `bug918056`.`history` WHERE `uid`='1' AND `nid`='14' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='1' AND `nid`='1591' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='1' AND `nid`='11501' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='1' AND `nid`='12648' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='1' AND `nid`='12652' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='1045' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='1046' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='11556' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12166' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12598' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12599' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12601' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12602' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12603' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12606' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12607' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12609' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12616' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12617' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12618' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12621' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12623' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12624' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12626' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12627' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12628' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12632' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12651' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12652' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12672' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12674' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12675' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12677' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12678' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12680' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12682' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12694' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12733' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12734' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='20' AND `nid`='12738' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='21' AND `nid`='12' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='21' AND `nid`='14' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='21' AND `nid`='16' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='21' AND `nid`='191' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='21' AND `nid`='307' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='21' AND `nid`='339' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='21' AND `nid`='340' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='21' AND `nid`='901' LIMIT 1;
DELETE FROM `bug918056`.`history` WHERE `uid`='21' AND `nid`='1039' LIMIT 1;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,148 @@
DROP DATABASE IF EXISTS upper_oob;
CREATE DATABASE upper_oob;
USE upper_oob;
CREATE TABLE t (
id int not null auto_increment primary key,
c varchar(64)
) engine=innodb;
insert into t values
(1, 'Afghanistan'),
(2, 'Algeria'),
(3, 'American Samoa'),
(4, 'Angola'),
(5, 'Anguilla'),
(6, 'Argentina'),
(7, 'Armenia'),
(8, 'Australia'),
(9, 'Austria'),
(10, 'Azerbaijan'),
(11, 'Bahrain'),
(12, 'Bangladesh'),
(13, 'Belarus'),
(14, 'Bolivia'),
(15, 'Brazil'),
(16, 'Brunei'),
(17, 'Bulgaria'),
(18, 'Cambodia'),
(19, 'Cameroon'),
(20, 'Canada'),
(21, 'Chad'),
(22, 'Chile'),
(23, 'China'),
(24, 'Colombia'),
(25, 'Congo, The Democratic Republic of the'),
(26, 'Czech Republic'),
(27, 'Dominican Republic'),
(28, 'Ecuador'),
(29, 'Egypt'),
(30, 'Estonia'),
(31, 'Ethiopia'),
(32, 'Faroe Islands'),
(33, 'Finland'),
(34, 'France'),
(35, 'French Guiana'),
(36, 'French Polynesia'),
(37, 'Gambia'),
(38, 'Germany'),
(39, 'Greece'),
(40, 'Greenland'),
(41, 'Holy See (Vatican City State)'),
(42, 'Hong Kong'),
(43, 'Hungary'),
(44, 'India'),
(45, 'Indonesia'),
(46, 'Iran'),
(47, 'Iraq'),
(48, 'Israel'),
(49, 'Italy'),
(50, 'Japan'),
(51, 'Kazakstan'),
(52, 'Kenya'),
(53, 'Kuwait'),
(54, 'Latvia'),
(55, 'Liechtenstein'),
(56, 'Lithuania'),
(57, 'Madagascar'),
(58, 'Malawi'),
(59, 'Malaysia'),
(60, 'Mexico'),
(61, 'Moldova'),
(62, 'Morocco'),
(63, 'Mozambique'),
(64, 'Myanmar'),
(65, 'Nauru'),
(66, 'Nepal'),
(67, 'Netherlands'),
(68, 'New Zealand'),
(69, 'Nigeria'),
(70, 'North Korea'),
(71, 'Oman'),
(72, 'Pakistan'),
(73, 'Paraguay'),
(74, 'Peru'),
(75, 'Philippines'),
(76, 'Poland'),
(77, 'Puerto Rico'),
(78, 'Romania'),
(79, 'Runion'),
(80, 'Russian Federation'),
(81, 'Saint Vincent and the Grenadines'),
(82, 'Saudi Arabia'),
(83, 'Senegal'),
(84, 'Slovakia'),
(85, 'South Africa'),
(86, 'South Korea'),
(87, 'Spain'),
(88, 'Sri Lanka'),
(89, 'Sudan'),
(90, 'Sweden'),
(91, 'Switzerland'),
(92, 'Taiwan'),
(93, 'Tanzania'),
(94, 'Thailand'),
(95, 'Tonga'),
(96, 'Tunisia'),
(97, 'Turkey'),
(98, 'Turkmenistan'),
(99, 'Tuvalu'),
(100, 'Ukraine'),
(101, 'United Arab Emirates'),
(102, 'United Kingdom'),
(103, 'United States'),
(104, 'Venezuela'),
(105, 'Vietnam'),
(106, 'Virgin Islands, U.S.'),
(107, 'Yemen'),
(108, 'Yugoslavia'),
(109, 'Zambia');
CREATE DATABASE IF NOT EXISTS percona;
USE percona;
DROP TABLE IF EXISTS checksums;
CREATE TABLE `checksums` (
`db` char(64) NOT NULL,
`tbl` char(64) NOT NULL,
`chunk` int(11) NOT NULL,
`chunk_time` float DEFAULT NULL,
`chunk_index` varchar(200) DEFAULT NULL,
`lower_boundary` text,
`upper_boundary` text,
`this_crc` char(40) NOT NULL,
`this_cnt` int(11) NOT NULL,
`master_crc` char(40) DEFAULT NULL,
`master_cnt` int(11) DEFAULT NULL,
`ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`db`,`tbl`,`chunk`),
KEY `ts_db_tbl` (`ts`,`db`,`tbl`)
) ENGINE=InnoDB;
SET SQL_LOG_BIN=0;
DELETE FROM upper_oob.t where id > 99;
INSERT INTO percona.checksums VALUES
('upper_oob','t',1,0.001144,'PRIMARY','1','50','398f3270',50,'398f3270',50,'2013-01-23 17:36:56'),
('upper_oob','t',2,0.000878,'PRIMARY','51','99','bd9a892d',49,'bd9a892d',49,'2013-01-23 17:36:56'),
('upper_oob','t',3,0.000763,'PRIMARY',NULL,'1','0',0,'0',0,'2013-01-23 17:36:56'),
('upper_oob','t',4,0.000877,'PRIMARY','99',NULL,'0',0,'0',0,'2013-01-23 17:36:56');
SET SQL_LOG_BIN=1;

View File

@@ -0,0 +1,10 @@
DELETE FROM `upper_oob`.`t` WHERE `id`='100' LIMIT 1;
DELETE FROM `upper_oob`.`t` WHERE `id`='101' LIMIT 1;
DELETE FROM `upper_oob`.`t` WHERE `id`='102' LIMIT 1;
DELETE FROM `upper_oob`.`t` WHERE `id`='103' LIMIT 1;
DELETE FROM `upper_oob`.`t` WHERE `id`='104' LIMIT 1;
DELETE FROM `upper_oob`.`t` WHERE `id`='105' LIMIT 1;
DELETE FROM `upper_oob`.`t` WHERE `id`='106' LIMIT 1;
DELETE FROM `upper_oob`.`t` WHERE `id`='107' LIMIT 1;
DELETE FROM `upper_oob`.`t` WHERE `id`='108' LIMIT 1;
DELETE FROM `upper_oob`.`t` WHERE `id`='109' LIMIT 1;

View File

@@ -0,0 +1,7 @@
USE percona;
TRUNCATE TABLE checksums;
INSERT INTO `checksums` VALUES
('upper_oob','t',1,0.001144,'PRIMARY','1','50','398f3270',50,'398f3270',50,'2013-01-23 17:36:56'),
('upper_oob','t',2,0.000878,'PRIMARY','51','99','bd9a892d',49,'bd9a892d',49,'2013-01-23 17:36:56'),
('upper_oob','t',3,0.000763,'PRIMARY',NULL,'1','0',0,'0',0,'2013-01-23 17:36:56'),
('upper_oob','t',4,0.000877,'PRIMARY','99',NULL,'0',10,'0',0,'2013-01-23 17:36:56');

View File

@@ -100,6 +100,7 @@ run_test() {
result() {
local result=$1
local test_name=${2:-""}
testno=$((testno + 1))
if [ $result -eq 0 ]; then
echo "ok $testno - $TEST_FILE $test_name"
else
@@ -110,7 +111,6 @@ result() {
cat $TEST_PT_TMPDIR/failed_result | sed -e 's/^/# /' -e '30q' >&2
fi
fi
testno=$((testno + 1))
return $result
}
@@ -121,19 +121,21 @@ plan() {
fi
}
done_testing() {
echo "1..$testno"
}
#
# The following subs are for the test files to call.
#
pass() {
local reason="${1:-""}"
result 0 "$reason"
}
fail() {
local reason="${1:-""}"
result 1 "$reason"
}
@@ -177,6 +179,42 @@ is() {
result $? "$test_name"
}
file_is_empty() {
local file=$1
local test_name=${2:-""}
test_command="-s $file"
if [ ! -f "$file" ]; then
echo "$file does not exist" > $TEST_PT_TMPDIR/failed_result
result 1 "$test_name"
fi
if [ -s "$file" ]; then
echo "$file is not empty:" > $TEST_PT_TMPDIR/failed_result
cat "$file" >> $TEST_PT_TMPDIR/failed_result
result 1 "$test_name"
else
result 0 "$test_name"
fi
}
file_contains() {
local file="$1"
local pat="$2"
local test_name=${3:-""}
test_command="grep -q '$pat' '$file'"
if [ ! -f "$file" ]; then
echo "$file does not exist" > $TEST_PT_TMPDIR/failed_result
result 1 "$test_name"
fi
grep -q "$pat" $file
if [ $? -ne 0 ]; then
echo "$file does not contain '$pat':" > $TEST_PT_TMPDIR/failed_result
cat "$file" >> $TEST_PT_TMPDIR/failed_result
result 1 "$test_name"
else
result 0 "$test_name"
fi
}
cmd_ok() {
local test_command=$1
local test_name=${2:-""}
@@ -226,7 +264,7 @@ diag() {
# Script starts here
# ############################################################################
testno=1
testno=0
failed_tests=0
if [ $# -eq 0 ]; then