Mini-overhaul for pt-fk-error-logger: make --run-time standard, remove --print, add --quiet, add --iterations, change default --interval to 30, use Cxn and Runtime, reconnect to MySQL, eval the critical operation, rewrite/update the docs.

This commit is contained in:
Daniel Nichter
2013-02-25 11:55:06 -07:00
parent 65833726fd
commit 6aef3e7028
4 changed files with 508 additions and 160 deletions

View File

@@ -17,10 +17,12 @@ BEGIN {
OptionParser
Quoter
DSNParser
Cxn
Daemon
Transformers
HTTPMicro
VersionCheck
Runtime
));
}
@@ -1595,6 +1597,159 @@ sub _d {
# End DSNParser package
# ###########################################################################
# ###########################################################################
# Cxn 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/Cxn.pm
# t/lib/Cxn.t
# See https://launchpad.net/percona-toolkit for more information.
# ###########################################################################
{
package Cxn;
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,
};
sub new {
my ( $class, %args ) = @_;
my @required_args = qw(DSNParser OptionParser);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
};
my ($dp, $o) = @args{@required_args};
my $dsn_defaults = $dp->parse_options($o);
my $prev_dsn = $args{prev_dsn};
my $dsn = $args{dsn};
if ( !$dsn ) {
$args{dsn_string} ||= 'h=' . ($dsn_defaults->{h} || 'localhost');
$dsn = $dp->parse(
$args{dsn_string}, $prev_dsn, $dsn_defaults);
}
elsif ( $prev_dsn ) {
$dsn = $dp->copy($prev_dsn, $dsn);
}
my $self = {
dsn => $dsn,
dbh => $args{dbh},
dsn_name => $dp->as_string($dsn, [qw(h P S)]),
hostname => '',
set => $args{set},
NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,
dbh_set => 0,
OptionParser => $o,
DSNParser => $dp,
is_cluster_node => undef,
};
return bless $self, $class;
}
sub connect {
my ( $self ) = @_;
my $dsn = $self->{dsn};
my $dp = $self->{DSNParser};
my $o = $self->{OptionParser};
my $dbh = $self->{dbh};
if ( !$dbh || !$dbh->ping() ) {
if ( $o->get('ask-pass') && !$self->{asked_for_pass} ) {
$dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
$self->{asked_for_pass} = 1;
}
$dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });
}
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name});
return $self->set_dbh($dbh);
}
sub set_dbh {
my ($self, $dbh) = @_;
if ( $self->{dbh} && $self->{dbh} == $dbh && $self->{dbh_set} ) {
PTDEBUG && _d($dbh, 'Already set dbh');
return $dbh;
}
PTDEBUG && _d($dbh, 'Setting dbh');
$dbh->{FetchHashKeyName} = 'NAME_lc' if $self->{NAME_lc};
my $sql = 'SELECT @@hostname, @@server_id';
PTDEBUG && _d($dbh, $sql);
my ($hostname, $server_id) = $dbh->selectrow_array($sql);
PTDEBUG && _d($dbh, 'hostname:', $hostname, $server_id);
if ( $hostname ) {
$self->{hostname} = $hostname;
}
if ( my $set = $self->{set}) {
$set->($dbh);
}
$self->{dbh} = $dbh;
$self->{dbh_set} = 1;
return $dbh;
}
sub lost_connection {
my ($self, $e) = @_;
return 0 unless $e;
return $e =~ m/MySQL server has gone away/
|| $e =~ m/Lost connection to MySQL server/;
}
sub dbh {
my ($self) = @_;
return $self->{dbh};
}
sub dsn {
my ($self) = @_;
return $self->{dsn};
}
sub name {
my ($self) = @_;
return $self->{dsn_name} if PERCONA_TOOLKIT_TEST_USE_DSN_NAMES;
return $self->{hostname} || $self->{dsn_name} || 'unknown host';
}
sub DESTROY {
my ($self) = @_;
if ( $self->{dbh}
&& blessed($self->{dbh})
&& $self->{dbh}->can("disconnect") ) {
PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name});
$self->{dbh}->disconnect();
}
return;
}
sub _d {
my ($package, undef, $line) = caller 0;
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
map { defined $_ ? $_ : 'undef' }
@_;
print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
}
1;
}
# ###########################################################################
# End Cxn package
# ###########################################################################
# ###########################################################################
# Daemon package
# This package is a copy without comments from the original. The original
@@ -3374,6 +3529,139 @@ sub _d {
# End VersionCheck package
# ###########################################################################
# ###########################################################################
# Runtime 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/Runtime.pm
# t/lib/Runtime.t
# See https://launchpad.net/percona-toolkit for more information.
# ###########################################################################
{
package Runtime;
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
sub new {
my ( $class, %args ) = @_;
my @required_args = qw(run_time now);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless exists $args{$arg};
}
my $run_time = $args{run_time};
if ( defined $run_time ) {
die "run_time must be > 0" if $run_time <= 0;
}
my $now = $args{now};
die "now must be a callback" unless ref $now eq 'CODE';
my $self = {
run_time => $run_time,
now => $now,
start_time => undef,
end_time => undef,
time_left => undef,
stop => 0,
};
return bless $self, $class;
}
sub time_left {
my ( $self, %args ) = @_;
if ( $self->{stop} ) {
PTDEBUG && _d("No time left because stop was called");
return 0;
}
my $now = $self->{now}->(%args);
PTDEBUG && _d("Current time:", $now);
if ( !defined $self->{start_time} ) {
$self->{start_time} = $now;
}
return unless defined $now;
my $run_time = $self->{run_time};
return unless defined $run_time;
if ( !$self->{end_time} ) {
$self->{end_time} = $now + $run_time;
PTDEBUG && _d("End time:", $self->{end_time});
}
$self->{time_left} = $self->{end_time} - $now;
PTDEBUG && _d("Time left:", $self->{time_left});
return $self->{time_left};
}
sub have_time {
my ( $self, %args ) = @_;
my $time_left = $self->time_left(%args);
return 1 if !defined $time_left; # run forever
return $time_left <= 0 ? 0 : 1; # <=0s means run time has elapsed
}
sub time_elapsed {
my ( $self, %args ) = @_;
my $start_time = $self->{start_time};
return 0 unless $start_time;
my $now = $self->{now}->(%args);
PTDEBUG && _d("Current time:", $now);
my $time_elapsed = $now - $start_time;
PTDEBUG && _d("Time elapsed:", $time_elapsed);
if ( $time_elapsed < 0 ) {
warn "Current time $now is earlier than start time $start_time";
}
return $time_elapsed;
}
sub reset {
my ( $self ) = @_;
$self->{start_time} = undef;
$self->{end_time} = undef;
$self->{time_left} = undef;
$self->{stop} = 0;
PTDEBUG && _d("Reset run time");
return;
}
sub stop {
my ( $self ) = @_;
$self->{stop} = 1;
return;
}
sub start {
my ( $self ) = @_;
$self->{stop} = 0;
return;
}
sub _d {
my ($package, undef, $line) = caller 0;
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
map { defined $_ ? $_ : 'undef' }
@_;
print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
}
1;
}
# ###########################################################################
# End Runtime package
# ###########################################################################
# ###########################################################################
# This is a combination of modules and programs in one -- a runnable module.
# http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last
@@ -3387,18 +3675,21 @@ package pt_fk_error_logger;
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use sigtrap qw(handler finish untrapped normal-signals);
use sigtrap 'handler', \&sig_int, 'normal-signals';
use Percona::Toolkit;
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
Transformers->import(qw(parse_timestamp));
my $oktorun;
my $oktorun = 1;
my $exit_status = 0;
sub main {
local @ARGV = @_; # set global ARGV for this package
$oktorun = 1;
local @ARGV = @_; # set global ARGV for this package
$oktorun = 1;
$exit_status = 0;
# ########################################################################
# Get configuration information.
@@ -3409,69 +3700,54 @@ sub main {
my $dp = $o->DSNParser();
$dp->prop('set-vars', $o->get('set-vars'));
my $dsn_defaults = $dp->parse_options($o);
my $src_dsn = @ARGV ? $dp->parse(shift @ARGV, $dsn_defaults) : $dsn_defaults;
my $dst_dsn = $o->get('dest');
# The source dsn is not an option so --dest cannot use OptionParser
# to inherit values from it. Thus, we do it manually. --dest will
# inherit from --user, --port, etc.
if ( $src_dsn && $dst_dsn ) {
# If dest DSN only has D and t, this will copy h, P, S, etc.
# from the source DSN.
$dst_dsn = $dp->copy($src_dsn, $dst_dsn);
my $src;
if ( my $src_dsn_string = shift @ARGV ) {
$src = Cxn->new(
dsn_string => $src_dsn_string,
DSNParser => $dp,
OptionParser => $o,
);
}
my $dst;
if ( my $dst_dsn = $o->get('dest') ) {
$dst = Cxn->new(
dsn => $dst_dsn,
prev_dsn => $src ? $src->dsn : undef,
DSNParser => $dp,
OptionParser => $o,
);
}
if ( !$o->get('help') ) {
if ( !$src_dsn ) {
$o->save_error('Missing or invalid source host');
if ( !$src ) {
$o->save_error('No DSN was specified.');
}
if ( $dst_dsn && !$dst_dsn->{D} ) {
$o->save_error("--dest requires a 'D' (database) part");
if ( $dst && !$dst->dsn->{D} ) {
$o->save_error("--dest requires a 'D' (database) part.");
}
if ( $dst_dsn && !$dst_dsn->{t} ) {
$o->save_error("--dest requires a 't' (table) part");
if ( $dst && !$dst->dsn->{t} ) {
$o->save_error("--dest requires a 't' (table) part.");
}
}
$o->usage_or_errors();
# ########################################################################
# Make common modules.
# Connect to MySQL.
# ########################################################################
my $q = new Quoter();
my %modules = (
o => $o,
dp => $dp,
q => $q,
);
my $q = Quoter->new();
$src->connect();
# ########################################################################
# Start working.
# ########################################################################
my $dbh = get_cxn($src_dsn, 1, %modules);
my $start = time();
my $end = $start + ($o->get('run-time') || 0); # When we should exit
my $now = $start;
my $dst_dbh;
my $ins_sth;
# Since the user might not have specified a hostname for the connection,
# try to extract it from the $dbh
if ( !$src_dsn->{h} ) {
($src_dsn->{h}) = $dbh->{mysql_hostinfo} =~ m/(\w+) via/;
PTDEBUG && _d('Got source host from dbh:', $src_dsn->{h});
}
if ( $dst_dsn ) {
my $db_tbl = join('.',
map { $q->quote($_) }
grep { $_ }
( $dst_dsn->{D}, $dst_dsn->{t} ));
$dst_dbh = get_cxn($dst_dsn, 1, %modules);
my $sql = "INSERT IGNORE INTO $db_tbl VALUES (?, ?)";
PTDEBUG && _d('insert sql:', $sql);
$ins_sth = $dst_dbh->prepare($sql);
if ( $dst ) {
$dst->connect();
my $db_tbl = $q->join_quote($dst->dsn->{D}, $dst->dsn->{t});
my $sql = "INSERT IGNORE INTO $db_tbl VALUES (?, ?)";
PTDEBUG && _d('--dest INSERT SQL:', $sql);
$ins_sth = $dst->dbh->prepare($sql);
}
# ########################################################################
@@ -3496,8 +3772,8 @@ sub main {
VersionCheck::version_check(
force => $o->got('version-check'),
instances => [
{ dbh => $dbh, dsn => $src_dsn },
($dst_dbh ? { dbh => $dst_dbh, dsn => $dst_dsn } : ())
{ dbh => $src->dbh, dsn => $src->dsn },
($dst ? { dbh => $dst->dbh, dsn => $dst->dsn } : ())
],
);
}
@@ -3505,43 +3781,77 @@ sub main {
# ########################################################################
# Start finding and logging foreign key errors.
# ########################################################################
while ( # Quit if:
($start == $end || $now < $end) # time is exceeded
&& $oktorun # or instructed to quit
)
{
my $text = $dbh->selectrow_hashref("SHOW /*!40100 ENGINE*/ INNODB STATUS")->{Status};
my ($ts, $fk_error) = get_fk_error($text);
PTDEBUG && _d('ts:', $ts, 'fk error:', $fk_error);
my $run_time = Runtime->new(
run_time => $o->get('run-time'),
now => sub { return time },
);
if ( $ts && $fk_error ) {
# Save and/or print the foreign key error.
if ( $ins_sth ) {
my $fk_ts = parse_timestamp($ts);
PTDEBUG && _d('Saving fk error', $ts, $fk_error);
eval {
$ins_sth->execute($fk_ts, $fk_error);
};
my $interval = $o->get('interval');
my $iters = $o->get('iterations');
PTDEBUG && _d('iterations:', $iters, 'interval:', $interval);
ITERATION:
while (
$oktorun
&& $run_time->have_time()
&& (!defined $iters || $iters--)
) {
my ($ts, $fk_error);
eval {
my $sql = "SHOW /*!40100 ENGINE*/ INNODB STATUS "
. "/* pt-fk-error-logger */";
PTDEBUG && _d($sql);
my $text = $src->dbh->selectrow_hashref($sql)->{status};
($ts, $fk_error) = get_fk_error($text);
};
if ( my $e = $EVAL_ERROR ) {
PTDEBUG && _d('Error getting InnoDB status:', $e);
if ( $src->lost_connection($e) ) {
eval { $src->connect() };
if ( $EVAL_ERROR ) {
warn $EVAL_ERROR;
PTDEBUG && _d($EVAL_ERROR);
warn "Lost connection to MySQL. Will try to reconnect "
. "in the next iteration.\n";
}
else {
PTDEBUG && _d('Reconnected to MySQL');
redo ITERATION;
}
}
print "$ts $fk_error\n\n" if $o->get('print') || !$o->got('dest');
}
# If there's an --interval argument, run forever or till specified.
# Otherwise just run once.
if ( $o->get('interval') ) {
sleep($o->get('interval'));
$now = time();
else {
warn "Error parsing SHOW ENGINE INNODB STATUS: $EVAL_ERROR";
$exit_status |= 1;
}
}
else {
$oktorun = 0;
if ( $ts && $fk_error ) {
# Save and/or print the foreign key error.
if ( $ins_sth ) {
my $fk_ts = parse_timestamp($ts);
PTDEBUG && _d('Saving fk error', $ts, $fk_error);
eval {
$ins_sth->execute($fk_ts, $fk_error);
};
if ( $EVAL_ERROR ) {
warn $EVAL_ERROR;
PTDEBUG && _d($EVAL_ERROR);
}
}
if ( !$o->get('quiet') ) {
print "$ts $fk_error\n\n";
}
}
}
# Sleep if there's an --iteration left.
if ( !defined $iters || $iters ) {
PTDEBUG && _d('Sleeping', $interval, 'seconds');
sleep $interval;
}
}
return 0;
PTDEBUG && _d('Done running, exiting', $exit_status);
return $exit_status;
}
# ############################################################################
@@ -3550,9 +3860,13 @@ sub main {
sub get_fk_error {
my ( $text ) = @_;
PTDEBUG && _d($text);
# Quick check if text even has a foreign key error.
return unless $text =~ m/LATEST FOREIGN KEY ERROR/;
if ( $text !~ m/LATEST FOREIGN KEY ERROR/ ) {
PTDEBUG && _d('No fk error');
return;
}
# InnoDB timestamp
my $idb_ts = qr/((?:\d{6}|\d{4}-\d\d-\d\d) .\d:\d\d:\d\d)/;
@@ -3564,24 +3878,11 @@ sub get_fk_error {
return $ts, $fke;
}
# Catches signals so the program can exit gracefully.
sub finish {
my ($signal) = @_;
print STDERR "Exiting on SIG$signal.\n";
sub sig_int {
my ( $signal ) = @_;
$oktorun = 0;
}
sub get_cxn {
my ( $dsn, $ac, %args ) = @_;
my $o = $args{o};
my $dp = $args{dp};
if ( $o->get('ask-pass') ) {
$dsn->{p} = OptionParser::prompt_noecho("Enter password: ");
}
my $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), {AutoCommit => $ac});
$dbh->{InactiveDestroy} = 1; # Because of forking.
return $dbh;
print STDERR "# Caught SIG$signal. Use 'kill -ABRT $PID' if "
. "the tool does not exit normally in a few seconds.\n";
}
sub _d {
@@ -3606,22 +3907,27 @@ if ( !caller ) { exit main(@ARGV); }
=head1 NAME
pt-fk-error-logger - Extract and log MySQL foreign key errors.
pt-fk-error-logger - Log MySQL foreign key errors.
=head1 SYNOPSIS
Usage: pt-fk-error-logger [OPTION...] SOURCE_DSN
Usage: pt-fk-error-logger [OPTIONS] DSN
pt-fk-error-logger extracts and saves information about the most recent foreign
key errors in a MySQL server.
pt-fk-error-logger logs information about foreign key errors on the given
DSN. Information is printed to C<STDOUT>, and it can also be saved to a
table by specifying L<"--dest">.
Print foreign key errors on host1:
pt-fk-error-logger h=host1
Save foreign key errors on host1 to db.foreign_key_errors table on host2:
Print foreign key errors on host1 once then exit:
pt-fk-error-logger h=host1 --dest h=host1,D=db,t=foreign_key_errors
pt-fk-error-logger h=host1 --iterations 1
Save foreign key errors on host1 to percona_schema.fke on host2:
pt-fk-error-logger h=host1 --dest h=host2,D=percona_schema,t=fke
=head1 RISKS
@@ -3650,11 +3956,15 @@ C<SHOW INNODB STATUS>. The errors are not parsed or interpreted in any
way. Foreign key errors are uniquely identified by their timestamp.
Only new (more recent) errors are printed or saved.
By default the tool runs forever, checking every L<"--interval"> seconds
for new foreign key errors. Specify L<"--run-time"> and/or L<"--iterations">
to limit how long the tool runs.
=head1 OUTPUT
If L<"--print"> is given or no L<"--dest"> is given, then pt-fk-error-logger
prints the foreign key error text to STDOUT exactly as it appeared in
C<SHOW INNODB STATUS>.
The foreign key error text from C<SHOW ENGINE INNODB STATUS> is printed
to C<STDOUT>, unless L<"--quiet"> is specified. Errors and warnings
are printed to C<STDERR>.
=head1 OPTIONS
@@ -3698,11 +4008,12 @@ pathname.
type: DSN
DSN for where to store foreign key errors; specify at least a database (D) and table (t).
Save foreign key errors in this table. The DSN must specify a database (D)
and table (t).
Missing values are filled in with the same values from the source host, so you
can usually omit most parts of this argument if you're storing foreign key
errors on the same server on which they happen.
Missing DSN values are inherited from the DSN being monitored, so you
can omit most values if you're saving foreign key errors on the same
host.
The following table is suggested:
@@ -3726,10 +4037,21 @@ Connect to host.
=item --interval
type: time; default: 0
type: time; default: 30
How often to check for foreign key errors.
=item --iterations
type: int
How many times to check for foreign key errors. By default, this option
is undefined which means an infinite number of iterations. The tool always
exits for L<"--run-time">, regardless of the value specified for this option.
For example, the tool will exit after 1 minute with
C<--run-time 1m --iterations 4 --interval 30> because 4 iterations at 30
second intervals would take 2 minutes, longer than the 1 mintue run-time.
=item --log
type: string
@@ -3757,15 +4079,15 @@ short form: -P; type: int
Port number to use for connection.
=item --print
=item --quiet
Print results on standard output. See L<"OUTPUT"> for more.
Do not print foreign key errors; only print errors and warnings to C<STDERR>.
=item --run-time
type: time
How long to run before exiting.
How long to run before exiting. By default, the tool runs forever.
=item --set-vars
@@ -3948,7 +4270,7 @@ L<http://www.percona.com/software/> for more software developed by Percona.
=head1 COPYRIGHT, LICENSE, AND WARRANTY
This program is copyright 2011-2012 Percona Ireland Ltd.
This program is copyright 2011-2013 Percona Ireland Ltd.
THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF

View File

@@ -27,8 +27,9 @@ if ( !$dbh ) {
$sb->create_dbs($dbh, [qw(test)]);
my $output;
my $cnf = '/tmp/12345/my.sandbox.cnf';
my $cmd = "$trunk/bin/pt-fk-error-logger -F $cnf ";
my $cnf = '/tmp/12345/my.sandbox.cnf';
my $cmd = "$trunk/bin/pt-fk-error-logger -F $cnf ";
my @args = qw(--iterations 1);
$sb->load_file('master', 't/pt-fk-error-logger/samples/fke_tbl.sql', 'test');
@@ -39,8 +40,45 @@ $sb->load_file('master', 't/pt-fk-error-logger/samples/fke_tbl.sql', 'test');
# First, create a foreign key error.
`/tmp/12345/use -D test < $trunk/t/pt-fk-error-logger/samples/fke.sql 1>/dev/null 2>/dev/null`;
# Then get and save that fke.
output(sub { pt_fk_error_logger::main('h=127.1,P=12345,u=msandbox,p=msandbox', '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors'); } );
$output = output(
sub {
pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox'),
}
);
like(
$output,
qr/Foreign key constraint fails/,
"Prints fk error by default"
);
$output = output(
sub {
pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox',
qw(--quiet))
}
);
is(
$output,
"",
"No output with --quiet"
);
# #############################################################################
# --dest
# #############################################################################
$output = output(
sub {
pt_fk_error_logger::main(@args,
'h=127.1,P=12345,u=msandbox,p=msandbox',
'--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors',
)
}
);
sleep 0.1;
# And then test that it was actually saved.
@@ -61,7 +99,7 @@ like(
# Check again to make sure that the same fke isn't saved twice.
my $first_ts = $fke->[0]->[0];
output(sub { pt_fk_error_logger::main('h=127.1,P=12345,u=msandbox,p=msandbox', '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors'); } );
output(sub { pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox', '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors'); } );
sleep 0.1;
$fke = $dbh->selectall_arrayref('SELECT * FROM test.foreign_key_errors');
is(
@@ -82,7 +120,7 @@ $dbh->do('INSERT INTO child VALUES (1, 2)');
eval {
$dbh->do('DELETE FROM parent WHERE id = 2'); # Causes foreign key error.
};
output( sub { pt_fk_error_logger::main('h=127.1,P=12345,u=msandbox,p=msandbox', '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors'); } );
output( sub { pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox', '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors'); } );
sleep 0.1;
$fke = $dbh->selectall_arrayref('SELECT * FROM test.foreign_key_errors');
like(
@@ -99,11 +137,14 @@ is(
# ##########################################################################
# Test printing the errors.
# ##########################################################################
$dbh->do('USE test');
eval {
$dbh->do('DELETE FROM parent WHERE id = 2'); # Causes foreign key error.
};
$output = output(sub { pt_fk_error_logger::main('h=127.1,P=12345,u=msandbox,p=msandbox'); });
$output = output(sub { pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox'); });
like(
$output,
qr/DELETE FROM parent WHERE id = 2/,
@@ -127,7 +168,7 @@ $sb->load_file('master1', 't/pt-fk-error-logger/samples/fke_tbl.sql', 'test');
$output = output(
sub {
pt_fk_error_logger::main('h=127.1,P=12348,u=msandbox,p=msandbox',
pt_fk_error_logger::main(@args, 'h=127.1,P=12348,u=msandbox,p=msandbox',
'--dest', 'h=127.1,P=12348,D=test,t=foreign_key_errors')
},
stderr => 1,
@@ -141,6 +182,23 @@ is(
diag(`$trunk/sandbox/stop-sandbox 12348 >/dev/null`);
# #############################################################################
# Test --pid
# #############################################################################
my $pid_file = "/tmp/pt-fk-error-log-test-$PID.pid";
diag(`touch $pid_file`);
$output = `$trunk/bin/pt-fk-error-logger h=127.1,P=12345,u=msandbox,p=msandbox --pid $pid_file --iterations 1 2>&1`;
like(
$output,
qr{PID file $pid_file already exists},
'Dies if PID file already exists (--pid without --daemonize) (issue 391)'
);
unlink $pid_file;
# #############################################################################
# Done.
# #############################################################################

View File

@@ -9,7 +9,7 @@ BEGIN {
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Test::More tests => 10;
use Test::More;
use PerconaTest;
require "$trunk/bin/pt-fk-error-logger";
@@ -70,4 +70,4 @@ test_get_fk_error(
# #############################################################################
# Done.
# #############################################################################
exit;
done_testing;

View File

@@ -1,32 +0,0 @@
#!/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 tests => 1;
use PerconaTest;
require "$trunk/bin/pt-fk-error-logger";
# #########################################################################
# Issue 391: Add --pid option to all scripts
# #########################################################################
`touch /tmp/mk-script.pid`;
my $output = `$trunk/bin/pt-fk-error-logger h=127.1,P=12345,u=msandbox,p=msandbox --print --pid /tmp/mk-script.pid 2>&1`;
like(
$output,
qr{PID file /tmp/mk-script.pid already exists},
'Dies if PID file already exists (--pid without --daemonize) (issue 391)'
);
`rm -rf /tmp/mk-script.pid`;
# #############################################################################
# Done.
# #############################################################################
exit;