Merge pt-fke-logger-2.2.

This commit is contained in:
Daniel Nichter
2013-02-27 16:41:28 -07:00
36 changed files with 1856 additions and 823 deletions

View File

@@ -6958,10 +6958,11 @@ Password to use when connecting.
type: string
Create the given PID file when daemonized. The file contains the process ID of
the daemonized instance. The PID file is removed when the daemonized instance
exits. The program checks for the existence of the PID file when starting; if
it exists and the process with the matching PID exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --plugin

View File

@@ -2148,13 +2148,14 @@ sub new {
OptionParser => $o,
DSNParser => $dp,
is_cluster_node => undef,
parent => $args{parent},
};
return bless $self, $class;
}
sub connect {
my ( $self ) = @_;
my ( $self, %opts ) = @_;
my $dsn = $self->{dsn};
my $dp = $self->{DSNParser};
my $o = $self->{OptionParser};
@@ -2165,11 +2166,18 @@ sub connect {
$dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
$self->{asked_for_pass} = 1;
}
$dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });
$dbh = $dp->get_dbh(
$dp->get_cxn_params($dsn),
{
AutoCommit => 1,
%opts,
},
);
}
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name});
return $self->set_dbh($dbh);
$dbh = $self->set_dbh($dbh);
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
return $dbh;
}
sub set_dbh {
@@ -2192,6 +2200,11 @@ sub set_dbh {
$self->{hostname} = $hostname;
}
if ( $self->{parent} ) {
PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
$dbh->{InactiveDestroy} = 1;
}
if ( my $set = $self->{set}) {
$set->($dbh);
}
@@ -2201,6 +2214,13 @@ sub set_dbh {
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};
@@ -2219,12 +2239,21 @@ sub name {
sub DESTROY {
my ($self) = @_;
if ( $self->{dbh}
PTDEBUG && _d('Destroying cxn');
if ( $self->{parent} ) {
PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
}
elsif ( $self->{dbh}
&& blessed($self->{dbh})
&& $self->{dbh}->can("disconnect") ) {
PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name});
&& $self->{dbh}->can("disconnect") )
{
PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
$self->{dsn_name});
$self->{dbh}->disconnect();
}
return;
}
@@ -5022,12 +5051,7 @@ sub main {
# Daemonize now that everything is setup and ready to work.
# ########################################################################
my $daemon;
if ( $o->get('daemonize') ) {
$daemon = new Daemon(o=>$o);
$daemon->daemonize();
PTDEBUG && _d('I am a daemon now');
}
elsif ( $o->get('pid') ) {
if ( $o->get('pid') ) {
# We're not daemoninzing, it just handles PID stuff.
$daemon = new Daemon(o=>$o);
$daemon->make_PID_file();
@@ -5118,7 +5142,7 @@ pt-config-diff - Diff MySQL configuration files and server variables.
=head1 SYNOPSIS
Usage: pt-config-diff [OPTION...] CONFIG CONFIG [CONFIG...]
Usage: pt-config-diff [OPTIONS] CONFIG CONFIG [CONFIG...]
pt-config-diff diffs MySQL configuration files and server variables.
CONFIG can be a filename or a DSN. At least two CONFIG sources must be given.
@@ -5238,11 +5262,6 @@ Read this comma-separated list of config files; if specified, this must be the
first option on the command line. (This option does not specify a CONFIG;
it's equivalent to C<--defaults-file>.)
=item --daemonize
Fork to the background and detach from the shell. POSIX
operating systems only.
=item --defaults-file
short form: -F; type: string
@@ -5276,11 +5295,11 @@ Password to use for connection.
type: string
Create the given PID file when daemonized. The file contains the process
ID of the daemonized instance. The PID file is removed when the
daemonized instance exits. The program checks for the existence of the
PID file when starting; if it exists and the process with the matching PID
exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

File diff suppressed because it is too large Load Diff

View File

@@ -5130,14 +5130,11 @@ Password to use when connecting.
type: string
Create the given PID file. The file contains the process ID of the script.
The PID file is removed when the script exits. Before starting, the script
checks if the PID file already exists. If it does not, then the script creates
and writes its own PID to it. If it does, then the script checks the following:
if the file contains a PID and a process is running with that PID, then
the script dies; or, if there is no process running with that PID, then the
script overwrites the file with its own PID and starts; else, if the file
contains no PID, then the script dies.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -1456,14 +1456,11 @@ printed to the fifo.
type: string
Create the given PID file. The file contains the process ID of the script.
The PID file is removed when the script exits. Before starting, the script
checks if the PID file already exists. If it does not, then the script creates
and writes its own PID to it. If it does, then the script checks the following:
if the file contains a PID and a process is running with that PID, then
the script dies; or, if there is no process running with that PID, then the
script overwrites the file with its own PID and starts; else, if the file
contains no PID, then the script dies.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --statistics

View File

@@ -4241,14 +4241,11 @@ Password to use when connecting.
type: string
Create the given PID file. The file contains the process ID of the script.
The PID file is removed when the script exits. Before starting, the script
checks if the PID file already exists. If it does not, then the script creates
and writes its own PID to it. If it does, then the script checks the following:
if the file contains a PID and a process is running with that PID, then
the script dies; or, if there is no process running with that PID, then the
script overwrites the file with its own PID and starts; else, if the file
contains no PID, then the script dies.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -17,10 +17,12 @@ BEGIN {
OptionParser
Quoter
DSNParser
Cxn
Daemon
Transformers
HTTPMicro
VersionCheck
Runtime
));
}
@@ -1595,6 +1597,181 @@ 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,
parent => $args{parent},
};
return bless $self, $class;
}
sub connect {
my ( $self, %opts ) = @_;
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,
%opts,
},
);
}
$dbh = $self->set_dbh($dbh);
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
return $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 ( $self->{parent} ) {
PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
$dbh->{InactiveDestroy} = 1;
}
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) = @_;
PTDEBUG && _d('Destroying cxn');
if ( $self->{parent} ) {
PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
}
elsif ( $self->{dbh}
&& blessed($self->{dbh})
&& $self->{dbh}->can("disconnect") )
{
PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
$self->{dsn_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 +3551,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 +3697,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;
$exit_status = 0;
# ########################################################################
# Get configuration information.
@@ -3409,69 +3722,56 @@ 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,
parent => $o->get('daemonize'),
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),
parent => $o->get('daemonize'),
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);
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('insert sql:', $sql);
$ins_sth = $dst_dbh->prepare($sql);
PTDEBUG && _d('--dest INSERT SQL:', $sql);
$ins_sth = $dst->dbh->prepare($sql);
}
# ########################################################################
@@ -3489,6 +3789,16 @@ sub main {
$daemon->make_PID_file();
}
# If we daemonized, the parent has already exited and we're the child.
# We shared a copy of every Cxn with the parent, and the parent's copies
# were destroyed but the dbhs were not disconnected because the parent
# attrib was true. Now, as the child, set it false so the dbhs will be
# disconnected when our Cxn copies are destroyed. If we didn't daemonize,
# then we're not really a parent (since we have no children), so set it
# false to auto-disconnect the dbhs when our Cxns are destroyed.
$src->{parent} = 0;
$dst->{parent} = 0 if $dst;
# ########################################################################
# Do the version-check
# ########################################################################
@@ -3496,8 +3806,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,15 +3815,48 @@ 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 },
);
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 "Lost connection to MySQL. Will try to reconnect "
. "in the next iteration.\n";
}
else {
PTDEBUG && _d('Reconnected to MySQL');
redo ITERATION;
}
}
else {
warn "Error parsing SHOW ENGINE INNODB STATUS: $EVAL_ERROR";
$exit_status |= 1;
}
}
else {
if ( $ts && $fk_error ) {
# Save and/or print the foreign key error.
if ( $ins_sth ) {
@@ -3527,21 +3870,22 @@ sub main {
PTDEBUG && _d($EVAL_ERROR);
}
}
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();
if ( !$o->get('quiet') ) {
print "$ts $fk_error\n\n";
}
else {
$oktorun = 0;
}
}
return 0;
# Sleep if there's an --iteration left.
if ( !defined $iters || $iters ) {
PTDEBUG && _d('Sleeping', $interval, 'seconds');
sleep $interval;
}
}
PTDEBUG && _d('Done running, exiting', $exit_status);
return $exit_status;
}
# ############################################################################
@@ -3550,9 +3894,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 +3912,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 +3941,28 @@ 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">. The tool runs for forever unless
L<"--run-time"> or L<"--iterations"> is specified.
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 +3991,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 +4043,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 +4072,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
@@ -3746,10 +4103,11 @@ Password to use when connecting.
type: string
Create the given PID file when daemonized. The file contains the process ID of
the daemonized instance. The PID file is removed when the daemonized instance
exits. The program checks for the existence of the PID file when starting; if
it exists and the process with the matching PID exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port
@@ -3757,15 +4115,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 +4306,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

@@ -5664,10 +5664,11 @@ Password to use when connecting.
type: string
Create the given PID file when daemonized. The file contains the process ID of
the daemonized instance. The PID file is removed when the daemonized instance
exits. The program checks for the existence of the PID file when starting; if
it exists and the process with the matching PID exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -4979,13 +4979,14 @@ sub new {
OptionParser => $o,
DSNParser => $dp,
is_cluster_node => undef,
parent => $args{parent},
};
return bless $self, $class;
}
sub connect {
my ( $self ) = @_;
my ( $self, %opts ) = @_;
my $dsn = $self->{dsn};
my $dp = $self->{DSNParser};
my $o = $self->{OptionParser};
@@ -4996,11 +4997,18 @@ sub connect {
$dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
$self->{asked_for_pass} = 1;
}
$dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });
$dbh = $dp->get_dbh(
$dp->get_cxn_params($dsn),
{
AutoCommit => 1,
%opts,
},
);
}
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name});
return $self->set_dbh($dbh);
$dbh = $self->set_dbh($dbh);
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
return $dbh;
}
sub set_dbh {
@@ -5023,6 +5031,11 @@ sub set_dbh {
$self->{hostname} = $hostname;
}
if ( $self->{parent} ) {
PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
$dbh->{InactiveDestroy} = 1;
}
if ( my $set = $self->{set}) {
$set->($dbh);
}
@@ -5032,6 +5045,13 @@ sub set_dbh {
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};
@@ -5050,12 +5070,21 @@ sub name {
sub DESTROY {
my ($self) = @_;
if ( $self->{dbh}
PTDEBUG && _d('Destroying cxn');
if ( $self->{parent} ) {
PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
}
elsif ( $self->{dbh}
&& blessed($self->{dbh})
&& $self->{dbh}->can("disconnect") ) {
PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name});
&& $self->{dbh}->can("disconnect") )
{
PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
$self->{dsn_name});
$self->{dbh}->disconnect();
}
return;
}
@@ -6464,6 +6493,7 @@ sub main {
$cxn = Cxn->new(
dsn_string => shift @ARGV,
NAME_lc => 0,
parent => $o->get('daemonize'),
DSNParser => $dp,
OptionParser => $o,
);
@@ -6643,6 +6673,15 @@ sub main {
$daemon->make_PID_file();
}
# If we daemonized, the parent has already exited and we're the child.
# We shared a copy of every Cxn with the parent, and the parent's copies
# were destroyed but the dbhs were not disconnected because the parent
# attrib was true. Now, as the child, set it false so the dbhs will be
# disconnected when our Cxn copies are destroyed. If we didn't daemonize,
# then we're not really a parent (since we have no children), so set it
# false to auto-disconnect the dbhs when our Cxns are destroyed.
$cxn->{parent} = 0 if $cxn;
# ########################################################################
# Do the version-check
# ########################################################################
@@ -7311,10 +7350,11 @@ Password to use when connecting.
type: string
Create the given PID file when daemonized. The file contains the process ID of
the daemonized instance. The PID file is removed when the daemonized instance
exits. The program checks for the existence of the PID file when starting; if
it exists and the process with the matching PID exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -4028,13 +4028,14 @@ sub new {
OptionParser => $o,
DSNParser => $dp,
is_cluster_node => undef,
parent => $args{parent},
};
return bless $self, $class;
}
sub connect {
my ( $self ) = @_;
my ( $self, %opts ) = @_;
my $dsn = $self->{dsn};
my $dp = $self->{DSNParser};
my $o = $self->{OptionParser};
@@ -4045,11 +4046,18 @@ sub connect {
$dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
$self->{asked_for_pass} = 1;
}
$dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });
$dbh = $dp->get_dbh(
$dp->get_cxn_params($dsn),
{
AutoCommit => 1,
%opts,
},
);
}
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name});
return $self->set_dbh($dbh);
$dbh = $self->set_dbh($dbh);
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
return $dbh;
}
sub set_dbh {
@@ -4072,6 +4080,11 @@ sub set_dbh {
$self->{hostname} = $hostname;
}
if ( $self->{parent} ) {
PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
$dbh->{InactiveDestroy} = 1;
}
if ( my $set = $self->{set}) {
$set->($dbh);
}
@@ -4081,6 +4094,13 @@ sub set_dbh {
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};
@@ -4099,12 +4119,21 @@ sub name {
sub DESTROY {
my ($self) = @_;
if ( $self->{dbh}
PTDEBUG && _d('Destroying cxn');
if ( $self->{parent} ) {
PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
}
elsif ( $self->{dbh}
&& blessed($self->{dbh})
&& $self->{dbh}->can("disconnect") ) {
PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name});
&& $self->{dbh}->can("disconnect") )
{
PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
$self->{dsn_name});
$self->{dbh}->disconnect();
}
return;
}
@@ -10839,10 +10868,11 @@ Password to use when connecting.
type: string
Create the given PID file. The file contains the process ID of the tool's
instance. The PID file is removed when the tool exits. The tool checks for
the existence of the PID file when starting; if it exists and the process with
the matching PID exists, the tool exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -9092,11 +9092,11 @@ Password to use when connecting.
type: string
Create the given PID file when daemonized. The file contains the process
ID of the daemonized instance. The PID file is removed when the
daemonized instance exits. The program checks for the existence of the
PID file when starting; if it exists and the process with the matching PID
exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -14967,11 +14967,11 @@ Password to use when connecting.
type: string
Create the given PID file when daemonized. The file contains the process
ID of the daemonized instance. The PID file is removed when the
daemonized instance exits. The program checks for the existence of the
PID file when starting; if it exists and the process with the matching PID
exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -2093,14 +2093,11 @@ Password to use when connecting.
type: string
Create the given PID file. The file contains the process ID of the script.
The PID file is removed when the script exits. Before starting, the script
checks if the PID file already exists. If it does not, then the script creates
and writes its own PID to it. If it does, then the script checks the following:
if the file contains a PID and a process is running with that PID, then
the script dies; or, if there is no process running with that PID, then the
script overwrites the file with its own PID and starts; else, if the file
contains no PID, then the script dies.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -4478,11 +4478,11 @@ Password to use when connecting.
type: string
Create the given PID file when daemonized. The file contains the process
ID of the daemonized instance. The PID file is removed when the
daemonized instance exits. The program checks for the existence of the
PID file when starting; if it exists and the process with the matching PID
exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -3960,14 +3960,11 @@ Password to use when connecting.
type: string
Create the given PID file. The file contains the process ID of the script.
The PID file is removed when the script exits. Before starting, the script
checks if the PID file already exists. If it does not, then the script creates
and writes its own PID to it. If it does, then the script checks the following:
if the file contains a PID and a process is running with that PID, then
the script dies; or, if there is no process running with that PID, then the
script overwrites the file with its own PID and starts; else, if the file
contains no PID, then the script dies.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -5312,11 +5312,11 @@ Password to use when connecting.
type: string
Create the given PID file when daemonized. The file contains the process
ID of the daemonized instance. The PID file is removed when the
daemonized instance exits. The program checks for the existence of the
PID file when starting; if it exists and the process with the matching PID
exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -1723,7 +1723,11 @@ Send mail to this list of addresses when data is collected.
type: string; default: /var/run/pt-stalk.pid
Create a PID file when daemonized.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --plugin

View File

@@ -3386,13 +3386,14 @@ sub new {
OptionParser => $o,
DSNParser => $dp,
is_cluster_node => undef,
parent => $args{parent},
};
return bless $self, $class;
}
sub connect {
my ( $self ) = @_;
my ( $self, %opts ) = @_;
my $dsn = $self->{dsn};
my $dp = $self->{DSNParser};
my $o = $self->{OptionParser};
@@ -3403,11 +3404,18 @@ sub connect {
$dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
$self->{asked_for_pass} = 1;
}
$dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });
$dbh = $dp->get_dbh(
$dp->get_cxn_params($dsn),
{
AutoCommit => 1,
%opts,
},
);
}
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name});
return $self->set_dbh($dbh);
$dbh = $self->set_dbh($dbh);
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
return $dbh;
}
sub set_dbh {
@@ -3430,6 +3438,11 @@ sub set_dbh {
$self->{hostname} = $hostname;
}
if ( $self->{parent} ) {
PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
$dbh->{InactiveDestroy} = 1;
}
if ( my $set = $self->{set}) {
$set->($dbh);
}
@@ -3439,6 +3452,13 @@ sub set_dbh {
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};
@@ -3457,12 +3477,21 @@ sub name {
sub DESTROY {
my ($self) = @_;
if ( $self->{dbh}
PTDEBUG && _d('Destroying cxn');
if ( $self->{parent} ) {
PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
}
elsif ( $self->{dbh}
&& blessed($self->{dbh})
&& $self->{dbh}->can("disconnect") ) {
PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name});
&& $self->{dbh}->can("disconnect") )
{
PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
$self->{dsn_name});
$self->{dbh}->disconnect();
}
return;
}
@@ -11590,14 +11619,11 @@ Password to use when connecting.
type: string
Create the given PID file. The file contains the process ID of the script.
The PID file is removed when the script exits. Before starting, the script
checks if the PID file already exists. If it does not, then the script creates
and writes its own PID to it. If it does, then the script checks the following:
if the file contains a PID and a process is running with that PID, then
the script dies; or, if there is no process running with that PID, then the
script overwrites the file with its own PID and starts; else, if the file
contains no PID, then the script dies.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -12051,14 +12051,11 @@ Password to use when connecting.
type: string
Create the given PID file. The file contains the process ID of the script.
The PID file is removed when the script exits. Before starting, the script
checks if the PID file already exists. If it does not, then the script creates
and writes its own PID to it. If it does, then the script checks the following:
if the file contains a PID and a process is running with that PID, then
the script dies; or, if there is no process running with that PID, then the
script overwrites the file with its own PID and starts; else, if the file
contains no PID, then the script dies.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -7192,11 +7192,11 @@ Password to use when connecting.
type: string
Create the given PID file when running. The file contains the process
ID of the daemonized instance. The PID file is removed when the
daemonized instance exits. The program checks for the existence of the
PID file when starting; if it exists and the process with the matching PID
exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -13483,11 +13483,11 @@ Password to use when connecting.
type: string
Create the given PID file when daemonized. The file contains the process
ID of the daemonized instance. The PID file is removed when the
daemonized instance exits. The program checks for the existence of the
PID file when starting; if it exists and the process with the matching PID
exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -5752,11 +5752,11 @@ Password to use when connecting.
type: string
Create the given PID file when daemonized. The file contains the process
ID of the daemonized instance. The PID file is removed when the
daemonized instance exits. The program checks for the existence of the
PID file when starting; if it exists and the process with the matching PID
exists, the program exits.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -2952,14 +2952,11 @@ Password to use when connecting.
type: string
Create the given PID file. The file contains the process ID of the script.
The PID file is removed when the script exits. Before starting, the script
checks if the PID file already exists. If it does not, then the script creates
and writes its own PID to it. If it does, then the script checks the following:
if the file contains a PID and a process is running with that PID, then
the script dies; or, if there is no process running with that PID, then the
script overwrites the file with its own PID and starts; else, if the file
contains no PID, then the script dies.
Create the given PID file. The tool won't start if the PID file already
exists and the PID it contains is different than the current PID. However,
if the PID file exists and the PID it contains is no longer running, the
tool will overwrite the PID file with the current PID. The PID file is
removed automatically when the tool exits.
=item --port

View File

@@ -175,6 +175,34 @@ Format EXPLAIN output as a tree.
For more free, open-source software developed Percona, visit
L<http://www.percona.com/software/>.
=head1 SPECIAL OPTION TYPES
=over
=item time
Time values are seconds by default. For example, C<--run-time 60> means
60 seconds. Time values support an optional suffix: s (seconds),
m (minutes), h (hours), d (days). C<--run-time 1m> means 1 minute
(the same as 60 seconds).
=item size
Size values are bytes by default. For example, C<--disk-space-free 1024>
means 1 Kibibyte. Size values support an optional suffix: k (Kibibyte),
M (Mebibyte), G (Gibibyte).
=item DSN
See L<"DSN (DATA SOURCE NAME) SPECIFICATIONS">.
=item Hash, hash, Array, array
Hash, hash, Array, and array values are comma-separated lists of values.
For example, C<--ignore-tables foo,bar> ignores tables C<foo> and C<bar>.
=back
=head1 CONFIGURATION FILES
Percona Toolkit tools can read options from configuration files. The

View File

@@ -108,13 +108,14 @@ sub new {
OptionParser => $o,
DSNParser => $dp,
is_cluster_node => undef,
parent => $args{parent},
};
return bless $self, $class;
}
sub connect {
my ( $self ) = @_;
my ( $self, %opts ) = @_;
my $dsn = $self->{dsn};
my $dp = $self->{DSNParser};
my $o = $self->{OptionParser};
@@ -126,11 +127,18 @@ sub connect {
$dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
$self->{asked_for_pass} = 1;
}
$dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });
$dbh = $dp->get_dbh(
$dp->get_cxn_params($dsn),
{
AutoCommit => 1,
%opts,
},
);
}
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name});
return $self->set_dbh($dbh);
$dbh = $self->set_dbh($dbh);
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
return $dbh;
}
sub set_dbh {
@@ -163,6 +171,11 @@ sub set_dbh {
$self->{hostname} = $hostname;
}
if ( $self->{parent} ) {
PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
$dbh->{InactiveDestroy} = 1;
}
# Call the set callback to let the caller SET any MySQL variables.
if ( my $set = $self->{set}) {
$set->($dbh);
@@ -173,6 +186,15 @@ sub set_dbh {
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/;
# The 1st pattern means that MySQL itself died or was stopped.
# The 2nd pattern means that our cxn was killed (KILL <id>).
}
# Sub: dbh
# Return the cxn's dbh.
sub dbh {
@@ -197,12 +219,21 @@ sub name {
sub DESTROY {
my ($self) = @_;
if ( $self->{dbh}
PTDEBUG && _d('Destroying cxn');
if ( $self->{parent} ) {
PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
}
elsif ( $self->{dbh}
&& blessed($self->{dbh})
&& $self->{dbh}->can("disconnect") ) {
PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name});
&& $self->{dbh}->can("disconnect") )
{
PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
$self->{dsn_name});
$self->{dbh}->disconnect();
}
return;
}

View File

@@ -18,13 +18,6 @@
# Runtime package
# ###########################################################################
{
# Package: Runtime
# Runtime keeps track of time to control how long a tool's main loop runs.
# This package was created to handle mk-query-digest --run-time-mode event.
# In essence, we abstract time so that the tool doesn't know/care whether
# now() comes from a clock, a log timestamp, or wherever. The creator of
# Runtime object determines how, or from where, time is gotten so that the
# caller of the object can simply ask, "What time is it?".
package Runtime;
use strict;
@@ -32,30 +25,24 @@ use warnings FATAL => 'all';
use English qw(-no_match_vars);
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
# Sub: new
#
# Parameters:
# %args - Arguments
#
# Required Arguments:
# now - Callback that sets current time.
# runtime - Amount of time to run in seconds, or undef for forever.
#
# Returns:
# Runtime object
sub new {
my ( $class, %args ) = @_;
my @required_args = qw(now);
my @required_args = qw(run_time now);
foreach my $arg ( @required_args ) {
die "I need a $arg argument" unless $args{$arg};
die "I need a $arg argument" unless exists $args{$arg};
}
if ( ($args{runtime} || 0) < 0 ) {
die "runtime argument must be greater than zero"
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 = {
%args,
run_time => $run_time,
now => $now,
start_time => undef,
end_time => undef,
time_left => undef,
@@ -66,8 +53,8 @@ sub new {
}
# Sub: time_left
# Return the number of runtime seconds left or undef for forever.
# The return may be less than zero if the runtime has been exceeded.
# Return the number of run time seconds left or undef for forever.
# The return may be less than zero if the run time has been exceeded.
# The first call to this subroutine "starts the clock", so to speak,
# if the now callbackup returns a defined value.
#
@@ -75,7 +62,7 @@ sub new {
# %args - Arguments passed to now callback.
#
# Returns:
# Number of runtime seconds left, possibly less than zero, or undef
# Number of run time seconds left, possibly less than zero, or undef
# if running forever.
sub time_left {
my ( $self, %args ) = @_;
@@ -99,14 +86,14 @@ sub time_left {
# we know the current time.
return unless defined $now;
# If runtime is also defined, then we can determine time left.
# If run_time is also defined, then we can determine time left.
# If it's not defined, then we're running forever.
my $runtime = $self->{runtime};
return unless defined $runtime;
my $run_time = $self->{run_time};
return unless defined $run_time;
# Set the end time once.
if ( !$self->{end_time} ) {
$self->{end_time} = $now + $runtime;
$self->{end_time} = $now + $run_time;
PTDEBUG && _d("End time:", $self->{end_time});
}
@@ -118,7 +105,7 @@ sub time_left {
}
# Sub: have_time
# Return true or false if there's runtime left. This sub is a simpler
# Return true or false if there's run time left. This sub is a simpler
# wrapper around <time_left()> which returns true (1) if time left is
# defined and greater than zero or undef, else returns false.
#
@@ -131,7 +118,7 @@ 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 runtime has elapsed
return $time_left <= 0 ? 0 : 1; # <=0s means run time has elapsed
}
# Sub: time_elapsed
@@ -173,7 +160,7 @@ sub reset {
$self->{end_time} = undef;
$self->{time_left} = undef;
$self->{stop} = 0;
PTDEBUG && _d("Reset runtime");
PTDEBUG && _d("Reset run time");
return;
}

View File

@@ -9,7 +9,7 @@ BEGIN {
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Test::More tests => 19;
use Test::More;
use Sandbox;
use OptionParser;
@@ -251,9 +251,65 @@ is_deeply(
@ARGV = ();
$o->get_opts();
# #############################################################################
# The parent of a forked Cxn should not disconnect the dbh in DESTORY
# because the child still has access to it.
# #############################################################################
my $sync_file = "/tmp/pt-cxn-sync.$PID";
my $outfile = "/tmp/pt-cxn-outfile.$PID";
my $pid;
{
my $parent_cxn = make_cxn(
dsn_string => 'h=127.1,P=12345,u=msandbox,p=msandbox',
parent => 1,
);
$parent_cxn->connect();
$pid = fork();
if ( defined($pid) && $pid == 0 ) {
# I am the child.
# Wait for the parent to leave this code block which will cause
# the $parent_cxn to be destroyed.
PerconaTest::wait_for_files($sync_file);
$parent_cxn->{parent} = 0;
eval {
$parent_cxn->dbh->do("SELECT 123 INTO OUTFILE '$outfile'");
$parent_cxn->dbh->disconnect();
};
warn $EVAL_ERROR if $EVAL_ERROR;
exit;
}
}
# Let the child know that we (the parent) have left that ^ code block,
# so our copy of $parent_cxn has been destroyed, but hopefully the child's
# copy is still alive, i.e. has an active/not-disconnected dbh.
diag(`touch $sync_file`);
# Wait for the child.
waitpid($pid, 0);
ok(
-f $outfile,
"Child created outfile"
);
my $output = `cat $outfile 2>/dev/null`;
is(
$output,
"123\n",
"Child executed query"
);
unlink $sync_file if -f $sync_file;
unlink $outfile if -f $outfile;
# #############################################################################
# Done.
# #############################################################################
$master_dbh->disconnect() if $master_dbh;
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
exit;
done_testing;

View File

@@ -11,6 +11,8 @@ use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Test::More;
$ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} = 1;
use PerconaTest;
use Sandbox;
require "$trunk/bin/pt-deadlock-logger";
@@ -25,8 +27,8 @@ if ( !$dbh1 || !$dbh2 ) {
}
my $output;
my $cnf = "/tmp/12345/my.sandbox.cnf";
my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1";
my $dsn = $sb->dsn_for('master');
my @args = ($dsn, qw(--iterations 1));
$dbh1->commit;
$dbh2->commit;
@@ -90,20 +92,29 @@ make_deadlock();
$output = $dbh1->selectrow_hashref('show /*!40101 engine*/ innodb status')->{status};
like($output, qr/WE ROLL BACK/, 'There was a deadlock');
$output = `$cmd --print`;
$output = output(
sub {
pt_deadlock_logger::main(@args);
}
);
like(
$output,
qr/127\.1.+msandbox.+GEN_CLUST_INDEX/,
'Deadlock logger prints the output'
);
$output = `$cmd`;
like(
$output,
qr/127\.1.+msandbox.+GEN_CLUST_INDEX/,
'--print is implicit'
$output = output(
sub {
pt_deadlock_logger::main(@args, qw(--quiet));
}
);
is(
$output,
"",
"No output with --quiet"
);
# #############################################################################
# Issue 943: mk-deadlock-logger reports the same deadlock with --interval
@@ -112,55 +123,59 @@ like(
# The deadlock from above won't be re-printed so even after running for
# 3 seconds and checking multiple times only the single, 3 line deadlock
# should be reported.
chomp($output = `$cmd --run-time 3 | wc -l`);
$output = output(
sub {
pt_deadlock_logger::main(@args, qw(--run-time 3));
}
);
$output =~ s/^\s+//;
my @lines = split("\n", $output);
is(
$output,
scalar @lines,
3,
"Doesn't re-print same deadlock (issue 943)"
);
) or diag($output);
# #############################################################################
# Check that deadlocks from previous test were stored in table.
# #############################################################################
`$cmd --dest D=test,t=deadlocks --create-dest-table`;
$output = output(
sub {
pt_deadlock_logger::main(@args, '--dest', 'D=test,t=deadlocks',
qw(--create-dest-table))
}
);
my $res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks');
ok(
scalar @$res,
'Deadlocks recorded in --dest table'
);
'Deadlock saved in --dest table'
) or diag($output);
# #############################################################################
# Check that --dest suppress --print output unless --print is explicit.
# In 2.1, --dest suppressed output (--print). In 2.2, output is only
# suppressed by --quiet.
# #############################################################################
$output = 'foo';
$dbh1->do('TRUNCATE TABLE test.deadlocks');
$output = `$cmd --dest D=test,t=deadlocks`;
is(
$output,
'',
'No output with --dest'
);
$res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks');
ok(
scalar @$res,
'Deadlocks still recorded in table'
);
$output = '';
$dbh1->do('TRUNCATE TABLE test.deadlocks');
$output = `$trunk/bin/pt-deadlock-logger --print --dest D=test,t=deadlocks --host 127.1 --port 12345 --user msandbox --password msandbox`;
like(
$output = output(
sub {
pt_deadlock_logger::main(@args, '--dest', 'D=test,t=deadlocks',
qw(--quiet))
}
);
is(
$output,
qr/127\.1.+msandbox.+GEN_CLUST_INDEX/,
'Prints output with --dest and explicit --print'
"",
"No output with --dest and --quiet"
);
$res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks');
ok(
scalar @$res,
'Deadlocks recorded in table again'
"... deadlock still saved in the table"
);
# #############################################################################
@@ -180,9 +195,7 @@ SKIP: {
make_deadlock();
$output = output(
sub { pt_deadlock_logger::main("F=/tmp/12345/my.sandbox.cnf",
qw(--print) );
}
sub { pt_deadlock_logger::main(@args) }
);
like(
@@ -200,4 +213,3 @@ $dbh2->commit;
$sb->wipe_clean($dbh1);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
done_testing;
exit;

View File

@@ -22,9 +22,6 @@ my $dbh1 = $sb->get_dbh_for('master');
if ( !$dbh1 ) {
plan skip_all => 'Cannot connect to sandbox master';
}
else {
plan tests => 4;
}
my $output;
my $cnf = "/tmp/12345/my.sandbox.cnf";
@@ -39,7 +36,7 @@ $sb->create_dbs($dbh1, ['test']);
# The clear-deadlocks table comes and goes quickly so we can really
# only search the debug output for evidence that it was created.
$output = `PTDEBUG=1 $trunk/bin/pt-deadlock-logger F=$cnf,D=test --clear-deadlocks test.make_deadlock 2>&1`;
$output = `PTDEBUG=1 $trunk/bin/pt-deadlock-logger F=$cnf,D=test --clear-deadlocks test.make_deadlock --iterations 1 2>&1`;
like(
$output,
qr/INSERT INTO test.make_deadlock/,
@@ -67,4 +64,4 @@ like(
# #############################################################################
$sb->wipe_clean($dbh1);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
exit;
done_testing;

View File

@@ -22,15 +22,10 @@ my $dbh1 = $sb->get_dbh_for('master');
if ( !$dbh1 ) {
plan skip_all => 'Cannot connect to sandbox master';
}
else {
plan tests => 3;
}
my $output;
my $cnf = "/tmp/12345/my.sandbox.cnf";
my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1";
my $dsn = $sb->dsn_for('master');
$sb->wipe_clean($dbh1);
$sb->create_dbs($dbh1, ['test']);
# #############################################################################
@@ -42,17 +37,25 @@ is_deeply(
'Deadlocks table does not exit (issue 386)'
);
`$cmd --dest D=test,t=issue_386 --run-time 1s --interval 1s --create-dest-table`;
$output = output(
sub {
pt_deadlock_logger::main($dsn,
'--dest', 'D=test,t=issue_386',
qw(--iterations 1 --create-dest-table)
)
},
stderr => 1,
);
is_deeply(
$dbh1->selectall_arrayref(q{show tables from `test` like 'issue_386'}),
[['issue_386']],
'Deadlocks table created with --create-dest-table (issue 386)'
);
) or diag($output);
# #############################################################################
# Done.
# #############################################################################
$sb->wipe_clean($dbh1);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
exit;
done_testing;

View File

@@ -21,7 +21,7 @@ my $output;
$output = `$trunk/bin/pt-deadlock-logger --dest D=test,t=deadlocks 2>&1`;
like(
$output,
qr/Missing or invalid source host/,
qr/No DSN was specified/,
'Requires source host'
);

View File

@@ -17,69 +17,96 @@ require "$trunk/bin/pt-deadlock-logger";
my $dp = new DSNParser(opts=>$dsn_opts);
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $dbh1 = $sb->get_dbh_for('master');
my $dbh = $sb->get_dbh_for('master');
if ( !$dbh1 ) {
if ( !$dbh ) {
plan skip_all => 'Cannot connect to sandbox master';
}
else {
plan tests => 10;
}
my $output;
my $cnf = "/tmp/12345/my.sandbox.cnf";
my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1";
my $dsn = $sb->dsn_for('master');
my @args = ($dsn, qw(--iterations 1));
$sb->wipe_clean($dbh1);
$sb->create_dbs($dbh1, ['test']);
$sb->wipe_clean($dbh);
$sb->create_dbs($dbh, ['test']);
# #############################################################################
# Issue 248: Add --user, --pass, --host, etc to all tools
# #############################################################################
# Test that source DSN inherits from --user, etc.
$output = `$trunk/bin/pt-deadlock-logger h=127.1,D=test,u=msandbox,p=msandbox --clear-deadlocks test.make_deadlock --port 12345 2>&1`;
$output = output(
sub {
pt_deadlock_logger::main(
"h=127.1,D=test,u=msandbox,p=msandbox",
qw(--clear-deadlocks test.make_deadlock --port 12345),
qw(--iterations 1)
)
}
);
unlike(
$output,
qr/failed/,
'Source DSN inherits from standard connection options (issue 248)'
);
# #########################################################################
# #############################################################################
# Issue 391: Add --pid option to all scripts
# #########################################################################
`touch /tmp/mk-script.pid`;
$output = `$cmd --clear-deadlocks test.make_deadlock --port 12345 --pid /tmp/mk-script.pid 2>&1`;
# #############################################################################
my $pid_file = "/tmp/pt-deadlock-logger-test.pid.$PID";
diag(`touch $pid_file`);
$output = output(
sub {
pt_deadlock_logger::main(@args, '--pid', $pid_file)
},
stderr => 1,
);
like(
$output,
qr{PID file /tmp/mk-script.pid already exists},
qr{PID file $pid_file already exists},
'Dies if PID file already exists (--pid without --daemonize) (issue 391)'
);
`rm -rf /tmp/mk-script.pid`;
unlink $pid_file if -f $pid_file;
# #############################################################################
# Check daemonization
# #############################################################################
my $deadlocks_tbl = load_file('t/pt-deadlock-logger/deadlocks_tbl.sql');
$dbh1->do('USE test');
$dbh1->do('DROP TABLE IF EXISTS deadlocks');
$dbh1->do("$deadlocks_tbl");
$dbh->do('USE test');
$dbh->do('DROP TABLE IF EXISTS deadlocks');
$sb->load_file('master', 't/pt-deadlock-logger/samples/deadlocks_tbl.sql', 'test');
my $pid_file = '/tmp/mk-deadlock-logger.pid';
unlink $pid_file
and diag("Unlinked existing $pid_file");
`$cmd --dest D=test,t=deadlocks --daemonize --run-time 6s --interval 1s --pid $pid_file 1>/dev/null 2>/dev/null`;
$output = `ps -eaf | grep '$cmd \-\-dest '`;
like($output, qr/\Q$cmd/, 'It lives daemonized');
$output = `$trunk/bin/pt-deadlock-logger $dsn --dest D=test,t=deadlocks --daemonize --run-time 10 --interval 1 --pid $pid_file 1>/dev/null 2>/dev/null`;
PerconaTest::wait_for_files($pid_file);
ok(-f $pid_file, 'PID file created');
my ($pid) = $output =~ /\s+(\d+)\s+/;
$output = `ps x | grep 'pt-deadlock-logger $dsn' | grep -v grep`;
like(
$output,
qr/\Qpt-deadlock-logger $dsn/,
'It lives daemonized'
) or diag($output);
my ($pid) = $output =~ /(\d+)/;
ok(
-f $pid_file,
'PID file created'
) or diag($output);
chomp($output = slurp_file($pid_file));
is($output, $pid, 'PID file has correct PID');
is(
$output,
$pid,
'PID file has correct PID'
);
# Kill it
kill 2, $pid;
PerconaTest::wait_until(sub { !kill 0, $pid });
ok(! -f $pid_file, 'PID file removed');
@@ -90,17 +117,25 @@ ok(
'PID file already exists'
);
$output = `$cmd --dest D=test,t=deadlocks --daemonize --run-time 1s --interval 1s --pid $pid_file 2>&1`;
$output = output(
sub {
pt_deadlock_logger::main(@args, '--pid', $pid_file,
qw(--daemonize))
},
stderr => 1,
);
like(
$output,
qr/PID file .+ already exists/,
qr/PID file $pid_file already exists/,
'Does not run if PID file already exists'
);
$output = `ps -eaf | grep 'pt-deadlock-logger \-\-dest '`;
unlike(
$output = `ps x | grep 'pt-deadlock-logger $dsn' | grep -v grep`;
is(
$output,
qr/$cmd/,
"",
'It does not lived daemonized'
);
@@ -109,6 +144,6 @@ unlink $pid_file;
# #############################################################################
# Done.
# #############################################################################
$sb->wipe_clean($dbh1);
$sb->wipe_clean($dbh);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
exit;
done_testing;

View File

@@ -29,6 +29,7 @@ $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 @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;