Merge lp:~percona-toolkit-dev/percona-toolkit/pt-fke-logger-2.2 into lp:percona-toolkit/2.2

Proposed by Daniel Nichter
Status: Merged
Approved by: Daniel Nichter
Approved revision: 539
Merged at revision: 534
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/pt-fke-logger-2.2
Merge into: lp:percona-toolkit/2.2
Diff against target: 3998 lines (+1891/-858)
35 files modified
bin/pt-archiver (+5/-4)
bin/pt-config-diff (+53/-34)
bin/pt-deadlock-logger (+734/-334)
bin/pt-duplicate-key-checker (+5/-8)
bin/pt-fifo-split (+5/-8)
bin/pt-find (+5/-8)
bin/pt-fk-error-logger (+489/-131)
bin/pt-heartbeat (+5/-4)
bin/pt-kill (+61/-21)
bin/pt-online-schema-change (+51/-21)
bin/pt-query-advisor (+5/-5)
bin/pt-query-digest (+5/-5)
bin/pt-show-grants (+5/-8)
bin/pt-slave-delay (+5/-5)
bin/pt-slave-find (+5/-8)
bin/pt-slave-restart (+5/-5)
bin/pt-stalk (+5/-1)
bin/pt-table-checksum (+51/-25)
bin/pt-table-sync (+5/-8)
bin/pt-table-usage (+5/-5)
bin/pt-upgrade (+5/-5)
bin/pt-variable-advisor (+5/-5)
bin/pt-visual-explain (+5/-8)
docs/percona-toolkit.pod (+28/-0)
lib/Cxn.pm (+48/-17)
lib/Runtime.pm (+23/-36)
t/lib/Cxn.t (+58/-2)
t/pt-deadlock-logger/basics.t (+54/-42)
t/pt-deadlock-logger/clear_deadlocks.t (+2/-5)
t/pt-deadlock-logger/create_dest_table.t (+12/-9)
t/pt-deadlock-logger/option_sanity.t (+1/-1)
t/pt-deadlock-logger/standard_options.t (+73/-38)
t/pt-fk-error-logger/basics.t (+66/-8)
t/pt-fk-error-logger/get_fk_error.t (+2/-2)
t/pt-fk-error-logger/standard_options.t (+0/-32)
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/pt-fke-logger-2.2
Reviewer Review Type Date Requested Status
Brian Fraser (community) Approve
Daniel Nichter Approve
Review via email: mp+150704@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Daniel Nichter (daniel-nichter) wrote :
Revision history for this message
Daniel Nichter (daniel-nichter) :
review: Approve
Revision history for this message
Brian Fraser (fraserbn) :
review: Approve
539. By Daniel Nichter

Don't manually re-set AutoCommit. Use =for comment instead of inline MAGIC.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/pt-archiver'
--- bin/pt-archiver 2013-02-22 17:47:57 +0000
+++ bin/pt-archiver 2013-02-27 23:41:26 +0000
@@ -6958,10 +6958,11 @@
69586958
6959type: string6959type: string
69606960
6961Create the given PID file when daemonized. The file contains the process ID of6961Create the given PID file. The tool won't start if the PID file already
6962the daemonized instance. The PID file is removed when the daemonized instance6962exists and the PID it contains is different than the current PID. However,
6963exits. The program checks for the existence of the PID file when starting; if6963if the PID file exists and the PID it contains is no longer running, the
6964it exists and the process with the matching PID exists, the program exits.6964tool will overwrite the PID file with the current PID. The PID file is
6965removed automatically when the tool exits.
69656966
6966=item --plugin6967=item --plugin
69676968
69686969
=== modified file 'bin/pt-config-diff'
--- bin/pt-config-diff 2013-02-22 15:00:55 +0000
+++ bin/pt-config-diff 2013-02-27 23:41:26 +0000
@@ -2138,23 +2138,24 @@
2138 }2138 }
21392139
2140 my $self = {2140 my $self = {
2141 dsn => $dsn,2141 dsn => $dsn,
2142 dbh => $args{dbh},2142 dbh => $args{dbh},
2143 dsn_name => $dp->as_string($dsn, [qw(h P S)]),2143 dsn_name => $dp->as_string($dsn, [qw(h P S)]),
2144 hostname => '',2144 hostname => '',
2145 set => $args{set},2145 set => $args{set},
2146 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,2146 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,
2147 dbh_set => 0,2147 dbh_set => 0,
2148 OptionParser => $o,2148 OptionParser => $o,
2149 DSNParser => $dp,2149 DSNParser => $dp,
2150 is_cluster_node => undef,2150 is_cluster_node => undef,
2151 parent => $args{parent},
2151 };2152 };
21522153
2153 return bless $self, $class;2154 return bless $self, $class;
2154}2155}
21552156
2156sub connect {2157sub connect {
2157 my ( $self ) = @_;2158 my ( $self, %opts ) = @_;
2158 my $dsn = $self->{dsn};2159 my $dsn = $self->{dsn};
2159 my $dp = $self->{DSNParser};2160 my $dp = $self->{DSNParser};
2160 my $o = $self->{OptionParser};2161 my $o = $self->{OptionParser};
@@ -2165,11 +2166,18 @@
2165 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");2166 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
2166 $self->{asked_for_pass} = 1;2167 $self->{asked_for_pass} = 1;
2167 }2168 }
2168 $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });2169 $dbh = $dp->get_dbh(
2170 $dp->get_cxn_params($dsn),
2171 {
2172 AutoCommit => 1,
2173 %opts,
2174 },
2175 );
2169 }2176 }
2170 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name});
21712177
2172 return $self->set_dbh($dbh);2178 $dbh = $self->set_dbh($dbh);
2179 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
2180 return $dbh;
2173}2181}
21742182
2175sub set_dbh {2183sub set_dbh {
@@ -2192,6 +2200,11 @@
2192 $self->{hostname} = $hostname;2200 $self->{hostname} = $hostname;
2193 }2201 }
21942202
2203 if ( $self->{parent} ) {
2204 PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
2205 $dbh->{InactiveDestroy} = 1;
2206 }
2207
2195 if ( my $set = $self->{set}) {2208 if ( my $set = $self->{set}) {
2196 $set->($dbh);2209 $set->($dbh);
2197 }2210 }
@@ -2201,6 +2214,13 @@
2201 return $dbh;2214 return $dbh;
2202}2215}
22032216
2217sub lost_connection {
2218 my ($self, $e) = @_;
2219 return 0 unless $e;
2220 return $e =~ m/MySQL server has gone away/
2221 || $e =~ m/Lost connection to MySQL server/;
2222}
2223
2204sub dbh {2224sub dbh {
2205 my ($self) = @_;2225 my ($self) = @_;
2206 return $self->{dbh};2226 return $self->{dbh};
@@ -2219,12 +2239,21 @@
22192239
2220sub DESTROY {2240sub DESTROY {
2221 my ($self) = @_;2241 my ($self) = @_;
2222 if ( $self->{dbh}2242
2223 && blessed($self->{dbh})2243 PTDEBUG && _d('Destroying cxn');
2224 && $self->{dbh}->can("disconnect") ) {2244
2225 PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name});2245 if ( $self->{parent} ) {
2246 PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
2247 }
2248 elsif ( $self->{dbh}
2249 && blessed($self->{dbh})
2250 && $self->{dbh}->can("disconnect") )
2251 {
2252 PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
2253 $self->{dsn_name});
2226 $self->{dbh}->disconnect();2254 $self->{dbh}->disconnect();
2227 }2255 }
2256
2228 return;2257 return;
2229}2258}
22302259
@@ -5022,12 +5051,7 @@
5022 # Daemonize now that everything is setup and ready to work.5051 # Daemonize now that everything is setup and ready to work.
5023 # ########################################################################5052 # ########################################################################
5024 my $daemon;5053 my $daemon;
5025 if ( $o->get('daemonize') ) {5054 if ( $o->get('pid') ) {
5026 $daemon = new Daemon(o=>$o);
5027 $daemon->daemonize();
5028 PTDEBUG && _d('I am a daemon now');
5029 }
5030 elsif ( $o->get('pid') ) {
5031 # We're not daemoninzing, it just handles PID stuff.5055 # We're not daemoninzing, it just handles PID stuff.
5032 $daemon = new Daemon(o=>$o);5056 $daemon = new Daemon(o=>$o);
5033 $daemon->make_PID_file();5057 $daemon->make_PID_file();
@@ -5118,7 +5142,7 @@
51185142
5119=head1 SYNOPSIS5143=head1 SYNOPSIS
51205144
5121Usage: pt-config-diff [OPTION...] CONFIG CONFIG [CONFIG...]5145Usage: pt-config-diff [OPTIONS] CONFIG CONFIG [CONFIG...]
51225146
5123pt-config-diff diffs MySQL configuration files and server variables.5147pt-config-diff diffs MySQL configuration files and server variables.
5124CONFIG can be a filename or a DSN. At least two CONFIG sources must be given.5148CONFIG can be a filename or a DSN. At least two CONFIG sources must be given.
@@ -5238,11 +5262,6 @@
5238first option on the command line. (This option does not specify a CONFIG;5262first option on the command line. (This option does not specify a CONFIG;
5239it's equivalent to C<--defaults-file>.)5263it's equivalent to C<--defaults-file>.)
52405264
5241=item --daemonize
5242
5243Fork to the background and detach from the shell. POSIX
5244operating systems only.
5245
5246=item --defaults-file5265=item --defaults-file
52475266
5248short form: -F; type: string5267short form: -F; type: string
@@ -5276,11 +5295,11 @@
52765295
5277type: string5296type: string
52785297
5279Create the given PID file when daemonized. The file contains the process5298Create the given PID file. The tool won't start if the PID file already
5280ID of the daemonized instance. The PID file is removed when the5299exists and the PID it contains is different than the current PID. However,
5281daemonized instance exits. The program checks for the existence of the5300if the PID file exists and the PID it contains is no longer running, the
5282PID file when starting; if it exists and the process with the matching PID5301tool will overwrite the PID file with the current PID. The PID file is
5283exists, the program exits.5302removed automatically when the tool exits.
52845303
5285=item --port5304=item --port
52865305
52875306
=== modified file 'bin/pt-deadlock-logger'
--- bin/pt-deadlock-logger 2013-02-22 17:47:57 +0000
+++ bin/pt-deadlock-logger 2013-02-27 23:41:26 +0000
@@ -23,9 +23,11 @@
23 VersionParser23 VersionParser
24 Quoter24 Quoter
25 DSNParser25 DSNParser
26 Cxn
26 Daemon27 Daemon
27 HTTPMicro28 HTTPMicro
28 VersionCheck29 VersionCheck
30 Runtime
29 ));31 ));
30}32}
3133
@@ -2439,6 +2441,181 @@
2439# ###########################################################################2441# ###########################################################################
24402442
2441# ###########################################################################2443# ###########################################################################
2444# Cxn package
2445# This package is a copy without comments from the original. The original
2446# with comments and its test file can be found in the Bazaar repository at,
2447# lib/Cxn.pm
2448# t/lib/Cxn.t
2449# See https://launchpad.net/percona-toolkit for more information.
2450# ###########################################################################
2451{
2452package Cxn;
2453
2454use strict;
2455use warnings FATAL => 'all';
2456use English qw(-no_match_vars);
2457use Scalar::Util qw(blessed);
2458use constant {
2459 PTDEBUG => $ENV{PTDEBUG} || 0,
2460 PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0,
2461};
2462
2463sub new {
2464 my ( $class, %args ) = @_;
2465 my @required_args = qw(DSNParser OptionParser);
2466 foreach my $arg ( @required_args ) {
2467 die "I need a $arg argument" unless $args{$arg};
2468 };
2469 my ($dp, $o) = @args{@required_args};
2470
2471 my $dsn_defaults = $dp->parse_options($o);
2472 my $prev_dsn = $args{prev_dsn};
2473 my $dsn = $args{dsn};
2474 if ( !$dsn ) {
2475 $args{dsn_string} ||= 'h=' . ($dsn_defaults->{h} || 'localhost');
2476
2477 $dsn = $dp->parse(
2478 $args{dsn_string}, $prev_dsn, $dsn_defaults);
2479 }
2480 elsif ( $prev_dsn ) {
2481 $dsn = $dp->copy($prev_dsn, $dsn);
2482 }
2483
2484 my $self = {
2485 dsn => $dsn,
2486 dbh => $args{dbh},
2487 dsn_name => $dp->as_string($dsn, [qw(h P S)]),
2488 hostname => '',
2489 set => $args{set},
2490 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,
2491 dbh_set => 0,
2492 OptionParser => $o,
2493 DSNParser => $dp,
2494 is_cluster_node => undef,
2495 parent => $args{parent},
2496 };
2497
2498 return bless $self, $class;
2499}
2500
2501sub connect {
2502 my ( $self, %opts ) = @_;
2503 my $dsn = $self->{dsn};
2504 my $dp = $self->{DSNParser};
2505 my $o = $self->{OptionParser};
2506
2507 my $dbh = $self->{dbh};
2508 if ( !$dbh || !$dbh->ping() ) {
2509 if ( $o->get('ask-pass') && !$self->{asked_for_pass} ) {
2510 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
2511 $self->{asked_for_pass} = 1;
2512 }
2513 $dbh = $dp->get_dbh(
2514 $dp->get_cxn_params($dsn),
2515 {
2516 AutoCommit => 1,
2517 %opts,
2518 },
2519 );
2520 }
2521
2522 $dbh = $self->set_dbh($dbh);
2523 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
2524 return $dbh;
2525}
2526
2527sub set_dbh {
2528 my ($self, $dbh) = @_;
2529
2530 if ( $self->{dbh} && $self->{dbh} == $dbh && $self->{dbh_set} ) {
2531 PTDEBUG && _d($dbh, 'Already set dbh');
2532 return $dbh;
2533 }
2534
2535 PTDEBUG && _d($dbh, 'Setting dbh');
2536
2537 $dbh->{FetchHashKeyName} = 'NAME_lc' if $self->{NAME_lc};
2538
2539 my $sql = 'SELECT @@hostname, @@server_id';
2540 PTDEBUG && _d($dbh, $sql);
2541 my ($hostname, $server_id) = $dbh->selectrow_array($sql);
2542 PTDEBUG && _d($dbh, 'hostname:', $hostname, $server_id);
2543 if ( $hostname ) {
2544 $self->{hostname} = $hostname;
2545 }
2546
2547 if ( $self->{parent} ) {
2548 PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
2549 $dbh->{InactiveDestroy} = 1;
2550 }
2551
2552 if ( my $set = $self->{set}) {
2553 $set->($dbh);
2554 }
2555
2556 $self->{dbh} = $dbh;
2557 $self->{dbh_set} = 1;
2558 return $dbh;
2559}
2560
2561sub lost_connection {
2562 my ($self, $e) = @_;
2563 return 0 unless $e;
2564 return $e =~ m/MySQL server has gone away/
2565 || $e =~ m/Lost connection to MySQL server/;
2566}
2567
2568sub dbh {
2569 my ($self) = @_;
2570 return $self->{dbh};
2571}
2572
2573sub dsn {
2574 my ($self) = @_;
2575 return $self->{dsn};
2576}
2577
2578sub name {
2579 my ($self) = @_;
2580 return $self->{dsn_name} if PERCONA_TOOLKIT_TEST_USE_DSN_NAMES;
2581 return $self->{hostname} || $self->{dsn_name} || 'unknown host';
2582}
2583
2584sub DESTROY {
2585 my ($self) = @_;
2586
2587 PTDEBUG && _d('Destroying cxn');
2588
2589 if ( $self->{parent} ) {
2590 PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
2591 }
2592 elsif ( $self->{dbh}
2593 && blessed($self->{dbh})
2594 && $self->{dbh}->can("disconnect") )
2595 {
2596 PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
2597 $self->{dsn_name});
2598 $self->{dbh}->disconnect();
2599 }
2600
2601 return;
2602}
2603
2604sub _d {
2605 my ($package, undef, $line) = caller 0;
2606 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
2607 map { defined $_ ? $_ : 'undef' }
2608 @_;
2609 print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
2610}
2611
26121;
2613}
2614# ###########################################################################
2615# End Cxn package
2616# ###########################################################################
2617
2618# ###########################################################################
2442# Daemon package2619# Daemon package
2443# This package is a copy without comments from the original. The original2620# This package is a copy without comments from the original. The original
2444# with comments and its test file can be found in the Bazaar repository at,2621# with comments and its test file can be found in the Bazaar repository at,
@@ -3865,6 +4042,139 @@
3865# ###########################################################################4042# ###########################################################################
38664043
3867# ###########################################################################4044# ###########################################################################
4045# Runtime package
4046# This package is a copy without comments from the original. The original
4047# with comments and its test file can be found in the Bazaar repository at,
4048# lib/Runtime.pm
4049# t/lib/Runtime.t
4050# See https://launchpad.net/percona-toolkit for more information.
4051# ###########################################################################
4052{
4053package Runtime;
4054
4055use strict;
4056use warnings FATAL => 'all';
4057use English qw(-no_match_vars);
4058use constant PTDEBUG => $ENV{PTDEBUG} || 0;
4059
4060sub new {
4061 my ( $class, %args ) = @_;
4062 my @required_args = qw(run_time now);
4063 foreach my $arg ( @required_args ) {
4064 die "I need a $arg argument" unless exists $args{$arg};
4065 }
4066
4067 my $run_time = $args{run_time};
4068 if ( defined $run_time ) {
4069 die "run_time must be > 0" if $run_time <= 0;
4070 }
4071
4072 my $now = $args{now};
4073 die "now must be a callback" unless ref $now eq 'CODE';
4074
4075 my $self = {
4076 run_time => $run_time,
4077 now => $now,
4078 start_time => undef,
4079 end_time => undef,
4080 time_left => undef,
4081 stop => 0,
4082 };
4083
4084 return bless $self, $class;
4085}
4086
4087sub time_left {
4088 my ( $self, %args ) = @_;
4089
4090 if ( $self->{stop} ) {
4091 PTDEBUG && _d("No time left because stop was called");
4092 return 0;
4093 }
4094
4095 my $now = $self->{now}->(%args);
4096 PTDEBUG && _d("Current time:", $now);
4097
4098 if ( !defined $self->{start_time} ) {
4099 $self->{start_time} = $now;
4100 }
4101
4102 return unless defined $now;
4103
4104 my $run_time = $self->{run_time};
4105 return unless defined $run_time;
4106
4107 if ( !$self->{end_time} ) {
4108 $self->{end_time} = $now + $run_time;
4109 PTDEBUG && _d("End time:", $self->{end_time});
4110 }
4111
4112 $self->{time_left} = $self->{end_time} - $now;
4113 PTDEBUG && _d("Time left:", $self->{time_left});
4114 return $self->{time_left};
4115}
4116
4117sub have_time {
4118 my ( $self, %args ) = @_;
4119 my $time_left = $self->time_left(%args);
4120 return 1 if !defined $time_left; # run forever
4121 return $time_left <= 0 ? 0 : 1; # <=0s means run time has elapsed
4122}
4123
4124sub time_elapsed {
4125 my ( $self, %args ) = @_;
4126
4127 my $start_time = $self->{start_time};
4128 return 0 unless $start_time;
4129
4130 my $now = $self->{now}->(%args);
4131 PTDEBUG && _d("Current time:", $now);
4132
4133 my $time_elapsed = $now - $start_time;
4134 PTDEBUG && _d("Time elapsed:", $time_elapsed);
4135 if ( $time_elapsed < 0 ) {
4136 warn "Current time $now is earlier than start time $start_time";
4137 }
4138 return $time_elapsed;
4139}
4140
4141sub reset {
4142 my ( $self ) = @_;
4143 $self->{start_time} = undef;
4144 $self->{end_time} = undef;
4145 $self->{time_left} = undef;
4146 $self->{stop} = 0;
4147 PTDEBUG && _d("Reset run time");
4148 return;
4149}
4150
4151sub stop {
4152 my ( $self ) = @_;
4153 $self->{stop} = 1;
4154 return;
4155}
4156
4157sub start {
4158 my ( $self ) = @_;
4159 $self->{stop} = 0;
4160 return;
4161}
4162
4163sub _d {
4164 my ($package, undef, $line) = caller 0;
4165 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
4166 map { defined $_ ? $_ : 'undef' }
4167 @_;
4168 print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
4169}
4170
41711;
4172}
4173# ###########################################################################
4174# End Runtime package
4175# ###########################################################################
4176
4177# ###########################################################################
3868# This is a combination of modules and programs in one -- a runnable module.4178# This is a combination of modules and programs in one -- a runnable module.
3869# http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last4179# http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last
3870# Or, look it up in the Camel book on pages 642 and 643 in the 3rd edition.4180# Or, look it up in the Camel book on pages 642 and 643 in the 3rd edition.
@@ -3877,21 +4187,15 @@
3877use English qw(-no_match_vars);4187use English qw(-no_match_vars);
3878use List::Util qw(max);4188use List::Util qw(max);
3879use Socket qw(inet_aton);4189use Socket qw(inet_aton);
3880use sigtrap qw(handler finish untrapped normal-signals);4190use Time::HiRes qw(sleep);
4191use File::Temp qw(tempfile);
4192use File::Spec;
4193
4194use sigtrap 'handler', \&sig_int, 'normal-signals';
38814195
3882use Percona::Toolkit;4196use Percona::Toolkit;
3883use constant PTDEBUG => $ENV{PTDEBUG} || 0;4197use constant PTDEBUG => $ENV{PTDEBUG} || 0;
38844198
3885my $o;
3886my $oktorun;
3887my $dp;
3888
3889# ########################################################################
3890# Configuration info.
3891# ########################################################################
3892my $source_dsn;
3893my $dest_dsn;
3894
3895# Some common patterns and variables4199# Some common patterns and variables
3896my $d = qr/(\d+)/; # Digit4200my $d = qr/(\d+)/; # Digit
3897my $t = qr/((?:\d+ \d+)|(?:[A-Fa-f0-9]+))/; # Transaction ID4201my $t = qr/((?:\d+ \d+)|(?:[A-Fa-f0-9]+))/; # Transaction ID
@@ -3934,48 +4238,54 @@
3934 'update' => 1,4238 'update' => 1,
3935);4239);
39364240
4241my $oktorun = 1;
4242my $exit_status = 0;
4243
3937sub main {4244sub main {
3938 local @ARGV = @_; # set global ARGV for this package4245 local @ARGV = @_; # set global ARGV for this package
39394246 $oktorun = 1;
3940 my $q = new Quoter();4247 $exit_status = 0;
39414248
3942 # ########################################################################4249 # ########################################################################
3943 # Get configuration information.4250 # Get configuration information.
3944 # ########################################################################4251 # ########################################################################
3945 $o = new OptionParser();4252 my $o = new OptionParser();
3946 $o->get_specs();4253 $o->get_specs();
3947 $o->get_opts();4254 $o->get_opts();
39484255
3949 $o->set('collapse', $o->get('print')) unless $o->got('collapse'); 4256 my $dp = $o->DSNParser();
39504257 $dp->prop('set-vars', $o->get('set-vars'));
3951 $dp = $o->DSNParser(); 4258
3952 my $dsn_defaults = $dp->parse_options($o);4259 my $src;
3953 $source_dsn = @ARGV ? $dp->parse(shift @ARGV,$dsn_defaults) : $dsn_defaults;4260 if ( my $src_dsn_string = shift @ARGV ) {
3954 $dest_dsn = $o->get('dest');4261 $src = Cxn->new(
39554262 dsn_string => $src_dsn_string,
3956 # The source dsn is not an option so --dest cannot use OptionParser4263 parent => $o->get('daemonize'),
3957 # to inherit values from it. Thus, we do it manually. --dest will4264 DSNParser => $dp,
3958 # inherit from --user, --port, etc.4265 OptionParser => $o,
3959 if ( $source_dsn && $dest_dsn ) {4266 );
3960 # If dest DSN only has D and t, this will copy h, P, S, etc.4267 }
3961 # from the source DSN.4268
3962 $dest_dsn = $dp->copy($source_dsn, $dest_dsn);4269 my $dst;
4270 if ( my $dst_dsn = $o->get('dest') ) {
4271 $dst = Cxn->new(
4272 dsn => $dst_dsn,
4273 prev_dsn => ($src ? $src->dsn : undef),
4274 parent => $o->get('daemonize'),
4275 DSNParser => $dp,
4276 OptionParser => $o,
4277 );
3963 }4278 }
39644279
3965 if ( !$o->get('help') ) {4280 if ( !$o->get('help') ) {
3966 if ( !$source_dsn ) {4281 if ( !$src ) {
3967 $o->save_error('Missing or invalid source host');4282 $o->save_error('No DSN was specified.');
3968 }4283 }
3969 if ( $dest_dsn && !$dest_dsn->{D} ) {4284 if ( $dst && !$dst->dsn->{D} ) {
3970 $o->save_error("--dest requires a 'D' (database) part");4285 $o->save_error("--dest requires a 'D' (database) part.");
3971 }4286 }
3972 if ( $dest_dsn && !$dest_dsn->{t} ) {4287 if ( $dst && !$dst->dsn->{t} ) {
3973 $o->save_error("--dest requires a 't' (table) part");4288 $o->save_error("--dest requires a 't' (table) part.");
3974 }
3975
3976 # Avoid running forever with zero second interval.
3977 if ( $o->get('run-time') && !$o->get('interval') ) {
3978 $o->set('interval', 1);
3979 }4289 }
3980 }4290 }
39814291
@@ -3984,43 +4294,29 @@
3984 # ########################################################################4294 # ########################################################################
3985 # Connect to MySQL and set up the --dest, if any.4295 # Connect to MySQL and set up the --dest, if any.
3986 # ########################################################################4296 # ########################################################################
3987 my $dbh = get_cxn($source_dsn, 1);4297 my $q = new Quoter();
3988 my $dest_dbh;4298
3989 my $sth;4299 $src->connect();
4300
4301 my @cols = @{ $o->get('columns') };
3990 my $ins_sth;4302 my $ins_sth;
39914303 my $ins_sql;
3992 # Since the user might not have specified a hostname for the connection,4304 if ( $dst ) {
3993 # try to extract it from the $dbh4305 $dst->connect(AutoCommit => 0);
3994 if ( !$source_dsn->{h} ) {4306
3995 ($source_dsn->{h}) = $dbh->{mysql_hostinfo} =~ m/(\w+) via/;4307 my $db_tbl = $q->join_quote($dst->dsn->{D}, $dst->dsn->{t});
3996 PTDEBUG && _d('Got source host from dbh:', $source_dsn->{h});
3997 }
3998
3999 my @cols = qw( server ts thread txn_id txn_time user hostname ip db tbl idx
4000 lock_type lock_mode wait_hold victim query );
4001 if ( $o->got('columns') ) {
4002 @cols = grep { $o->get('columns')->{$_} } @cols;
4003 }
4004
4005 if ( $dest_dsn ) {
4006 my $db_tbl =
4007 join('.',
4008 map { $q->quote($_) }
4009 grep { $_ }
4010 ( $dest_dsn->{D}, $dest_dsn->{t} ));
4011 $dest_dbh = get_cxn($dest_dsn, 0);
4012 my $cols = join(',', map { $q->quote($_) } @cols);4308 my $cols = join(',', map { $q->quote($_) } @cols);
4013 my $parms = join(',', map { '?' } @cols);4309 my $parms = join(',', map { '?' } @cols);
4014 my $sql = "INSERT IGNORE INTO $db_tbl($cols) VALUES($parms)";4310 $ins_sql = "INSERT IGNORE INTO $db_tbl ($cols) VALUES ($parms) "
4015 PTDEBUG && _d($sql);4311 . "/* pt-deadlock-logger */";
4016 $ins_sth = $dest_dbh->prepare($sql);4312 PTDEBUG && _d($ins_sql);
4313 $ins_sth = $dst->dbh->prepare($ins_sql);
40174314
4018 if ( $o->get('create-dest-table') ) {4315 if ( $o->get('create-dest-table') ) {
4019 my $db_tbl = $q->quote($dest_dsn->{D}, $dest_dsn->{t});4316 my $sql = $o->read_para_after(__FILE__, qr/MAGIC_dest_table/);
4020 $sql = $o->read_para_after(__FILE__, qr/MAGIC_dest_table/);4317 $sql =~ s/deadlocks/IF NOT EXISTS $db_tbl/;
4021 $sql =~ s/deadlocks/IF NOT EXISTS $db_tbl/;
4022 PTDEBUG && _d($sql);4318 PTDEBUG && _d($sql);
4023 $dest_dbh->do($sql);4319 $dst->dbh->do($sql);
4024 }4320 }
4025 }4321 }
40264322
@@ -4039,6 +4335,16 @@
4039 $daemon->make_PID_file();4335 $daemon->make_PID_file();
4040 }4336 }
40414337
4338 # If we daemonized, the parent has already exited and we're the child.
4339 # We shared a copy of every Cxn with the parent, and the parent's copies
4340 # were destroyed but the dbhs were not disconnected because the parent
4341 # attrib was true. Now, as the child, set it false so the dbhs will be
4342 # disconnected when our Cxn copies are destroyed. If we didn't daemonize,
4343 # then we're not really a parent (since we have no children), so set it
4344 # false to auto-disconnect the dbhs when our Cxns are destroyed.
4345 $src->{parent} = 0;
4346 $dst->{parent} = 0 if $dst;
4347
4042 # ########################################################################4348 # ########################################################################
4043 # Do the version-check4349 # Do the version-check
4044 # ########################################################################4350 # ########################################################################
@@ -4046,117 +4352,155 @@
4046 VersionCheck::version_check(4352 VersionCheck::version_check(
4047 force => $o->got('version-check'),4353 force => $o->got('version-check'),
4048 instances => [4354 instances => [
4049 { dbh => $dbh, dsn => $source_dsn },4355 { dbh => $src->dbh, dsn => $src->dsn },
4050 ($dest_dsn ? { dbh => $dest_dsn, dsn => $dest_dsn } : ()),4356 ($dst ? { dbh => $dst->dbh, dsn => $dst->dsn } : ())
4051 ],4357 ],
4052 );4358 );
4053 }4359 }
40544360
4055 # ########################################################################4361 # ########################################################################
4362 # Set upt the --clear-deadlocks table.
4363 # ########################################################################
4364 my $clear_deadlocks_table_def;
4365 my $clear_deadlocks_table = $o->get('clear-deadlocks');
4366 if ( $clear_deadlocks_table ) {
4367 $clear_deadlocks_table_def
4368 = $o->read_para_after(__FILE__, qr/MAGIC_clear_deadlocks/);
4369 if ( VersionParser->new($src->dbh) < '4.1.2') {
4370 $clear_deadlocks_table_def =~ s/ENGINE=/TYPE=/;
4371 }
4372 $clear_deadlocks_table_def
4373 =~ s/percona_schema.clear_deadlocks/$clear_deadlocks_table/;
4374 PTDEBUG && _d('--clear-deadlocks table:', $clear_deadlocks_table_def);
4375 }
4376
4377 # ########################################################################
4056 # Start looking for and logging deadlocks.4378 # Start looking for and logging deadlocks.
4057 # ########################################################################4379 # ########################################################################
4058 my $last_fingerprint = '';4380 my $sep = $o->get('tab') ? "\t" : ' ';
40594381 my $last_fingerprint = '';
4060 $oktorun = 1;4382 my $parse_deadlocks_options = {
4061 my $start = time();4383 'server' => $src->dsn->{h} || $src->{hostname},
4062 my $end = $start + ($o->get('run-time') || 0); # When we should exit4384 'numeric-ip' => $o->got('numeric-ip'),
4063 my $now = $start;4385 };
4064 while ( # Quit if:4386
4065 ($start == $end || $now < $end) # time is exceeded4387 my $run_time = Runtime->new(
4066 && $oktorun # or instructed to quit4388 run_time => $o->get('run-time'),
4067 )4389 now => sub { return time },
4068 {4390 );
4069 my $text = $dbh->selectrow_hashref("SHOW /*!40100 ENGINE*/ INNODB STATUS")->{Status};4391
4070 my $parse_deadlocks_options = {4392 my $interval = $o->get('interval');
4071 'numeric-ip' => $o->got('numeric-ip'),4393 my $iters = $o->get('iterations');
4072 'collapse' => $o->got('collapse'),4394 PTDEBUG && _d('iterations:', $iters, 'interval:', $interval);
4395
4396 ITERATION:
4397 while (
4398 $oktorun
4399 && $run_time->have_time()
4400 && (!defined $iters || $iters--)
4401 ) {
4402
4403 my %txns;
4404 my $fingerprint;
4405 eval {
4406 my $sql = "SHOW /*!40100 ENGINE*/ INNODB STATUS "
4407 . "/* pt-deadlock-logger */";
4408 my $text = $src->dbh->selectrow_hashref($sql)->{status};
4409
4410 %txns = %{parse_deadlocks($text, $parse_deadlocks_options)};
4411 $fingerprint = fingerprint(\%txns);
4073 };4412 };
4074 my %txns = %{parse_deadlocks($text, $parse_deadlocks_options)};4413 if ( my $e = $EVAL_ERROR ) {
4075 my $fingerprint = fingerprint(\%txns);4414 PTDEBUG && _d('Error getting InnoDB status:', $e);
40764415 if ( $src->lost_connection($e) ) {
4077 if ( $ins_sth ) {4416 eval { $src->connect() };
4078 foreach my $txn (sort { $a->{thread} <=> $b->{thread} } values %txns) {4417 if ( $EVAL_ERROR ) {
4079 $ins_sth->execute(@{$txn}{@cols});4418 warn "Lost connection to " . $src->name . ". Will try "
4080 }4419 . "to reconnect in the next iteration.\n";
4081 $dest_dbh->commit();4420 }
4421 else {
4422 PTDEBUG && _d('Reconnected to MySQL');
4423 redo ITERATION;
4424 }
4425 }
4426 else {
4427 warn "Error getting SHOW ENGINE INNODB STATUS: $EVAL_ERROR";
4428 $exit_status |= 1;
4429 }
4082 }4430 }
4431 else {
4432 if ( $ins_sth ) {
4433 eval {
4434 PTDEBUG && _d('Saving deadlock to --dest');
4435 foreach my $txn (
4436 sort { $a->{thread} <=> $b->{thread} } values %txns
4437 ) {
4438 $ins_sth->execute(@{$txn}{@cols});
4439 }
4440 $dst->dbh->commit();
4441 };
4442 if ( my $e = $EVAL_ERROR ) {
4443 PTDEBUG && _d('Error saving to --dest:', $e);
4444 if ( $dst->lost_connection($e) ) {
4445 eval {
4446 $ins_sth->finish() if $ins_sth;
4447 $dst->dbh->disconnect() if $dst->dbh;
4448 $dst->connect(AutoCommit => 0);
4449 $ins_sth = $dst->dbh->prepare($ins_sql);
4450 };
4451 if ( $EVAL_ERROR ) {
4452 warn "Lost connection to " . $dst->name . ". Will try "
4453 . "to reconnect in the next iteration.\n";
4454 }
4455 else {
4456 PTDEBUG && _d('Reconnected to MySQL (--dest)');
4457 redo ITERATION;
4458 }
4459 }
4460 else {
4461 warn "Error saving to --dest: $EVAL_ERROR";
4462 $exit_status |= 1;
4463 }
4464 }
4465 }
40834466
4084 if ( $fingerprint ne $last_fingerprint ) {4467 if ( $fingerprint ne $last_fingerprint ) {
4085 PTDEBUG && _d('New deadlock');4468 PTDEBUG && _d('New deadlock');
4086 if ( $o->got('print') || !$o->got('dest') ) {4469 if ( !$o->get('quiet') ) {
4087 my $sep = $o->get('tab') ? "\t" : ' ';4470 print join($sep, @cols), "\n";
4088 print join($sep, @cols), "\n";4471 foreach my $txn (
4089 foreach my $txn (sort {$a->{thread}<=>$b->{thread}} values %txns) {4472 sort { $a->{thread} <=> $b->{thread} } values %txns
4090 # If 'collapse' is on, it's already been taken care of,4473 ) {
4091 # but if it's unset, by default strip whitespace.
4092 if ( !$o->got('collapse') ) {
4093 $txn->{query} =~ s/\s+/ /g;4474 $txn->{query} =~ s/\s+/ /g;
4475 print join($sep, map { $txn->{$_} } @cols), "\n";
4094 }4476 }
4095 print join($sep, map { $txn->{$_} } @cols), "\n";
4096 }4477 }
4097 }4478 }
4098 }4479 else {
4099 else {4480 PTDEBUG && _d('Same deadlock, not printing');
4100 PTDEBUG && _d('Same deadlock, not printing');4481 }
4101 }4482
4102 # Save deadlock's fingerprint for next interval.4483 $last_fingerprint = $fingerprint;
4103 $last_fingerprint = $fingerprint;4484
41044485 if ( $clear_deadlocks_table ) {
4105 # If specified, clear the deadlock... 4486 clear_deadlocks(
4106 if ( my $db_tbl = $o->get('clear-deadlocks') ) {4487 dsn => $src->dsn,
4107 PTDEBUG && _d('Creating --clear-deadlocks table', $db_tbl);4488 table => $clear_deadlocks_table,
4108 $dbh->{AutoCommit} = 0;4489 table_def => $clear_deadlocks_table_def,
4109 my $sql = $o->read_para_after(__FILE__, qr/MAGIC_clear_deadlocks/);4490 DSNParser => $dp,
41104491 );
4111 if ( VersionParser->new($dbh) < '4.1.2') {4492 }
4112 $sql =~ s/ENGINE=/TYPE=/;4493 }
4113 }4494
4114 $sql =~ s/test.deadlock_maker/$db_tbl/;4495 # Sleep if there's an --iteration left.
4115 PTDEBUG && _d($sql);4496 if ( !defined $iters || $iters ) {
4116 $dbh->do($sql);4497 PTDEBUG && _d('Sleeping', $interval, 'seconds');
4117 $sql = "INSERT INTO $db_tbl(a) VALUES(1)";4498 sleep $interval;
4118 PTDEBUG && _d($sql);
4119 $dbh->do($sql); # I'm holding locks on the table now.
4120
4121 # Fork off a child to try to take a lock on the table.
4122 my $pid = fork();
4123 if ( defined($pid) && $pid == 0 ) { # I am a child
4124 my $dbh_child = get_cxn($source_dsn, 0);
4125 $sql = "SELECT * FROM $db_tbl FOR UPDATE";
4126 PTDEBUG && _d($sql);
4127 eval { $dbh_child->do($sql); }; # Should block against parent.
4128 PTDEBUG && _d($EVAL_ERROR); # Parent inserted value 0.
4129 $sql = "COMMIT";
4130 PTDEBUG && _d($sql);
4131 $dbh_child->do($sql);
4132 exit;
4133 }
4134 elsif ( !defined($pid) ) {
4135 die("Unable to fork for clearing deadlocks!\n");
4136 }
4137 sleep 1;
4138 $sql = "INSERT INTO $db_tbl(a) VALUES(0)";# Will make child deadlock
4139 PTDEBUG && _d($sql);
4140 eval { $dbh->do($sql); };
4141 PTDEBUG && _d($EVAL_ERROR);
4142 waitpid($pid, 0);
4143 $sql = "DROP TABLE $db_tbl";
4144 PTDEBUG && _d($sql);
4145 $dbh->do($sql);
4146 }
4147
4148 # If there's an --interval argument, run forever or till specified.
4149 # Otherwise just run once.
4150 if ( $o->get('interval') ) {
4151 sleep($o->get('interval'));
4152 $now = time();
4153 }
4154 else {
4155 $oktorun = 0;
4156 }4499 }
4157 }4500 }
41584501
4159 return 0;4502 PTDEBUG && _d('Done running, exiting', $exit_status);
4503 return $exit_status;
4160}4504}
41614505
4162# ############################################################################4506# ############################################################################
@@ -4244,7 +4588,7 @@
42444588
4245 my ( $query_text ) = $body =~ m/\nMySQL thread id .*\n((?s).*)/;4589 my ( $query_text ) = $body =~ m/\nMySQL thread id .*\n((?s).*)/;
4246 $query_text =~ s/\s+$//;4590 $query_text =~ s/\s+$//;
4247 $query_text =~ s/\s+/ /g if $args->{'collapse'};4591 $query_text =~ s/\s+/ /g;
42484592
4249 @{$hash}{qw(thread hostname ip user query)}4593 @{$hash}{qw(thread hostname ip user query)}
4250 = ($mysql_thread_id, $hostname, $ip, $user, $query_text);4594 = ($mysql_thread_id, $hostname, $ip, $user, $query_text);
@@ -4290,13 +4634,100 @@
4290 foreach my $txn ( values %txns ) {4634 foreach my $txn ( values %txns ) {
4291 $txn->{victim} = $txn->{id} == $victim ? 1 : 0;4635 $txn->{victim} = $txn->{id} == $victim ? 1 : 0;
4292 $txn->{ts} = $ts;4636 $txn->{ts} = $ts;
4293 $txn->{server} = $source_dsn->{h} || '';4637 $txn->{server} = $args->{server} || '';
4294 $txn->{ip} = inet_aton($txn->{ip}) if $args->{'numeric-ip'};4638 $txn->{ip} = inet_aton($txn->{ip}) if $args->{'numeric-ip'};
4295 }4639 }
42964640
4297 return \%txns;4641 return \%txns;
4298}4642}
42994643
4644sub clear_deadlocks {
4645 my (%args) = @_;
4646 my @required_args = qw(dsn table table_def DSNParser);
4647 foreach my $arg ( @required_args ) {
4648 die "I need a $arg argument" unless $args{$arg};
4649 }
4650 my $dsn = $args{dsn};
4651 my $table = $args{table};
4652 my $table_def = $args{table_def};
4653 my $dp = $args{DSNParser};
4654 PTDEBUG && _d('Clearing deadlocks with table', $table, $table_def);
4655
4656 my $parent_dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit=>0 });
4657 $parent_dbh->{InactiveDestroy} = 1; # because of forking
4658
4659 # Create the deadlocks table.
4660 PTDEBUG && _d($table_def);
4661 $parent_dbh->do($table_def);
4662
4663 # Get a lock on it.
4664 my $sql = "INSERT INTO $table (a) VALUES (1) "
4665 . "/* pt-deadlock-logger clear deadlocks parent */";
4666 PTDEBUG && _d($sql);
4667 $parent_dbh->do($sql);
4668
4669 my ($sync_fh, $sync_file) = tempfile(
4670 'pt-deadlock-logger-clear-deadlocks.XXXXXXX',
4671 DIR => File::Spec->tmpdir(),
4672 );
4673 PTDEBUG && _d('Sync file:', $sync_file);
4674 close $sync_fh;
4675 unlink $sync_file;
4676
4677 # Fork a child to try to take a lock on the table.
4678 my $pid = fork();
4679 if ( defined($pid) && $pid == 0 ) {
4680 # I am the child
4681 PTDEBUG && _d('Clear deadlocks child', $PID);
4682 my $child_dbh = $dp->get_dbh($dp->get_cxn_params($dsn), {AutoCommit=>0});
4683 my $sql = "SELECT * FROM $table FOR UPDATE "
4684 . "/* pt-deadlock-logger clear deadlocks child */";
4685 PTDEBUG && _d($sql);
4686 open my $fh, '>', $sync_file
4687 or die "Error creating $sync_file: $OS_ERROR";
4688 close $fh;
4689 PTDEBUG && _d('Clear deadlocks child ready (child)');
4690 eval { $child_dbh->do($sql); }; # Should block against parent.
4691 PTDEBUG && _d($EVAL_ERROR); # Parent inserted value 0.
4692 $child_dbh->commit();
4693 $child_dbh->disconnect();
4694 exit;
4695 }
4696 elsif ( !defined($pid) ) {
4697 die "Failed to fork for --clear-deadlocks: " . ($OS_ERROR || '');
4698 }
4699
4700 # Wait up to 10s for the child to connect and become ready.
4701 for ( 1..40 ) {
4702 last if -f $sync_file;
4703 PTDEBUG && _d('Waiting for the clear deadlocks child');
4704 sleep 0.25;
4705 }
4706 PTDEBUG && _d('Clear deadlocks child ready (parent)');
4707 sleep 0.25; # wait for child to exec its SELECT statement
4708
4709 # Make the child deadlock.
4710 $sql = "INSERT INTO $table (a) VALUES (0) "
4711 . "/* pt-deadlock-logger clear deadlocks parent */";
4712 PTDEBUG && _d($sql);
4713 eval { $parent_dbh->do($sql); };
4714 PTDEBUG && _d($EVAL_ERROR);
4715
4716 # Reap the child.
4717 waitpid($pid, 0);
4718
4719 # Drop the table.
4720 $sql = "DROP TABLE IF EXISTS $table";
4721 PTDEBUG && _d($sql);
4722 $parent_dbh->do($sql);
4723
4724 $parent_dbh->disconnect();
4725
4726 unlink $sync_file;
4727
4728 return;
4729}
4730
4300sub fingerprint {4731sub fingerprint {
4301 my ( $txns ) = @_;4732 my ( $txns ) = @_;
4302 my $fingerprint = '';4733 my $fingerprint = '';
@@ -4308,21 +4739,11 @@
4308 return $fingerprint;4739 return $fingerprint;
4309}4740}
43104741
4311# Catches signals so the program can exit gracefully.4742sub sig_int {
4312sub finish {4743 my ( $signal ) = @_;
4313 my ($signal) = @_;
4314 print STDERR "Exiting on SIG$signal.\n";
4315 $oktorun = 0;4744 $oktorun = 0;
4316}4745 print STDERR "# Caught SIG$signal. Use 'kill -ABRT $PID' if "
43174746 . "the tool does not exit normally in a few seconds.\n";
4318sub get_cxn {
4319 my ( $dsn, $ac ) = @_;
4320 if ( $o->get('ask-pass') ) {
4321 $dsn->{p} = OptionParser::prompt_noecho("Enter password: ");
4322 }
4323 my $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), {AutoCommit => $ac});
4324 $dbh->{InactiveDestroy} = 1; # Because of forking.
4325 return $dbh;
4326}4747}
43274748
4328sub _d {4749sub _d {
@@ -4347,32 +4768,28 @@
43474768
4348=head1 NAME4769=head1 NAME
43494770
4350pt-deadlock-logger - Extract and log MySQL deadlock information.4771pt-deadlock-logger - Log MySQL deadlocks.
43514772
4352=head1 SYNOPSIS4773=head1 SYNOPSIS
43534774
4354Usage: pt-deadlock-logger [OPTION...] SOURCE_DSN4775Usage: pt-deadlock-logger [OPTIONS] DSN
43554776
4356pt-deadlock-logger extracts and saves information about the most recent deadlock4777pt-deadlock-logger logs information about MySQL deadlocks on the given
4357in a MySQL server.4778DSN. Information is printed to C<STDOUT>, and it can also be saved to a
43584779table by specifying L<"--dest">. The tool runs for forever unless
4359Print deadlocks on SOURCE_DSN:4780L<"--run-time"> or L<"--iterations"> is specified.
43604781
4361 pt-deadlock-logger SOURCE_DSN4782Print deadlocks on host1:
43624783
4363Store deadlock information from SOURCE_DSN in test.deadlocks table on SOURCE_DSN4784 pt-fk-error-logger h=host1
4364(source and destination are the same host):4785
43654786Print deadlocks on host1 once then exit:
4366 pt-deadlock-logger SOURCE_DSN --dest D=test,t=deadlocks4787
43674788 pt-fk-error-logger h=host1 --iterations 1
4368Store deadlock information from SOURCE_DSN in test.deadlocks table on DEST_DSN4789
4369(source and destination are different hosts):4790Save deadlocks on host1 to percona_schema.fke on host2:
43704791
4371 pt-deadlock-logger SOURCE_DSN --dest DEST_DSN,D=test,t=deadlocks4792 pt-fk-error-logger h=host1 --dest h=host2,D=percona_schema,t=deadlocks
4372
4373Daemonize and check for deadlocks on SOURCE_DSN every 30 seconds for 4 hours:
4374
4375 pt-deadlock-logger SOURCE_DSN --dest D=test,t=deadlocks --daemonize --run-time 4h --interval 30s
43764793
4377=head1 RISKS4794=head1 RISKS
43784795
@@ -4398,43 +4815,93 @@
43984815
4399=head1 DESCRIPTION4816=head1 DESCRIPTION
44004817
4401pt-deadlock-logger extracts deadlock data from a MySQL server. Currently only4818pt-deadlock-logger prints information about MySQL deadlocks by polling
4402InnoDB deadlock information is available. You can print the information to4819and parsing C<SHOW ENGINE INNODB STATUS>. When a new deadlock occurs,
4403standard output, store it in a database table, or both. If neither4820it's printed to C<STDOUT> and, if specified, saved to L<"--dest">.
4404L<"--print"> nor L<"--dest"> are given, then the deadlock information is4821
4405printed by default. If only L<"--dest"> is given, then the deadlock4822Only new deadlocks are printed. A fingerprint for each deadlock is created
4406information is only stored. If both options are given, then the deadlock4823using the deadlock's server, ts, and thread values (even if these
4407information is printed and stored.4824columns are not specified by L<"--columns">). A deadlock is printed if
44084825its fingerprint is different than the last deadlock's fingerprint.
4409The source host can be specified using one of two methods. The first method is4826
4410to use at least one of the standard connection-related command line options:4827The L<"--dest"> statement uses C<INSERT IGNORE> to eliminate duplicate
4411L<"--defaults-file">, L<"--password">, L<"--host">, L<"--port">, L<"--socket">4828deadlocks, so every deadlock is saved for every L<"--iterations">.
4412or L<"--user">. These options only apply to the source host; they cannot be
4413used to specify the destination host.
4414
4415The second method to specify the source host, or the optional destination host
4416using L<"--dest">, is a DSN. A DSN is a special syntax that can be either just
4417a hostname (like C<server.domain.com> or C<1.2.3.4>), or a
4418C<key=value,key=value> string. Keys are a single letter:
4419
4420 KEY MEANING
4421 === =======
4422 h Connect to host
4423 P Port number to use for connection
4424 S Socket file to use for connection
4425 u User for login if not current user
4426 p Password to use when connecting
4427 F Only read default options from the given file
4428
4429If you omit any values from the destination host DSN, they are filled in with
4430values from the source host, so you don't need to specify them in both places.
4431C<pt-deadlock-logger> reads all normal MySQL option files, such as ~/.my.cnf, so
4432you may not need to specify username, password and other common options at all.
44334829
4434=head1 OUTPUT4830=head1 OUTPUT
44354831
4436You can choose which columns are output and/or saved to L<"--dest"> with the4832New deadlocks are printed to C<STDOUT>, unless L<"--quiet"> is specified.
4437L<"--columns"> argument. The default columns are as follows:4833Errors and warnings are printed to C<STDERR>.
4834
4835See also L<"--columns"> and L<"--tab">.
4836
4837=head1 INNODB CAVEATS AND DETAILS
4838
4839InnoDB's output is hard to parse and sometimes there's no way to do it right.
4840
4841Sometimes not all information (for example, username or IP address) is included
4842in the deadlock information. In this case there's nothing for the script to put
4843in those columns. It may also be the case that the deadlock output is so long
4844(because there were a lot of locks) that the whole thing is truncated.
4845
4846Though there are usually two transactions involved in a deadlock, there are more
4847locks than that; at a minimum, one more lock than transactions is necessary to
4848create a cycle in the waits-for graph. pt-deadlock-logger prints the
4849transactions (always two in the InnoDB output, even when there are more
4850transactions in the waits-for graph than that) and fills in locks. It prefers
4851waited-for over held when choosing lock information to output, but you can
4852figure out the rest with a moment's thought. If you see one wait-for and one
4853held lock, you're looking at the same lock, so of course you'd prefer to see
4854both wait-for locks and get more information. If the two waited-for locks are
4855not on the same table, more than two transactions were involved in the deadlock.
4856
4857Finally, keep in mind that, because usernames with spaces are not quoted by
4858InnoDB, the tool will generally misreport the second word of these usernames
4859as the hostname.
4860
4861=head1 OPTIONS
4862
4863This tool accepts additional command-line arguments. Refer to the
4864L<"SYNOPSIS"> and usage information for details.
4865
4866=over
4867
4868=item --ask-pass
4869
4870Prompt for a password when connecting to MySQL.
4871
4872=item --charset
4873
4874short form: -A; type: string
4875
4876Default character set. If the value is utf8, sets Perl's binmode on
4877STDOUT to utf8, passes the mysql_enable_utf8 option to DBD::mysql, and runs SET
4878NAMES UTF8 after connecting to MySQL. Any other value sets binmode on STDOUT
4879without the utf8 layer, and runs SET NAMES after connecting to MySQL.
4880
4881=item --clear-deadlocks
4882
4883type: string
4884
4885Use this table to create a small deadlock. This usually has the effect of
4886clearing out a huge deadlock, which otherwise consumes the entire output of
4887C<SHOW INNODB STATUS>. The table must not exist. pt-deadlock-logger will
4888create it with the following structure:
4889
4890=for comment ignore-pt-internal-value
4891MAGIC_clear_deadlocks
4892
4893 CREATE TABLE percona_schema.clear_deadlocks (
4894 a INT PRIMARY KEY
4895 ) ENGINE=InnoDB
4896
4897After creating the table and causing a small deadlock, the tool will drop the
4898table again.
4899
4900=item --columns
4901
4902type: Array; default: server, ts, thread, txn_id, txn_time, user, hostname, ip, db, tbl, idx, lock_type, lock_mode, wait_hold, victim, query
4903
4904The columns are:
44384905
4439=over4906=over
44404907
@@ -4509,78 +4976,6 @@
45094976
4510=back4977=back
45114978
4512=head1 INNODB CAVEATS AND DETAILS
4513
4514InnoDB's output is hard to parse and sometimes there's no way to do it right.
4515
4516Sometimes not all information (for example, username or IP address) is included
4517in the deadlock information. In this case there's nothing for the script to put
4518in those columns. It may also be the case that the deadlock output is so long
4519(because there were a lot of locks) that the whole thing is truncated.
4520
4521Though there are usually two transactions involved in a deadlock, there are more
4522locks than that; at a minimum, one more lock than transactions is necessary to
4523create a cycle in the waits-for graph. pt-deadlock-logger prints the
4524transactions (always two in the InnoDB output, even when there are more
4525transactions in the waits-for graph than that) and fills in locks. It prefers
4526waited-for over held when choosing lock information to output, but you can
4527figure out the rest with a moment's thought. If you see one wait-for and one
4528held lock, you're looking at the same lock, so of course you'd prefer to see
4529both wait-for locks and get more information. If the two waited-for locks are
4530not on the same table, more than two transactions were involved in the deadlock.
4531
4532Finally, keep in mind that, because usernames with spaces are not quoted by
4533InnoDB, the tool will generally misreport the second word of these usernames
4534as the hostname.
4535
4536=head1 OPTIONS
4537
4538This tool accepts additional command-line arguments. Refer to the
4539L<"SYNOPSIS"> and usage information for details.
4540
4541=over
4542
4543=item --ask-pass
4544
4545Prompt for a password when connecting to MySQL.
4546
4547=item --charset
4548
4549short form: -A; type: string
4550
4551Default character set. If the value is utf8, sets Perl's binmode on
4552STDOUT to utf8, passes the mysql_enable_utf8 option to DBD::mysql, and runs SET
4553NAMES UTF8 after connecting to MySQL. Any other value sets binmode on STDOUT
4554without the utf8 layer, and runs SET NAMES after connecting to MySQL.
4555
4556=item --clear-deadlocks
4557
4558type: string
4559
4560Use this table to create a small deadlock. This usually has the effect of
4561clearing out a huge deadlock, which otherwise consumes the entire output of
4562C<SHOW INNODB STATUS>. The table must not exist. pt-deadlock-logger will
4563create it with the following MAGIC_clear_deadlocks structure:
4564
4565 CREATE TABLE test.deadlock_maker(a INT PRIMARY KEY) ENGINE=InnoDB;
4566
4567After creating the table and causing a small deadlock, the tool will drop the
4568table again.
4569
4570=item --[no]collapse
4571
4572Collapse whitespace in queries to a single space. This might make it easier to
4573inspect on the command line or in a query. By default, whitespace is collapsed
4574when printing with L<"--print">, but not modified when storing to L<"--dest">.
4575(That is, the default is different for each action).
4576
4577=item --columns
4578
4579type: hash
4580
4581Output only this comma-separated list of columns. See L<"OUTPUT"> for more
4582details on columns.
4583
4584=item --config4979=item --config
45854980
4586type: Array4981type: Array
@@ -4617,12 +5012,12 @@
4617can usually omit most parts of this argument if you're storing deadlocks on the5012can usually omit most parts of this argument if you're storing deadlocks on the
4618same server on which they happen.5013same server on which they happen.
46195014
4620By default, whitespace in the query column is left intact;5015The following table structure is suggested if you want to store all the
4621use L<"--[no]collapse"> if you want whitespace collapsed.
4622
4623The following MAGIC_dest_table is suggested if you want to store all the
4624information pt-deadlock-logger can extract about deadlocks:5016information pt-deadlock-logger can extract about deadlocks:
46255017
5018=for comment ignore-pt-internal-value
5019MAGIC_dest_table
5020
4626 CREATE TABLE deadlocks (5021 CREATE TABLE deadlocks (
4627 server char(20) NOT NULL,5022 server char(20) NOT NULL,
4628 ts datetime NOT NULL,5023 ts datetime NOT NULL,
@@ -4658,12 +5053,23 @@
46585053
4659=item --interval5054=item --interval
46605055
4661type: time5056type: time; default: 30
46625057
4663How often to check for deadlocks. If no L<"--run-time"> is specified,5058How often to check for deadlocks. If no L<"--run-time"> is specified,
4664pt-deadlock-logger runs forever, checking for deadlocks at every interval.5059pt-deadlock-logger runs forever, checking for deadlocks at every interval.
4665See also L<"--run-time">.5060See also L<"--run-time">.
46665061
5062=item --iterations
5063
5064type: int
5065
5066How many times to check for deadlocks. By default, this option
5067is undefined which means an infinite number of iterations. The tool always
5068exits for L<"--run-time">, regardless of the value specified for this option.
5069For example, the tool will exit after 1 minute with
5070C<--run-time 1m --iterations 4 --interval 30> because 4 iterations at 30
5071second intervals would take 2 minutes, longer than the 1 mintue run-time.
5072
4667=item --log5073=item --log
46685074
4669type: string5075type: string
@@ -4684,10 +5090,11 @@
46845090
4685type: string5091type: string
46865092
4687Create the given PID file when daemonized. The file contains the process ID of5093Create the given PID file. The tool won't start if the PID file already
4688the daemonized instance. The PID file is removed when the daemonized instance5094exists and the PID it contains is different than the current PID. However,
4689exits. The program checks for the existence of the PID file when starting; if5095if the PID file exists and the PID it contains is no longer running, the
4690it exists and the process with the matching PID exists, the program exits.5096tool will overwrite the PID file with the current PID. The PID file is
5097removed automatically when the tool exits.
46915098
4692=item --port5099=item --port
46935100
@@ -4695,16 +5102,9 @@
46955102
4696Port number to use for connection.5103Port number to use for connection.
46975104
4698=item --print5105=item --quiet
46995106
4700Print results on standard output. See L<"OUTPUT"> for more. By default,5107Do not deadlocks; only print errors and warnings to C<STDERR>.
4701enables L<"--[no]collapse"> unless you explicitly disable it.
4702
4703If L<"--interval"> or L<"--run-time"> is specified, only new deadlocks are
4704printed at each interval. A fingerprint for each deadlock is created using
4705L<"--columns"> server, ts and thread (even if those columns were not specified
4706by L<"--columns">) and if the current deadlock's fingerprint is different from
4707the last deadlock's fingerprint, then it is printed.
47085108
4709=item --run-time5109=item --run-time
47105110
@@ -4729,7 +5129,7 @@
47295129
4730=item --tab5130=item --tab
47315131
4732Print tab-separated columns, instead of aligned.5132Use tabs to separate columns instead of spaces.
47335133
4734=item --user5134=item --user
47355135
@@ -4886,7 +5286,7 @@
48865286
4887=head1 AUTHORS5287=head1 AUTHORS
48885288
4889Baron Schwartz5289Baron Schwartz and Daniel Nichter
48905290
4891=head1 ABOUT PERCONA TOOLKIT5291=head1 ABOUT PERCONA TOOLKIT
48925292
48935293
=== modified file 'bin/pt-duplicate-key-checker'
--- bin/pt-duplicate-key-checker 2013-02-22 17:47:57 +0000
+++ bin/pt-duplicate-key-checker 2013-02-27 23:41:26 +0000
@@ -5130,14 +5130,11 @@
51305130
5131type: string5131type: string
51325132
5133Create the given PID file. The file contains the process ID of the script.5133Create the given PID file. The tool won't start if the PID file already
5134The PID file is removed when the script exits. Before starting, the script5134exists and the PID it contains is different than the current PID. However,
5135checks if the PID file already exists. If it does not, then the script creates5135if the PID file exists and the PID it contains is no longer running, the
5136and writes its own PID to it. If it does, then the script checks the following:5136tool will overwrite the PID file with the current PID. The PID file is
5137if the file contains a PID and a process is running with that PID, then5137removed automatically when the tool exits.
5138the script dies; or, if there is no process running with that PID, then the
5139script overwrites the file with its own PID and starts; else, if the file
5140contains no PID, then the script dies.
51415138
5142=item --port5139=item --port
51435140
51445141
=== modified file 'bin/pt-fifo-split'
--- bin/pt-fifo-split 2013-01-03 00:54:18 +0000
+++ bin/pt-fifo-split 2013-02-27 23:41:26 +0000
@@ -1456,14 +1456,11 @@
14561456
1457type: string1457type: string
14581458
1459Create the given PID file. The file contains the process ID of the script.1459Create the given PID file. The tool won't start if the PID file already
1460The PID file is removed when the script exits. Before starting, the script1460exists and the PID it contains is different than the current PID. However,
1461checks if the PID file already exists. If it does not, then the script creates1461if the PID file exists and the PID it contains is no longer running, the
1462and writes its own PID to it. If it does, then the script checks the following:1462tool will overwrite the PID file with the current PID. The PID file is
1463if the file contains a PID and a process is running with that PID, then1463removed automatically when the tool exits.
1464the script dies; or, if there is no process running with that PID, then the
1465script overwrites the file with its own PID and starts; else, if the file
1466contains no PID, then the script dies.
14671464
1468=item --statistics1465=item --statistics
14691466
14701467
=== modified file 'bin/pt-find'
--- bin/pt-find 2013-02-22 17:47:57 +0000
+++ bin/pt-find 2013-02-27 23:41:26 +0000
@@ -4241,14 +4241,11 @@
42414241
4242type: string4242type: string
42434243
4244Create the given PID file. The file contains the process ID of the script.4244Create the given PID file. The tool won't start if the PID file already
4245The PID file is removed when the script exits. Before starting, the script4245exists and the PID it contains is different than the current PID. However,
4246checks if the PID file already exists. If it does not, then the script creates4246if the PID file exists and the PID it contains is no longer running, the
4247and writes its own PID to it. If it does, then the script checks the following:4247tool will overwrite the PID file with the current PID. The PID file is
4248if the file contains a PID and a process is running with that PID, then4248removed automatically when the tool exits.
4249the script dies; or, if there is no process running with that PID, then the
4250script overwrites the file with its own PID and starts; else, if the file
4251contains no PID, then the script dies.
42524249
4253=item --port4250=item --port
42544251
42554252
=== modified file 'bin/pt-fk-error-logger'
--- bin/pt-fk-error-logger 2013-02-22 17:47:57 +0000
+++ bin/pt-fk-error-logger 2013-02-27 23:41:26 +0000
@@ -17,10 +17,12 @@
17 OptionParser17 OptionParser
18 Quoter18 Quoter
19 DSNParser19 DSNParser
20 Cxn
20 Daemon21 Daemon
21 Transformers22 Transformers
22 HTTPMicro23 HTTPMicro
23 VersionCheck24 VersionCheck
25 Runtime
24 ));26 ));
25}27}
2628
@@ -1596,6 +1598,181 @@
1596# ###########################################################################1598# ###########################################################################
15971599
1598# ###########################################################################1600# ###########################################################################
1601# Cxn package
1602# This package is a copy without comments from the original. The original
1603# with comments and its test file can be found in the Bazaar repository at,
1604# lib/Cxn.pm
1605# t/lib/Cxn.t
1606# See https://launchpad.net/percona-toolkit for more information.
1607# ###########################################################################
1608{
1609package Cxn;
1610
1611use strict;
1612use warnings FATAL => 'all';
1613use English qw(-no_match_vars);
1614use Scalar::Util qw(blessed);
1615use constant {
1616 PTDEBUG => $ENV{PTDEBUG} || 0,
1617 PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0,
1618};
1619
1620sub new {
1621 my ( $class, %args ) = @_;
1622 my @required_args = qw(DSNParser OptionParser);
1623 foreach my $arg ( @required_args ) {
1624 die "I need a $arg argument" unless $args{$arg};
1625 };
1626 my ($dp, $o) = @args{@required_args};
1627
1628 my $dsn_defaults = $dp->parse_options($o);
1629 my $prev_dsn = $args{prev_dsn};
1630 my $dsn = $args{dsn};
1631 if ( !$dsn ) {
1632 $args{dsn_string} ||= 'h=' . ($dsn_defaults->{h} || 'localhost');
1633
1634 $dsn = $dp->parse(
1635 $args{dsn_string}, $prev_dsn, $dsn_defaults);
1636 }
1637 elsif ( $prev_dsn ) {
1638 $dsn = $dp->copy($prev_dsn, $dsn);
1639 }
1640
1641 my $self = {
1642 dsn => $dsn,
1643 dbh => $args{dbh},
1644 dsn_name => $dp->as_string($dsn, [qw(h P S)]),
1645 hostname => '',
1646 set => $args{set},
1647 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,
1648 dbh_set => 0,
1649 OptionParser => $o,
1650 DSNParser => $dp,
1651 is_cluster_node => undef,
1652 parent => $args{parent},
1653 };
1654
1655 return bless $self, $class;
1656}
1657
1658sub connect {
1659 my ( $self, %opts ) = @_;
1660 my $dsn = $self->{dsn};
1661 my $dp = $self->{DSNParser};
1662 my $o = $self->{OptionParser};
1663
1664 my $dbh = $self->{dbh};
1665 if ( !$dbh || !$dbh->ping() ) {
1666 if ( $o->get('ask-pass') && !$self->{asked_for_pass} ) {
1667 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
1668 $self->{asked_for_pass} = 1;
1669 }
1670 $dbh = $dp->get_dbh(
1671 $dp->get_cxn_params($dsn),
1672 {
1673 AutoCommit => 1,
1674 %opts,
1675 },
1676 );
1677 }
1678
1679 $dbh = $self->set_dbh($dbh);
1680 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
1681 return $dbh;
1682}
1683
1684sub set_dbh {
1685 my ($self, $dbh) = @_;
1686
1687 if ( $self->{dbh} && $self->{dbh} == $dbh && $self->{dbh_set} ) {
1688 PTDEBUG && _d($dbh, 'Already set dbh');
1689 return $dbh;
1690 }
1691
1692 PTDEBUG && _d($dbh, 'Setting dbh');
1693
1694 $dbh->{FetchHashKeyName} = 'NAME_lc' if $self->{NAME_lc};
1695
1696 my $sql = 'SELECT @@hostname, @@server_id';
1697 PTDEBUG && _d($dbh, $sql);
1698 my ($hostname, $server_id) = $dbh->selectrow_array($sql);
1699 PTDEBUG && _d($dbh, 'hostname:', $hostname, $server_id);
1700 if ( $hostname ) {
1701 $self->{hostname} = $hostname;
1702 }
1703
1704 if ( $self->{parent} ) {
1705 PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
1706 $dbh->{InactiveDestroy} = 1;
1707 }
1708
1709 if ( my $set = $self->{set}) {
1710 $set->($dbh);
1711 }
1712
1713 $self->{dbh} = $dbh;
1714 $self->{dbh_set} = 1;
1715 return $dbh;
1716}
1717
1718sub lost_connection {
1719 my ($self, $e) = @_;
1720 return 0 unless $e;
1721 return $e =~ m/MySQL server has gone away/
1722 || $e =~ m/Lost connection to MySQL server/;
1723}
1724
1725sub dbh {
1726 my ($self) = @_;
1727 return $self->{dbh};
1728}
1729
1730sub dsn {
1731 my ($self) = @_;
1732 return $self->{dsn};
1733}
1734
1735sub name {
1736 my ($self) = @_;
1737 return $self->{dsn_name} if PERCONA_TOOLKIT_TEST_USE_DSN_NAMES;
1738 return $self->{hostname} || $self->{dsn_name} || 'unknown host';
1739}
1740
1741sub DESTROY {
1742 my ($self) = @_;
1743
1744 PTDEBUG && _d('Destroying cxn');
1745
1746 if ( $self->{parent} ) {
1747 PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
1748 }
1749 elsif ( $self->{dbh}
1750 && blessed($self->{dbh})
1751 && $self->{dbh}->can("disconnect") )
1752 {
1753 PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
1754 $self->{dsn_name});
1755 $self->{dbh}->disconnect();
1756 }
1757
1758 return;
1759}
1760
1761sub _d {
1762 my ($package, undef, $line) = caller 0;
1763 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
1764 map { defined $_ ? $_ : 'undef' }
1765 @_;
1766 print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
1767}
1768
17691;
1770}
1771# ###########################################################################
1772# End Cxn package
1773# ###########################################################################
1774
1775# ###########################################################################
1599# Daemon package1776# Daemon package
1600# This package is a copy without comments from the original. The original1777# This package is a copy without comments from the original. The original
1601# with comments and its test file can be found in the Bazaar repository at,1778# with comments and its test file can be found in the Bazaar repository at,
@@ -3375,6 +3552,139 @@
3375# ###########################################################################3552# ###########################################################################
33763553
3377# ###########################################################################3554# ###########################################################################
3555# Runtime package
3556# This package is a copy without comments from the original. The original
3557# with comments and its test file can be found in the Bazaar repository at,
3558# lib/Runtime.pm
3559# t/lib/Runtime.t
3560# See https://launchpad.net/percona-toolkit for more information.
3561# ###########################################################################
3562{
3563package Runtime;
3564
3565use strict;
3566use warnings FATAL => 'all';
3567use English qw(-no_match_vars);
3568use constant PTDEBUG => $ENV{PTDEBUG} || 0;
3569
3570sub new {
3571 my ( $class, %args ) = @_;
3572 my @required_args = qw(run_time now);
3573 foreach my $arg ( @required_args ) {
3574 die "I need a $arg argument" unless exists $args{$arg};
3575 }
3576
3577 my $run_time = $args{run_time};
3578 if ( defined $run_time ) {
3579 die "run_time must be > 0" if $run_time <= 0;
3580 }
3581
3582 my $now = $args{now};
3583 die "now must be a callback" unless ref $now eq 'CODE';
3584
3585 my $self = {
3586 run_time => $run_time,
3587 now => $now,
3588 start_time => undef,
3589 end_time => undef,
3590 time_left => undef,
3591 stop => 0,
3592 };
3593
3594 return bless $self, $class;
3595}
3596
3597sub time_left {
3598 my ( $self, %args ) = @_;
3599
3600 if ( $self->{stop} ) {
3601 PTDEBUG && _d("No time left because stop was called");
3602 return 0;
3603 }
3604
3605 my $now = $self->{now}->(%args);
3606 PTDEBUG && _d("Current time:", $now);
3607
3608 if ( !defined $self->{start_time} ) {
3609 $self->{start_time} = $now;
3610 }
3611
3612 return unless defined $now;
3613
3614 my $run_time = $self->{run_time};
3615 return unless defined $run_time;
3616
3617 if ( !$self->{end_time} ) {
3618 $self->{end_time} = $now + $run_time;
3619 PTDEBUG && _d("End time:", $self->{end_time});
3620 }
3621
3622 $self->{time_left} = $self->{end_time} - $now;
3623 PTDEBUG && _d("Time left:", $self->{time_left});
3624 return $self->{time_left};
3625}
3626
3627sub have_time {
3628 my ( $self, %args ) = @_;
3629 my $time_left = $self->time_left(%args);
3630 return 1 if !defined $time_left; # run forever
3631 return $time_left <= 0 ? 0 : 1; # <=0s means run time has elapsed
3632}
3633
3634sub time_elapsed {
3635 my ( $self, %args ) = @_;
3636
3637 my $start_time = $self->{start_time};
3638 return 0 unless $start_time;
3639
3640 my $now = $self->{now}->(%args);
3641 PTDEBUG && _d("Current time:", $now);
3642
3643 my $time_elapsed = $now - $start_time;
3644 PTDEBUG && _d("Time elapsed:", $time_elapsed);
3645 if ( $time_elapsed < 0 ) {
3646 warn "Current time $now is earlier than start time $start_time";
3647 }
3648 return $time_elapsed;
3649}
3650
3651sub reset {
3652 my ( $self ) = @_;
3653 $self->{start_time} = undef;
3654 $self->{end_time} = undef;
3655 $self->{time_left} = undef;
3656 $self->{stop} = 0;
3657 PTDEBUG && _d("Reset run time");
3658 return;
3659}
3660
3661sub stop {
3662 my ( $self ) = @_;
3663 $self->{stop} = 1;
3664 return;
3665}
3666
3667sub start {
3668 my ( $self ) = @_;
3669 $self->{stop} = 0;
3670 return;
3671}
3672
3673sub _d {
3674 my ($package, undef, $line) = caller 0;
3675 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
3676 map { defined $_ ? $_ : 'undef' }
3677 @_;
3678 print STDERR "# $package:$line $PID ", join(' ', @_), "\n";
3679}
3680
36811;
3682}
3683# ###########################################################################
3684# End Runtime package
3685# ###########################################################################
3686
3687# ###########################################################################
3378# This is a combination of modules and programs in one -- a runnable module.3688# This is a combination of modules and programs in one -- a runnable module.
3379# http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last3689# http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last
3380# Or, look it up in the Camel book on pages 642 and 643 in the 3rd edition.3690# Or, look it up in the Camel book on pages 642 and 643 in the 3rd edition.
@@ -3387,18 +3697,21 @@
3387use strict;3697use strict;
3388use warnings FATAL => 'all';3698use warnings FATAL => 'all';
3389use English qw(-no_match_vars);3699use English qw(-no_match_vars);
3390use sigtrap qw(handler finish untrapped normal-signals);3700
3701use sigtrap 'handler', \&sig_int, 'normal-signals';
33913702
3392use Percona::Toolkit;3703use Percona::Toolkit;
3393use constant PTDEBUG => $ENV{PTDEBUG} || 0;3704use constant PTDEBUG => $ENV{PTDEBUG} || 0;
33943705
3395Transformers->import(qw(parse_timestamp));3706Transformers->import(qw(parse_timestamp));
33963707
3397my $oktorun;3708my $oktorun = 1;
3709my $exit_status = 0;
33983710
3399sub main {3711sub main {
3400 local @ARGV = @_; # set global ARGV for this package3712 local @ARGV = @_; # set global ARGV for this package
3401 $oktorun = 1;3713 $oktorun = 1;
3714 $exit_status = 0;
34023715
3403 # ########################################################################3716 # ########################################################################
3404 # Get configuration information.3717 # Get configuration information.
@@ -3409,69 +3722,56 @@
34093722
3410 my $dp = $o->DSNParser();3723 my $dp = $o->DSNParser();
3411 $dp->prop('set-vars', $o->get('set-vars'));3724 $dp->prop('set-vars', $o->get('set-vars'));
3412 my $dsn_defaults = $dp->parse_options($o);3725
3413 my $src_dsn = @ARGV ? $dp->parse(shift @ARGV, $dsn_defaults) : $dsn_defaults;3726 my $src;
3414 my $dst_dsn = $o->get('dest');3727 if ( my $src_dsn_string = shift @ARGV ) {
34153728 $src = Cxn->new(
3416 # The source dsn is not an option so --dest cannot use OptionParser3729 dsn_string => $src_dsn_string,
3417 # to inherit values from it. Thus, we do it manually. --dest will3730 parent => $o->get('daemonize'),
3418 # inherit from --user, --port, etc.3731 DSNParser => $dp,
3419 if ( $src_dsn && $dst_dsn ) {3732 OptionParser => $o,
3420 # If dest DSN only has D and t, this will copy h, P, S, etc.3733 );
3421 # from the source DSN.3734 }
3422 $dst_dsn = $dp->copy($src_dsn, $dst_dsn);3735
3736 my $dst;
3737 if ( my $dst_dsn = $o->get('dest') ) {
3738 $dst = Cxn->new(
3739 dsn => $dst_dsn,
3740 prev_dsn => ($src ? $src->dsn : undef),
3741 parent => $o->get('daemonize'),
3742 DSNParser => $dp,
3743 OptionParser => $o,
3744 );
3423 }3745 }
34243746
3425 if ( !$o->get('help') ) {3747 if ( !$o->get('help') ) {
3426 if ( !$src_dsn ) {3748 if ( !$src ) {
3427 $o->save_error('Missing or invalid source host');3749 $o->save_error('No DSN was specified.');
3428 }3750 }
3429 if ( $dst_dsn && !$dst_dsn->{D} ) {3751 if ( $dst && !$dst->dsn->{D} ) {
3430 $o->save_error("--dest requires a 'D' (database) part");3752 $o->save_error("--dest requires a 'D' (database) part.");
3431 }3753 }
3432 if ( $dst_dsn && !$dst_dsn->{t} ) {3754 if ( $dst && !$dst->dsn->{t} ) {
3433 $o->save_error("--dest requires a 't' (table) part");3755 $o->save_error("--dest requires a 't' (table) part.");
3434 }3756 }
3435 }3757 }
34363758
3437 $o->usage_or_errors();3759 $o->usage_or_errors();
34383760
3439 # ########################################################################3761 # ########################################################################
3440 # Make common modules.3762 # Connect to MySQL.
3441 # ########################################################################3763 # ########################################################################
3442 my $q = new Quoter();3764 my $q = Quoter->new();
3443 my %modules = (3765
3444 o => $o,3766 $src->connect();
3445 dp => $dp,3767
3446 q => $q,
3447 );
3448
3449 # ########################################################################
3450 # Start working.
3451 # ########################################################################
3452 my $dbh = get_cxn($src_dsn, 1, %modules);
3453 my $start = time();
3454 my $end = $start + ($o->get('run-time') || 0); # When we should exit
3455 my $now = $start;
3456 my $dst_dbh;
3457 my $ins_sth;3768 my $ins_sth;
34583769 if ( $dst ) {
3459 # Since the user might not have specified a hostname for the connection,3770 $dst->connect();
3460 # try to extract it from the $dbh3771 my $db_tbl = $q->join_quote($dst->dsn->{D}, $dst->dsn->{t});
3461 if ( !$src_dsn->{h} ) {3772 my $sql = "INSERT IGNORE INTO $db_tbl VALUES (?, ?)";
3462 ($src_dsn->{h}) = $dbh->{mysql_hostinfo} =~ m/(\w+) via/;3773 PTDEBUG && _d('--dest INSERT SQL:', $sql);
3463 PTDEBUG && _d('Got source host from dbh:', $src_dsn->{h});3774 $ins_sth = $dst->dbh->prepare($sql);
3464 }
3465
3466 if ( $dst_dsn ) {
3467 my $db_tbl = join('.',
3468 map { $q->quote($_) }
3469 grep { $_ }
3470 ( $dst_dsn->{D}, $dst_dsn->{t} ));
3471 $dst_dbh = get_cxn($dst_dsn, 1, %modules);
3472 my $sql = "INSERT IGNORE INTO $db_tbl VALUES (?, ?)";
3473 PTDEBUG && _d('insert sql:', $sql);
3474 $ins_sth = $dst_dbh->prepare($sql);
3475 }3775 }
34763776
3477 # ########################################################################3777 # ########################################################################
@@ -3489,6 +3789,16 @@
3489 $daemon->make_PID_file();3789 $daemon->make_PID_file();
3490 }3790 }
34913791
3792 # If we daemonized, the parent has already exited and we're the child.
3793 # We shared a copy of every Cxn with the parent, and the parent's copies
3794 # were destroyed but the dbhs were not disconnected because the parent
3795 # attrib was true. Now, as the child, set it false so the dbhs will be
3796 # disconnected when our Cxn copies are destroyed. If we didn't daemonize,
3797 # then we're not really a parent (since we have no children), so set it
3798 # false to auto-disconnect the dbhs when our Cxns are destroyed.
3799 $src->{parent} = 0;
3800 $dst->{parent} = 0 if $dst;
3801
3492 # ########################################################################3802 # ########################################################################
3493 # Do the version-check3803 # Do the version-check
3494 # ########################################################################3804 # ########################################################################
@@ -3496,8 +3806,8 @@
3496 VersionCheck::version_check(3806 VersionCheck::version_check(
3497 force => $o->got('version-check'),3807 force => $o->got('version-check'),
3498 instances => [3808 instances => [
3499 { dbh => $dbh, dsn => $src_dsn },3809 { dbh => $src->dbh, dsn => $src->dsn },
3500 ($dst_dbh ? { dbh => $dst_dbh, dsn => $dst_dsn } : ())3810 ($dst ? { dbh => $dst->dbh, dsn => $dst->dsn } : ())
3501 ],3811 ],
3502 );3812 );
3503 }3813 }
@@ -3505,43 +3815,77 @@
3505 # ########################################################################3815 # ########################################################################
3506 # Start finding and logging foreign key errors.3816 # Start finding and logging foreign key errors.
3507 # ########################################################################3817 # ########################################################################
3508 while ( # Quit if:3818 my $run_time = Runtime->new(
3509 ($start == $end || $now < $end) # time is exceeded3819 run_time => $o->get('run-time'),
3510 && $oktorun # or instructed to quit3820 now => sub { return time },
3511 )3821 );
3512 {3822
3513 my $text = $dbh->selectrow_hashref("SHOW /*!40100 ENGINE*/ INNODB STATUS")->{Status};3823 my $interval = $o->get('interval');
3514 my ($ts, $fk_error) = get_fk_error($text);3824 my $iters = $o->get('iterations');
3515 PTDEBUG && _d('ts:', $ts, 'fk error:', $fk_error);3825 PTDEBUG && _d('iterations:', $iters, 'interval:', $interval);
35163826
3517 if ( $ts && $fk_error ) {3827 ITERATION:
3518 # Save and/or print the foreign key error.3828 while (
3519 if ( $ins_sth ) {3829 $oktorun
3520 my $fk_ts = parse_timestamp($ts);3830 && $run_time->have_time()
3521 PTDEBUG && _d('Saving fk error', $ts, $fk_error);3831 && (!defined $iters || $iters--)
3522 eval {3832 ) {
3523 $ins_sth->execute($fk_ts, $fk_error);3833 my ($ts, $fk_error);
3524 };3834 eval {
3835 my $sql = "SHOW /*!40100 ENGINE*/ INNODB STATUS "
3836 . "/* pt-fk-error-logger */";
3837 PTDEBUG && _d($sql);
3838 my $text = $src->dbh->selectrow_hashref($sql)->{status};
3839 ($ts, $fk_error) = get_fk_error($text);
3840 };
3841 if ( my $e = $EVAL_ERROR ) {
3842 PTDEBUG && _d('Error getting InnoDB status:', $e);
3843 if ( $src->lost_connection($e) ) {
3844 eval { $src->connect() };
3525 if ( $EVAL_ERROR ) {3845 if ( $EVAL_ERROR ) {
3526 warn $EVAL_ERROR;3846 warn "Lost connection to MySQL. Will try to reconnect "
3527 PTDEBUG && _d($EVAL_ERROR);3847 . "in the next iteration.\n";
3528 }3848 }
3529 }3849 else {
3530 print "$ts $fk_error\n\n" if $o->get('print') || !$o->got('dest');3850 PTDEBUG && _d('Reconnected to MySQL');
3531 }3851 redo ITERATION;
35323852 }
3533 # If there's an --interval argument, run forever or till specified.3853 }
3534 # Otherwise just run once.3854 else {
3535 if ( $o->get('interval') ) {3855 warn "Error parsing SHOW ENGINE INNODB STATUS: $EVAL_ERROR";
3536 sleep($o->get('interval'));3856 $exit_status |= 1;
3537 $now = time();3857 }
3538 }3858 }
3539 else {3859 else {
3540 $oktorun = 0;3860 if ( $ts && $fk_error ) {
3861 # Save and/or print the foreign key error.
3862 if ( $ins_sth ) {
3863 my $fk_ts = parse_timestamp($ts);
3864 PTDEBUG && _d('Saving fk error', $ts, $fk_error);
3865 eval {
3866 $ins_sth->execute($fk_ts, $fk_error);
3867 };
3868 if ( $EVAL_ERROR ) {
3869 warn $EVAL_ERROR;
3870 PTDEBUG && _d($EVAL_ERROR);
3871 }
3872 }
3873
3874 if ( !$o->get('quiet') ) {
3875 print "$ts $fk_error\n\n";
3876 }
3877 }
3878 }
3879
3880 # Sleep if there's an --iteration left.
3881 if ( !defined $iters || $iters ) {
3882 PTDEBUG && _d('Sleeping', $interval, 'seconds');
3883 sleep $interval;
3541 }3884 }
3542 }3885 }
35433886
3544 return 0;3887 PTDEBUG && _d('Done running, exiting', $exit_status);
3888 return $exit_status;
3545}3889}
35463890
3547# ############################################################################3891# ############################################################################
@@ -3550,9 +3894,13 @@
35503894
3551sub get_fk_error {3895sub get_fk_error {
3552 my ( $text ) = @_;3896 my ( $text ) = @_;
3897 PTDEBUG && _d($text);
35533898
3554 # Quick check if text even has a foreign key error.3899 # Quick check if text even has a foreign key error.
3555 return unless $text =~ m/LATEST FOREIGN KEY ERROR/;3900 if ( $text !~ m/LATEST FOREIGN KEY ERROR/ ) {
3901 PTDEBUG && _d('No fk error');
3902 return;
3903 }
35563904
3557 # InnoDB timestamp3905 # InnoDB timestamp
3558 my $idb_ts = qr/((?:\d{6}|\d{4}-\d\d-\d\d) .\d:\d\d:\d\d)/;3906 my $idb_ts = qr/((?:\d{6}|\d{4}-\d\d-\d\d) .\d:\d\d:\d\d)/;
@@ -3564,24 +3912,11 @@
3564 return $ts, $fke;3912 return $ts, $fke;
3565}3913}
35663914
3567# Catches signals so the program can exit gracefully.3915sub sig_int {
3568sub finish {3916 my ( $signal ) = @_;
3569 my ($signal) = @_;
3570 print STDERR "Exiting on SIG$signal.\n";
3571 $oktorun = 0;3917 $oktorun = 0;
3572}3918 print STDERR "# Caught SIG$signal. Use 'kill -ABRT $PID' if "
35733919 . "the tool does not exit normally in a few seconds.\n";
3574sub get_cxn {
3575 my ( $dsn, $ac, %args ) = @_;
3576 my $o = $args{o};
3577 my $dp = $args{dp};
3578
3579 if ( $o->get('ask-pass') ) {
3580 $dsn->{p} = OptionParser::prompt_noecho("Enter password: ");
3581 }
3582 my $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), {AutoCommit => $ac});
3583 $dbh->{InactiveDestroy} = 1; # Because of forking.
3584 return $dbh;
3585}3920}
35863921
3587sub _d {3922sub _d {
@@ -3606,22 +3941,28 @@
36063941
3607=head1 NAME3942=head1 NAME
36083943
3609pt-fk-error-logger - Extract and log MySQL foreign key errors.3944pt-fk-error-logger - Log MySQL foreign key errors.
36103945
3611=head1 SYNOPSIS3946=head1 SYNOPSIS
36123947
3613Usage: pt-fk-error-logger [OPTION...] SOURCE_DSN3948Usage: pt-fk-error-logger [OPTIONS] DSN
36143949
3615pt-fk-error-logger extracts and saves information about the most recent foreign3950pt-fk-error-logger logs information about foreign key errors on the given
3616key errors in a MySQL server.3951DSN. Information is printed to C<STDOUT>, and it can also be saved to a
3952table by specifying L<"--dest">. The tool runs for forever unless
3953L<"--run-time"> or L<"--iterations"> is specified.
36173954
3618Print foreign key errors on host1:3955Print foreign key errors on host1:
36193956
3620 pt-fk-error-logger h=host13957 pt-fk-error-logger h=host1
36213958
3622Save foreign key errors on host1 to db.foreign_key_errors table on host2:3959Print foreign key errors on host1 once then exit:
36233960
3624 pt-fk-error-logger h=host1 --dest h=host1,D=db,t=foreign_key_errors3961 pt-fk-error-logger h=host1 --iterations 1
3962
3963Save foreign key errors on host1 to percona_schema.fke on host2:
3964
3965 pt-fk-error-logger h=host1 --dest h=host2,D=percona_schema,t=fke
36253966
3626=head1 RISKS3967=head1 RISKS
36273968
@@ -3650,11 +3991,15 @@
3650way. Foreign key errors are uniquely identified by their timestamp.3991way. Foreign key errors are uniquely identified by their timestamp.
3651Only new (more recent) errors are printed or saved.3992Only new (more recent) errors are printed or saved.
36523993
3994By default the tool runs forever, checking every L<"--interval"> seconds
3995for new foreign key errors. Specify L<"--run-time"> and/or L<"--iterations">
3996to limit how long the tool runs.
3997
3653=head1 OUTPUT3998=head1 OUTPUT
36543999
3655If L<"--print"> is given or no L<"--dest"> is given, then pt-fk-error-logger4000The foreign key error text from C<SHOW ENGINE INNODB STATUS> is printed
3656prints the foreign key error text to STDOUT exactly as it appeared in4001to C<STDOUT>, unless L<"--quiet"> is specified. Errors and warnings
3657C<SHOW INNODB STATUS>.4002are printed to C<STDERR>.
36584003
3659=head1 OPTIONS4004=head1 OPTIONS
36604005
@@ -3698,11 +4043,12 @@
36984043
3699type: DSN4044type: DSN
37004045
3701DSN for where to store foreign key errors; specify at least a database (D) and table (t).4046Save foreign key errors in this table. The DSN must specify a database (D)
4047and table (t).
37024048
3703Missing values are filled in with the same values from the source host, so you4049Missing DSN values are inherited from the DSN being monitored, so you
3704can usually omit most parts of this argument if you're storing foreign key4050can omit most values if you're saving foreign key errors on the same
3705errors on the same server on which they happen.4051host.
37064052
3707The following table is suggested:4053The following table is suggested:
37084054
@@ -3726,10 +4072,21 @@
37264072
3727=item --interval4073=item --interval
37284074
3729type: time; default: 04075type: time; default: 30
37304076
3731How often to check for foreign key errors.4077How often to check for foreign key errors.
37324078
4079=item --iterations
4080
4081type: int
4082
4083How many times to check for foreign key errors. By default, this option
4084is undefined which means an infinite number of iterations. The tool always
4085exits for L<"--run-time">, regardless of the value specified for this option.
4086For example, the tool will exit after 1 minute with
4087C<--run-time 1m --iterations 4 --interval 30> because 4 iterations at 30
4088second intervals would take 2 minutes, longer than the 1 mintue run-time.
4089
3733=item --log4090=item --log
37344091
3735type: string4092type: string
@@ -3746,10 +4103,11 @@
37464103
3747type: string4104type: string
37484105
3749Create the given PID file when daemonized. The file contains the process ID of4106Create the given PID file. The tool won't start if the PID file already
3750the daemonized instance. The PID file is removed when the daemonized instance4107exists and the PID it contains is different than the current PID. However,
3751exits. The program checks for the existence of the PID file when starting; if4108if the PID file exists and the PID it contains is no longer running, the
3752it exists and the process with the matching PID exists, the program exits.4109tool will overwrite the PID file with the current PID. The PID file is
4110removed automatically when the tool exits.
37534111
3754=item --port4112=item --port
37554113
@@ -3757,15 +4115,15 @@
37574115
3758Port number to use for connection.4116Port number to use for connection.
37594117
3760=item --print4118=item --quiet
37614119
3762Print results on standard output. See L<"OUTPUT"> for more.4120Do not print foreign key errors; only print errors and warnings to C<STDERR>.
37634121
3764=item --run-time4122=item --run-time
37654123
3766type: time4124type: time
37674125
3768How long to run before exiting.4126How long to run before exiting. By default, the tool runs forever.
37694127
3770=item --set-vars4128=item --set-vars
37714129
@@ -3948,7 +4306,7 @@
39484306
3949=head1 COPYRIGHT, LICENSE, AND WARRANTY4307=head1 COPYRIGHT, LICENSE, AND WARRANTY
39504308
3951This program is copyright 2011-2012 Percona Ireland Ltd.4309This program is copyright 2011-2013 Percona Ireland Ltd.
39524310
3953THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED4311THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
3954WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF4312WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
39554313
=== modified file 'bin/pt-heartbeat'
--- bin/pt-heartbeat 2013-02-22 17:47:57 +0000
+++ bin/pt-heartbeat 2013-02-27 23:41:26 +0000
@@ -5664,10 +5664,11 @@
56645664
5665type: string5665type: string
56665666
5667Create the given PID file when daemonized. The file contains the process ID of5667Create the given PID file. The tool won't start if the PID file already
5668the daemonized instance. The PID file is removed when the daemonized instance5668exists and the PID it contains is different than the current PID. However,
5669exits. The program checks for the existence of the PID file when starting; if5669if the PID file exists and the PID it contains is no longer running, the
5670it exists and the process with the matching PID exists, the program exits.5670tool will overwrite the PID file with the current PID. The PID file is
5671removed automatically when the tool exits.
56715672
5672=item --port5673=item --port
56735674
56745675
=== modified file 'bin/pt-kill'
--- bin/pt-kill 2013-02-22 17:47:57 +0000
+++ bin/pt-kill 2013-02-27 23:41:26 +0000
@@ -4969,23 +4969,24 @@
4969 }4969 }
49704970
4971 my $self = {4971 my $self = {
4972 dsn => $dsn,4972 dsn => $dsn,
4973 dbh => $args{dbh},4973 dbh => $args{dbh},
4974 dsn_name => $dp->as_string($dsn, [qw(h P S)]),4974 dsn_name => $dp->as_string($dsn, [qw(h P S)]),
4975 hostname => '',4975 hostname => '',
4976 set => $args{set},4976 set => $args{set},
4977 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,4977 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,
4978 dbh_set => 0,4978 dbh_set => 0,
4979 OptionParser => $o,4979 OptionParser => $o,
4980 DSNParser => $dp,4980 DSNParser => $dp,
4981 is_cluster_node => undef,4981 is_cluster_node => undef,
4982 parent => $args{parent},
4982 };4983 };
49834984
4984 return bless $self, $class;4985 return bless $self, $class;
4985}4986}
49864987
4987sub connect {4988sub connect {
4988 my ( $self ) = @_;4989 my ( $self, %opts ) = @_;
4989 my $dsn = $self->{dsn};4990 my $dsn = $self->{dsn};
4990 my $dp = $self->{DSNParser};4991 my $dp = $self->{DSNParser};
4991 my $o = $self->{OptionParser};4992 my $o = $self->{OptionParser};
@@ -4996,11 +4997,18 @@
4996 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");4997 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
4997 $self->{asked_for_pass} = 1;4998 $self->{asked_for_pass} = 1;
4998 }4999 }
4999 $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });5000 $dbh = $dp->get_dbh(
5001 $dp->get_cxn_params($dsn),
5002 {
5003 AutoCommit => 1,
5004 %opts,
5005 },
5006 );
5000 }5007 }
5001 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name});
50025008
5003 return $self->set_dbh($dbh);5009 $dbh = $self->set_dbh($dbh);
5010 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
5011 return $dbh;
5004}5012}
50055013
5006sub set_dbh {5014sub set_dbh {
@@ -5023,6 +5031,11 @@
5023 $self->{hostname} = $hostname;5031 $self->{hostname} = $hostname;
5024 }5032 }
50255033
5034 if ( $self->{parent} ) {
5035 PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
5036 $dbh->{InactiveDestroy} = 1;
5037 }
5038
5026 if ( my $set = $self->{set}) {5039 if ( my $set = $self->{set}) {
5027 $set->($dbh);5040 $set->($dbh);
5028 }5041 }
@@ -5032,6 +5045,13 @@
5032 return $dbh;5045 return $dbh;
5033}5046}
50345047
5048sub lost_connection {
5049 my ($self, $e) = @_;
5050 return 0 unless $e;
5051 return $e =~ m/MySQL server has gone away/
5052 || $e =~ m/Lost connection to MySQL server/;
5053}
5054
5035sub dbh {5055sub dbh {
5036 my ($self) = @_;5056 my ($self) = @_;
5037 return $self->{dbh};5057 return $self->{dbh};
@@ -5050,12 +5070,21 @@
50505070
5051sub DESTROY {5071sub DESTROY {
5052 my ($self) = @_;5072 my ($self) = @_;
5053 if ( $self->{dbh}5073
5054 && blessed($self->{dbh})5074 PTDEBUG && _d('Destroying cxn');
5055 && $self->{dbh}->can("disconnect") ) {5075
5056 PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name});5076 if ( $self->{parent} ) {
5077 PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
5078 }
5079 elsif ( $self->{dbh}
5080 && blessed($self->{dbh})
5081 && $self->{dbh}->can("disconnect") )
5082 {
5083 PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
5084 $self->{dsn_name});
5057 $self->{dbh}->disconnect();5085 $self->{dbh}->disconnect();
5058 }5086 }
5087
5059 return;5088 return;
5060}5089}
50615090
@@ -6464,6 +6493,7 @@
6464 $cxn = Cxn->new(6493 $cxn = Cxn->new(
6465 dsn_string => shift @ARGV,6494 dsn_string => shift @ARGV,
6466 NAME_lc => 0,6495 NAME_lc => 0,
6496 parent => $o->get('daemonize'),
6467 DSNParser => $dp,6497 DSNParser => $dp,
6468 OptionParser => $o,6498 OptionParser => $o,
6469 );6499 );
@@ -6643,6 +6673,15 @@
6643 $daemon->make_PID_file();6673 $daemon->make_PID_file();
6644 }6674 }
66456675
6676 # If we daemonized, the parent has already exited and we're the child.
6677 # We shared a copy of every Cxn with the parent, and the parent's copies
6678 # were destroyed but the dbhs were not disconnected because the parent
6679 # attrib was true. Now, as the child, set it false so the dbhs will be
6680 # disconnected when our Cxn copies are destroyed. If we didn't daemonize,
6681 # then we're not really a parent (since we have no children), so set it
6682 # false to auto-disconnect the dbhs when our Cxns are destroyed.
6683 $cxn->{parent} = 0 if $cxn;
6684
6646 # ########################################################################6685 # ########################################################################
6647 # Do the version-check6686 # Do the version-check
6648 # ########################################################################6687 # ########################################################################
@@ -7311,10 +7350,11 @@
73117350
7312type: string7351type: string
73137352
7314Create the given PID file when daemonized. The file contains the process ID of7353Create the given PID file. The tool won't start if the PID file already
7315the daemonized instance. The PID file is removed when the daemonized instance7354exists and the PID it contains is different than the current PID. However,
7316exits. The program checks for the existence of the PID file when starting; if7355if the PID file exists and the PID it contains is no longer running, the
7317it exists and the process with the matching PID exists, the program exits.7356tool will overwrite the PID file with the current PID. The PID file is
7357removed automatically when the tool exits.
73187358
7319=item --port7359=item --port
73207360
73217361
=== modified file 'bin/pt-online-schema-change'
--- bin/pt-online-schema-change 2013-02-22 17:47:57 +0000
+++ bin/pt-online-schema-change 2013-02-27 23:41:26 +0000
@@ -4018,23 +4018,24 @@
4018 }4018 }
40194019
4020 my $self = {4020 my $self = {
4021 dsn => $dsn,4021 dsn => $dsn,
4022 dbh => $args{dbh},4022 dbh => $args{dbh},
4023 dsn_name => $dp->as_string($dsn, [qw(h P S)]),4023 dsn_name => $dp->as_string($dsn, [qw(h P S)]),
4024 hostname => '',4024 hostname => '',
4025 set => $args{set},4025 set => $args{set},
4026 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,4026 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,
4027 dbh_set => 0,4027 dbh_set => 0,
4028 OptionParser => $o,4028 OptionParser => $o,
4029 DSNParser => $dp,4029 DSNParser => $dp,
4030 is_cluster_node => undef,4030 is_cluster_node => undef,
4031 parent => $args{parent},
4031 };4032 };
40324033
4033 return bless $self, $class;4034 return bless $self, $class;
4034}4035}
40354036
4036sub connect {4037sub connect {
4037 my ( $self ) = @_;4038 my ( $self, %opts ) = @_;
4038 my $dsn = $self->{dsn};4039 my $dsn = $self->{dsn};
4039 my $dp = $self->{DSNParser};4040 my $dp = $self->{DSNParser};
4040 my $o = $self->{OptionParser};4041 my $o = $self->{OptionParser};
@@ -4045,11 +4046,18 @@
4045 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");4046 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
4046 $self->{asked_for_pass} = 1;4047 $self->{asked_for_pass} = 1;
4047 }4048 }
4048 $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });4049 $dbh = $dp->get_dbh(
4050 $dp->get_cxn_params($dsn),
4051 {
4052 AutoCommit => 1,
4053 %opts,
4054 },
4055 );
4049 }4056 }
4050 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name});
40514057
4052 return $self->set_dbh($dbh);4058 $dbh = $self->set_dbh($dbh);
4059 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
4060 return $dbh;
4053}4061}
40544062
4055sub set_dbh {4063sub set_dbh {
@@ -4072,6 +4080,11 @@
4072 $self->{hostname} = $hostname;4080 $self->{hostname} = $hostname;
4073 }4081 }
40744082
4083 if ( $self->{parent} ) {
4084 PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
4085 $dbh->{InactiveDestroy} = 1;
4086 }
4087
4075 if ( my $set = $self->{set}) {4088 if ( my $set = $self->{set}) {
4076 $set->($dbh);4089 $set->($dbh);
4077 }4090 }
@@ -4081,6 +4094,13 @@
4081 return $dbh;4094 return $dbh;
4082}4095}
40834096
4097sub lost_connection {
4098 my ($self, $e) = @_;
4099 return 0 unless $e;
4100 return $e =~ m/MySQL server has gone away/
4101 || $e =~ m/Lost connection to MySQL server/;
4102}
4103
4084sub dbh {4104sub dbh {
4085 my ($self) = @_;4105 my ($self) = @_;
4086 return $self->{dbh};4106 return $self->{dbh};
@@ -4099,12 +4119,21 @@
40994119
4100sub DESTROY {4120sub DESTROY {
4101 my ($self) = @_;4121 my ($self) = @_;
4102 if ( $self->{dbh}4122
4103 && blessed($self->{dbh})4123 PTDEBUG && _d('Destroying cxn');
4104 && $self->{dbh}->can("disconnect") ) {4124
4105 PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name});4125 if ( $self->{parent} ) {
4126 PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
4127 }
4128 elsif ( $self->{dbh}
4129 && blessed($self->{dbh})
4130 && $self->{dbh}->can("disconnect") )
4131 {
4132 PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
4133 $self->{dsn_name});
4106 $self->{dbh}->disconnect();4134 $self->{dbh}->disconnect();
4107 }4135 }
4136
4108 return;4137 return;
4109}4138}
41104139
@@ -10839,10 +10868,11 @@
1083910868
10840type: string10869type: string
1084110870
10842Create the given PID file. The file contains the process ID of the tool's10871Create the given PID file. The tool won't start if the PID file already
10843instance. The PID file is removed when the tool exits. The tool checks for10872exists and the PID it contains is different than the current PID. However,
10844the existence of the PID file when starting; if it exists and the process with10873if the PID file exists and the PID it contains is no longer running, the
10845the matching PID exists, the tool exits.10874tool will overwrite the PID file with the current PID. The PID file is
10875removed automatically when the tool exits.
1084610876
10847=item --port10877=item --port
1084810878
1084910879
=== modified file 'bin/pt-query-advisor'
--- bin/pt-query-advisor 2013-02-22 17:47:57 +0000
+++ bin/pt-query-advisor 2013-02-27 23:41:26 +0000
@@ -9092,11 +9092,11 @@
90929092
9093type: string9093type: string
90949094
9095Create the given PID file when daemonized. The file contains the process9095Create the given PID file. The tool won't start if the PID file already
9096ID of the daemonized instance. The PID file is removed when the9096exists and the PID it contains is different than the current PID. However,
9097daemonized instance exits. The program checks for the existence of the9097if the PID file exists and the PID it contains is no longer running, the
9098PID file when starting; if it exists and the process with the matching PID9098tool will overwrite the PID file with the current PID. The PID file is
9099exists, the program exits.9099removed automatically when the tool exits.
91009100
9101=item --port9101=item --port
91029102
91039103
=== modified file 'bin/pt-query-digest'
--- bin/pt-query-digest 2013-02-25 15:13:35 +0000
+++ bin/pt-query-digest 2013-02-27 23:41:26 +0000
@@ -14967,11 +14967,11 @@
1496714967
14968type: string14968type: string
1496914969
14970Create the given PID file when daemonized. The file contains the process14970Create the given PID file. The tool won't start if the PID file already
14971ID of the daemonized instance. The PID file is removed when the14971exists and the PID it contains is different than the current PID. However,
14972daemonized instance exits. The program checks for the existence of the14972if the PID file exists and the PID it contains is no longer running, the
14973PID file when starting; if it exists and the process with the matching PID14973tool will overwrite the PID file with the current PID. The PID file is
14974exists, the program exits.14974removed automatically when the tool exits.
1497514975
14976=item --port14976=item --port
1497714977
1497814978
=== modified file 'bin/pt-show-grants'
--- bin/pt-show-grants 2013-01-03 00:54:18 +0000
+++ bin/pt-show-grants 2013-02-27 23:41:26 +0000
@@ -2093,14 +2093,11 @@
20932093
2094type: string2094type: string
20952095
2096Create the given PID file. The file contains the process ID of the script.2096Create the given PID file. The tool won't start if the PID file already
2097The PID file is removed when the script exits. Before starting, the script2097exists and the PID it contains is different than the current PID. However,
2098checks if the PID file already exists. If it does not, then the script creates2098if the PID file exists and the PID it contains is no longer running, the
2099and writes its own PID to it. If it does, then the script checks the following:2099tool will overwrite the PID file with the current PID. The PID file is
2100if the file contains a PID and a process is running with that PID, then2100removed automatically when the tool exits.
2101the script dies; or, if there is no process running with that PID, then the
2102script overwrites the file with its own PID and starts; else, if the file
2103contains no PID, then the script dies.
21042101
2105=item --port2102=item --port
21062103
21072104
=== modified file 'bin/pt-slave-delay'
--- bin/pt-slave-delay 2013-02-22 15:00:55 +0000
+++ bin/pt-slave-delay 2013-02-27 23:41:26 +0000
@@ -4478,11 +4478,11 @@
44784478
4479type: string4479type: string
44804480
4481Create the given PID file when daemonized. The file contains the process4481Create the given PID file. The tool won't start if the PID file already
4482ID of the daemonized instance. The PID file is removed when the4482exists and the PID it contains is different than the current PID. However,
4483daemonized instance exits. The program checks for the existence of the4483if the PID file exists and the PID it contains is no longer running, the
4484PID file when starting; if it exists and the process with the matching PID4484tool will overwrite the PID file with the current PID. The PID file is
4485exists, the program exits.4485removed automatically when the tool exits.
44864486
4487=item --port4487=item --port
44884488
44894489
=== modified file 'bin/pt-slave-find'
--- bin/pt-slave-find 2013-02-01 17:06:13 +0000
+++ bin/pt-slave-find 2013-02-27 23:41:26 +0000
@@ -3960,14 +3960,11 @@
39603960
3961type: string3961type: string
39623962
3963Create the given PID file. The file contains the process ID of the script.3963Create the given PID file. The tool won't start if the PID file already
3964The PID file is removed when the script exits. Before starting, the script3964exists and the PID it contains is different than the current PID. However,
3965checks if the PID file already exists. If it does not, then the script creates3965if the PID file exists and the PID it contains is no longer running, the
3966and writes its own PID to it. If it does, then the script checks the following:3966tool will overwrite the PID file with the current PID. The PID file is
3967if the file contains a PID and a process is running with that PID, then3967removed automatically when the tool exits.
3968the script dies; or, if there is no process running with that PID, then the
3969script overwrites the file with its own PID and starts; else, if the file
3970contains no PID, then the script dies.
39713968
3972=item --port3969=item --port
39733970
39743971
=== modified file 'bin/pt-slave-restart'
--- bin/pt-slave-restart 2013-02-22 17:47:57 +0000
+++ bin/pt-slave-restart 2013-02-27 23:41:26 +0000
@@ -5312,11 +5312,11 @@
53125312
5313type: string5313type: string
53145314
5315Create the given PID file when daemonized. The file contains the process5315Create the given PID file. The tool won't start if the PID file already
5316ID of the daemonized instance. The PID file is removed when the5316exists and the PID it contains is different than the current PID. However,
5317daemonized instance exits. The program checks for the existence of the5317if the PID file exists and the PID it contains is no longer running, the
5318PID file when starting; if it exists and the process with the matching PID5318tool will overwrite the PID file with the current PID. The PID file is
5319exists, the program exits.5319removed automatically when the tool exits.
53205320
5321=item --port5321=item --port
53225322
53235323
=== modified file 'bin/pt-stalk'
--- bin/pt-stalk 2013-01-24 19:08:09 +0000
+++ bin/pt-stalk 2013-02-27 23:41:26 +0000
@@ -1723,7 +1723,11 @@
17231723
1724type: string; default: /var/run/pt-stalk.pid1724type: string; default: /var/run/pt-stalk.pid
17251725
1726Create a PID file when daemonized.1726Create the given PID file. The tool won't start if the PID file already
1727exists and the PID it contains is different than the current PID. However,
1728if the PID file exists and the PID it contains is no longer running, the
1729tool will overwrite the PID file with the current PID. The PID file is
1730removed automatically when the tool exits.
17271731
1728=item --plugin1732=item --plugin
17291733
17301734
=== modified file 'bin/pt-table-checksum'
--- bin/pt-table-checksum 2013-02-22 17:47:57 +0000
+++ bin/pt-table-checksum 2013-02-27 23:41:26 +0000
@@ -3376,23 +3376,24 @@
3376 }3376 }
33773377
3378 my $self = {3378 my $self = {
3379 dsn => $dsn,3379 dsn => $dsn,
3380 dbh => $args{dbh},3380 dbh => $args{dbh},
3381 dsn_name => $dp->as_string($dsn, [qw(h P S)]),3381 dsn_name => $dp->as_string($dsn, [qw(h P S)]),
3382 hostname => '',3382 hostname => '',
3383 set => $args{set},3383 set => $args{set},
3384 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,3384 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,
3385 dbh_set => 0,3385 dbh_set => 0,
3386 OptionParser => $o,3386 OptionParser => $o,
3387 DSNParser => $dp,3387 DSNParser => $dp,
3388 is_cluster_node => undef,3388 is_cluster_node => undef,
3389 parent => $args{parent},
3389 };3390 };
33903391
3391 return bless $self, $class;3392 return bless $self, $class;
3392}3393}
33933394
3394sub connect {3395sub connect {
3395 my ( $self ) = @_;3396 my ( $self, %opts ) = @_;
3396 my $dsn = $self->{dsn};3397 my $dsn = $self->{dsn};
3397 my $dp = $self->{DSNParser};3398 my $dp = $self->{DSNParser};
3398 my $o = $self->{OptionParser};3399 my $o = $self->{OptionParser};
@@ -3403,11 +3404,18 @@
3403 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");3404 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
3404 $self->{asked_for_pass} = 1;3405 $self->{asked_for_pass} = 1;
3405 }3406 }
3406 $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });3407 $dbh = $dp->get_dbh(
3408 $dp->get_cxn_params($dsn),
3409 {
3410 AutoCommit => 1,
3411 %opts,
3412 },
3413 );
3407 }3414 }
3408 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name});
34093415
3410 return $self->set_dbh($dbh);3416 $dbh = $self->set_dbh($dbh);
3417 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
3418 return $dbh;
3411}3419}
34123420
3413sub set_dbh {3421sub set_dbh {
@@ -3430,6 +3438,11 @@
3430 $self->{hostname} = $hostname;3438 $self->{hostname} = $hostname;
3431 }3439 }
34323440
3441 if ( $self->{parent} ) {
3442 PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
3443 $dbh->{InactiveDestroy} = 1;
3444 }
3445
3433 if ( my $set = $self->{set}) {3446 if ( my $set = $self->{set}) {
3434 $set->($dbh);3447 $set->($dbh);
3435 }3448 }
@@ -3439,6 +3452,13 @@
3439 return $dbh;3452 return $dbh;
3440}3453}
34413454
3455sub lost_connection {
3456 my ($self, $e) = @_;
3457 return 0 unless $e;
3458 return $e =~ m/MySQL server has gone away/
3459 || $e =~ m/Lost connection to MySQL server/;
3460}
3461
3442sub dbh {3462sub dbh {
3443 my ($self) = @_;3463 my ($self) = @_;
3444 return $self->{dbh};3464 return $self->{dbh};
@@ -3457,12 +3477,21 @@
34573477
3458sub DESTROY {3478sub DESTROY {
3459 my ($self) = @_;3479 my ($self) = @_;
3460 if ( $self->{dbh}3480
3461 && blessed($self->{dbh})3481 PTDEBUG && _d('Destroying cxn');
3462 && $self->{dbh}->can("disconnect") ) {3482
3463 PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name});3483 if ( $self->{parent} ) {
3484 PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
3485 }
3486 elsif ( $self->{dbh}
3487 && blessed($self->{dbh})
3488 && $self->{dbh}->can("disconnect") )
3489 {
3490 PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
3491 $self->{dsn_name});
3464 $self->{dbh}->disconnect();3492 $self->{dbh}->disconnect();
3465 }3493 }
3494
3466 return;3495 return;
3467}3496}
34683497
@@ -11590,14 +11619,11 @@
1159011619
11591type: string11620type: string
1159211621
11593Create the given PID file. The file contains the process ID of the script.11622Create the given PID file. The tool won't start if the PID file already
11594The PID file is removed when the script exits. Before starting, the script11623exists and the PID it contains is different than the current PID. However,
11595checks if the PID file already exists. If it does not, then the script creates11624if the PID file exists and the PID it contains is no longer running, the
11596and writes its own PID to it. If it does, then the script checks the following:11625tool will overwrite the PID file with the current PID. The PID file is
11597if the file contains a PID and a process is running with that PID, then11626removed automatically when the tool exits.
11598the script dies; or, if there is no process running with that PID, then the
11599script overwrites the file with its own PID and starts; else, if the file
11600contains no PID, then the script dies.
1160111627
11602=item --port11628=item --port
1160311629
1160411630
=== modified file 'bin/pt-table-sync'
--- bin/pt-table-sync 2013-02-22 17:47:57 +0000
+++ bin/pt-table-sync 2013-02-27 23:41:26 +0000
@@ -12051,14 +12051,11 @@
1205112051
12052type: string12052type: string
1205312053
12054Create the given PID file. The file contains the process ID of the script.12054Create the given PID file. The tool won't start if the PID file already
12055The PID file is removed when the script exits. Before starting, the script12055exists and the PID it contains is different than the current PID. However,
12056checks if the PID file already exists. If it does not, then the script creates12056if the PID file exists and the PID it contains is no longer running, the
12057and writes its own PID to it. If it does, then the script checks the following:12057tool will overwrite the PID file with the current PID. The PID file is
12058if the file contains a PID and a process is running with that PID, then12058removed automatically when the tool exits.
12059the script dies; or, if there is no process running with that PID, then the
12060script overwrites the file with its own PID and starts; else, if the file
12061contains no PID, then the script dies.
1206212059
12063=item --port12060=item --port
1206412061
1206512062
=== modified file 'bin/pt-table-usage'
--- bin/pt-table-usage 2013-02-19 20:01:58 +0000
+++ bin/pt-table-usage 2013-02-27 23:41:26 +0000
@@ -7192,11 +7192,11 @@
71927192
7193type: string7193type: string
71947194
7195Create the given PID file when running. The file contains the process7195Create the given PID file. The tool won't start if the PID file already
7196ID of the daemonized instance. The PID file is removed when the7196exists and the PID it contains is different than the current PID. However,
7197daemonized instance exits. The program checks for the existence of the7197if the PID file exists and the PID it contains is no longer running, the
7198PID file when starting; if it exists and the process with the matching PID7198tool will overwrite the PID file with the current PID. The PID file is
7199exists, the program exits.7199removed automatically when the tool exits.
72007200
7201=item --port7201=item --port
72027202
72037203
=== modified file 'bin/pt-upgrade'
--- bin/pt-upgrade 2013-02-22 17:47:57 +0000
+++ bin/pt-upgrade 2013-02-27 23:41:26 +0000
@@ -13483,11 +13483,11 @@
1348313483
13484type: string13484type: string
1348513485
13486Create the given PID file when daemonized. The file contains the process13486Create the given PID file. The tool won't start if the PID file already
13487ID of the daemonized instance. The PID file is removed when the13487exists and the PID it contains is different than the current PID. However,
13488daemonized instance exits. The program checks for the existence of the13488if the PID file exists and the PID it contains is no longer running, the
13489PID file when starting; if it exists and the process with the matching PID13489tool will overwrite the PID file with the current PID. The PID file is
13490exists, the program exits.13490removed automatically when the tool exits.
1349113491
13492=item --port13492=item --port
1349313493
1349413494
=== modified file 'bin/pt-variable-advisor'
--- bin/pt-variable-advisor 2013-02-22 15:00:55 +0000
+++ bin/pt-variable-advisor 2013-02-27 23:41:26 +0000
@@ -5752,11 +5752,11 @@
57525752
5753type: string5753type: string
57545754
5755Create the given PID file when daemonized. The file contains the process5755Create the given PID file. The tool won't start if the PID file already
5756ID of the daemonized instance. The PID file is removed when the5756exists and the PID it contains is different than the current PID. However,
5757daemonized instance exits. The program checks for the existence of the5757if the PID file exists and the PID it contains is no longer running, the
5758PID file when starting; if it exists and the process with the matching PID5758tool will overwrite the PID file with the current PID. The PID file is
5759exists, the program exits.5759removed automatically when the tool exits.
57605760
5761=item --port5761=item --port
57625762
57635763
=== modified file 'bin/pt-visual-explain'
--- bin/pt-visual-explain 2013-02-19 20:01:58 +0000
+++ bin/pt-visual-explain 2013-02-27 23:41:26 +0000
@@ -2952,14 +2952,11 @@
29522952
2953type: string2953type: string
29542954
2955Create the given PID file. The file contains the process ID of the script.2955Create the given PID file. The tool won't start if the PID file already
2956The PID file is removed when the script exits. Before starting, the script2956exists and the PID it contains is different than the current PID. However,
2957checks if the PID file already exists. If it does not, then the script creates2957if the PID file exists and the PID it contains is no longer running, the
2958and writes its own PID to it. If it does, then the script checks the following:2958tool will overwrite the PID file with the current PID. The PID file is
2959if the file contains a PID and a process is running with that PID, then2959removed automatically when the tool exits.
2960the script dies; or, if there is no process running with that PID, then the
2961script overwrites the file with its own PID and starts; else, if the file
2962contains no PID, then the script dies.
29632960
2964=item --port2961=item --port
29652962
29662963
=== modified file 'docs/percona-toolkit.pod'
--- docs/percona-toolkit.pod 2013-01-03 00:54:18 +0000
+++ docs/percona-toolkit.pod 2013-02-27 23:41:26 +0000
@@ -175,6 +175,34 @@
175For more free, open-source software developed Percona, visit175For more free, open-source software developed Percona, visit
176L<http://www.percona.com/software/>.176L<http://www.percona.com/software/>.
177177
178=head1 SPECIAL OPTION TYPES
179
180=over
181
182=item time
183
184Time values are seconds by default. For example, C<--run-time 60> means
18560 seconds. Time values support an optional suffix: s (seconds),
186m (minutes), h (hours), d (days). C<--run-time 1m> means 1 minute
187(the same as 60 seconds).
188
189=item size
190
191Size values are bytes by default. For example, C<--disk-space-free 1024>
192means 1 Kibibyte. Size values support an optional suffix: k (Kibibyte),
193M (Mebibyte), G (Gibibyte).
194
195=item DSN
196
197See L<"DSN (DATA SOURCE NAME) SPECIFICATIONS">.
198
199=item Hash, hash, Array, array
200
201Hash, hash, Array, and array values are comma-separated lists of values.
202For example, C<--ignore-tables foo,bar> ignores tables C<foo> and C<bar>.
203
204=back
205
178=head1 CONFIGURATION FILES206=head1 CONFIGURATION FILES
179207
180Percona Toolkit tools can read options from configuration files. The208Percona Toolkit tools can read options from configuration files. The
181209
=== modified file 'lib/Cxn.pm'
--- lib/Cxn.pm 2013-01-03 00:19:16 +0000
+++ lib/Cxn.pm 2013-02-27 23:41:26 +0000
@@ -98,23 +98,24 @@
98 }98 }
9999
100 my $self = {100 my $self = {
101 dsn => $dsn,101 dsn => $dsn,
102 dbh => $args{dbh},102 dbh => $args{dbh},
103 dsn_name => $dp->as_string($dsn, [qw(h P S)]),103 dsn_name => $dp->as_string($dsn, [qw(h P S)]),
104 hostname => '',104 hostname => '',
105 set => $args{set},105 set => $args{set},
106 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,106 NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1,
107 dbh_set => 0,107 dbh_set => 0,
108 OptionParser => $o,108 OptionParser => $o,
109 DSNParser => $dp,109 DSNParser => $dp,
110 is_cluster_node => undef,110 is_cluster_node => undef,
111 parent => $args{parent},
111 };112 };
112113
113 return bless $self, $class;114 return bless $self, $class;
114}115}
115116
116sub connect {117sub connect {
117 my ( $self ) = @_;118 my ( $self, %opts ) = @_;
118 my $dsn = $self->{dsn};119 my $dsn = $self->{dsn};
119 my $dp = $self->{DSNParser};120 my $dp = $self->{DSNParser};
120 my $o = $self->{OptionParser};121 my $o = $self->{OptionParser};
@@ -126,11 +127,18 @@
126 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");127 $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: ");
127 $self->{asked_for_pass} = 1;128 $self->{asked_for_pass} = 1;
128 }129 }
129 $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 });130 $dbh = $dp->get_dbh(
131 $dp->get_cxn_params($dsn),
132 {
133 AutoCommit => 1,
134 %opts,
135 },
136 );
130 }137 }
131 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name});
132138
133 return $self->set_dbh($dbh);139 $dbh = $self->set_dbh($dbh);
140 PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
141 return $dbh;
134}142}
135143
136sub set_dbh {144sub set_dbh {
@@ -163,6 +171,11 @@
163 $self->{hostname} = $hostname;171 $self->{hostname} = $hostname;
164 }172 }
165173
174 if ( $self->{parent} ) {
175 PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent');
176 $dbh->{InactiveDestroy} = 1;
177 }
178
166 # Call the set callback to let the caller SET any MySQL variables.179 # Call the set callback to let the caller SET any MySQL variables.
167 if ( my $set = $self->{set}) {180 if ( my $set = $self->{set}) {
168 $set->($dbh);181 $set->($dbh);
@@ -173,6 +186,15 @@
173 return $dbh;186 return $dbh;
174}187}
175188
189sub lost_connection {
190 my ($self, $e) = @_;
191 return 0 unless $e;
192 return $e =~ m/MySQL server has gone away/
193 || $e =~ m/Lost connection to MySQL server/;
194 # The 1st pattern means that MySQL itself died or was stopped.
195 # The 2nd pattern means that our cxn was killed (KILL <id>).
196}
197
176# Sub: dbh198# Sub: dbh
177# Return the cxn's dbh.199# Return the cxn's dbh.
178sub dbh {200sub dbh {
@@ -197,12 +219,21 @@
197219
198sub DESTROY {220sub DESTROY {
199 my ($self) = @_;221 my ($self) = @_;
200 if ( $self->{dbh}222
201 && blessed($self->{dbh})223 PTDEBUG && _d('Destroying cxn');
202 && $self->{dbh}->can("disconnect") ) {224
203 PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name});225 if ( $self->{parent} ) {
226 PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent');
227 }
228 elsif ( $self->{dbh}
229 && blessed($self->{dbh})
230 && $self->{dbh}->can("disconnect") )
231 {
232 PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname},
233 $self->{dsn_name});
204 $self->{dbh}->disconnect();234 $self->{dbh}->disconnect();
205 }235 }
236
206 return;237 return;
207}238}
208239
209240
=== modified file 'lib/Runtime.pm'
--- lib/Runtime.pm 2013-01-03 00:19:16 +0000
+++ lib/Runtime.pm 2013-02-27 23:41:26 +0000
@@ -18,13 +18,6 @@
18# Runtime package18# Runtime package
19# ###########################################################################19# ###########################################################################
20{20{
21# Package: Runtime
22# Runtime keeps track of time to control how long a tool's main loop runs.
23# This package was created to handle mk-query-digest --run-time-mode event.
24# In essence, we abstract time so that the tool doesn't know/care whether
25# now() comes from a clock, a log timestamp, or wherever. The creator of
26# Runtime object determines how, or from where, time is gotten so that the
27# caller of the object can simply ask, "What time is it?".
28package Runtime;21package Runtime;
2922
30use strict;23use strict;
@@ -32,30 +25,24 @@
32use English qw(-no_match_vars);25use English qw(-no_match_vars);
33use constant PTDEBUG => $ENV{PTDEBUG} || 0;26use constant PTDEBUG => $ENV{PTDEBUG} || 0;
3427
35# Sub: new
36#
37# Parameters:
38# %args - Arguments
39#
40# Required Arguments:
41# now - Callback that sets current time.
42# runtime - Amount of time to run in seconds, or undef for forever.
43#
44# Returns:
45# Runtime object
46sub new {28sub new {
47 my ( $class, %args ) = @_;29 my ( $class, %args ) = @_;
48 my @required_args = qw(now);30 my @required_args = qw(run_time now);
49 foreach my $arg ( @required_args ) {31 foreach my $arg ( @required_args ) {
50 die "I need a $arg argument" unless $args{$arg};32 die "I need a $arg argument" unless exists $args{$arg};
51 }33 }
5234
53 if ( ($args{runtime} || 0) < 0 ) {35 my $run_time = $args{run_time};
54 die "runtime argument must be greater than zero"36 if ( defined $run_time ) {
55 }37 die "run_time must be > 0" if $run_time <= 0;
38 }
39
40 my $now = $args{now};
41 die "now must be a callback" unless ref $now eq 'CODE';
5642
57 my $self = {43 my $self = {
58 %args,44 run_time => $run_time,
45 now => $now,
59 start_time => undef,46 start_time => undef,
60 end_time => undef,47 end_time => undef,
61 time_left => undef,48 time_left => undef,
@@ -66,8 +53,8 @@
66}53}
6754
68# Sub: time_left55# Sub: time_left
69# Return the number of runtime seconds left or undef for forever.56# Return the number of run time seconds left or undef for forever.
70# The return may be less than zero if the runtime has been exceeded.57# The return may be less than zero if the run time has been exceeded.
71# The first call to this subroutine "starts the clock", so to speak,58# The first call to this subroutine "starts the clock", so to speak,
72# if the now callbackup returns a defined value.59# if the now callbackup returns a defined value.
73#60#
@@ -75,7 +62,7 @@
75# %args - Arguments passed to now callback.62# %args - Arguments passed to now callback.
76#63#
77# Returns:64# Returns:
78# Number of runtime seconds left, possibly less than zero, or undef65# Number of run time seconds left, possibly less than zero, or undef
79# if running forever.66# if running forever.
80sub time_left {67sub time_left {
81 my ( $self, %args ) = @_;68 my ( $self, %args ) = @_;
@@ -99,14 +86,14 @@
99 # we know the current time.86 # we know the current time.
100 return unless defined $now;87 return unless defined $now;
10188
102 # If runtime is also defined, then we can determine time left.89 # If run_time is also defined, then we can determine time left.
103 # If it's not defined, then we're running forever.90 # If it's not defined, then we're running forever.
104 my $runtime = $self->{runtime};91 my $run_time = $self->{run_time};
105 return unless defined $runtime;92 return unless defined $run_time;
10693
107 # Set the end time once.94 # Set the end time once.
108 if ( !$self->{end_time} ) {95 if ( !$self->{end_time} ) {
109 $self->{end_time} = $now + $runtime;96 $self->{end_time} = $now + $run_time;
110 PTDEBUG && _d("End time:", $self->{end_time});97 PTDEBUG && _d("End time:", $self->{end_time});
111 }98 }
11299
@@ -118,7 +105,7 @@
118}105}
119106
120# Sub: have_time107# Sub: have_time
121# Return true or false if there's runtime left. This sub is a simpler108# Return true or false if there's run time left. This sub is a simpler
122# wrapper around <time_left()> which returns true (1) if time left is109# wrapper around <time_left()> which returns true (1) if time left is
123# defined and greater than zero or undef, else returns false.110# defined and greater than zero or undef, else returns false.
124#111#
@@ -131,7 +118,7 @@
131 my ( $self, %args ) = @_;118 my ( $self, %args ) = @_;
132 my $time_left = $self->time_left(%args);119 my $time_left = $self->time_left(%args);
133 return 1 if !defined $time_left; # run forever120 return 1 if !defined $time_left; # run forever
134 return $time_left <= 0 ? 0 : 1; # <=0s means runtime has elapsed121 return $time_left <= 0 ? 0 : 1; # <=0s means run time has elapsed
135}122}
136123
137# Sub: time_elapsed124# Sub: time_elapsed
@@ -173,7 +160,7 @@
173 $self->{end_time} = undef;160 $self->{end_time} = undef;
174 $self->{time_left} = undef;161 $self->{time_left} = undef;
175 $self->{stop} = 0;162 $self->{stop} = 0;
176 PTDEBUG && _d("Reset runtime");163 PTDEBUG && _d("Reset run time");
177 return;164 return;
178}165}
179166
180167
=== modified file 't/lib/Cxn.t'
--- t/lib/Cxn.t 2012-11-08 20:47:00 +0000
+++ t/lib/Cxn.t 2013-02-27 23:41:26 +0000
@@ -9,7 +9,7 @@
9use strict;9use strict;
10use warnings FATAL => 'all';10use warnings FATAL => 'all';
11use English qw(-no_match_vars);11use English qw(-no_match_vars);
12use Test::More tests => 19;12use Test::More;
1313
14use Sandbox;14use Sandbox;
15use OptionParser;15use OptionParser;
@@ -252,8 +252,64 @@
252$o->get_opts();252$o->get_opts();
253253
254# #############################################################################254# #############################################################################
255# The parent of a forked Cxn should not disconnect the dbh in DESTORY
256# because the child still has access to it.
257# #############################################################################
258
259my $sync_file = "/tmp/pt-cxn-sync.$PID";
260my $outfile = "/tmp/pt-cxn-outfile.$PID";
261
262my $pid;
263{
264 my $parent_cxn = make_cxn(
265 dsn_string => 'h=127.1,P=12345,u=msandbox,p=msandbox',
266 parent => 1,
267 );
268 $parent_cxn->connect();
269
270 $pid = fork();
271 if ( defined($pid) && $pid == 0 ) {
272 # I am the child.
273 # Wait for the parent to leave this code block which will cause
274 # the $parent_cxn to be destroyed.
275 PerconaTest::wait_for_files($sync_file);
276 $parent_cxn->{parent} = 0;
277 eval {
278 $parent_cxn->dbh->do("SELECT 123 INTO OUTFILE '$outfile'");
279 $parent_cxn->dbh->disconnect();
280 };
281 warn $EVAL_ERROR if $EVAL_ERROR;
282 exit;
283 }
284}
285
286# Let the child know that we (the parent) have left that ^ code block,
287# so our copy of $parent_cxn has been destroyed, but hopefully the child's
288# copy is still alive, i.e. has an active/not-disconnected dbh.
289diag(`touch $sync_file`);
290
291# Wait for the child.
292waitpid($pid, 0);
293
294ok(
295 -f $outfile,
296 "Child created outfile"
297);
298
299my $output = `cat $outfile 2>/dev/null`;
300
301is(
302 $output,
303 "123\n",
304 "Child executed query"
305);
306
307unlink $sync_file if -f $sync_file;
308unlink $outfile if -f $outfile;
309
310# #############################################################################
255# Done.311# Done.
256# #############################################################################312# #############################################################################
257$master_dbh->disconnect() if $master_dbh;313$master_dbh->disconnect() if $master_dbh;
258ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");314ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
259exit;315done_testing;
260316
=== modified file 't/pt-deadlock-logger/basics.t'
--- t/pt-deadlock-logger/basics.t 2012-10-22 21:30:29 +0000
+++ t/pt-deadlock-logger/basics.t 2013-02-27 23:41:26 +0000
@@ -11,6 +11,8 @@
11use English qw(-no_match_vars);11use English qw(-no_match_vars);
12use Test::More;12use Test::More;
1313
14$ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} = 1;
15
14use PerconaTest;16use PerconaTest;
15use Sandbox;17use Sandbox;
16require "$trunk/bin/pt-deadlock-logger";18require "$trunk/bin/pt-deadlock-logger";
@@ -25,8 +27,8 @@
25}27}
2628
27my $output;29my $output;
28my $cnf = "/tmp/12345/my.sandbox.cnf";30my $dsn = $sb->dsn_for('master');
29my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1";31my @args = ($dsn, qw(--iterations 1));
3032
31$dbh1->commit;33$dbh1->commit;
32$dbh2->commit;34$dbh2->commit;
@@ -90,21 +92,30 @@
90$output = $dbh1->selectrow_hashref('show /*!40101 engine*/ innodb status')->{status};92$output = $dbh1->selectrow_hashref('show /*!40101 engine*/ innodb status')->{status};
91like($output, qr/WE ROLL BACK/, 'There was a deadlock');93like($output, qr/WE ROLL BACK/, 'There was a deadlock');
9294
93$output = `$cmd --print`;95$output = output(
96 sub {
97 pt_deadlock_logger::main(@args);
98 }
99);
100
94like(101like(
95 $output,102 $output,
96 qr/127\.1.+msandbox.+GEN_CLUST_INDEX/,103 qr/127\.1.+msandbox.+GEN_CLUST_INDEX/,
97 'Deadlock logger prints the output'104 'Deadlock logger prints the output'
98);105);
99106
100$output = `$cmd`;107$output = output(
101like(108 sub {
109 pt_deadlock_logger::main(@args, qw(--quiet));
110 }
111);
112
113is(
102 $output,114 $output,
103 qr/127\.1.+msandbox.+GEN_CLUST_INDEX/,115 "",
104 '--print is implicit'116 "No output with --quiet"
105);117);
106118
107
108# #############################################################################119# #############################################################################
109# Issue 943: mk-deadlock-logger reports the same deadlock with --interval120# Issue 943: mk-deadlock-logger reports the same deadlock with --interval
110# #############################################################################121# #############################################################################
@@ -112,55 +123,59 @@
112# The deadlock from above won't be re-printed so even after running for123# The deadlock from above won't be re-printed so even after running for
113# 3 seconds and checking multiple times only the single, 3 line deadlock124# 3 seconds and checking multiple times only the single, 3 line deadlock
114# should be reported.125# should be reported.
115chomp($output = `$cmd --run-time 3 | wc -l`);126
127$output = output(
128 sub {
129 pt_deadlock_logger::main(@args, qw(--run-time 3));
130 }
131);
116$output =~ s/^\s+//;132$output =~ s/^\s+//;
133my @lines = split("\n", $output);
117is(134is(
118 $output,135 scalar @lines,
119 3,136 3,
120 "Doesn't re-print same deadlock (issue 943)"137 "Doesn't re-print same deadlock (issue 943)"
121);138) or diag($output);
122139
123# #############################################################################140# #############################################################################
124# Check that deadlocks from previous test were stored in table.141# Check that deadlocks from previous test were stored in table.
125# #############################################################################142# #############################################################################
126`$cmd --dest D=test,t=deadlocks --create-dest-table`;143$output = output(
144 sub {
145 pt_deadlock_logger::main(@args, '--dest', 'D=test,t=deadlocks',
146 qw(--create-dest-table))
147 }
148);
149
127my $res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks');150my $res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks');
128ok(151ok(
129 scalar @$res,152 scalar @$res,
130 'Deadlocks recorded in --dest table'153 'Deadlock saved in --dest table'
131);154) or diag($output);
132155
133# #############################################################################156# #############################################################################
134# Check that --dest suppress --print output unless --print is explicit.157# In 2.1, --dest suppressed output (--print). In 2.2, output is only
135# #############################################################################158# suppressed by --quiet.
136$output = 'foo';159# #############################################################################
137$dbh1->do('TRUNCATE TABLE test.deadlocks');
138$output = `$cmd --dest D=test,t=deadlocks`;
139is(
140 $output,
141 '',
142 'No output with --dest'
143);
144
145$res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks');
146ok(
147 scalar @$res,
148 'Deadlocks still recorded in table'
149);
150
151$output = '';160$output = '';
152$dbh1->do('TRUNCATE TABLE test.deadlocks');161$dbh1->do('TRUNCATE TABLE test.deadlocks');
153$output = `$trunk/bin/pt-deadlock-logger --print --dest D=test,t=deadlocks --host 127.1 --port 12345 --user msandbox --password msandbox`;162$output = output(
154like(163 sub {
164 pt_deadlock_logger::main(@args, '--dest', 'D=test,t=deadlocks',
165 qw(--quiet))
166 }
167);
168
169is(
155 $output,170 $output,
156 qr/127\.1.+msandbox.+GEN_CLUST_INDEX/,171 "",
157 'Prints output with --dest and explicit --print'172 "No output with --dest and --quiet"
158);173);
159174
160$res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks');175$res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks');
161ok(176ok(
162 scalar @$res,177 scalar @$res,
163 'Deadlocks recorded in table again'178 "... deadlock still saved in the table"
164);179);
165180
166# #############################################################################181# #############################################################################
@@ -180,9 +195,7 @@
180 make_deadlock();195 make_deadlock();
181196
182 $output = output(197 $output = output(
183 sub { pt_deadlock_logger::main("F=/tmp/12345/my.sandbox.cnf",198 sub { pt_deadlock_logger::main(@args) }
184 qw(--print) );
185 }
186 );199 );
187200
188 like(201 like(
@@ -200,4 +213,3 @@
200$sb->wipe_clean($dbh1);213$sb->wipe_clean($dbh1);
201ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");214ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
202done_testing;215done_testing;
203exit;
204216
=== modified file 't/pt-deadlock-logger/clear_deadlocks.t'
--- t/pt-deadlock-logger/clear_deadlocks.t 2012-06-03 19:14:30 +0000
+++ t/pt-deadlock-logger/clear_deadlocks.t 2013-02-27 23:41:26 +0000
@@ -22,9 +22,6 @@
22if ( !$dbh1 ) {22if ( !$dbh1 ) {
23 plan skip_all => 'Cannot connect to sandbox master';23 plan skip_all => 'Cannot connect to sandbox master';
24}24}
25else {
26 plan tests => 4;
27}
2825
29my $output;26my $output;
30my $cnf = "/tmp/12345/my.sandbox.cnf";27my $cnf = "/tmp/12345/my.sandbox.cnf";
@@ -39,7 +36,7 @@
3936
40# The clear-deadlocks table comes and goes quickly so we can really37# The clear-deadlocks table comes and goes quickly so we can really
41# only search the debug output for evidence that it was created.38# only search the debug output for evidence that it was created.
42$output = `PTDEBUG=1 $trunk/bin/pt-deadlock-logger F=$cnf,D=test --clear-deadlocks test.make_deadlock 2>&1`;39$output = `PTDEBUG=1 $trunk/bin/pt-deadlock-logger F=$cnf,D=test --clear-deadlocks test.make_deadlock --iterations 1 2>&1`;
43like(40like(
44 $output,41 $output,
45 qr/INSERT INTO test.make_deadlock/,42 qr/INSERT INTO test.make_deadlock/,
@@ -67,4 +64,4 @@
67# #############################################################################64# #############################################################################
68$sb->wipe_clean($dbh1);65$sb->wipe_clean($dbh1);
69ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");66ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
70exit;67done_testing;
7168
=== modified file 't/pt-deadlock-logger/create_dest_table.t'
--- t/pt-deadlock-logger/create_dest_table.t 2012-06-09 18:43:33 +0000
+++ t/pt-deadlock-logger/create_dest_table.t 2013-02-27 23:41:26 +0000
@@ -22,15 +22,10 @@
22if ( !$dbh1 ) {22if ( !$dbh1 ) {
23 plan skip_all => 'Cannot connect to sandbox master';23 plan skip_all => 'Cannot connect to sandbox master';
24}24}
25else {
26 plan tests => 3;
27}
2825
29my $output;26my $output;
30my $cnf = "/tmp/12345/my.sandbox.cnf";27my $dsn = $sb->dsn_for('master');
31my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1";
3228
33$sb->wipe_clean($dbh1);
34$sb->create_dbs($dbh1, ['test']);29$sb->create_dbs($dbh1, ['test']);
3530
36# #############################################################################31# #############################################################################
@@ -42,17 +37,25 @@
42 'Deadlocks table does not exit (issue 386)'37 'Deadlocks table does not exit (issue 386)'
43);38);
4439
45`$cmd --dest D=test,t=issue_386 --run-time 1s --interval 1s --create-dest-table`;40$output = output(
41 sub {
42 pt_deadlock_logger::main($dsn,
43 '--dest', 'D=test,t=issue_386',
44 qw(--iterations 1 --create-dest-table)
45 )
46 },
47 stderr => 1,
48);
4649
47is_deeply(50is_deeply(
48 $dbh1->selectall_arrayref(q{show tables from `test` like 'issue_386'}),51 $dbh1->selectall_arrayref(q{show tables from `test` like 'issue_386'}),
49 [['issue_386']],52 [['issue_386']],
50 'Deadlocks table created with --create-dest-table (issue 386)'53 'Deadlocks table created with --create-dest-table (issue 386)'
51);54) or diag($output);
5255
53# #############################################################################56# #############################################################################
54# Done.57# Done.
55# #############################################################################58# #############################################################################
56$sb->wipe_clean($dbh1);59$sb->wipe_clean($dbh1);
57ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");60ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
58exit;61done_testing;
5962
=== modified file 't/pt-deadlock-logger/option_sanity.t'
--- t/pt-deadlock-logger/option_sanity.t 2012-10-22 18:16:42 +0000
+++ t/pt-deadlock-logger/option_sanity.t 2013-02-27 23:41:26 +0000
@@ -21,7 +21,7 @@
21$output = `$trunk/bin/pt-deadlock-logger --dest D=test,t=deadlocks 2>&1`;21$output = `$trunk/bin/pt-deadlock-logger --dest D=test,t=deadlocks 2>&1`;
22like(22like(
23 $output,23 $output,
24 qr/Missing or invalid source host/,24 qr/No DSN was specified/,
25 'Requires source host'25 'Requires source host'
26);26);
2727
2828
=== renamed file 't/pt-deadlock-logger/deadlocks_tbl.sql' => 't/pt-deadlock-logger/samples/deadlocks_tbl.sql'
=== modified file 't/pt-deadlock-logger/standard_options.t'
--- t/pt-deadlock-logger/standard_options.t 2012-07-23 04:52:41 +0000
+++ t/pt-deadlock-logger/standard_options.t 2013-02-27 23:41:26 +0000
@@ -17,69 +17,96 @@
1717
18my $dp = new DSNParser(opts=>$dsn_opts);18my $dp = new DSNParser(opts=>$dsn_opts);
19my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);19my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
20my $dbh1 = $sb->get_dbh_for('master');20my $dbh = $sb->get_dbh_for('master');
2121
22if ( !$dbh1 ) {22if ( !$dbh ) {
23 plan skip_all => 'Cannot connect to sandbox master';23 plan skip_all => 'Cannot connect to sandbox master';
24}24}
25else {
26 plan tests => 10;
27}
2825
29my $output;26my $output;
30my $cnf = "/tmp/12345/my.sandbox.cnf";27my $dsn = $sb->dsn_for('master');
31my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1";28my @args = ($dsn, qw(--iterations 1));
3229
33$sb->wipe_clean($dbh1);30$sb->wipe_clean($dbh);
34$sb->create_dbs($dbh1, ['test']);31$sb->create_dbs($dbh, ['test']);
3532
36# #############################################################################33# #############################################################################
37# Issue 248: Add --user, --pass, --host, etc to all tools34# Issue 248: Add --user, --pass, --host, etc to all tools
38# #############################################################################35# #############################################################################
3936
40# Test that source DSN inherits from --user, etc.37# Test that source DSN inherits from --user, etc.
41$output = `$trunk/bin/pt-deadlock-logger h=127.1,D=test,u=msandbox,p=msandbox --clear-deadlocks test.make_deadlock --port 12345 2>&1`;38$output = output(
39 sub {
40 pt_deadlock_logger::main(
41 "h=127.1,D=test,u=msandbox,p=msandbox",
42 qw(--clear-deadlocks test.make_deadlock --port 12345),
43 qw(--iterations 1)
44 )
45 }
46);
47
42unlike(48unlike(
43 $output,49 $output,
44 qr/failed/,50 qr/failed/,
45 'Source DSN inherits from standard connection options (issue 248)'51 'Source DSN inherits from standard connection options (issue 248)'
46);52);
4753
48# #########################################################################54# #############################################################################
49# Issue 391: Add --pid option to all scripts55# Issue 391: Add --pid option to all scripts
50# #########################################################################56# #############################################################################
51`touch /tmp/mk-script.pid`;57
52$output = `$cmd --clear-deadlocks test.make_deadlock --port 12345 --pid /tmp/mk-script.pid 2>&1`;58my $pid_file = "/tmp/pt-deadlock-logger-test.pid.$PID";
59diag(`touch $pid_file`);
60
61$output = output(
62 sub {
63 pt_deadlock_logger::main(@args, '--pid', $pid_file)
64 },
65 stderr => 1,
66);
67
53like(68like(
54 $output,69 $output,
55 qr{PID file /tmp/mk-script.pid already exists},70 qr{PID file $pid_file already exists},
56 'Dies if PID file already exists (--pid without --daemonize) (issue 391)'71 'Dies if PID file already exists (--pid without --daemonize) (issue 391)'
57);72);
58`rm -rf /tmp/mk-script.pid`;73
74unlink $pid_file if -f $pid_file;
5975
60# #############################################################################76# #############################################################################
61# Check daemonization77# Check daemonization
62# #############################################################################78# #############################################################################
63my $deadlocks_tbl = load_file('t/pt-deadlock-logger/deadlocks_tbl.sql');79$dbh->do('USE test');
64$dbh1->do('USE test');80$dbh->do('DROP TABLE IF EXISTS deadlocks');
65$dbh1->do('DROP TABLE IF EXISTS deadlocks');81$sb->load_file('master', 't/pt-deadlock-logger/samples/deadlocks_tbl.sql', 'test');
66$dbh1->do("$deadlocks_tbl");82
6783$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`;
68my $pid_file = '/tmp/mk-deadlock-logger.pid';
69unlink $pid_file
70 and diag("Unlinked existing $pid_file");
71
72`$cmd --dest D=test,t=deadlocks --daemonize --run-time 6s --interval 1s --pid $pid_file 1>/dev/null 2>/dev/null`;
73$output = `ps -eaf | grep '$cmd \-\-dest '`;
74like($output, qr/\Q$cmd/, 'It lives daemonized');
7584
76PerconaTest::wait_for_files($pid_file);85PerconaTest::wait_for_files($pid_file);
77ok(-f $pid_file, 'PID file created');86
78my ($pid) = $output =~ /\s+(\d+)\s+/;87$output = `ps x | grep 'pt-deadlock-logger $dsn' | grep -v grep`;
88like(
89 $output,
90 qr/\Qpt-deadlock-logger $dsn/,
91 'It lives daemonized'
92) or diag($output);
93
94my ($pid) = $output =~ /(\d+)/;
95
96ok(
97 -f $pid_file,
98 'PID file created'
99) or diag($output);
100
79chomp($output = slurp_file($pid_file));101chomp($output = slurp_file($pid_file));
80is($output, $pid, 'PID file has correct PID');102is(
103 $output,
104 $pid,
105 'PID file has correct PID'
106);
81107
82# Kill it108# Kill it
109kill 2, $pid;
83PerconaTest::wait_until(sub { !kill 0, $pid });110PerconaTest::wait_until(sub { !kill 0, $pid });
84ok(! -f $pid_file, 'PID file removed');111ok(! -f $pid_file, 'PID file removed');
85112
@@ -90,17 +117,25 @@
90 'PID file already exists'117 'PID file already exists'
91);118);
92119
93$output = `$cmd --dest D=test,t=deadlocks --daemonize --run-time 1s --interval 1s --pid $pid_file 2>&1`;120$output = output(
121 sub {
122 pt_deadlock_logger::main(@args, '--pid', $pid_file,
123 qw(--daemonize))
124 },
125 stderr => 1,
126);
127
94like(128like(
95 $output,129 $output,
96 qr/PID file .+ already exists/,130 qr/PID file $pid_file already exists/,
97 'Does not run if PID file already exists'131 'Does not run if PID file already exists'
98);132);
99133
100$output = `ps -eaf | grep 'pt-deadlock-logger \-\-dest '`;134$output = `ps x | grep 'pt-deadlock-logger $dsn' | grep -v grep`;
101unlike(135
136is(
102 $output,137 $output,
103 qr/$cmd/,138 "",
104 'It does not lived daemonized'139 'It does not lived daemonized'
105);140);
106141
@@ -109,6 +144,6 @@
109# #############################################################################144# #############################################################################
110# Done.145# Done.
111# #############################################################################146# #############################################################################
112$sb->wipe_clean($dbh1);147$sb->wipe_clean($dbh);
113ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");148ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
114exit;149done_testing;
115150
=== modified file 't/pt-fk-error-logger/basics.t'
--- t/pt-fk-error-logger/basics.t 2012-11-07 18:38:09 +0000
+++ t/pt-fk-error-logger/basics.t 2013-02-27 23:41:26 +0000
@@ -27,8 +27,9 @@
27$sb->create_dbs($dbh, [qw(test)]);27$sb->create_dbs($dbh, [qw(test)]);
2828
29my $output;29my $output;
30my $cnf = '/tmp/12345/my.sandbox.cnf';30my $cnf = '/tmp/12345/my.sandbox.cnf';
31my $cmd = "$trunk/bin/pt-fk-error-logger -F $cnf ";31my $cmd = "$trunk/bin/pt-fk-error-logger -F $cnf ";
32my @args = qw(--iterations 1);
3233
33$sb->load_file('master', 't/pt-fk-error-logger/samples/fke_tbl.sql', 'test');34$sb->load_file('master', 't/pt-fk-error-logger/samples/fke_tbl.sql', 'test');
3435
@@ -39,8 +40,45 @@
39# First, create a foreign key error.40# First, create a foreign key error.
40`/tmp/12345/use -D test < $trunk/t/pt-fk-error-logger/samples/fke.sql 1>/dev/null 2>/dev/null`;41`/tmp/12345/use -D test < $trunk/t/pt-fk-error-logger/samples/fke.sql 1>/dev/null 2>/dev/null`;
4142
42# Then get and save that fke.43$output = output(
43output(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'); } );44 sub {
45 pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox'),
46 }
47);
48
49like(
50 $output,
51 qr/Foreign key constraint fails/,
52 "Prints fk error by default"
53);
54
55$output = output(
56 sub {
57 pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox',
58 qw(--quiet))
59 }
60);
61
62is(
63 $output,
64 "",
65 "No output with --quiet"
66);
67
68
69# #############################################################################
70# --dest
71# #############################################################################
72
73$output = output(
74 sub {
75 pt_fk_error_logger::main(@args,
76 'h=127.1,P=12345,u=msandbox,p=msandbox',
77 '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors',
78 )
79 }
80);
81
44sleep 0.1;82sleep 0.1;
4583
46# And then test that it was actually saved.84# And then test that it was actually saved.
@@ -61,7 +99,7 @@
6199
62# Check again to make sure that the same fke isn't saved twice.100# Check again to make sure that the same fke isn't saved twice.
63my $first_ts = $fke->[0]->[0];101my $first_ts = $fke->[0]->[0];
64output(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'); } );102output(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'); } );
65sleep 0.1;103sleep 0.1;
66$fke = $dbh->selectall_arrayref('SELECT * FROM test.foreign_key_errors');104$fke = $dbh->selectall_arrayref('SELECT * FROM test.foreign_key_errors');
67is(105is(
@@ -82,7 +120,7 @@
82eval {120eval {
83 $dbh->do('DELETE FROM parent WHERE id = 2'); # Causes foreign key error.121 $dbh->do('DELETE FROM parent WHERE id = 2'); # Causes foreign key error.
84};122};
85output( 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'); } );123output( 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'); } );
86sleep 0.1;124sleep 0.1;
87$fke = $dbh->selectall_arrayref('SELECT * FROM test.foreign_key_errors');125$fke = $dbh->selectall_arrayref('SELECT * FROM test.foreign_key_errors');
88like(126like(
@@ -99,11 +137,14 @@
99# ##########################################################################137# ##########################################################################
100# Test printing the errors.138# Test printing the errors.
101# ##########################################################################139# ##########################################################################
140
102$dbh->do('USE test');141$dbh->do('USE test');
103eval {142eval {
104 $dbh->do('DELETE FROM parent WHERE id = 2'); # Causes foreign key error.143 $dbh->do('DELETE FROM parent WHERE id = 2'); # Causes foreign key error.
105};144};
106$output = output(sub { pt_fk_error_logger::main('h=127.1,P=12345,u=msandbox,p=msandbox'); });145
146$output = output(sub { pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox'); });
147
107like(148like(
108 $output,149 $output,
109 qr/DELETE FROM parent WHERE id = 2/,150 qr/DELETE FROM parent WHERE id = 2/,
@@ -127,7 +168,7 @@
127168
128$output = output(169$output = output(
129 sub {170 sub {
130 pt_fk_error_logger::main('h=127.1,P=12348,u=msandbox,p=msandbox',171 pt_fk_error_logger::main(@args, 'h=127.1,P=12348,u=msandbox,p=msandbox',
131 '--dest', 'h=127.1,P=12348,D=test,t=foreign_key_errors')172 '--dest', 'h=127.1,P=12348,D=test,t=foreign_key_errors')
132 },173 },
133 stderr => 1,174 stderr => 1,
@@ -142,6 +183,23 @@
142diag(`$trunk/sandbox/stop-sandbox 12348 >/dev/null`);183diag(`$trunk/sandbox/stop-sandbox 12348 >/dev/null`);
143184
144# #############################################################################185# #############################################################################
186# Test --pid
187# #############################################################################
188
189my $pid_file = "/tmp/pt-fk-error-log-test-$PID.pid";
190diag(`touch $pid_file`);
191
192$output = `$trunk/bin/pt-fk-error-logger h=127.1,P=12345,u=msandbox,p=msandbox --pid $pid_file --iterations 1 2>&1`;
193
194like(
195 $output,
196 qr{PID file $pid_file already exists},
197 'Dies if PID file already exists (--pid without --daemonize) (issue 391)'
198);
199
200unlink $pid_file;
201
202# #############################################################################
145# Done.203# Done.
146# #############################################################################204# #############################################################################
147$sb->wipe_clean($dbh);205$sb->wipe_clean($dbh);
148206
=== modified file 't/pt-fk-error-logger/get_fk_error.t'
--- t/pt-fk-error-logger/get_fk_error.t 2011-07-12 22:56:55 +0000
+++ t/pt-fk-error-logger/get_fk_error.t 2013-02-27 23:41:26 +0000
@@ -9,7 +9,7 @@
9use strict;9use strict;
10use warnings FATAL => 'all';10use warnings FATAL => 'all';
11use English qw(-no_match_vars);11use English qw(-no_match_vars);
12use Test::More tests => 10;12use Test::More;
1313
14use PerconaTest;14use PerconaTest;
15require "$trunk/bin/pt-fk-error-logger";15require "$trunk/bin/pt-fk-error-logger";
@@ -70,4 +70,4 @@
70# #############################################################################70# #############################################################################
71# Done.71# Done.
72# #############################################################################72# #############################################################################
73exit;73done_testing;
7474
=== removed file 't/pt-fk-error-logger/standard_options.t'
--- t/pt-fk-error-logger/standard_options.t 2011-07-12 22:56:55 +0000
+++ t/pt-fk-error-logger/standard_options.t 1970-01-01 00:00:00 +0000
@@ -1,32 +0,0 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More tests => 1;
13
14use PerconaTest;
15require "$trunk/bin/pt-fk-error-logger";
16
17# #########################################################################
18# Issue 391: Add --pid option to all scripts
19# #########################################################################
20`touch /tmp/mk-script.pid`;
21my $output = `$trunk/bin/pt-fk-error-logger h=127.1,P=12345,u=msandbox,p=msandbox --print --pid /tmp/mk-script.pid 2>&1`;
22like(
23 $output,
24 qr{PID file /tmp/mk-script.pid already exists},
25 'Dies if PID file already exists (--pid without --daemonize) (issue 391)'
26);
27`rm -rf /tmp/mk-script.pid`;
28
29# #############################################################################
30# Done.
31# #############################################################################
32exit;

Subscribers

People subscribed via source and target branches