Merge lp:~percona-toolkit-dev/percona-toolkit/pt-deadlock-logger-2.2 into lp:~percona-toolkit-dev/percona-toolkit/pt-fke-logger-2.2
- pt-deadlock-logger-2.2
- Merge into pt-fke-logger-2.2
Proposed by
Daniel Nichter
Status: | Merged |
---|---|
Merged at revision: | 534 |
Proposed branch: | lp:~percona-toolkit-dev/percona-toolkit/pt-deadlock-logger-2.2 |
Merge into: | lp:~percona-toolkit-dev/percona-toolkit/pt-fke-logger-2.2 |
Diff against target: |
1800 lines (+900/-452) 8 files modified
bin/pt-deadlock-logger (+724/-342) bin/pt-fk-error-logger (+2/-1) lib/Cxn.pm (+32/-14) 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) |
To merge this branch: | bzr merge lp:~percona-toolkit-dev/percona-toolkit/pt-deadlock-logger-2.2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel Nichter | Approve | ||
Review via email:
|
Commit message
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bin/pt-deadlock-logger' |
2 | --- bin/pt-deadlock-logger 2013-02-22 17:47:57 +0000 |
3 | +++ bin/pt-deadlock-logger 2013-02-26 01:08:24 +0000 |
4 | @@ -23,9 +23,11 @@ |
5 | VersionParser |
6 | Quoter |
7 | DSNParser |
8 | + Cxn |
9 | Daemon |
10 | HTTPMicro |
11 | VersionCheck |
12 | + Runtime |
13 | )); |
14 | } |
15 | |
16 | @@ -2439,6 +2441,177 @@ |
17 | # ########################################################################### |
18 | |
19 | # ########################################################################### |
20 | +# Cxn package |
21 | +# This package is a copy without comments from the original. The original |
22 | +# with comments and its test file can be found in the Bazaar repository at, |
23 | +# lib/Cxn.pm |
24 | +# t/lib/Cxn.t |
25 | +# See https://launchpad.net/percona-toolkit for more information. |
26 | +# ########################################################################### |
27 | +{ |
28 | +package Cxn; |
29 | + |
30 | +use strict; |
31 | +use warnings FATAL => 'all'; |
32 | +use English qw(-no_match_vars); |
33 | +use Scalar::Util qw(blessed); |
34 | +use constant { |
35 | + PTDEBUG => $ENV{PTDEBUG} || 0, |
36 | + PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0, |
37 | +}; |
38 | + |
39 | +sub new { |
40 | + my ( $class, %args ) = @_; |
41 | + my @required_args = qw(DSNParser OptionParser); |
42 | + foreach my $arg ( @required_args ) { |
43 | + die "I need a $arg argument" unless $args{$arg}; |
44 | + }; |
45 | + my ($dp, $o) = @args{@required_args}; |
46 | + |
47 | + my $dsn_defaults = $dp->parse_options($o); |
48 | + my $prev_dsn = $args{prev_dsn}; |
49 | + my $dsn = $args{dsn}; |
50 | + if ( !$dsn ) { |
51 | + $args{dsn_string} ||= 'h=' . ($dsn_defaults->{h} || 'localhost'); |
52 | + |
53 | + $dsn = $dp->parse( |
54 | + $args{dsn_string}, $prev_dsn, $dsn_defaults); |
55 | + } |
56 | + elsif ( $prev_dsn ) { |
57 | + $dsn = $dp->copy($prev_dsn, $dsn); |
58 | + } |
59 | + |
60 | + my $self = { |
61 | + dsn => $dsn, |
62 | + dbh => $args{dbh}, |
63 | + dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
64 | + hostname => '', |
65 | + set => $args{set}, |
66 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
67 | + dbh_set => 0, |
68 | + OptionParser => $o, |
69 | + DSNParser => $dp, |
70 | + is_cluster_node => undef, |
71 | + parent => $args{parent}, |
72 | + }; |
73 | + |
74 | + return bless $self, $class; |
75 | +} |
76 | + |
77 | +sub connect { |
78 | + my ( $self, %opts ) = @_; |
79 | + my $dsn = $self->{dsn}; |
80 | + my $dp = $self->{DSNParser}; |
81 | + my $o = $self->{OptionParser}; |
82 | + |
83 | + my $dbh = $self->{dbh}; |
84 | + if ( !$dbh || !$dbh->ping() ) { |
85 | + if ( $o->get('ask-pass') && !$self->{asked_for_pass} ) { |
86 | + $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); |
87 | + $self->{asked_for_pass} = 1; |
88 | + } |
89 | + $dbh = $dp->get_dbh( |
90 | + $dp->get_cxn_params($dsn), |
91 | + { |
92 | + AutoCommit => 1, |
93 | + %opts, |
94 | + }, |
95 | + ); |
96 | + } |
97 | + PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name}); |
98 | + |
99 | + return $self->set_dbh($dbh); |
100 | +} |
101 | + |
102 | +sub set_dbh { |
103 | + my ($self, $dbh) = @_; |
104 | + |
105 | + if ( $self->{dbh} && $self->{dbh} == $dbh && $self->{dbh_set} ) { |
106 | + PTDEBUG && _d($dbh, 'Already set dbh'); |
107 | + return $dbh; |
108 | + } |
109 | + |
110 | + PTDEBUG && _d($dbh, 'Setting dbh'); |
111 | + |
112 | + $dbh->{FetchHashKeyName} = 'NAME_lc' if $self->{NAME_lc}; |
113 | + |
114 | + my $sql = 'SELECT @@hostname, @@server_id'; |
115 | + PTDEBUG && _d($dbh, $sql); |
116 | + my ($hostname, $server_id) = $dbh->selectrow_array($sql); |
117 | + PTDEBUG && _d($dbh, 'hostname:', $hostname, $server_id); |
118 | + if ( $hostname ) { |
119 | + $self->{hostname} = $hostname; |
120 | + } |
121 | + |
122 | + if ( $self->{parent} ) { |
123 | + PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent'); |
124 | + $dbh->{InactiveDestroy} = 1; |
125 | + } |
126 | + |
127 | + if ( my $set = $self->{set}) { |
128 | + $set->($dbh); |
129 | + } |
130 | + |
131 | + $self->{dbh} = $dbh; |
132 | + $self->{dbh_set} = 1; |
133 | + return $dbh; |
134 | +} |
135 | + |
136 | +sub lost_connection { |
137 | + my ($self, $e) = @_; |
138 | + return 0 unless $e; |
139 | + return $e =~ m/MySQL server has gone away/ |
140 | + || $e =~ m/Lost connection to MySQL server/; |
141 | +} |
142 | + |
143 | +sub dbh { |
144 | + my ($self) = @_; |
145 | + return $self->{dbh}; |
146 | +} |
147 | + |
148 | +sub dsn { |
149 | + my ($self) = @_; |
150 | + return $self->{dsn}; |
151 | +} |
152 | + |
153 | +sub name { |
154 | + my ($self) = @_; |
155 | + return $self->{dsn_name} if PERCONA_TOOLKIT_TEST_USE_DSN_NAMES; |
156 | + return $self->{hostname} || $self->{dsn_name} || 'unknown host'; |
157 | +} |
158 | + |
159 | +sub DESTROY { |
160 | + my ($self) = @_; |
161 | + |
162 | + if ( $self->{parent} ) { |
163 | + PTDEBUG && _d('Not disconnecting dbh in parent'); |
164 | + } |
165 | + elsif ( $self->{dbh} |
166 | + && blessed($self->{dbh}) |
167 | + && $self->{dbh}->can("disconnect") ) |
168 | + { |
169 | + PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name}); |
170 | + $self->{dbh}->disconnect(); |
171 | + } |
172 | + |
173 | + return; |
174 | +} |
175 | + |
176 | +sub _d { |
177 | + my ($package, undef, $line) = caller 0; |
178 | + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
179 | + map { defined $_ ? $_ : 'undef' } |
180 | + @_; |
181 | + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; |
182 | +} |
183 | + |
184 | +1; |
185 | +} |
186 | +# ########################################################################### |
187 | +# End Cxn package |
188 | +# ########################################################################### |
189 | + |
190 | +# ########################################################################### |
191 | # Daemon package |
192 | # This package is a copy without comments from the original. The original |
193 | # with comments and its test file can be found in the Bazaar repository at, |
194 | @@ -3865,6 +4038,139 @@ |
195 | # ########################################################################### |
196 | |
197 | # ########################################################################### |
198 | +# Runtime package |
199 | +# This package is a copy without comments from the original. The original |
200 | +# with comments and its test file can be found in the Bazaar repository at, |
201 | +# lib/Runtime.pm |
202 | +# t/lib/Runtime.t |
203 | +# See https://launchpad.net/percona-toolkit for more information. |
204 | +# ########################################################################### |
205 | +{ |
206 | +package Runtime; |
207 | + |
208 | +use strict; |
209 | +use warnings FATAL => 'all'; |
210 | +use English qw(-no_match_vars); |
211 | +use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
212 | + |
213 | +sub new { |
214 | + my ( $class, %args ) = @_; |
215 | + my @required_args = qw(run_time now); |
216 | + foreach my $arg ( @required_args ) { |
217 | + die "I need a $arg argument" unless exists $args{$arg}; |
218 | + } |
219 | + |
220 | + my $run_time = $args{run_time}; |
221 | + if ( defined $run_time ) { |
222 | + die "run_time must be > 0" if $run_time <= 0; |
223 | + } |
224 | + |
225 | + my $now = $args{now}; |
226 | + die "now must be a callback" unless ref $now eq 'CODE'; |
227 | + |
228 | + my $self = { |
229 | + run_time => $run_time, |
230 | + now => $now, |
231 | + start_time => undef, |
232 | + end_time => undef, |
233 | + time_left => undef, |
234 | + stop => 0, |
235 | + }; |
236 | + |
237 | + return bless $self, $class; |
238 | +} |
239 | + |
240 | +sub time_left { |
241 | + my ( $self, %args ) = @_; |
242 | + |
243 | + if ( $self->{stop} ) { |
244 | + PTDEBUG && _d("No time left because stop was called"); |
245 | + return 0; |
246 | + } |
247 | + |
248 | + my $now = $self->{now}->(%args); |
249 | + PTDEBUG && _d("Current time:", $now); |
250 | + |
251 | + if ( !defined $self->{start_time} ) { |
252 | + $self->{start_time} = $now; |
253 | + } |
254 | + |
255 | + return unless defined $now; |
256 | + |
257 | + my $run_time = $self->{run_time}; |
258 | + return unless defined $run_time; |
259 | + |
260 | + if ( !$self->{end_time} ) { |
261 | + $self->{end_time} = $now + $run_time; |
262 | + PTDEBUG && _d("End time:", $self->{end_time}); |
263 | + } |
264 | + |
265 | + $self->{time_left} = $self->{end_time} - $now; |
266 | + PTDEBUG && _d("Time left:", $self->{time_left}); |
267 | + return $self->{time_left}; |
268 | +} |
269 | + |
270 | +sub have_time { |
271 | + my ( $self, %args ) = @_; |
272 | + my $time_left = $self->time_left(%args); |
273 | + return 1 if !defined $time_left; # run forever |
274 | + return $time_left <= 0 ? 0 : 1; # <=0s means run time has elapsed |
275 | +} |
276 | + |
277 | +sub time_elapsed { |
278 | + my ( $self, %args ) = @_; |
279 | + |
280 | + my $start_time = $self->{start_time}; |
281 | + return 0 unless $start_time; |
282 | + |
283 | + my $now = $self->{now}->(%args); |
284 | + PTDEBUG && _d("Current time:", $now); |
285 | + |
286 | + my $time_elapsed = $now - $start_time; |
287 | + PTDEBUG && _d("Time elapsed:", $time_elapsed); |
288 | + if ( $time_elapsed < 0 ) { |
289 | + warn "Current time $now is earlier than start time $start_time"; |
290 | + } |
291 | + return $time_elapsed; |
292 | +} |
293 | + |
294 | +sub reset { |
295 | + my ( $self ) = @_; |
296 | + $self->{start_time} = undef; |
297 | + $self->{end_time} = undef; |
298 | + $self->{time_left} = undef; |
299 | + $self->{stop} = 0; |
300 | + PTDEBUG && _d("Reset run time"); |
301 | + return; |
302 | +} |
303 | + |
304 | +sub stop { |
305 | + my ( $self ) = @_; |
306 | + $self->{stop} = 1; |
307 | + return; |
308 | +} |
309 | + |
310 | +sub start { |
311 | + my ( $self ) = @_; |
312 | + $self->{stop} = 0; |
313 | + return; |
314 | +} |
315 | + |
316 | +sub _d { |
317 | + my ($package, undef, $line) = caller 0; |
318 | + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
319 | + map { defined $_ ? $_ : 'undef' } |
320 | + @_; |
321 | + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; |
322 | +} |
323 | + |
324 | +1; |
325 | +} |
326 | +# ########################################################################### |
327 | +# End Runtime package |
328 | +# ########################################################################### |
329 | + |
330 | +# ########################################################################### |
331 | # This is a combination of modules and programs in one -- a runnable module. |
332 | # http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last |
333 | # Or, look it up in the Camel book on pages 642 and 643 in the 3rd edition. |
334 | @@ -3877,21 +4183,15 @@ |
335 | use English qw(-no_match_vars); |
336 | use List::Util qw(max); |
337 | use Socket qw(inet_aton); |
338 | -use sigtrap qw(handler finish untrapped normal-signals); |
339 | +use Time::HiRes qw(sleep); |
340 | +use File::Temp qw(tempfile); |
341 | +use File::Spec; |
342 | + |
343 | +use sigtrap 'handler', \&sig_int, 'normal-signals'; |
344 | |
345 | use Percona::Toolkit; |
346 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
347 | |
348 | -my $o; |
349 | -my $oktorun; |
350 | -my $dp; |
351 | - |
352 | -# ######################################################################## |
353 | -# Configuration info. |
354 | -# ######################################################################## |
355 | -my $source_dsn; |
356 | -my $dest_dsn; |
357 | - |
358 | # Some common patterns and variables |
359 | my $d = qr/(\d+)/; # Digit |
360 | my $t = qr/((?:\d+ \d+)|(?:[A-Fa-f0-9]+))/; # Transaction ID |
361 | @@ -3934,48 +4234,54 @@ |
362 | 'update' => 1, |
363 | ); |
364 | |
365 | +my $oktorun = 1; |
366 | +my $exit_status = 0; |
367 | + |
368 | sub main { |
369 | - local @ARGV = @_; # set global ARGV for this package |
370 | - |
371 | - my $q = new Quoter(); |
372 | + local @ARGV = @_; # set global ARGV for this package |
373 | + $oktorun = 1; |
374 | + $exit_status = 0; |
375 | |
376 | # ######################################################################## |
377 | # Get configuration information. |
378 | # ######################################################################## |
379 | - $o = new OptionParser(); |
380 | + my $o = new OptionParser(); |
381 | $o->get_specs(); |
382 | $o->get_opts(); |
383 | |
384 | - $o->set('collapse', $o->get('print')) unless $o->got('collapse'); |
385 | - |
386 | - $dp = $o->DSNParser(); |
387 | - my $dsn_defaults = $dp->parse_options($o); |
388 | - $source_dsn = @ARGV ? $dp->parse(shift @ARGV,$dsn_defaults) : $dsn_defaults; |
389 | - $dest_dsn = $o->get('dest'); |
390 | - |
391 | - # The source dsn is not an option so --dest cannot use OptionParser |
392 | - # to inherit values from it. Thus, we do it manually. --dest will |
393 | - # inherit from --user, --port, etc. |
394 | - if ( $source_dsn && $dest_dsn ) { |
395 | - # If dest DSN only has D and t, this will copy h, P, S, etc. |
396 | - # from the source DSN. |
397 | - $dest_dsn = $dp->copy($source_dsn, $dest_dsn); |
398 | + my $dp = $o->DSNParser(); |
399 | + $dp->prop('set-vars', $o->get('set-vars')); |
400 | + |
401 | + my $src; |
402 | + if ( my $src_dsn_string = shift @ARGV ) { |
403 | + $src = Cxn->new( |
404 | + dsn_string => $src_dsn_string, |
405 | + DSNParser => $dp, |
406 | + OptionParser => $o, |
407 | + parent => $o->get('daemonize'), |
408 | + ); |
409 | + } |
410 | + |
411 | + my $dst; |
412 | + if ( my $dst_dsn = $o->get('dest') ) { |
413 | + $dst = Cxn->new( |
414 | + dsn => $dst_dsn, |
415 | + prev_dsn => ($src ? $src->dsn : undef), |
416 | + DSNParser => $dp, |
417 | + OptionParser => $o, |
418 | + parent => $o->get('daemonize'), |
419 | + ); |
420 | } |
421 | |
422 | if ( !$o->get('help') ) { |
423 | - if ( !$source_dsn ) { |
424 | - $o->save_error('Missing or invalid source host'); |
425 | - } |
426 | - if ( $dest_dsn && !$dest_dsn->{D} ) { |
427 | - $o->save_error("--dest requires a 'D' (database) part"); |
428 | - } |
429 | - if ( $dest_dsn && !$dest_dsn->{t} ) { |
430 | - $o->save_error("--dest requires a 't' (table) part"); |
431 | - } |
432 | - |
433 | - # Avoid running forever with zero second interval. |
434 | - if ( $o->get('run-time') && !$o->get('interval') ) { |
435 | - $o->set('interval', 1); |
436 | + if ( !$src ) { |
437 | + $o->save_error('No DSN was specified.'); |
438 | + } |
439 | + if ( $dst && !$dst->dsn->{D} ) { |
440 | + $o->save_error("--dest requires a 'D' (database) part."); |
441 | + } |
442 | + if ( $dst && !$dst->dsn->{t} ) { |
443 | + $o->save_error("--dest requires a 't' (table) part."); |
444 | } |
445 | } |
446 | |
447 | @@ -3984,43 +4290,29 @@ |
448 | # ######################################################################## |
449 | # Connect to MySQL and set up the --dest, if any. |
450 | # ######################################################################## |
451 | - my $dbh = get_cxn($source_dsn, 1); |
452 | - my $dest_dbh; |
453 | - my $sth; |
454 | + my $q = new Quoter(); |
455 | + |
456 | + $src->connect(); |
457 | + |
458 | + my @cols = @{ $o->get('columns') }; |
459 | my $ins_sth; |
460 | - |
461 | - # Since the user might not have specified a hostname for the connection, |
462 | - # try to extract it from the $dbh |
463 | - if ( !$source_dsn->{h} ) { |
464 | - ($source_dsn->{h}) = $dbh->{mysql_hostinfo} =~ m/(\w+) via/; |
465 | - PTDEBUG && _d('Got source host from dbh:', $source_dsn->{h}); |
466 | - } |
467 | - |
468 | - my @cols = qw( server ts thread txn_id txn_time user hostname ip db tbl idx |
469 | - lock_type lock_mode wait_hold victim query ); |
470 | - if ( $o->got('columns') ) { |
471 | - @cols = grep { $o->get('columns')->{$_} } @cols; |
472 | - } |
473 | - |
474 | - if ( $dest_dsn ) { |
475 | - my $db_tbl = |
476 | - join('.', |
477 | - map { $q->quote($_) } |
478 | - grep { $_ } |
479 | - ( $dest_dsn->{D}, $dest_dsn->{t} )); |
480 | - $dest_dbh = get_cxn($dest_dsn, 0); |
481 | + my $ins_sql; |
482 | + if ( $dst ) { |
483 | + $dst->connect(AutoCommit => 0); |
484 | + |
485 | + my $db_tbl = $q->join_quote($dst->dsn->{D}, $dst->dsn->{t}); |
486 | my $cols = join(',', map { $q->quote($_) } @cols); |
487 | - my $parms = join(',', map { '?' } @cols); |
488 | - my $sql = "INSERT IGNORE INTO $db_tbl($cols) VALUES($parms)"; |
489 | - PTDEBUG && _d($sql); |
490 | - $ins_sth = $dest_dbh->prepare($sql); |
491 | + my $parms = join(',', map { '?' } @cols); |
492 | + $ins_sql = "INSERT IGNORE INTO $db_tbl ($cols) VALUES ($parms) " |
493 | + . "/* pt-deadlock-logger */"; |
494 | + PTDEBUG && _d($ins_sql); |
495 | + $ins_sth = $dst->dbh->prepare($ins_sql); |
496 | |
497 | if ( $o->get('create-dest-table') ) { |
498 | - my $db_tbl = $q->quote($dest_dsn->{D}, $dest_dsn->{t}); |
499 | - $sql = $o->read_para_after(__FILE__, qr/MAGIC_dest_table/); |
500 | - $sql =~ s/deadlocks/IF NOT EXISTS $db_tbl/; |
501 | + my $sql = $o->read_para_after(__FILE__, qr/MAGIC_dest_table/); |
502 | + $sql =~ s/deadlocks/IF NOT EXISTS $db_tbl/; |
503 | PTDEBUG && _d($sql); |
504 | - $dest_dbh->do($sql); |
505 | + $dst->dbh->do($sql); |
506 | } |
507 | } |
508 | |
509 | @@ -4039,6 +4331,10 @@ |
510 | $daemon->make_PID_file(); |
511 | } |
512 | |
513 | + # Let Cxn::DESTROY() disconenct the dbh. |
514 | + $src->{parent} = 0; |
515 | + $dst->{parent} = 0 if $dst; |
516 | + |
517 | # ######################################################################## |
518 | # Do the version-check |
519 | # ######################################################################## |
520 | @@ -4046,117 +4342,155 @@ |
521 | VersionCheck::version_check( |
522 | force => $o->got('version-check'), |
523 | instances => [ |
524 | - { dbh => $dbh, dsn => $source_dsn }, |
525 | - ($dest_dsn ? { dbh => $dest_dsn, dsn => $dest_dsn } : ()), |
526 | + { dbh => $src->dbh, dsn => $src->dsn }, |
527 | + ($dst ? { dbh => $dst->dbh, dsn => $dst->dsn } : ()) |
528 | ], |
529 | ); |
530 | } |
531 | |
532 | # ######################################################################## |
533 | + # Set upt the --clear-deadlocks table. |
534 | + # ######################################################################## |
535 | + my $clear_deadlocks_table_def; |
536 | + my $clear_deadlocks_table = $o->get('clear-deadlocks'); |
537 | + if ( $clear_deadlocks_table ) { |
538 | + $clear_deadlocks_table_def |
539 | + = $o->read_para_after(__FILE__, qr/MAGIC_clear_deadlocks/); |
540 | + if ( VersionParser->new($src->dbh) < '4.1.2') { |
541 | + $clear_deadlocks_table_def =~ s/ENGINE=/TYPE=/; |
542 | + } |
543 | + $clear_deadlocks_table_def |
544 | + =~ s/percona_schema.clear_deadlocks/$clear_deadlocks_table/; |
545 | + PTDEBUG && _d('--clear-deadlocks table:', $clear_deadlocks_table_def); |
546 | + } |
547 | + |
548 | + # ######################################################################## |
549 | # Start looking for and logging deadlocks. |
550 | # ######################################################################## |
551 | - my $last_fingerprint = ''; |
552 | - |
553 | - $oktorun = 1; |
554 | - my $start = time(); |
555 | - my $end = $start + ($o->get('run-time') || 0); # When we should exit |
556 | - my $now = $start; |
557 | - while ( # Quit if: |
558 | - ($start == $end || $now < $end) # time is exceeded |
559 | - && $oktorun # or instructed to quit |
560 | - ) |
561 | - { |
562 | - my $text = $dbh->selectrow_hashref("SHOW /*!40100 ENGINE*/ INNODB STATUS")->{Status}; |
563 | - my $parse_deadlocks_options = { |
564 | - 'numeric-ip' => $o->got('numeric-ip'), |
565 | - 'collapse' => $o->got('collapse'), |
566 | + my $sep = $o->get('tab') ? "\t" : ' '; |
567 | + my $last_fingerprint = ''; |
568 | + my $parse_deadlocks_options = { |
569 | + 'server' => $src->dsn->{h} || $src->{hostname}, |
570 | + 'numeric-ip' => $o->got('numeric-ip'), |
571 | + }; |
572 | + |
573 | + my $run_time = Runtime->new( |
574 | + run_time => $o->get('run-time'), |
575 | + now => sub { return time }, |
576 | + ); |
577 | + |
578 | + my $interval = $o->get('interval'); |
579 | + my $iters = $o->get('iterations'); |
580 | + PTDEBUG && _d('iterations:', $iters, 'interval:', $interval); |
581 | + |
582 | + ITERATION: |
583 | + while ( |
584 | + $oktorun |
585 | + && $run_time->have_time() |
586 | + && (!defined $iters || $iters--) |
587 | + ) { |
588 | + |
589 | + my %txns; |
590 | + my $fingerprint; |
591 | + eval { |
592 | + my $sql = "SHOW /*!40100 ENGINE*/ INNODB STATUS " |
593 | + . "/* pt-deadlock-logger */"; |
594 | + my $text = $src->dbh->selectrow_hashref($sql)->{status}; |
595 | + |
596 | + %txns = %{parse_deadlocks($text, $parse_deadlocks_options)}; |
597 | + $fingerprint = fingerprint(\%txns); |
598 | }; |
599 | - my %txns = %{parse_deadlocks($text, $parse_deadlocks_options)}; |
600 | - my $fingerprint = fingerprint(\%txns); |
601 | - |
602 | - if ( $ins_sth ) { |
603 | - foreach my $txn (sort { $a->{thread} <=> $b->{thread} } values %txns) { |
604 | - $ins_sth->execute(@{$txn}{@cols}); |
605 | - } |
606 | - $dest_dbh->commit(); |
607 | + if ( my $e = $EVAL_ERROR ) { |
608 | + PTDEBUG && _d('Error getting InnoDB status:', $e); |
609 | + if ( $src->lost_connection($e) ) { |
610 | + eval { $src->connect() }; |
611 | + if ( $EVAL_ERROR ) { |
612 | + warn "Lost connection to " . $src->name . ". Will try " |
613 | + . "to reconnect in the next iteration.\n"; |
614 | + } |
615 | + else { |
616 | + PTDEBUG && _d('Reconnected to MySQL'); |
617 | + redo ITERATION; |
618 | + } |
619 | + } |
620 | + else { |
621 | + warn "Error getting SHOW ENGINE INNODB STATUS: $EVAL_ERROR"; |
622 | + $exit_status |= 1; |
623 | + } |
624 | } |
625 | + else { |
626 | + if ( $ins_sth ) { |
627 | + eval { |
628 | + PTDEBUG && _d('Saving deadlock to --dest'); |
629 | + foreach my $txn ( |
630 | + sort { $a->{thread} <=> $b->{thread} } values %txns |
631 | + ) { |
632 | + $ins_sth->execute(@{$txn}{@cols}); |
633 | + } |
634 | + $dst->dbh->commit(); |
635 | + }; |
636 | + if ( my $e = $EVAL_ERROR ) { |
637 | + PTDEBUG && _d('Error saving to --dest:', $e); |
638 | + if ( $dst->lost_connection($e) ) { |
639 | + eval { |
640 | + $ins_sth->finish() if $ins_sth; |
641 | + $dst->dbh->disconnect() if $dst->dbh; |
642 | + $dst->connect(AutoCommit => 0); |
643 | + $ins_sth = $dst->dbh->prepare($ins_sql); |
644 | + }; |
645 | + if ( $EVAL_ERROR ) { |
646 | + warn "Lost connection to " . $dst->name . ". Will try " |
647 | + . "to reconnect in the next iteration.\n"; |
648 | + } |
649 | + else { |
650 | + PTDEBUG && _d('Reconnected to MySQL (--dest)'); |
651 | + redo ITERATION; |
652 | + } |
653 | + } |
654 | + else { |
655 | + warn "Error saving to --dest: $EVAL_ERROR"; |
656 | + $exit_status |= 1; |
657 | + } |
658 | + } |
659 | + } |
660 | |
661 | - if ( $fingerprint ne $last_fingerprint ) { |
662 | - PTDEBUG && _d('New deadlock'); |
663 | - if ( $o->got('print') || !$o->got('dest') ) { |
664 | - my $sep = $o->get('tab') ? "\t" : ' '; |
665 | - print join($sep, @cols), "\n"; |
666 | - foreach my $txn (sort {$a->{thread}<=>$b->{thread}} values %txns) { |
667 | - # If 'collapse' is on, it's already been taken care of, |
668 | - # but if it's unset, by default strip whitespace. |
669 | - if ( !$o->got('collapse') ) { |
670 | + if ( $fingerprint ne $last_fingerprint ) { |
671 | + PTDEBUG && _d('New deadlock'); |
672 | + if ( !$o->get('quiet') ) { |
673 | + print join($sep, @cols), "\n"; |
674 | + foreach my $txn ( |
675 | + sort { $a->{thread} <=> $b->{thread} } values %txns |
676 | + ) { |
677 | $txn->{query} =~ s/\s+/ /g; |
678 | + print join($sep, map { $txn->{$_} } @cols), "\n"; |
679 | } |
680 | - print join($sep, map { $txn->{$_} } @cols), "\n"; |
681 | } |
682 | } |
683 | - } |
684 | - else { |
685 | - PTDEBUG && _d('Same deadlock, not printing'); |
686 | - } |
687 | - # Save deadlock's fingerprint for next interval. |
688 | - $last_fingerprint = $fingerprint; |
689 | - |
690 | - # If specified, clear the deadlock... |
691 | - if ( my $db_tbl = $o->get('clear-deadlocks') ) { |
692 | - PTDEBUG && _d('Creating --clear-deadlocks table', $db_tbl); |
693 | - $dbh->{AutoCommit} = 0; |
694 | - my $sql = $o->read_para_after(__FILE__, qr/MAGIC_clear_deadlocks/); |
695 | - |
696 | - if ( VersionParser->new($dbh) < '4.1.2') { |
697 | - $sql =~ s/ENGINE=/TYPE=/; |
698 | - } |
699 | - $sql =~ s/test.deadlock_maker/$db_tbl/; |
700 | - PTDEBUG && _d($sql); |
701 | - $dbh->do($sql); |
702 | - $sql = "INSERT INTO $db_tbl(a) VALUES(1)"; |
703 | - PTDEBUG && _d($sql); |
704 | - $dbh->do($sql); # I'm holding locks on the table now. |
705 | - |
706 | - # Fork off a child to try to take a lock on the table. |
707 | - my $pid = fork(); |
708 | - if ( defined($pid) && $pid == 0 ) { # I am a child |
709 | - my $dbh_child = get_cxn($source_dsn, 0); |
710 | - $sql = "SELECT * FROM $db_tbl FOR UPDATE"; |
711 | - PTDEBUG && _d($sql); |
712 | - eval { $dbh_child->do($sql); }; # Should block against parent. |
713 | - PTDEBUG && _d($EVAL_ERROR); # Parent inserted value 0. |
714 | - $sql = "COMMIT"; |
715 | - PTDEBUG && _d($sql); |
716 | - $dbh_child->do($sql); |
717 | - exit; |
718 | - } |
719 | - elsif ( !defined($pid) ) { |
720 | - die("Unable to fork for clearing deadlocks!\n"); |
721 | - } |
722 | - sleep 1; |
723 | - $sql = "INSERT INTO $db_tbl(a) VALUES(0)";# Will make child deadlock |
724 | - PTDEBUG && _d($sql); |
725 | - eval { $dbh->do($sql); }; |
726 | - PTDEBUG && _d($EVAL_ERROR); |
727 | - waitpid($pid, 0); |
728 | - $sql = "DROP TABLE $db_tbl"; |
729 | - PTDEBUG && _d($sql); |
730 | - $dbh->do($sql); |
731 | - } |
732 | - |
733 | - # If there's an --interval argument, run forever or till specified. |
734 | - # Otherwise just run once. |
735 | - if ( $o->get('interval') ) { |
736 | - sleep($o->get('interval')); |
737 | - $now = time(); |
738 | - } |
739 | - else { |
740 | - $oktorun = 0; |
741 | + else { |
742 | + PTDEBUG && _d('Same deadlock, not printing'); |
743 | + } |
744 | + |
745 | + $last_fingerprint = $fingerprint; |
746 | + |
747 | + if ( $clear_deadlocks_table ) { |
748 | + clear_deadlocks( |
749 | + dsn => $src->dsn, |
750 | + table => $clear_deadlocks_table, |
751 | + table_def => $clear_deadlocks_table_def, |
752 | + DSNParser => $dp, |
753 | + ); |
754 | + } |
755 | + } |
756 | + |
757 | + # Sleep if there's an --iteration left. |
758 | + if ( !defined $iters || $iters ) { |
759 | + PTDEBUG && _d('Sleeping', $interval, 'seconds'); |
760 | + sleep $interval; |
761 | } |
762 | } |
763 | |
764 | - return 0; |
765 | + PTDEBUG && _d('Done running, exiting', $exit_status); |
766 | + return $exit_status; |
767 | } |
768 | |
769 | # ############################################################################ |
770 | @@ -4244,7 +4578,7 @@ |
771 | |
772 | my ( $query_text ) = $body =~ m/\nMySQL thread id .*\n((?s).*)/; |
773 | $query_text =~ s/\s+$//; |
774 | - $query_text =~ s/\s+/ /g if $args->{'collapse'}; |
775 | + $query_text =~ s/\s+/ /g; |
776 | |
777 | @{$hash}{qw(thread hostname ip user query)} |
778 | = ($mysql_thread_id, $hostname, $ip, $user, $query_text); |
779 | @@ -4290,13 +4624,101 @@ |
780 | foreach my $txn ( values %txns ) { |
781 | $txn->{victim} = $txn->{id} == $victim ? 1 : 0; |
782 | $txn->{ts} = $ts; |
783 | - $txn->{server} = $source_dsn->{h} || ''; |
784 | + $txn->{server} = $args->{server} || ''; |
785 | $txn->{ip} = inet_aton($txn->{ip}) if $args->{'numeric-ip'}; |
786 | } |
787 | |
788 | return \%txns; |
789 | } |
790 | |
791 | +sub clear_deadlocks { |
792 | + my (%args) = @_; |
793 | + my @required_args = qw(dsn table table_def DSNParser); |
794 | + foreach my $arg ( @required_args ) { |
795 | + die "I need a $arg argument" unless $args{$arg}; |
796 | + } |
797 | + my $dsn = $args{dsn}; |
798 | + my $table = $args{table}; |
799 | + my $table_def = $args{table_def}; |
800 | + my $dp = $args{DSNParser}; |
801 | + PTDEBUG && _d('Clearing deadlocks with table', $table, $table_def); |
802 | + |
803 | + my $parent_dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit=>0 }); |
804 | + $parent_dbh->{AutoCommit} = 0; |
805 | + $parent_dbh->{InactiveDestroy} = 1; # because of forking |
806 | + |
807 | + # Create the deadlocks table. |
808 | + PTDEBUG && _d($table_def); |
809 | + $parent_dbh->do($table_def); |
810 | + |
811 | + # Get a lock on it. |
812 | + my $sql = "INSERT INTO $table (a) VALUES (1) " |
813 | + . "/* pt-deadlock-logger clear deadlocks parent */"; |
814 | + PTDEBUG && _d($sql); |
815 | + $parent_dbh->do($sql); |
816 | + |
817 | + my ($sync_fh, $sync_file) = tempfile( |
818 | + 'pt-deadlock-logger-clear-deadlocks.XXXXXXX', |
819 | + DIR => File::Spec->tmpdir(), |
820 | + ); |
821 | + PTDEBUG && _d('Sync file:', $sync_file); |
822 | + close $sync_fh; |
823 | + unlink $sync_file; |
824 | + |
825 | + # Fork a child to try to take a lock on the table. |
826 | + my $pid = fork(); |
827 | + if ( defined($pid) && $pid == 0 ) { |
828 | + # I am the child |
829 | + PTDEBUG && _d('Clear deadlocks child', $PID); |
830 | + my $child_dbh = $dp->get_dbh($dp->get_cxn_params($dsn), {AutoCommit=>0}); |
831 | + my $sql = "SELECT * FROM $table FOR UPDATE " |
832 | + . "/* pt-deadlock-logger clear deadlocks child */"; |
833 | + PTDEBUG && _d($sql); |
834 | + open my $fh, '>', $sync_file |
835 | + or die "Error creating $sync_file: $OS_ERROR"; |
836 | + close $fh; |
837 | + PTDEBUG && _d('Clear deadlocks child ready (child)'); |
838 | + eval { $child_dbh->do($sql); }; # Should block against parent. |
839 | + PTDEBUG && _d($EVAL_ERROR); # Parent inserted value 0. |
840 | + $child_dbh->commit(); |
841 | + $child_dbh->disconnect(); |
842 | + exit; |
843 | + } |
844 | + elsif ( !defined($pid) ) { |
845 | + die "Failed to fork for --clear-deadlocks: " . ($OS_ERROR || ''); |
846 | + } |
847 | + |
848 | + # Wait up to 10s for the child to connect and become ready. |
849 | + for ( 1..40 ) { |
850 | + last if -f $sync_file; |
851 | + PTDEBUG && _d('Waiting for the clear deadlocks child'); |
852 | + sleep 0.25; |
853 | + } |
854 | + PTDEBUG && _d('Clear deadlocks child ready (parent)'); |
855 | + sleep 0.25; # wait for child to exec its SELECT statement |
856 | + |
857 | + # Make the child deadlock. |
858 | + $sql = "INSERT INTO $table (a) VALUES (0) " |
859 | + . "/* pt-deadlock-logger clear deadlocks parent */"; |
860 | + PTDEBUG && _d($sql); |
861 | + eval { $parent_dbh->do($sql); }; |
862 | + PTDEBUG && _d($EVAL_ERROR); |
863 | + |
864 | + # Reap the child. |
865 | + waitpid($pid, 0); |
866 | + |
867 | + # Drop the table. |
868 | + $sql = "DROP TABLE IF EXISTS $table"; |
869 | + PTDEBUG && _d($sql); |
870 | + $parent_dbh->do($sql); |
871 | + |
872 | + $parent_dbh->disconnect(); |
873 | + |
874 | + unlink $sync_file; |
875 | + |
876 | + return; |
877 | +} |
878 | + |
879 | sub fingerprint { |
880 | my ( $txns ) = @_; |
881 | my $fingerprint = ''; |
882 | @@ -4308,21 +4730,11 @@ |
883 | return $fingerprint; |
884 | } |
885 | |
886 | -# Catches signals so the program can exit gracefully. |
887 | -sub finish { |
888 | - my ($signal) = @_; |
889 | - print STDERR "Exiting on SIG$signal.\n"; |
890 | +sub sig_int { |
891 | + my ( $signal ) = @_; |
892 | $oktorun = 0; |
893 | -} |
894 | - |
895 | -sub get_cxn { |
896 | - my ( $dsn, $ac ) = @_; |
897 | - if ( $o->get('ask-pass') ) { |
898 | - $dsn->{p} = OptionParser::prompt_noecho("Enter password: "); |
899 | - } |
900 | - my $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), {AutoCommit => $ac}); |
901 | - $dbh->{InactiveDestroy} = 1; # Because of forking. |
902 | - return $dbh; |
903 | + print STDERR "# Caught SIG$signal. Use 'kill -ABRT $PID' if " |
904 | + . "the tool does not exit normally in a few seconds.\n"; |
905 | } |
906 | |
907 | sub _d { |
908 | @@ -4347,32 +4759,28 @@ |
909 | |
910 | =head1 NAME |
911 | |
912 | -pt-deadlock-logger - Extract and log MySQL deadlock information. |
913 | +pt-deadlock-logger - Log MySQL deadlocks. |
914 | |
915 | =head1 SYNOPSIS |
916 | |
917 | -Usage: pt-deadlock-logger [OPTION...] SOURCE_DSN |
918 | - |
919 | -pt-deadlock-logger extracts and saves information about the most recent deadlock |
920 | -in a MySQL server. |
921 | - |
922 | -Print deadlocks on SOURCE_DSN: |
923 | - |
924 | - pt-deadlock-logger SOURCE_DSN |
925 | - |
926 | -Store deadlock information from SOURCE_DSN in test.deadlocks table on SOURCE_DSN |
927 | -(source and destination are the same host): |
928 | - |
929 | - pt-deadlock-logger SOURCE_DSN --dest D=test,t=deadlocks |
930 | - |
931 | -Store deadlock information from SOURCE_DSN in test.deadlocks table on DEST_DSN |
932 | -(source and destination are different hosts): |
933 | - |
934 | - pt-deadlock-logger SOURCE_DSN --dest DEST_DSN,D=test,t=deadlocks |
935 | - |
936 | -Daemonize and check for deadlocks on SOURCE_DSN every 30 seconds for 4 hours: |
937 | - |
938 | - pt-deadlock-logger SOURCE_DSN --dest D=test,t=deadlocks --daemonize --run-time 4h --interval 30s |
939 | +Usage: pt-deadlock-logger [OPTIONS] DSN |
940 | + |
941 | +pt-deadlock-logger logs information about MySQL deadlocks on the given |
942 | +DSN. Information is printed to C<STDOUT>, and it can also be saved to a |
943 | +table by specifying L<"--dest">. The tool runs for forever unless |
944 | +L<"--run-time"> or L<"--iterations"> is specified. |
945 | + |
946 | +Print deadlocks on host1: |
947 | + |
948 | + pt-fk-error-logger h=host1 |
949 | + |
950 | +Print deadlocks on host1 once then exit: |
951 | + |
952 | + pt-fk-error-logger h=host1 --iterations 1 |
953 | + |
954 | +Save deadlocks on host1 to percona_schema.fke on host2: |
955 | + |
956 | + pt-fk-error-logger h=host1 --dest h=host2,D=percona_schema,t=deadlocks |
957 | |
958 | =head1 RISKS |
959 | |
960 | @@ -4398,116 +4806,24 @@ |
961 | |
962 | =head1 DESCRIPTION |
963 | |
964 | -pt-deadlock-logger extracts deadlock data from a MySQL server. Currently only |
965 | -InnoDB deadlock information is available. You can print the information to |
966 | -standard output, store it in a database table, or both. If neither |
967 | -L<"--print"> nor L<"--dest"> are given, then the deadlock information is |
968 | -printed by default. If only L<"--dest"> is given, then the deadlock |
969 | -information is only stored. If both options are given, then the deadlock |
970 | -information is printed and stored. |
971 | - |
972 | -The source host can be specified using one of two methods. The first method is |
973 | -to use at least one of the standard connection-related command line options: |
974 | -L<"--defaults-file">, L<"--password">, L<"--host">, L<"--port">, L<"--socket"> |
975 | -or L<"--user">. These options only apply to the source host; they cannot be |
976 | -used to specify the destination host. |
977 | - |
978 | -The second method to specify the source host, or the optional destination host |
979 | -using L<"--dest">, is a DSN. A DSN is a special syntax that can be either just |
980 | -a hostname (like C<server.domain.com> or C<1.2.3.4>), or a |
981 | -C<key=value,key=value> string. Keys are a single letter: |
982 | - |
983 | - KEY MEANING |
984 | - === ======= |
985 | - h Connect to host |
986 | - P Port number to use for connection |
987 | - S Socket file to use for connection |
988 | - u User for login if not current user |
989 | - p Password to use when connecting |
990 | - F Only read default options from the given file |
991 | - |
992 | -If you omit any values from the destination host DSN, they are filled in with |
993 | -values from the source host, so you don't need to specify them in both places. |
994 | -C<pt-deadlock-logger> reads all normal MySQL option files, such as ~/.my.cnf, so |
995 | -you may not need to specify username, password and other common options at all. |
996 | +pt-deadlock-logger prints information about MySQL deadlocks by polling |
997 | +and parsing C<SHOW ENGINE INNODB STATUS>. When a new deadlock occurs, |
998 | +it's printed to C<STDOUT> and, if specified, saved to L<"--dest">. |
999 | + |
1000 | +Only new deadlocks are printed. A fingerprint for each deadlock is created |
1001 | +using the deadlock's server, ts, and thread values (even if these |
1002 | +columns are not specified by L<"--columns">). A deadlock is printed if |
1003 | +its fingerprint is different than the last deadlock's fingerprint. |
1004 | + |
1005 | +The L<"--dest"> statement uses C<INSERT IGNORE> to eliminate duplicate |
1006 | +deadlocks, so every deadlock is saved for every L<"--iterations">. |
1007 | |
1008 | =head1 OUTPUT |
1009 | |
1010 | -You can choose which columns are output and/or saved to L<"--dest"> with the |
1011 | -L<"--columns"> argument. The default columns are as follows: |
1012 | - |
1013 | -=over |
1014 | - |
1015 | -=item server |
1016 | - |
1017 | -The (source) server on which the deadlock occurred. This might be useful if |
1018 | -you're tracking deadlocks on many servers. |
1019 | - |
1020 | -=item ts |
1021 | - |
1022 | -The date and time of the last detected deadlock. |
1023 | - |
1024 | -=item thread |
1025 | - |
1026 | -The MySQL thread number, which is the same as the connection ID in SHOW FULL |
1027 | -PROCESSLIST. |
1028 | - |
1029 | -=item txn_id |
1030 | - |
1031 | -The InnoDB transaction ID, which InnoDB expresses as two unsigned integers. I |
1032 | -have multiplied them out to be one number. |
1033 | - |
1034 | -=item txn_time |
1035 | - |
1036 | -How long the transaction was active when the deadlock happened. |
1037 | - |
1038 | -=item user |
1039 | - |
1040 | -The connection's database username. |
1041 | - |
1042 | -=item hostname |
1043 | - |
1044 | -The connection's host. |
1045 | - |
1046 | -=item ip |
1047 | - |
1048 | -The connection's IP address. If you specify L<"--numeric-ip">, this is |
1049 | -converted to an unsigned integer. |
1050 | - |
1051 | -=item db |
1052 | - |
1053 | -The database in which the deadlock occurred. |
1054 | - |
1055 | -=item tbl |
1056 | - |
1057 | -The table on which the deadlock occurred. |
1058 | - |
1059 | -=item idx |
1060 | - |
1061 | -The index on which the deadlock occurred. |
1062 | - |
1063 | -=item lock_type |
1064 | - |
1065 | -The lock type the transaction held on the lock that caused the deadlock. |
1066 | - |
1067 | -=item lock_mode |
1068 | - |
1069 | -The lock mode of the lock that caused the deadlock. |
1070 | - |
1071 | -=item wait_hold |
1072 | - |
1073 | -Whether the transaction was waiting for the lock or holding the lock. Usually |
1074 | -you will see the two waited-for locks. |
1075 | - |
1076 | -=item victim |
1077 | - |
1078 | -Whether the transaction was selected as the deadlock victim and rolled back. |
1079 | - |
1080 | -=item query |
1081 | - |
1082 | -The query that caused the deadlock. |
1083 | - |
1084 | -=back |
1085 | +New deadlocks are printed to C<STDOUT>, unless L<"--quiet"> is specified. |
1086 | +Errors and warnings are printed to C<STDERR>. |
1087 | + |
1088 | +See also L<"--columns"> and L<"--tab">. |
1089 | |
1090 | =head1 INNODB CAVEATS AND DETAILS |
1091 | |
1092 | @@ -4562,24 +4878,89 @@ |
1093 | C<SHOW INNODB STATUS>. The table must not exist. pt-deadlock-logger will |
1094 | create it with the following MAGIC_clear_deadlocks structure: |
1095 | |
1096 | - CREATE TABLE test.deadlock_maker(a INT PRIMARY KEY) ENGINE=InnoDB; |
1097 | + CREATE TABLE percona_schema.clear_deadlocks (a INT PRIMARY KEY) ENGINE=InnoDB; |
1098 | |
1099 | After creating the table and causing a small deadlock, the tool will drop the |
1100 | table again. |
1101 | |
1102 | -=item --[no]collapse |
1103 | - |
1104 | -Collapse whitespace in queries to a single space. This might make it easier to |
1105 | -inspect on the command line or in a query. By default, whitespace is collapsed |
1106 | -when printing with L<"--print">, but not modified when storing to L<"--dest">. |
1107 | -(That is, the default is different for each action). |
1108 | - |
1109 | =item --columns |
1110 | |
1111 | -type: hash |
1112 | - |
1113 | -Output only this comma-separated list of columns. See L<"OUTPUT"> for more |
1114 | -details on columns. |
1115 | +type: Array; default: server, ts, thread, txn_id, txn_time, user, hostname, ip, db, tbl, idx, lock_type, lock_mode, wait_hold, victim, query |
1116 | + |
1117 | +The columns are: |
1118 | + |
1119 | +=over |
1120 | + |
1121 | +=item server |
1122 | + |
1123 | +The (source) server on which the deadlock occurred. This might be useful if |
1124 | +you're tracking deadlocks on many servers. |
1125 | + |
1126 | +=item ts |
1127 | + |
1128 | +The date and time of the last detected deadlock. |
1129 | + |
1130 | +=item thread |
1131 | + |
1132 | +The MySQL thread number, which is the same as the connection ID in SHOW FULL |
1133 | +PROCESSLIST. |
1134 | + |
1135 | +=item txn_id |
1136 | + |
1137 | +The InnoDB transaction ID, which InnoDB expresses as two unsigned integers. I |
1138 | +have multiplied them out to be one number. |
1139 | + |
1140 | +=item txn_time |
1141 | + |
1142 | +How long the transaction was active when the deadlock happened. |
1143 | + |
1144 | +=item user |
1145 | + |
1146 | +The connection's database username. |
1147 | + |
1148 | +=item hostname |
1149 | + |
1150 | +The connection's host. |
1151 | + |
1152 | +=item ip |
1153 | + |
1154 | +The connection's IP address. If you specify L<"--numeric-ip">, this is |
1155 | +converted to an unsigned integer. |
1156 | + |
1157 | +=item db |
1158 | + |
1159 | +The database in which the deadlock occurred. |
1160 | + |
1161 | +=item tbl |
1162 | + |
1163 | +The table on which the deadlock occurred. |
1164 | + |
1165 | +=item idx |
1166 | + |
1167 | +The index on which the deadlock occurred. |
1168 | + |
1169 | +=item lock_type |
1170 | + |
1171 | +The lock type the transaction held on the lock that caused the deadlock. |
1172 | + |
1173 | +=item lock_mode |
1174 | + |
1175 | +The lock mode of the lock that caused the deadlock. |
1176 | + |
1177 | +=item wait_hold |
1178 | + |
1179 | +Whether the transaction was waiting for the lock or holding the lock. Usually |
1180 | +you will see the two waited-for locks. |
1181 | + |
1182 | +=item victim |
1183 | + |
1184 | +Whether the transaction was selected as the deadlock victim and rolled back. |
1185 | + |
1186 | +=item query |
1187 | + |
1188 | +The query that caused the deadlock. |
1189 | + |
1190 | +=back |
1191 | |
1192 | =item --config |
1193 | |
1194 | @@ -4617,9 +4998,6 @@ |
1195 | can usually omit most parts of this argument if you're storing deadlocks on the |
1196 | same server on which they happen. |
1197 | |
1198 | -By default, whitespace in the query column is left intact; |
1199 | -use L<"--[no]collapse"> if you want whitespace collapsed. |
1200 | - |
1201 | The following MAGIC_dest_table is suggested if you want to store all the |
1202 | information pt-deadlock-logger can extract about deadlocks: |
1203 | |
1204 | @@ -4658,12 +5036,23 @@ |
1205 | |
1206 | =item --interval |
1207 | |
1208 | -type: time |
1209 | +type: time; default: 30 |
1210 | |
1211 | How often to check for deadlocks. If no L<"--run-time"> is specified, |
1212 | pt-deadlock-logger runs forever, checking for deadlocks at every interval. |
1213 | See also L<"--run-time">. |
1214 | |
1215 | +=item --iterations |
1216 | + |
1217 | +type: int |
1218 | + |
1219 | +How many times to check for deadlocks. By default, this option |
1220 | +is undefined which means an infinite number of iterations. The tool always |
1221 | +exits for L<"--run-time">, regardless of the value specified for this option. |
1222 | +For example, the tool will exit after 1 minute with |
1223 | +C<--run-time 1m --iterations 4 --interval 30> because 4 iterations at 30 |
1224 | +second intervals would take 2 minutes, longer than the 1 mintue run-time. |
1225 | + |
1226 | =item --log |
1227 | |
1228 | type: string |
1229 | @@ -4695,16 +5084,9 @@ |
1230 | |
1231 | Port number to use for connection. |
1232 | |
1233 | -=item --print |
1234 | - |
1235 | -Print results on standard output. See L<"OUTPUT"> for more. By default, |
1236 | -enables L<"--[no]collapse"> unless you explicitly disable it. |
1237 | - |
1238 | -If L<"--interval"> or L<"--run-time"> is specified, only new deadlocks are |
1239 | -printed at each interval. A fingerprint for each deadlock is created using |
1240 | -L<"--columns"> server, ts and thread (even if those columns were not specified |
1241 | -by L<"--columns">) and if the current deadlock's fingerprint is different from |
1242 | -the last deadlock's fingerprint, then it is printed. |
1243 | +=item --quiet |
1244 | + |
1245 | +Do not deadlocks; only print errors and warnings to C<STDERR>. |
1246 | |
1247 | =item --run-time |
1248 | |
1249 | @@ -4729,7 +5111,7 @@ |
1250 | |
1251 | =item --tab |
1252 | |
1253 | -Print tab-separated columns, instead of aligned. |
1254 | +Use tabs to separate columns instead of spaces. |
1255 | |
1256 | =item --user |
1257 | |
1258 | @@ -4886,7 +5268,7 @@ |
1259 | |
1260 | =head1 AUTHORS |
1261 | |
1262 | -Baron Schwartz |
1263 | +Baron Schwartz and Daniel Nichter |
1264 | |
1265 | =head1 ABOUT PERCONA TOOLKIT |
1266 | |
1267 | |
1268 | === modified file 'bin/pt-fk-error-logger' |
1269 | --- bin/pt-fk-error-logger 2013-02-25 18:55:06 +0000 |
1270 | +++ bin/pt-fk-error-logger 2013-02-26 01:08:24 +0000 |
1271 | @@ -3915,7 +3915,8 @@ |
1272 | |
1273 | pt-fk-error-logger logs information about foreign key errors on the given |
1274 | DSN. Information is printed to C<STDOUT>, and it can also be saved to a |
1275 | -table by specifying L<"--dest">. |
1276 | +table by specifying L<"--dest">. The tool runs for forever unless |
1277 | +L<"--run-time"> or L<"--iterations"> is specified. |
1278 | |
1279 | Print foreign key errors on host1: |
1280 | |
1281 | |
1282 | === modified file 'lib/Cxn.pm' |
1283 | --- lib/Cxn.pm 2013-02-25 18:52:22 +0000 |
1284 | +++ lib/Cxn.pm 2013-02-26 01:08:24 +0000 |
1285 | @@ -98,23 +98,24 @@ |
1286 | } |
1287 | |
1288 | my $self = { |
1289 | - dsn => $dsn, |
1290 | - dbh => $args{dbh}, |
1291 | - dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
1292 | - hostname => '', |
1293 | - set => $args{set}, |
1294 | - NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
1295 | - dbh_set => 0, |
1296 | - OptionParser => $o, |
1297 | - DSNParser => $dp, |
1298 | + dsn => $dsn, |
1299 | + dbh => $args{dbh}, |
1300 | + dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
1301 | + hostname => '', |
1302 | + set => $args{set}, |
1303 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
1304 | + dbh_set => 0, |
1305 | + OptionParser => $o, |
1306 | + DSNParser => $dp, |
1307 | is_cluster_node => undef, |
1308 | + parent => $args{parent}, |
1309 | }; |
1310 | |
1311 | return bless $self, $class; |
1312 | } |
1313 | |
1314 | sub connect { |
1315 | - my ( $self ) = @_; |
1316 | + my ( $self, %opts ) = @_; |
1317 | my $dsn = $self->{dsn}; |
1318 | my $dp = $self->{DSNParser}; |
1319 | my $o = $self->{OptionParser}; |
1320 | @@ -126,7 +127,13 @@ |
1321 | $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); |
1322 | $self->{asked_for_pass} = 1; |
1323 | } |
1324 | - $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 }); |
1325 | + $dbh = $dp->get_dbh( |
1326 | + $dp->get_cxn_params($dsn), |
1327 | + { |
1328 | + AutoCommit => 1, |
1329 | + %opts, |
1330 | + }, |
1331 | + ); |
1332 | } |
1333 | PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name}); |
1334 | |
1335 | @@ -163,6 +170,11 @@ |
1336 | $self->{hostname} = $hostname; |
1337 | } |
1338 | |
1339 | + if ( $self->{parent} ) { |
1340 | + PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent'); |
1341 | + $dbh->{InactiveDestroy} = 1; |
1342 | + } |
1343 | + |
1344 | # Call the set callback to let the caller SET any MySQL variables. |
1345 | if ( my $set = $self->{set}) { |
1346 | $set->($dbh); |
1347 | @@ -206,12 +218,18 @@ |
1348 | |
1349 | sub DESTROY { |
1350 | my ($self) = @_; |
1351 | - if ( $self->{dbh} |
1352 | - && blessed($self->{dbh}) |
1353 | - && $self->{dbh}->can("disconnect") ) { |
1354 | + |
1355 | + if ( $self->{parent} ) { |
1356 | + PTDEBUG && _d('Not disconnecting dbh in parent'); |
1357 | + } |
1358 | + elsif ( $self->{dbh} |
1359 | + && blessed($self->{dbh}) |
1360 | + && $self->{dbh}->can("disconnect") ) |
1361 | + { |
1362 | PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name}); |
1363 | $self->{dbh}->disconnect(); |
1364 | } |
1365 | + |
1366 | return; |
1367 | } |
1368 | |
1369 | |
1370 | === modified file 't/pt-deadlock-logger/basics.t' |
1371 | --- t/pt-deadlock-logger/basics.t 2012-10-22 21:30:29 +0000 |
1372 | +++ t/pt-deadlock-logger/basics.t 2013-02-26 01:08:24 +0000 |
1373 | @@ -11,6 +11,8 @@ |
1374 | use English qw(-no_match_vars); |
1375 | use Test::More; |
1376 | |
1377 | +$ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} = 1; |
1378 | + |
1379 | use PerconaTest; |
1380 | use Sandbox; |
1381 | require "$trunk/bin/pt-deadlock-logger"; |
1382 | @@ -25,8 +27,8 @@ |
1383 | } |
1384 | |
1385 | my $output; |
1386 | -my $cnf = "/tmp/12345/my.sandbox.cnf"; |
1387 | -my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1"; |
1388 | +my $dsn = $sb->dsn_for('master'); |
1389 | +my @args = ($dsn, qw(--iterations 1)); |
1390 | |
1391 | $dbh1->commit; |
1392 | $dbh2->commit; |
1393 | @@ -90,21 +92,30 @@ |
1394 | $output = $dbh1->selectrow_hashref('show /*!40101 engine*/ innodb status')->{status}; |
1395 | like($output, qr/WE ROLL BACK/, 'There was a deadlock'); |
1396 | |
1397 | -$output = `$cmd --print`; |
1398 | +$output = output( |
1399 | + sub { |
1400 | + pt_deadlock_logger::main(@args); |
1401 | + } |
1402 | +); |
1403 | + |
1404 | like( |
1405 | $output, |
1406 | qr/127\.1.+msandbox.+GEN_CLUST_INDEX/, |
1407 | 'Deadlock logger prints the output' |
1408 | ); |
1409 | |
1410 | -$output = `$cmd`; |
1411 | -like( |
1412 | +$output = output( |
1413 | + sub { |
1414 | + pt_deadlock_logger::main(@args, qw(--quiet)); |
1415 | + } |
1416 | +); |
1417 | + |
1418 | +is( |
1419 | $output, |
1420 | - qr/127\.1.+msandbox.+GEN_CLUST_INDEX/, |
1421 | - '--print is implicit' |
1422 | + "", |
1423 | + "No output with --quiet" |
1424 | ); |
1425 | |
1426 | - |
1427 | # ############################################################################# |
1428 | # Issue 943: mk-deadlock-logger reports the same deadlock with --interval |
1429 | # ############################################################################# |
1430 | @@ -112,55 +123,59 @@ |
1431 | # The deadlock from above won't be re-printed so even after running for |
1432 | # 3 seconds and checking multiple times only the single, 3 line deadlock |
1433 | # should be reported. |
1434 | -chomp($output = `$cmd --run-time 3 | wc -l`); |
1435 | + |
1436 | +$output = output( |
1437 | + sub { |
1438 | + pt_deadlock_logger::main(@args, qw(--run-time 3)); |
1439 | + } |
1440 | +); |
1441 | $output =~ s/^\s+//; |
1442 | +my @lines = split("\n", $output); |
1443 | is( |
1444 | - $output, |
1445 | + scalar @lines, |
1446 | 3, |
1447 | "Doesn't re-print same deadlock (issue 943)" |
1448 | -); |
1449 | +) or diag($output); |
1450 | |
1451 | # ############################################################################# |
1452 | # Check that deadlocks from previous test were stored in table. |
1453 | # ############################################################################# |
1454 | -`$cmd --dest D=test,t=deadlocks --create-dest-table`; |
1455 | +$output = output( |
1456 | + sub { |
1457 | + pt_deadlock_logger::main(@args, '--dest', 'D=test,t=deadlocks', |
1458 | + qw(--create-dest-table)) |
1459 | + } |
1460 | +); |
1461 | + |
1462 | my $res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks'); |
1463 | ok( |
1464 | scalar @$res, |
1465 | - 'Deadlocks recorded in --dest table' |
1466 | -); |
1467 | - |
1468 | -# ############################################################################# |
1469 | -# Check that --dest suppress --print output unless --print is explicit. |
1470 | -# ############################################################################# |
1471 | -$output = 'foo'; |
1472 | -$dbh1->do('TRUNCATE TABLE test.deadlocks'); |
1473 | -$output = `$cmd --dest D=test,t=deadlocks`; |
1474 | -is( |
1475 | - $output, |
1476 | - '', |
1477 | - 'No output with --dest' |
1478 | -); |
1479 | - |
1480 | -$res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks'); |
1481 | -ok( |
1482 | - scalar @$res, |
1483 | - 'Deadlocks still recorded in table' |
1484 | -); |
1485 | - |
1486 | + 'Deadlock saved in --dest table' |
1487 | +) or diag($output); |
1488 | + |
1489 | +# ############################################################################# |
1490 | +# In 2.1, --dest suppressed output (--print). In 2.2, output is only |
1491 | +# suppressed by --quiet. |
1492 | +# ############################################################################# |
1493 | $output = ''; |
1494 | $dbh1->do('TRUNCATE TABLE test.deadlocks'); |
1495 | -$output = `$trunk/bin/pt-deadlock-logger --print --dest D=test,t=deadlocks --host 127.1 --port 12345 --user msandbox --password msandbox`; |
1496 | -like( |
1497 | +$output = output( |
1498 | + sub { |
1499 | + pt_deadlock_logger::main(@args, '--dest', 'D=test,t=deadlocks', |
1500 | + qw(--quiet)) |
1501 | + } |
1502 | +); |
1503 | + |
1504 | +is( |
1505 | $output, |
1506 | - qr/127\.1.+msandbox.+GEN_CLUST_INDEX/, |
1507 | - 'Prints output with --dest and explicit --print' |
1508 | + "", |
1509 | + "No output with --dest and --quiet" |
1510 | ); |
1511 | |
1512 | $res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks'); |
1513 | ok( |
1514 | scalar @$res, |
1515 | - 'Deadlocks recorded in table again' |
1516 | + "... deadlock still saved in the table" |
1517 | ); |
1518 | |
1519 | # ############################################################################# |
1520 | @@ -180,9 +195,7 @@ |
1521 | make_deadlock(); |
1522 | |
1523 | $output = output( |
1524 | - sub { pt_deadlock_logger::main("F=/tmp/12345/my.sandbox.cnf", |
1525 | - qw(--print) ); |
1526 | - } |
1527 | + sub { pt_deadlock_logger::main(@args) } |
1528 | ); |
1529 | |
1530 | like( |
1531 | @@ -200,4 +213,3 @@ |
1532 | $sb->wipe_clean($dbh1); |
1533 | ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
1534 | done_testing; |
1535 | -exit; |
1536 | |
1537 | === modified file 't/pt-deadlock-logger/clear_deadlocks.t' |
1538 | --- t/pt-deadlock-logger/clear_deadlocks.t 2012-06-03 19:14:30 +0000 |
1539 | +++ t/pt-deadlock-logger/clear_deadlocks.t 2013-02-26 01:08:24 +0000 |
1540 | @@ -22,9 +22,6 @@ |
1541 | if ( !$dbh1 ) { |
1542 | plan skip_all => 'Cannot connect to sandbox master'; |
1543 | } |
1544 | -else { |
1545 | - plan tests => 4; |
1546 | -} |
1547 | |
1548 | my $output; |
1549 | my $cnf = "/tmp/12345/my.sandbox.cnf"; |
1550 | @@ -39,7 +36,7 @@ |
1551 | |
1552 | # The clear-deadlocks table comes and goes quickly so we can really |
1553 | # only search the debug output for evidence that it was created. |
1554 | -$output = `PTDEBUG=1 $trunk/bin/pt-deadlock-logger F=$cnf,D=test --clear-deadlocks test.make_deadlock 2>&1`; |
1555 | +$output = `PTDEBUG=1 $trunk/bin/pt-deadlock-logger F=$cnf,D=test --clear-deadlocks test.make_deadlock --iterations 1 2>&1`; |
1556 | like( |
1557 | $output, |
1558 | qr/INSERT INTO test.make_deadlock/, |
1559 | @@ -67,4 +64,4 @@ |
1560 | # ############################################################################# |
1561 | $sb->wipe_clean($dbh1); |
1562 | ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
1563 | -exit; |
1564 | +done_testing; |
1565 | |
1566 | === modified file 't/pt-deadlock-logger/create_dest_table.t' |
1567 | --- t/pt-deadlock-logger/create_dest_table.t 2012-06-09 18:43:33 +0000 |
1568 | +++ t/pt-deadlock-logger/create_dest_table.t 2013-02-26 01:08:24 +0000 |
1569 | @@ -22,15 +22,10 @@ |
1570 | if ( !$dbh1 ) { |
1571 | plan skip_all => 'Cannot connect to sandbox master'; |
1572 | } |
1573 | -else { |
1574 | - plan tests => 3; |
1575 | -} |
1576 | |
1577 | my $output; |
1578 | -my $cnf = "/tmp/12345/my.sandbox.cnf"; |
1579 | -my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1"; |
1580 | +my $dsn = $sb->dsn_for('master'); |
1581 | |
1582 | -$sb->wipe_clean($dbh1); |
1583 | $sb->create_dbs($dbh1, ['test']); |
1584 | |
1585 | # ############################################################################# |
1586 | @@ -42,17 +37,25 @@ |
1587 | 'Deadlocks table does not exit (issue 386)' |
1588 | ); |
1589 | |
1590 | -`$cmd --dest D=test,t=issue_386 --run-time 1s --interval 1s --create-dest-table`; |
1591 | +$output = output( |
1592 | + sub { |
1593 | + pt_deadlock_logger::main($dsn, |
1594 | + '--dest', 'D=test,t=issue_386', |
1595 | + qw(--iterations 1 --create-dest-table) |
1596 | + ) |
1597 | + }, |
1598 | + stderr => 1, |
1599 | +); |
1600 | |
1601 | is_deeply( |
1602 | $dbh1->selectall_arrayref(q{show tables from `test` like 'issue_386'}), |
1603 | [['issue_386']], |
1604 | 'Deadlocks table created with --create-dest-table (issue 386)' |
1605 | -); |
1606 | +) or diag($output); |
1607 | |
1608 | # ############################################################################# |
1609 | # Done. |
1610 | # ############################################################################# |
1611 | $sb->wipe_clean($dbh1); |
1612 | ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
1613 | -exit; |
1614 | +done_testing; |
1615 | |
1616 | === modified file 't/pt-deadlock-logger/option_sanity.t' |
1617 | --- t/pt-deadlock-logger/option_sanity.t 2012-10-22 18:16:42 +0000 |
1618 | +++ t/pt-deadlock-logger/option_sanity.t 2013-02-26 01:08:24 +0000 |
1619 | @@ -21,7 +21,7 @@ |
1620 | $output = `$trunk/bin/pt-deadlock-logger --dest D=test,t=deadlocks 2>&1`; |
1621 | like( |
1622 | $output, |
1623 | - qr/Missing or invalid source host/, |
1624 | + qr/No DSN was specified/, |
1625 | 'Requires source host' |
1626 | ); |
1627 | |
1628 | |
1629 | === renamed file 't/pt-deadlock-logger/deadlocks_tbl.sql' => 't/pt-deadlock-logger/samples/deadlocks_tbl.sql' |
1630 | === modified file 't/pt-deadlock-logger/standard_options.t' |
1631 | --- t/pt-deadlock-logger/standard_options.t 2012-07-23 04:52:41 +0000 |
1632 | +++ t/pt-deadlock-logger/standard_options.t 2013-02-26 01:08:24 +0000 |
1633 | @@ -17,69 +17,96 @@ |
1634 | |
1635 | my $dp = new DSNParser(opts=>$dsn_opts); |
1636 | my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); |
1637 | -my $dbh1 = $sb->get_dbh_for('master'); |
1638 | +my $dbh = $sb->get_dbh_for('master'); |
1639 | |
1640 | -if ( !$dbh1 ) { |
1641 | +if ( !$dbh ) { |
1642 | plan skip_all => 'Cannot connect to sandbox master'; |
1643 | } |
1644 | -else { |
1645 | - plan tests => 10; |
1646 | -} |
1647 | |
1648 | my $output; |
1649 | -my $cnf = "/tmp/12345/my.sandbox.cnf"; |
1650 | -my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1"; |
1651 | +my $dsn = $sb->dsn_for('master'); |
1652 | +my @args = ($dsn, qw(--iterations 1)); |
1653 | |
1654 | -$sb->wipe_clean($dbh1); |
1655 | -$sb->create_dbs($dbh1, ['test']); |
1656 | +$sb->wipe_clean($dbh); |
1657 | +$sb->create_dbs($dbh, ['test']); |
1658 | |
1659 | # ############################################################################# |
1660 | # Issue 248: Add --user, --pass, --host, etc to all tools |
1661 | # ############################################################################# |
1662 | |
1663 | # Test that source DSN inherits from --user, etc. |
1664 | -$output = `$trunk/bin/pt-deadlock-logger h=127.1,D=test,u=msandbox,p=msandbox --clear-deadlocks test.make_deadlock --port 12345 2>&1`; |
1665 | +$output = output( |
1666 | + sub { |
1667 | + pt_deadlock_logger::main( |
1668 | + "h=127.1,D=test,u=msandbox,p=msandbox", |
1669 | + qw(--clear-deadlocks test.make_deadlock --port 12345), |
1670 | + qw(--iterations 1) |
1671 | + ) |
1672 | + } |
1673 | +); |
1674 | + |
1675 | unlike( |
1676 | $output, |
1677 | qr/failed/, |
1678 | 'Source DSN inherits from standard connection options (issue 248)' |
1679 | ); |
1680 | |
1681 | -# ######################################################################### |
1682 | +# ############################################################################# |
1683 | # Issue 391: Add --pid option to all scripts |
1684 | -# ######################################################################### |
1685 | -`touch /tmp/mk-script.pid`; |
1686 | -$output = `$cmd --clear-deadlocks test.make_deadlock --port 12345 --pid /tmp/mk-script.pid 2>&1`; |
1687 | +# ############################################################################# |
1688 | + |
1689 | +my $pid_file = "/tmp/pt-deadlock-logger-test.pid.$PID"; |
1690 | +diag(`touch $pid_file`); |
1691 | + |
1692 | +$output = output( |
1693 | + sub { |
1694 | + pt_deadlock_logger::main(@args, '--pid', $pid_file) |
1695 | + }, |
1696 | + stderr => 1, |
1697 | +); |
1698 | + |
1699 | like( |
1700 | $output, |
1701 | - qr{PID file /tmp/mk-script.pid already exists}, |
1702 | + qr{PID file $pid_file already exists}, |
1703 | 'Dies if PID file already exists (--pid without --daemonize) (issue 391)' |
1704 | ); |
1705 | -`rm -rf /tmp/mk-script.pid`; |
1706 | + |
1707 | +unlink $pid_file if -f $pid_file; |
1708 | |
1709 | # ############################################################################# |
1710 | # Check daemonization |
1711 | # ############################################################################# |
1712 | -my $deadlocks_tbl = load_file('t/pt-deadlock-logger/deadlocks_tbl.sql'); |
1713 | -$dbh1->do('USE test'); |
1714 | -$dbh1->do('DROP TABLE IF EXISTS deadlocks'); |
1715 | -$dbh1->do("$deadlocks_tbl"); |
1716 | - |
1717 | -my $pid_file = '/tmp/mk-deadlock-logger.pid'; |
1718 | -unlink $pid_file |
1719 | - and diag("Unlinked existing $pid_file"); |
1720 | - |
1721 | -`$cmd --dest D=test,t=deadlocks --daemonize --run-time 6s --interval 1s --pid $pid_file 1>/dev/null 2>/dev/null`; |
1722 | -$output = `ps -eaf | grep '$cmd \-\-dest '`; |
1723 | -like($output, qr/\Q$cmd/, 'It lives daemonized'); |
1724 | +$dbh->do('USE test'); |
1725 | +$dbh->do('DROP TABLE IF EXISTS deadlocks'); |
1726 | +$sb->load_file('master', 't/pt-deadlock-logger/samples/deadlocks_tbl.sql', 'test'); |
1727 | + |
1728 | +$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`; |
1729 | |
1730 | PerconaTest::wait_for_files($pid_file); |
1731 | -ok(-f $pid_file, 'PID file created'); |
1732 | -my ($pid) = $output =~ /\s+(\d+)\s+/; |
1733 | + |
1734 | +$output = `ps x | grep 'pt-deadlock-logger $dsn' | grep -v grep`; |
1735 | +like( |
1736 | + $output, |
1737 | + qr/\Qpt-deadlock-logger $dsn/, |
1738 | + 'It lives daemonized' |
1739 | +) or diag($output); |
1740 | + |
1741 | +my ($pid) = $output =~ /(\d+)/; |
1742 | + |
1743 | +ok( |
1744 | + -f $pid_file, |
1745 | + 'PID file created' |
1746 | +) or diag($output); |
1747 | + |
1748 | chomp($output = slurp_file($pid_file)); |
1749 | -is($output, $pid, 'PID file has correct PID'); |
1750 | +is( |
1751 | + $output, |
1752 | + $pid, |
1753 | + 'PID file has correct PID' |
1754 | +); |
1755 | |
1756 | # Kill it |
1757 | +kill 2, $pid; |
1758 | PerconaTest::wait_until(sub { !kill 0, $pid }); |
1759 | ok(! -f $pid_file, 'PID file removed'); |
1760 | |
1761 | @@ -90,17 +117,25 @@ |
1762 | 'PID file already exists' |
1763 | ); |
1764 | |
1765 | -$output = `$cmd --dest D=test,t=deadlocks --daemonize --run-time 1s --interval 1s --pid $pid_file 2>&1`; |
1766 | +$output = output( |
1767 | + sub { |
1768 | + pt_deadlock_logger::main(@args, '--pid', $pid_file, |
1769 | + qw(--daemonize)) |
1770 | + }, |
1771 | + stderr => 1, |
1772 | +); |
1773 | + |
1774 | like( |
1775 | $output, |
1776 | - qr/PID file .+ already exists/, |
1777 | + qr/PID file $pid_file already exists/, |
1778 | 'Does not run if PID file already exists' |
1779 | ); |
1780 | |
1781 | -$output = `ps -eaf | grep 'pt-deadlock-logger \-\-dest '`; |
1782 | -unlike( |
1783 | +$output = `ps x | grep 'pt-deadlock-logger $dsn' | grep -v grep`; |
1784 | + |
1785 | +is( |
1786 | $output, |
1787 | - qr/$cmd/, |
1788 | + "", |
1789 | 'It does not lived daemonized' |
1790 | ); |
1791 | |
1792 | @@ -109,6 +144,6 @@ |
1793 | # ############################################################################# |
1794 | # Done. |
1795 | # ############################################################################# |
1796 | -$sb->wipe_clean($dbh1); |
1797 | +$sb->wipe_clean($dbh); |
1798 | ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
1799 | -exit; |
1800 | +done_testing; |
Passes all its tests.