Merge lp:~percona-toolkit-dev/percona-toolkit/pt-fke-logger-2.2 into lp:percona-toolkit/2.2
- pt-fke-logger-2.2
- Merge into 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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brian Fraser (community) | Approve | ||
Daniel Nichter | Approve | ||
Review via email: mp+150704@code.launchpad.net |
Commit message
Description of the change
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
1 | === modified file 'bin/pt-archiver' |
2 | --- bin/pt-archiver 2013-02-22 17:47:57 +0000 |
3 | +++ bin/pt-archiver 2013-02-27 23:41:26 +0000 |
4 | @@ -6958,10 +6958,11 @@ |
5 | |
6 | type: string |
7 | |
8 | -Create the given PID file when daemonized. The file contains the process ID of |
9 | -the daemonized instance. The PID file is removed when the daemonized instance |
10 | -exits. The program checks for the existence of the PID file when starting; if |
11 | -it exists and the process with the matching PID exists, the program exits. |
12 | +Create the given PID file. The tool won't start if the PID file already |
13 | +exists and the PID it contains is different than the current PID. However, |
14 | +if the PID file exists and the PID it contains is no longer running, the |
15 | +tool will overwrite the PID file with the current PID. The PID file is |
16 | +removed automatically when the tool exits. |
17 | |
18 | =item --plugin |
19 | |
20 | |
21 | === modified file 'bin/pt-config-diff' |
22 | --- bin/pt-config-diff 2013-02-22 15:00:55 +0000 |
23 | +++ bin/pt-config-diff 2013-02-27 23:41:26 +0000 |
24 | @@ -2138,23 +2138,24 @@ |
25 | } |
26 | |
27 | my $self = { |
28 | - dsn => $dsn, |
29 | - dbh => $args{dbh}, |
30 | - dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
31 | - hostname => '', |
32 | - set => $args{set}, |
33 | - NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
34 | - dbh_set => 0, |
35 | - OptionParser => $o, |
36 | - DSNParser => $dp, |
37 | + dsn => $dsn, |
38 | + dbh => $args{dbh}, |
39 | + dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
40 | + hostname => '', |
41 | + set => $args{set}, |
42 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
43 | + dbh_set => 0, |
44 | + OptionParser => $o, |
45 | + DSNParser => $dp, |
46 | is_cluster_node => undef, |
47 | + parent => $args{parent}, |
48 | }; |
49 | |
50 | return bless $self, $class; |
51 | } |
52 | |
53 | sub connect { |
54 | - my ( $self ) = @_; |
55 | + my ( $self, %opts ) = @_; |
56 | my $dsn = $self->{dsn}; |
57 | my $dp = $self->{DSNParser}; |
58 | my $o = $self->{OptionParser}; |
59 | @@ -2165,11 +2166,18 @@ |
60 | $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); |
61 | $self->{asked_for_pass} = 1; |
62 | } |
63 | - $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 }); |
64 | + $dbh = $dp->get_dbh( |
65 | + $dp->get_cxn_params($dsn), |
66 | + { |
67 | + AutoCommit => 1, |
68 | + %opts, |
69 | + }, |
70 | + ); |
71 | } |
72 | - PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name}); |
73 | |
74 | - return $self->set_dbh($dbh); |
75 | + $dbh = $self->set_dbh($dbh); |
76 | + PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name}); |
77 | + return $dbh; |
78 | } |
79 | |
80 | sub set_dbh { |
81 | @@ -2192,6 +2200,11 @@ |
82 | $self->{hostname} = $hostname; |
83 | } |
84 | |
85 | + if ( $self->{parent} ) { |
86 | + PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent'); |
87 | + $dbh->{InactiveDestroy} = 1; |
88 | + } |
89 | + |
90 | if ( my $set = $self->{set}) { |
91 | $set->($dbh); |
92 | } |
93 | @@ -2201,6 +2214,13 @@ |
94 | return $dbh; |
95 | } |
96 | |
97 | +sub lost_connection { |
98 | + my ($self, $e) = @_; |
99 | + return 0 unless $e; |
100 | + return $e =~ m/MySQL server has gone away/ |
101 | + || $e =~ m/Lost connection to MySQL server/; |
102 | +} |
103 | + |
104 | sub dbh { |
105 | my ($self) = @_; |
106 | return $self->{dbh}; |
107 | @@ -2219,12 +2239,21 @@ |
108 | |
109 | sub DESTROY { |
110 | my ($self) = @_; |
111 | - if ( $self->{dbh} |
112 | - && blessed($self->{dbh}) |
113 | - && $self->{dbh}->can("disconnect") ) { |
114 | - PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name}); |
115 | + |
116 | + PTDEBUG && _d('Destroying cxn'); |
117 | + |
118 | + if ( $self->{parent} ) { |
119 | + PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent'); |
120 | + } |
121 | + elsif ( $self->{dbh} |
122 | + && blessed($self->{dbh}) |
123 | + && $self->{dbh}->can("disconnect") ) |
124 | + { |
125 | + PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname}, |
126 | + $self->{dsn_name}); |
127 | $self->{dbh}->disconnect(); |
128 | } |
129 | + |
130 | return; |
131 | } |
132 | |
133 | @@ -5022,12 +5051,7 @@ |
134 | # Daemonize now that everything is setup and ready to work. |
135 | # ######################################################################## |
136 | my $daemon; |
137 | - if ( $o->get('daemonize') ) { |
138 | - $daemon = new Daemon(o=>$o); |
139 | - $daemon->daemonize(); |
140 | - PTDEBUG && _d('I am a daemon now'); |
141 | - } |
142 | - elsif ( $o->get('pid') ) { |
143 | + if ( $o->get('pid') ) { |
144 | # We're not daemoninzing, it just handles PID stuff. |
145 | $daemon = new Daemon(o=>$o); |
146 | $daemon->make_PID_file(); |
147 | @@ -5118,7 +5142,7 @@ |
148 | |
149 | =head1 SYNOPSIS |
150 | |
151 | -Usage: pt-config-diff [OPTION...] CONFIG CONFIG [CONFIG...] |
152 | +Usage: pt-config-diff [OPTIONS] CONFIG CONFIG [CONFIG...] |
153 | |
154 | pt-config-diff diffs MySQL configuration files and server variables. |
155 | CONFIG can be a filename or a DSN. At least two CONFIG sources must be given. |
156 | @@ -5238,11 +5262,6 @@ |
157 | first option on the command line. (This option does not specify a CONFIG; |
158 | it's equivalent to C<--defaults-file>.) |
159 | |
160 | -=item --daemonize |
161 | - |
162 | -Fork to the background and detach from the shell. POSIX |
163 | -operating systems only. |
164 | - |
165 | =item --defaults-file |
166 | |
167 | short form: -F; type: string |
168 | @@ -5276,11 +5295,11 @@ |
169 | |
170 | type: string |
171 | |
172 | -Create the given PID file when daemonized. The file contains the process |
173 | -ID of the daemonized instance. The PID file is removed when the |
174 | -daemonized instance exits. The program checks for the existence of the |
175 | -PID file when starting; if it exists and the process with the matching PID |
176 | -exists, the program exits. |
177 | +Create the given PID file. The tool won't start if the PID file already |
178 | +exists and the PID it contains is different than the current PID. However, |
179 | +if the PID file exists and the PID it contains is no longer running, the |
180 | +tool will overwrite the PID file with the current PID. The PID file is |
181 | +removed automatically when the tool exits. |
182 | |
183 | =item --port |
184 | |
185 | |
186 | === modified file 'bin/pt-deadlock-logger' |
187 | --- bin/pt-deadlock-logger 2013-02-22 17:47:57 +0000 |
188 | +++ bin/pt-deadlock-logger 2013-02-27 23:41:26 +0000 |
189 | @@ -23,9 +23,11 @@ |
190 | VersionParser |
191 | Quoter |
192 | DSNParser |
193 | + Cxn |
194 | Daemon |
195 | HTTPMicro |
196 | VersionCheck |
197 | + Runtime |
198 | )); |
199 | } |
200 | |
201 | @@ -2439,6 +2441,181 @@ |
202 | # ########################################################################### |
203 | |
204 | # ########################################################################### |
205 | +# Cxn package |
206 | +# This package is a copy without comments from the original. The original |
207 | +# with comments and its test file can be found in the Bazaar repository at, |
208 | +# lib/Cxn.pm |
209 | +# t/lib/Cxn.t |
210 | +# See https://launchpad.net/percona-toolkit for more information. |
211 | +# ########################################################################### |
212 | +{ |
213 | +package Cxn; |
214 | + |
215 | +use strict; |
216 | +use warnings FATAL => 'all'; |
217 | +use English qw(-no_match_vars); |
218 | +use Scalar::Util qw(blessed); |
219 | +use constant { |
220 | + PTDEBUG => $ENV{PTDEBUG} || 0, |
221 | + PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0, |
222 | +}; |
223 | + |
224 | +sub new { |
225 | + my ( $class, %args ) = @_; |
226 | + my @required_args = qw(DSNParser OptionParser); |
227 | + foreach my $arg ( @required_args ) { |
228 | + die "I need a $arg argument" unless $args{$arg}; |
229 | + }; |
230 | + my ($dp, $o) = @args{@required_args}; |
231 | + |
232 | + my $dsn_defaults = $dp->parse_options($o); |
233 | + my $prev_dsn = $args{prev_dsn}; |
234 | + my $dsn = $args{dsn}; |
235 | + if ( !$dsn ) { |
236 | + $args{dsn_string} ||= 'h=' . ($dsn_defaults->{h} || 'localhost'); |
237 | + |
238 | + $dsn = $dp->parse( |
239 | + $args{dsn_string}, $prev_dsn, $dsn_defaults); |
240 | + } |
241 | + elsif ( $prev_dsn ) { |
242 | + $dsn = $dp->copy($prev_dsn, $dsn); |
243 | + } |
244 | + |
245 | + my $self = { |
246 | + dsn => $dsn, |
247 | + dbh => $args{dbh}, |
248 | + dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
249 | + hostname => '', |
250 | + set => $args{set}, |
251 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
252 | + dbh_set => 0, |
253 | + OptionParser => $o, |
254 | + DSNParser => $dp, |
255 | + is_cluster_node => undef, |
256 | + parent => $args{parent}, |
257 | + }; |
258 | + |
259 | + return bless $self, $class; |
260 | +} |
261 | + |
262 | +sub connect { |
263 | + my ( $self, %opts ) = @_; |
264 | + my $dsn = $self->{dsn}; |
265 | + my $dp = $self->{DSNParser}; |
266 | + my $o = $self->{OptionParser}; |
267 | + |
268 | + my $dbh = $self->{dbh}; |
269 | + if ( !$dbh || !$dbh->ping() ) { |
270 | + if ( $o->get('ask-pass') && !$self->{asked_for_pass} ) { |
271 | + $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); |
272 | + $self->{asked_for_pass} = 1; |
273 | + } |
274 | + $dbh = $dp->get_dbh( |
275 | + $dp->get_cxn_params($dsn), |
276 | + { |
277 | + AutoCommit => 1, |
278 | + %opts, |
279 | + }, |
280 | + ); |
281 | + } |
282 | + |
283 | + $dbh = $self->set_dbh($dbh); |
284 | + PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name}); |
285 | + return $dbh; |
286 | +} |
287 | + |
288 | +sub set_dbh { |
289 | + my ($self, $dbh) = @_; |
290 | + |
291 | + if ( $self->{dbh} && $self->{dbh} == $dbh && $self->{dbh_set} ) { |
292 | + PTDEBUG && _d($dbh, 'Already set dbh'); |
293 | + return $dbh; |
294 | + } |
295 | + |
296 | + PTDEBUG && _d($dbh, 'Setting dbh'); |
297 | + |
298 | + $dbh->{FetchHashKeyName} = 'NAME_lc' if $self->{NAME_lc}; |
299 | + |
300 | + my $sql = 'SELECT @@hostname, @@server_id'; |
301 | + PTDEBUG && _d($dbh, $sql); |
302 | + my ($hostname, $server_id) = $dbh->selectrow_array($sql); |
303 | + PTDEBUG && _d($dbh, 'hostname:', $hostname, $server_id); |
304 | + if ( $hostname ) { |
305 | + $self->{hostname} = $hostname; |
306 | + } |
307 | + |
308 | + if ( $self->{parent} ) { |
309 | + PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent'); |
310 | + $dbh->{InactiveDestroy} = 1; |
311 | + } |
312 | + |
313 | + if ( my $set = $self->{set}) { |
314 | + $set->($dbh); |
315 | + } |
316 | + |
317 | + $self->{dbh} = $dbh; |
318 | + $self->{dbh_set} = 1; |
319 | + return $dbh; |
320 | +} |
321 | + |
322 | +sub lost_connection { |
323 | + my ($self, $e) = @_; |
324 | + return 0 unless $e; |
325 | + return $e =~ m/MySQL server has gone away/ |
326 | + || $e =~ m/Lost connection to MySQL server/; |
327 | +} |
328 | + |
329 | +sub dbh { |
330 | + my ($self) = @_; |
331 | + return $self->{dbh}; |
332 | +} |
333 | + |
334 | +sub dsn { |
335 | + my ($self) = @_; |
336 | + return $self->{dsn}; |
337 | +} |
338 | + |
339 | +sub name { |
340 | + my ($self) = @_; |
341 | + return $self->{dsn_name} if PERCONA_TOOLKIT_TEST_USE_DSN_NAMES; |
342 | + return $self->{hostname} || $self->{dsn_name} || 'unknown host'; |
343 | +} |
344 | + |
345 | +sub DESTROY { |
346 | + my ($self) = @_; |
347 | + |
348 | + PTDEBUG && _d('Destroying cxn'); |
349 | + |
350 | + if ( $self->{parent} ) { |
351 | + PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent'); |
352 | + } |
353 | + elsif ( $self->{dbh} |
354 | + && blessed($self->{dbh}) |
355 | + && $self->{dbh}->can("disconnect") ) |
356 | + { |
357 | + PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname}, |
358 | + $self->{dsn_name}); |
359 | + $self->{dbh}->disconnect(); |
360 | + } |
361 | + |
362 | + return; |
363 | +} |
364 | + |
365 | +sub _d { |
366 | + my ($package, undef, $line) = caller 0; |
367 | + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
368 | + map { defined $_ ? $_ : 'undef' } |
369 | + @_; |
370 | + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; |
371 | +} |
372 | + |
373 | +1; |
374 | +} |
375 | +# ########################################################################### |
376 | +# End Cxn package |
377 | +# ########################################################################### |
378 | + |
379 | +# ########################################################################### |
380 | # Daemon package |
381 | # This package is a copy without comments from the original. The original |
382 | # with comments and its test file can be found in the Bazaar repository at, |
383 | @@ -3865,6 +4042,139 @@ |
384 | # ########################################################################### |
385 | |
386 | # ########################################################################### |
387 | +# Runtime package |
388 | +# This package is a copy without comments from the original. The original |
389 | +# with comments and its test file can be found in the Bazaar repository at, |
390 | +# lib/Runtime.pm |
391 | +# t/lib/Runtime.t |
392 | +# See https://launchpad.net/percona-toolkit for more information. |
393 | +# ########################################################################### |
394 | +{ |
395 | +package Runtime; |
396 | + |
397 | +use strict; |
398 | +use warnings FATAL => 'all'; |
399 | +use English qw(-no_match_vars); |
400 | +use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
401 | + |
402 | +sub new { |
403 | + my ( $class, %args ) = @_; |
404 | + my @required_args = qw(run_time now); |
405 | + foreach my $arg ( @required_args ) { |
406 | + die "I need a $arg argument" unless exists $args{$arg}; |
407 | + } |
408 | + |
409 | + my $run_time = $args{run_time}; |
410 | + if ( defined $run_time ) { |
411 | + die "run_time must be > 0" if $run_time <= 0; |
412 | + } |
413 | + |
414 | + my $now = $args{now}; |
415 | + die "now must be a callback" unless ref $now eq 'CODE'; |
416 | + |
417 | + my $self = { |
418 | + run_time => $run_time, |
419 | + now => $now, |
420 | + start_time => undef, |
421 | + end_time => undef, |
422 | + time_left => undef, |
423 | + stop => 0, |
424 | + }; |
425 | + |
426 | + return bless $self, $class; |
427 | +} |
428 | + |
429 | +sub time_left { |
430 | + my ( $self, %args ) = @_; |
431 | + |
432 | + if ( $self->{stop} ) { |
433 | + PTDEBUG && _d("No time left because stop was called"); |
434 | + return 0; |
435 | + } |
436 | + |
437 | + my $now = $self->{now}->(%args); |
438 | + PTDEBUG && _d("Current time:", $now); |
439 | + |
440 | + if ( !defined $self->{start_time} ) { |
441 | + $self->{start_time} = $now; |
442 | + } |
443 | + |
444 | + return unless defined $now; |
445 | + |
446 | + my $run_time = $self->{run_time}; |
447 | + return unless defined $run_time; |
448 | + |
449 | + if ( !$self->{end_time} ) { |
450 | + $self->{end_time} = $now + $run_time; |
451 | + PTDEBUG && _d("End time:", $self->{end_time}); |
452 | + } |
453 | + |
454 | + $self->{time_left} = $self->{end_time} - $now; |
455 | + PTDEBUG && _d("Time left:", $self->{time_left}); |
456 | + return $self->{time_left}; |
457 | +} |
458 | + |
459 | +sub have_time { |
460 | + my ( $self, %args ) = @_; |
461 | + my $time_left = $self->time_left(%args); |
462 | + return 1 if !defined $time_left; # run forever |
463 | + return $time_left <= 0 ? 0 : 1; # <=0s means run time has elapsed |
464 | +} |
465 | + |
466 | +sub time_elapsed { |
467 | + my ( $self, %args ) = @_; |
468 | + |
469 | + my $start_time = $self->{start_time}; |
470 | + return 0 unless $start_time; |
471 | + |
472 | + my $now = $self->{now}->(%args); |
473 | + PTDEBUG && _d("Current time:", $now); |
474 | + |
475 | + my $time_elapsed = $now - $start_time; |
476 | + PTDEBUG && _d("Time elapsed:", $time_elapsed); |
477 | + if ( $time_elapsed < 0 ) { |
478 | + warn "Current time $now is earlier than start time $start_time"; |
479 | + } |
480 | + return $time_elapsed; |
481 | +} |
482 | + |
483 | +sub reset { |
484 | + my ( $self ) = @_; |
485 | + $self->{start_time} = undef; |
486 | + $self->{end_time} = undef; |
487 | + $self->{time_left} = undef; |
488 | + $self->{stop} = 0; |
489 | + PTDEBUG && _d("Reset run time"); |
490 | + return; |
491 | +} |
492 | + |
493 | +sub stop { |
494 | + my ( $self ) = @_; |
495 | + $self->{stop} = 1; |
496 | + return; |
497 | +} |
498 | + |
499 | +sub start { |
500 | + my ( $self ) = @_; |
501 | + $self->{stop} = 0; |
502 | + return; |
503 | +} |
504 | + |
505 | +sub _d { |
506 | + my ($package, undef, $line) = caller 0; |
507 | + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
508 | + map { defined $_ ? $_ : 'undef' } |
509 | + @_; |
510 | + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; |
511 | +} |
512 | + |
513 | +1; |
514 | +} |
515 | +# ########################################################################### |
516 | +# End Runtime package |
517 | +# ########################################################################### |
518 | + |
519 | +# ########################################################################### |
520 | # This is a combination of modules and programs in one -- a runnable module. |
521 | # http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last |
522 | # Or, look it up in the Camel book on pages 642 and 643 in the 3rd edition. |
523 | @@ -3877,21 +4187,15 @@ |
524 | use English qw(-no_match_vars); |
525 | use List::Util qw(max); |
526 | use Socket qw(inet_aton); |
527 | -use sigtrap qw(handler finish untrapped normal-signals); |
528 | +use Time::HiRes qw(sleep); |
529 | +use File::Temp qw(tempfile); |
530 | +use File::Spec; |
531 | + |
532 | +use sigtrap 'handler', \&sig_int, 'normal-signals'; |
533 | |
534 | use Percona::Toolkit; |
535 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
536 | |
537 | -my $o; |
538 | -my $oktorun; |
539 | -my $dp; |
540 | - |
541 | -# ######################################################################## |
542 | -# Configuration info. |
543 | -# ######################################################################## |
544 | -my $source_dsn; |
545 | -my $dest_dsn; |
546 | - |
547 | # Some common patterns and variables |
548 | my $d = qr/(\d+)/; # Digit |
549 | my $t = qr/((?:\d+ \d+)|(?:[A-Fa-f0-9]+))/; # Transaction ID |
550 | @@ -3934,48 +4238,54 @@ |
551 | 'update' => 1, |
552 | ); |
553 | |
554 | +my $oktorun = 1; |
555 | +my $exit_status = 0; |
556 | + |
557 | sub main { |
558 | - local @ARGV = @_; # set global ARGV for this package |
559 | - |
560 | - my $q = new Quoter(); |
561 | + local @ARGV = @_; # set global ARGV for this package |
562 | + $oktorun = 1; |
563 | + $exit_status = 0; |
564 | |
565 | # ######################################################################## |
566 | # Get configuration information. |
567 | # ######################################################################## |
568 | - $o = new OptionParser(); |
569 | + my $o = new OptionParser(); |
570 | $o->get_specs(); |
571 | $o->get_opts(); |
572 | |
573 | - $o->set('collapse', $o->get('print')) unless $o->got('collapse'); |
574 | - |
575 | - $dp = $o->DSNParser(); |
576 | - my $dsn_defaults = $dp->parse_options($o); |
577 | - $source_dsn = @ARGV ? $dp->parse(shift @ARGV,$dsn_defaults) : $dsn_defaults; |
578 | - $dest_dsn = $o->get('dest'); |
579 | - |
580 | - # The source dsn is not an option so --dest cannot use OptionParser |
581 | - # to inherit values from it. Thus, we do it manually. --dest will |
582 | - # inherit from --user, --port, etc. |
583 | - if ( $source_dsn && $dest_dsn ) { |
584 | - # If dest DSN only has D and t, this will copy h, P, S, etc. |
585 | - # from the source DSN. |
586 | - $dest_dsn = $dp->copy($source_dsn, $dest_dsn); |
587 | + my $dp = $o->DSNParser(); |
588 | + $dp->prop('set-vars', $o->get('set-vars')); |
589 | + |
590 | + my $src; |
591 | + if ( my $src_dsn_string = shift @ARGV ) { |
592 | + $src = Cxn->new( |
593 | + dsn_string => $src_dsn_string, |
594 | + parent => $o->get('daemonize'), |
595 | + DSNParser => $dp, |
596 | + OptionParser => $o, |
597 | + ); |
598 | + } |
599 | + |
600 | + my $dst; |
601 | + if ( my $dst_dsn = $o->get('dest') ) { |
602 | + $dst = Cxn->new( |
603 | + dsn => $dst_dsn, |
604 | + prev_dsn => ($src ? $src->dsn : undef), |
605 | + parent => $o->get('daemonize'), |
606 | + DSNParser => $dp, |
607 | + OptionParser => $o, |
608 | + ); |
609 | } |
610 | |
611 | if ( !$o->get('help') ) { |
612 | - if ( !$source_dsn ) { |
613 | - $o->save_error('Missing or invalid source host'); |
614 | - } |
615 | - if ( $dest_dsn && !$dest_dsn->{D} ) { |
616 | - $o->save_error("--dest requires a 'D' (database) part"); |
617 | - } |
618 | - if ( $dest_dsn && !$dest_dsn->{t} ) { |
619 | - $o->save_error("--dest requires a 't' (table) part"); |
620 | - } |
621 | - |
622 | - # Avoid running forever with zero second interval. |
623 | - if ( $o->get('run-time') && !$o->get('interval') ) { |
624 | - $o->set('interval', 1); |
625 | + if ( !$src ) { |
626 | + $o->save_error('No DSN was specified.'); |
627 | + } |
628 | + if ( $dst && !$dst->dsn->{D} ) { |
629 | + $o->save_error("--dest requires a 'D' (database) part."); |
630 | + } |
631 | + if ( $dst && !$dst->dsn->{t} ) { |
632 | + $o->save_error("--dest requires a 't' (table) part."); |
633 | } |
634 | } |
635 | |
636 | @@ -3984,43 +4294,29 @@ |
637 | # ######################################################################## |
638 | # Connect to MySQL and set up the --dest, if any. |
639 | # ######################################################################## |
640 | - my $dbh = get_cxn($source_dsn, 1); |
641 | - my $dest_dbh; |
642 | - my $sth; |
643 | + my $q = new Quoter(); |
644 | + |
645 | + $src->connect(); |
646 | + |
647 | + my @cols = @{ $o->get('columns') }; |
648 | my $ins_sth; |
649 | - |
650 | - # Since the user might not have specified a hostname for the connection, |
651 | - # try to extract it from the $dbh |
652 | - if ( !$source_dsn->{h} ) { |
653 | - ($source_dsn->{h}) = $dbh->{mysql_hostinfo} =~ m/(\w+) via/; |
654 | - PTDEBUG && _d('Got source host from dbh:', $source_dsn->{h}); |
655 | - } |
656 | - |
657 | - my @cols = qw( server ts thread txn_id txn_time user hostname ip db tbl idx |
658 | - lock_type lock_mode wait_hold victim query ); |
659 | - if ( $o->got('columns') ) { |
660 | - @cols = grep { $o->get('columns')->{$_} } @cols; |
661 | - } |
662 | - |
663 | - if ( $dest_dsn ) { |
664 | - my $db_tbl = |
665 | - join('.', |
666 | - map { $q->quote($_) } |
667 | - grep { $_ } |
668 | - ( $dest_dsn->{D}, $dest_dsn->{t} )); |
669 | - $dest_dbh = get_cxn($dest_dsn, 0); |
670 | + my $ins_sql; |
671 | + if ( $dst ) { |
672 | + $dst->connect(AutoCommit => 0); |
673 | + |
674 | + my $db_tbl = $q->join_quote($dst->dsn->{D}, $dst->dsn->{t}); |
675 | my $cols = join(',', map { $q->quote($_) } @cols); |
676 | - my $parms = join(',', map { '?' } @cols); |
677 | - my $sql = "INSERT IGNORE INTO $db_tbl($cols) VALUES($parms)"; |
678 | - PTDEBUG && _d($sql); |
679 | - $ins_sth = $dest_dbh->prepare($sql); |
680 | + my $parms = join(',', map { '?' } @cols); |
681 | + $ins_sql = "INSERT IGNORE INTO $db_tbl ($cols) VALUES ($parms) " |
682 | + . "/* pt-deadlock-logger */"; |
683 | + PTDEBUG && _d($ins_sql); |
684 | + $ins_sth = $dst->dbh->prepare($ins_sql); |
685 | |
686 | if ( $o->get('create-dest-table') ) { |
687 | - my $db_tbl = $q->quote($dest_dsn->{D}, $dest_dsn->{t}); |
688 | - $sql = $o->read_para_after(__FILE__, qr/MAGIC_dest_table/); |
689 | - $sql =~ s/deadlocks/IF NOT EXISTS $db_tbl/; |
690 | + my $sql = $o->read_para_after(__FILE__, qr/MAGIC_dest_table/); |
691 | + $sql =~ s/deadlocks/IF NOT EXISTS $db_tbl/; |
692 | PTDEBUG && _d($sql); |
693 | - $dest_dbh->do($sql); |
694 | + $dst->dbh->do($sql); |
695 | } |
696 | } |
697 | |
698 | @@ -4039,6 +4335,16 @@ |
699 | $daemon->make_PID_file(); |
700 | } |
701 | |
702 | + # If we daemonized, the parent has already exited and we're the child. |
703 | + # We shared a copy of every Cxn with the parent, and the parent's copies |
704 | + # were destroyed but the dbhs were not disconnected because the parent |
705 | + # attrib was true. Now, as the child, set it false so the dbhs will be |
706 | + # disconnected when our Cxn copies are destroyed. If we didn't daemonize, |
707 | + # then we're not really a parent (since we have no children), so set it |
708 | + # false to auto-disconnect the dbhs when our Cxns are destroyed. |
709 | + $src->{parent} = 0; |
710 | + $dst->{parent} = 0 if $dst; |
711 | + |
712 | # ######################################################################## |
713 | # Do the version-check |
714 | # ######################################################################## |
715 | @@ -4046,117 +4352,155 @@ |
716 | VersionCheck::version_check( |
717 | force => $o->got('version-check'), |
718 | instances => [ |
719 | - { dbh => $dbh, dsn => $source_dsn }, |
720 | - ($dest_dsn ? { dbh => $dest_dsn, dsn => $dest_dsn } : ()), |
721 | + { dbh => $src->dbh, dsn => $src->dsn }, |
722 | + ($dst ? { dbh => $dst->dbh, dsn => $dst->dsn } : ()) |
723 | ], |
724 | ); |
725 | } |
726 | |
727 | # ######################################################################## |
728 | + # Set upt the --clear-deadlocks table. |
729 | + # ######################################################################## |
730 | + my $clear_deadlocks_table_def; |
731 | + my $clear_deadlocks_table = $o->get('clear-deadlocks'); |
732 | + if ( $clear_deadlocks_table ) { |
733 | + $clear_deadlocks_table_def |
734 | + = $o->read_para_after(__FILE__, qr/MAGIC_clear_deadlocks/); |
735 | + if ( VersionParser->new($src->dbh) < '4.1.2') { |
736 | + $clear_deadlocks_table_def =~ s/ENGINE=/TYPE=/; |
737 | + } |
738 | + $clear_deadlocks_table_def |
739 | + =~ s/percona_schema.clear_deadlocks/$clear_deadlocks_table/; |
740 | + PTDEBUG && _d('--clear-deadlocks table:', $clear_deadlocks_table_def); |
741 | + } |
742 | + |
743 | + # ######################################################################## |
744 | # Start looking for and logging deadlocks. |
745 | # ######################################################################## |
746 | - my $last_fingerprint = ''; |
747 | - |
748 | - $oktorun = 1; |
749 | - my $start = time(); |
750 | - my $end = $start + ($o->get('run-time') || 0); # When we should exit |
751 | - my $now = $start; |
752 | - while ( # Quit if: |
753 | - ($start == $end || $now < $end) # time is exceeded |
754 | - && $oktorun # or instructed to quit |
755 | - ) |
756 | - { |
757 | - my $text = $dbh->selectrow_hashref("SHOW /*!40100 ENGINE*/ INNODB STATUS")->{Status}; |
758 | - my $parse_deadlocks_options = { |
759 | - 'numeric-ip' => $o->got('numeric-ip'), |
760 | - 'collapse' => $o->got('collapse'), |
761 | + my $sep = $o->get('tab') ? "\t" : ' '; |
762 | + my $last_fingerprint = ''; |
763 | + my $parse_deadlocks_options = { |
764 | + 'server' => $src->dsn->{h} || $src->{hostname}, |
765 | + 'numeric-ip' => $o->got('numeric-ip'), |
766 | + }; |
767 | + |
768 | + my $run_time = Runtime->new( |
769 | + run_time => $o->get('run-time'), |
770 | + now => sub { return time }, |
771 | + ); |
772 | + |
773 | + my $interval = $o->get('interval'); |
774 | + my $iters = $o->get('iterations'); |
775 | + PTDEBUG && _d('iterations:', $iters, 'interval:', $interval); |
776 | + |
777 | + ITERATION: |
778 | + while ( |
779 | + $oktorun |
780 | + && $run_time->have_time() |
781 | + && (!defined $iters || $iters--) |
782 | + ) { |
783 | + |
784 | + my %txns; |
785 | + my $fingerprint; |
786 | + eval { |
787 | + my $sql = "SHOW /*!40100 ENGINE*/ INNODB STATUS " |
788 | + . "/* pt-deadlock-logger */"; |
789 | + my $text = $src->dbh->selectrow_hashref($sql)->{status}; |
790 | + |
791 | + %txns = %{parse_deadlocks($text, $parse_deadlocks_options)}; |
792 | + $fingerprint = fingerprint(\%txns); |
793 | }; |
794 | - my %txns = %{parse_deadlocks($text, $parse_deadlocks_options)}; |
795 | - my $fingerprint = fingerprint(\%txns); |
796 | - |
797 | - if ( $ins_sth ) { |
798 | - foreach my $txn (sort { $a->{thread} <=> $b->{thread} } values %txns) { |
799 | - $ins_sth->execute(@{$txn}{@cols}); |
800 | - } |
801 | - $dest_dbh->commit(); |
802 | + if ( my $e = $EVAL_ERROR ) { |
803 | + PTDEBUG && _d('Error getting InnoDB status:', $e); |
804 | + if ( $src->lost_connection($e) ) { |
805 | + eval { $src->connect() }; |
806 | + if ( $EVAL_ERROR ) { |
807 | + warn "Lost connection to " . $src->name . ". Will try " |
808 | + . "to reconnect in the next iteration.\n"; |
809 | + } |
810 | + else { |
811 | + PTDEBUG && _d('Reconnected to MySQL'); |
812 | + redo ITERATION; |
813 | + } |
814 | + } |
815 | + else { |
816 | + warn "Error getting SHOW ENGINE INNODB STATUS: $EVAL_ERROR"; |
817 | + $exit_status |= 1; |
818 | + } |
819 | } |
820 | + else { |
821 | + if ( $ins_sth ) { |
822 | + eval { |
823 | + PTDEBUG && _d('Saving deadlock to --dest'); |
824 | + foreach my $txn ( |
825 | + sort { $a->{thread} <=> $b->{thread} } values %txns |
826 | + ) { |
827 | + $ins_sth->execute(@{$txn}{@cols}); |
828 | + } |
829 | + $dst->dbh->commit(); |
830 | + }; |
831 | + if ( my $e = $EVAL_ERROR ) { |
832 | + PTDEBUG && _d('Error saving to --dest:', $e); |
833 | + if ( $dst->lost_connection($e) ) { |
834 | + eval { |
835 | + $ins_sth->finish() if $ins_sth; |
836 | + $dst->dbh->disconnect() if $dst->dbh; |
837 | + $dst->connect(AutoCommit => 0); |
838 | + $ins_sth = $dst->dbh->prepare($ins_sql); |
839 | + }; |
840 | + if ( $EVAL_ERROR ) { |
841 | + warn "Lost connection to " . $dst->name . ". Will try " |
842 | + . "to reconnect in the next iteration.\n"; |
843 | + } |
844 | + else { |
845 | + PTDEBUG && _d('Reconnected to MySQL (--dest)'); |
846 | + redo ITERATION; |
847 | + } |
848 | + } |
849 | + else { |
850 | + warn "Error saving to --dest: $EVAL_ERROR"; |
851 | + $exit_status |= 1; |
852 | + } |
853 | + } |
854 | + } |
855 | |
856 | - if ( $fingerprint ne $last_fingerprint ) { |
857 | - PTDEBUG && _d('New deadlock'); |
858 | - if ( $o->got('print') || !$o->got('dest') ) { |
859 | - my $sep = $o->get('tab') ? "\t" : ' '; |
860 | - print join($sep, @cols), "\n"; |
861 | - foreach my $txn (sort {$a->{thread}<=>$b->{thread}} values %txns) { |
862 | - # If 'collapse' is on, it's already been taken care of, |
863 | - # but if it's unset, by default strip whitespace. |
864 | - if ( !$o->got('collapse') ) { |
865 | + if ( $fingerprint ne $last_fingerprint ) { |
866 | + PTDEBUG && _d('New deadlock'); |
867 | + if ( !$o->get('quiet') ) { |
868 | + print join($sep, @cols), "\n"; |
869 | + foreach my $txn ( |
870 | + sort { $a->{thread} <=> $b->{thread} } values %txns |
871 | + ) { |
872 | $txn->{query} =~ s/\s+/ /g; |
873 | + print join($sep, map { $txn->{$_} } @cols), "\n"; |
874 | } |
875 | - print join($sep, map { $txn->{$_} } @cols), "\n"; |
876 | } |
877 | } |
878 | - } |
879 | - else { |
880 | - PTDEBUG && _d('Same deadlock, not printing'); |
881 | - } |
882 | - # Save deadlock's fingerprint for next interval. |
883 | - $last_fingerprint = $fingerprint; |
884 | - |
885 | - # If specified, clear the deadlock... |
886 | - if ( my $db_tbl = $o->get('clear-deadlocks') ) { |
887 | - PTDEBUG && _d('Creating --clear-deadlocks table', $db_tbl); |
888 | - $dbh->{AutoCommit} = 0; |
889 | - my $sql = $o->read_para_after(__FILE__, qr/MAGIC_clear_deadlocks/); |
890 | - |
891 | - if ( VersionParser->new($dbh) < '4.1.2') { |
892 | - $sql =~ s/ENGINE=/TYPE=/; |
893 | - } |
894 | - $sql =~ s/test.deadlock_maker/$db_tbl/; |
895 | - PTDEBUG && _d($sql); |
896 | - $dbh->do($sql); |
897 | - $sql = "INSERT INTO $db_tbl(a) VALUES(1)"; |
898 | - PTDEBUG && _d($sql); |
899 | - $dbh->do($sql); # I'm holding locks on the table now. |
900 | - |
901 | - # Fork off a child to try to take a lock on the table. |
902 | - my $pid = fork(); |
903 | - if ( defined($pid) && $pid == 0 ) { # I am a child |
904 | - my $dbh_child = get_cxn($source_dsn, 0); |
905 | - $sql = "SELECT * FROM $db_tbl FOR UPDATE"; |
906 | - PTDEBUG && _d($sql); |
907 | - eval { $dbh_child->do($sql); }; # Should block against parent. |
908 | - PTDEBUG && _d($EVAL_ERROR); # Parent inserted value 0. |
909 | - $sql = "COMMIT"; |
910 | - PTDEBUG && _d($sql); |
911 | - $dbh_child->do($sql); |
912 | - exit; |
913 | - } |
914 | - elsif ( !defined($pid) ) { |
915 | - die("Unable to fork for clearing deadlocks!\n"); |
916 | - } |
917 | - sleep 1; |
918 | - $sql = "INSERT INTO $db_tbl(a) VALUES(0)";# Will make child deadlock |
919 | - PTDEBUG && _d($sql); |
920 | - eval { $dbh->do($sql); }; |
921 | - PTDEBUG && _d($EVAL_ERROR); |
922 | - waitpid($pid, 0); |
923 | - $sql = "DROP TABLE $db_tbl"; |
924 | - PTDEBUG && _d($sql); |
925 | - $dbh->do($sql); |
926 | - } |
927 | - |
928 | - # If there's an --interval argument, run forever or till specified. |
929 | - # Otherwise just run once. |
930 | - if ( $o->get('interval') ) { |
931 | - sleep($o->get('interval')); |
932 | - $now = time(); |
933 | - } |
934 | - else { |
935 | - $oktorun = 0; |
936 | + else { |
937 | + PTDEBUG && _d('Same deadlock, not printing'); |
938 | + } |
939 | + |
940 | + $last_fingerprint = $fingerprint; |
941 | + |
942 | + if ( $clear_deadlocks_table ) { |
943 | + clear_deadlocks( |
944 | + dsn => $src->dsn, |
945 | + table => $clear_deadlocks_table, |
946 | + table_def => $clear_deadlocks_table_def, |
947 | + DSNParser => $dp, |
948 | + ); |
949 | + } |
950 | + } |
951 | + |
952 | + # Sleep if there's an --iteration left. |
953 | + if ( !defined $iters || $iters ) { |
954 | + PTDEBUG && _d('Sleeping', $interval, 'seconds'); |
955 | + sleep $interval; |
956 | } |
957 | } |
958 | |
959 | - return 0; |
960 | + PTDEBUG && _d('Done running, exiting', $exit_status); |
961 | + return $exit_status; |
962 | } |
963 | |
964 | # ############################################################################ |
965 | @@ -4244,7 +4588,7 @@ |
966 | |
967 | my ( $query_text ) = $body =~ m/\nMySQL thread id .*\n((?s).*)/; |
968 | $query_text =~ s/\s+$//; |
969 | - $query_text =~ s/\s+/ /g if $args->{'collapse'}; |
970 | + $query_text =~ s/\s+/ /g; |
971 | |
972 | @{$hash}{qw(thread hostname ip user query)} |
973 | = ($mysql_thread_id, $hostname, $ip, $user, $query_text); |
974 | @@ -4290,13 +4634,100 @@ |
975 | foreach my $txn ( values %txns ) { |
976 | $txn->{victim} = $txn->{id} == $victim ? 1 : 0; |
977 | $txn->{ts} = $ts; |
978 | - $txn->{server} = $source_dsn->{h} || ''; |
979 | + $txn->{server} = $args->{server} || ''; |
980 | $txn->{ip} = inet_aton($txn->{ip}) if $args->{'numeric-ip'}; |
981 | } |
982 | |
983 | return \%txns; |
984 | } |
985 | |
986 | +sub clear_deadlocks { |
987 | + my (%args) = @_; |
988 | + my @required_args = qw(dsn table table_def DSNParser); |
989 | + foreach my $arg ( @required_args ) { |
990 | + die "I need a $arg argument" unless $args{$arg}; |
991 | + } |
992 | + my $dsn = $args{dsn}; |
993 | + my $table = $args{table}; |
994 | + my $table_def = $args{table_def}; |
995 | + my $dp = $args{DSNParser}; |
996 | + PTDEBUG && _d('Clearing deadlocks with table', $table, $table_def); |
997 | + |
998 | + my $parent_dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit=>0 }); |
999 | + $parent_dbh->{InactiveDestroy} = 1; # because of forking |
1000 | + |
1001 | + # Create the deadlocks table. |
1002 | + PTDEBUG && _d($table_def); |
1003 | + $parent_dbh->do($table_def); |
1004 | + |
1005 | + # Get a lock on it. |
1006 | + my $sql = "INSERT INTO $table (a) VALUES (1) " |
1007 | + . "/* pt-deadlock-logger clear deadlocks parent */"; |
1008 | + PTDEBUG && _d($sql); |
1009 | + $parent_dbh->do($sql); |
1010 | + |
1011 | + my ($sync_fh, $sync_file) = tempfile( |
1012 | + 'pt-deadlock-logger-clear-deadlocks.XXXXXXX', |
1013 | + DIR => File::Spec->tmpdir(), |
1014 | + ); |
1015 | + PTDEBUG && _d('Sync file:', $sync_file); |
1016 | + close $sync_fh; |
1017 | + unlink $sync_file; |
1018 | + |
1019 | + # Fork a child to try to take a lock on the table. |
1020 | + my $pid = fork(); |
1021 | + if ( defined($pid) && $pid == 0 ) { |
1022 | + # I am the child |
1023 | + PTDEBUG && _d('Clear deadlocks child', $PID); |
1024 | + my $child_dbh = $dp->get_dbh($dp->get_cxn_params($dsn), {AutoCommit=>0}); |
1025 | + my $sql = "SELECT * FROM $table FOR UPDATE " |
1026 | + . "/* pt-deadlock-logger clear deadlocks child */"; |
1027 | + PTDEBUG && _d($sql); |
1028 | + open my $fh, '>', $sync_file |
1029 | + or die "Error creating $sync_file: $OS_ERROR"; |
1030 | + close $fh; |
1031 | + PTDEBUG && _d('Clear deadlocks child ready (child)'); |
1032 | + eval { $child_dbh->do($sql); }; # Should block against parent. |
1033 | + PTDEBUG && _d($EVAL_ERROR); # Parent inserted value 0. |
1034 | + $child_dbh->commit(); |
1035 | + $child_dbh->disconnect(); |
1036 | + exit; |
1037 | + } |
1038 | + elsif ( !defined($pid) ) { |
1039 | + die "Failed to fork for --clear-deadlocks: " . ($OS_ERROR || ''); |
1040 | + } |
1041 | + |
1042 | + # Wait up to 10s for the child to connect and become ready. |
1043 | + for ( 1..40 ) { |
1044 | + last if -f $sync_file; |
1045 | + PTDEBUG && _d('Waiting for the clear deadlocks child'); |
1046 | + sleep 0.25; |
1047 | + } |
1048 | + PTDEBUG && _d('Clear deadlocks child ready (parent)'); |
1049 | + sleep 0.25; # wait for child to exec its SELECT statement |
1050 | + |
1051 | + # Make the child deadlock. |
1052 | + $sql = "INSERT INTO $table (a) VALUES (0) " |
1053 | + . "/* pt-deadlock-logger clear deadlocks parent */"; |
1054 | + PTDEBUG && _d($sql); |
1055 | + eval { $parent_dbh->do($sql); }; |
1056 | + PTDEBUG && _d($EVAL_ERROR); |
1057 | + |
1058 | + # Reap the child. |
1059 | + waitpid($pid, 0); |
1060 | + |
1061 | + # Drop the table. |
1062 | + $sql = "DROP TABLE IF EXISTS $table"; |
1063 | + PTDEBUG && _d($sql); |
1064 | + $parent_dbh->do($sql); |
1065 | + |
1066 | + $parent_dbh->disconnect(); |
1067 | + |
1068 | + unlink $sync_file; |
1069 | + |
1070 | + return; |
1071 | +} |
1072 | + |
1073 | sub fingerprint { |
1074 | my ( $txns ) = @_; |
1075 | my $fingerprint = ''; |
1076 | @@ -4308,21 +4739,11 @@ |
1077 | return $fingerprint; |
1078 | } |
1079 | |
1080 | -# Catches signals so the program can exit gracefully. |
1081 | -sub finish { |
1082 | - my ($signal) = @_; |
1083 | - print STDERR "Exiting on SIG$signal.\n"; |
1084 | +sub sig_int { |
1085 | + my ( $signal ) = @_; |
1086 | $oktorun = 0; |
1087 | -} |
1088 | - |
1089 | -sub get_cxn { |
1090 | - my ( $dsn, $ac ) = @_; |
1091 | - if ( $o->get('ask-pass') ) { |
1092 | - $dsn->{p} = OptionParser::prompt_noecho("Enter password: "); |
1093 | - } |
1094 | - my $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), {AutoCommit => $ac}); |
1095 | - $dbh->{InactiveDestroy} = 1; # Because of forking. |
1096 | - return $dbh; |
1097 | + print STDERR "# Caught SIG$signal. Use 'kill -ABRT $PID' if " |
1098 | + . "the tool does not exit normally in a few seconds.\n"; |
1099 | } |
1100 | |
1101 | sub _d { |
1102 | @@ -4347,32 +4768,28 @@ |
1103 | |
1104 | =head1 NAME |
1105 | |
1106 | -pt-deadlock-logger - Extract and log MySQL deadlock information. |
1107 | +pt-deadlock-logger - Log MySQL deadlocks. |
1108 | |
1109 | =head1 SYNOPSIS |
1110 | |
1111 | -Usage: pt-deadlock-logger [OPTION...] SOURCE_DSN |
1112 | - |
1113 | -pt-deadlock-logger extracts and saves information about the most recent deadlock |
1114 | -in a MySQL server. |
1115 | - |
1116 | -Print deadlocks on SOURCE_DSN: |
1117 | - |
1118 | - pt-deadlock-logger SOURCE_DSN |
1119 | - |
1120 | -Store deadlock information from SOURCE_DSN in test.deadlocks table on SOURCE_DSN |
1121 | -(source and destination are the same host): |
1122 | - |
1123 | - pt-deadlock-logger SOURCE_DSN --dest D=test,t=deadlocks |
1124 | - |
1125 | -Store deadlock information from SOURCE_DSN in test.deadlocks table on DEST_DSN |
1126 | -(source and destination are different hosts): |
1127 | - |
1128 | - pt-deadlock-logger SOURCE_DSN --dest DEST_DSN,D=test,t=deadlocks |
1129 | - |
1130 | -Daemonize and check for deadlocks on SOURCE_DSN every 30 seconds for 4 hours: |
1131 | - |
1132 | - pt-deadlock-logger SOURCE_DSN --dest D=test,t=deadlocks --daemonize --run-time 4h --interval 30s |
1133 | +Usage: pt-deadlock-logger [OPTIONS] DSN |
1134 | + |
1135 | +pt-deadlock-logger logs information about MySQL deadlocks on the given |
1136 | +DSN. Information is printed to C<STDOUT>, and it can also be saved to a |
1137 | +table by specifying L<"--dest">. The tool runs for forever unless |
1138 | +L<"--run-time"> or L<"--iterations"> is specified. |
1139 | + |
1140 | +Print deadlocks on host1: |
1141 | + |
1142 | + pt-fk-error-logger h=host1 |
1143 | + |
1144 | +Print deadlocks on host1 once then exit: |
1145 | + |
1146 | + pt-fk-error-logger h=host1 --iterations 1 |
1147 | + |
1148 | +Save deadlocks on host1 to percona_schema.fke on host2: |
1149 | + |
1150 | + pt-fk-error-logger h=host1 --dest h=host2,D=percona_schema,t=deadlocks |
1151 | |
1152 | =head1 RISKS |
1153 | |
1154 | @@ -4398,43 +4815,93 @@ |
1155 | |
1156 | =head1 DESCRIPTION |
1157 | |
1158 | -pt-deadlock-logger extracts deadlock data from a MySQL server. Currently only |
1159 | -InnoDB deadlock information is available. You can print the information to |
1160 | -standard output, store it in a database table, or both. If neither |
1161 | -L<"--print"> nor L<"--dest"> are given, then the deadlock information is |
1162 | -printed by default. If only L<"--dest"> is given, then the deadlock |
1163 | -information is only stored. If both options are given, then the deadlock |
1164 | -information is printed and stored. |
1165 | - |
1166 | -The source host can be specified using one of two methods. The first method is |
1167 | -to use at least one of the standard connection-related command line options: |
1168 | -L<"--defaults-file">, L<"--password">, L<"--host">, L<"--port">, L<"--socket"> |
1169 | -or L<"--user">. These options only apply to the source host; they cannot be |
1170 | -used to specify the destination host. |
1171 | - |
1172 | -The second method to specify the source host, or the optional destination host |
1173 | -using L<"--dest">, is a DSN. A DSN is a special syntax that can be either just |
1174 | -a hostname (like C<server.domain.com> or C<1.2.3.4>), or a |
1175 | -C<key=value,key=value> string. Keys are a single letter: |
1176 | - |
1177 | - KEY MEANING |
1178 | - === ======= |
1179 | - h Connect to host |
1180 | - P Port number to use for connection |
1181 | - S Socket file to use for connection |
1182 | - u User for login if not current user |
1183 | - p Password to use when connecting |
1184 | - F Only read default options from the given file |
1185 | - |
1186 | -If you omit any values from the destination host DSN, they are filled in with |
1187 | -values from the source host, so you don't need to specify them in both places. |
1188 | -C<pt-deadlock-logger> reads all normal MySQL option files, such as ~/.my.cnf, so |
1189 | -you may not need to specify username, password and other common options at all. |
1190 | +pt-deadlock-logger prints information about MySQL deadlocks by polling |
1191 | +and parsing C<SHOW ENGINE INNODB STATUS>. When a new deadlock occurs, |
1192 | +it's printed to C<STDOUT> and, if specified, saved to L<"--dest">. |
1193 | + |
1194 | +Only new deadlocks are printed. A fingerprint for each deadlock is created |
1195 | +using the deadlock's server, ts, and thread values (even if these |
1196 | +columns are not specified by L<"--columns">). A deadlock is printed if |
1197 | +its fingerprint is different than the last deadlock's fingerprint. |
1198 | + |
1199 | +The L<"--dest"> statement uses C<INSERT IGNORE> to eliminate duplicate |
1200 | +deadlocks, so every deadlock is saved for every L<"--iterations">. |
1201 | |
1202 | =head1 OUTPUT |
1203 | |
1204 | -You can choose which columns are output and/or saved to L<"--dest"> with the |
1205 | -L<"--columns"> argument. The default columns are as follows: |
1206 | +New deadlocks are printed to C<STDOUT>, unless L<"--quiet"> is specified. |
1207 | +Errors and warnings are printed to C<STDERR>. |
1208 | + |
1209 | +See also L<"--columns"> and L<"--tab">. |
1210 | + |
1211 | +=head1 INNODB CAVEATS AND DETAILS |
1212 | + |
1213 | +InnoDB's output is hard to parse and sometimes there's no way to do it right. |
1214 | + |
1215 | +Sometimes not all information (for example, username or IP address) is included |
1216 | +in the deadlock information. In this case there's nothing for the script to put |
1217 | +in those columns. It may also be the case that the deadlock output is so long |
1218 | +(because there were a lot of locks) that the whole thing is truncated. |
1219 | + |
1220 | +Though there are usually two transactions involved in a deadlock, there are more |
1221 | +locks than that; at a minimum, one more lock than transactions is necessary to |
1222 | +create a cycle in the waits-for graph. pt-deadlock-logger prints the |
1223 | +transactions (always two in the InnoDB output, even when there are more |
1224 | +transactions in the waits-for graph than that) and fills in locks. It prefers |
1225 | +waited-for over held when choosing lock information to output, but you can |
1226 | +figure out the rest with a moment's thought. If you see one wait-for and one |
1227 | +held lock, you're looking at the same lock, so of course you'd prefer to see |
1228 | +both wait-for locks and get more information. If the two waited-for locks are |
1229 | +not on the same table, more than two transactions were involved in the deadlock. |
1230 | + |
1231 | +Finally, keep in mind that, because usernames with spaces are not quoted by |
1232 | +InnoDB, the tool will generally misreport the second word of these usernames |
1233 | +as the hostname. |
1234 | + |
1235 | +=head1 OPTIONS |
1236 | + |
1237 | +This tool accepts additional command-line arguments. Refer to the |
1238 | +L<"SYNOPSIS"> and usage information for details. |
1239 | + |
1240 | +=over |
1241 | + |
1242 | +=item --ask-pass |
1243 | + |
1244 | +Prompt for a password when connecting to MySQL. |
1245 | + |
1246 | +=item --charset |
1247 | + |
1248 | +short form: -A; type: string |
1249 | + |
1250 | +Default character set. If the value is utf8, sets Perl's binmode on |
1251 | +STDOUT to utf8, passes the mysql_enable_utf8 option to DBD::mysql, and runs SET |
1252 | +NAMES UTF8 after connecting to MySQL. Any other value sets binmode on STDOUT |
1253 | +without the utf8 layer, and runs SET NAMES after connecting to MySQL. |
1254 | + |
1255 | +=item --clear-deadlocks |
1256 | + |
1257 | +type: string |
1258 | + |
1259 | +Use this table to create a small deadlock. This usually has the effect of |
1260 | +clearing out a huge deadlock, which otherwise consumes the entire output of |
1261 | +C<SHOW INNODB STATUS>. The table must not exist. pt-deadlock-logger will |
1262 | +create it with the following structure: |
1263 | + |
1264 | +=for comment ignore-pt-internal-value |
1265 | +MAGIC_clear_deadlocks |
1266 | + |
1267 | + CREATE TABLE percona_schema.clear_deadlocks ( |
1268 | + a INT PRIMARY KEY |
1269 | + ) ENGINE=InnoDB |
1270 | + |
1271 | +After creating the table and causing a small deadlock, the tool will drop the |
1272 | +table again. |
1273 | + |
1274 | +=item --columns |
1275 | + |
1276 | +type: Array; default: server, ts, thread, txn_id, txn_time, user, hostname, ip, db, tbl, idx, lock_type, lock_mode, wait_hold, victim, query |
1277 | + |
1278 | +The columns are: |
1279 | |
1280 | =over |
1281 | |
1282 | @@ -4509,78 +4976,6 @@ |
1283 | |
1284 | =back |
1285 | |
1286 | -=head1 INNODB CAVEATS AND DETAILS |
1287 | - |
1288 | -InnoDB's output is hard to parse and sometimes there's no way to do it right. |
1289 | - |
1290 | -Sometimes not all information (for example, username or IP address) is included |
1291 | -in the deadlock information. In this case there's nothing for the script to put |
1292 | -in those columns. It may also be the case that the deadlock output is so long |
1293 | -(because there were a lot of locks) that the whole thing is truncated. |
1294 | - |
1295 | -Though there are usually two transactions involved in a deadlock, there are more |
1296 | -locks than that; at a minimum, one more lock than transactions is necessary to |
1297 | -create a cycle in the waits-for graph. pt-deadlock-logger prints the |
1298 | -transactions (always two in the InnoDB output, even when there are more |
1299 | -transactions in the waits-for graph than that) and fills in locks. It prefers |
1300 | -waited-for over held when choosing lock information to output, but you can |
1301 | -figure out the rest with a moment's thought. If you see one wait-for and one |
1302 | -held lock, you're looking at the same lock, so of course you'd prefer to see |
1303 | -both wait-for locks and get more information. If the two waited-for locks are |
1304 | -not on the same table, more than two transactions were involved in the deadlock. |
1305 | - |
1306 | -Finally, keep in mind that, because usernames with spaces are not quoted by |
1307 | -InnoDB, the tool will generally misreport the second word of these usernames |
1308 | -as the hostname. |
1309 | - |
1310 | -=head1 OPTIONS |
1311 | - |
1312 | -This tool accepts additional command-line arguments. Refer to the |
1313 | -L<"SYNOPSIS"> and usage information for details. |
1314 | - |
1315 | -=over |
1316 | - |
1317 | -=item --ask-pass |
1318 | - |
1319 | -Prompt for a password when connecting to MySQL. |
1320 | - |
1321 | -=item --charset |
1322 | - |
1323 | -short form: -A; type: string |
1324 | - |
1325 | -Default character set. If the value is utf8, sets Perl's binmode on |
1326 | -STDOUT to utf8, passes the mysql_enable_utf8 option to DBD::mysql, and runs SET |
1327 | -NAMES UTF8 after connecting to MySQL. Any other value sets binmode on STDOUT |
1328 | -without the utf8 layer, and runs SET NAMES after connecting to MySQL. |
1329 | - |
1330 | -=item --clear-deadlocks |
1331 | - |
1332 | -type: string |
1333 | - |
1334 | -Use this table to create a small deadlock. This usually has the effect of |
1335 | -clearing out a huge deadlock, which otherwise consumes the entire output of |
1336 | -C<SHOW INNODB STATUS>. The table must not exist. pt-deadlock-logger will |
1337 | -create it with the following MAGIC_clear_deadlocks structure: |
1338 | - |
1339 | - CREATE TABLE test.deadlock_maker(a INT PRIMARY KEY) ENGINE=InnoDB; |
1340 | - |
1341 | -After creating the table and causing a small deadlock, the tool will drop the |
1342 | -table again. |
1343 | - |
1344 | -=item --[no]collapse |
1345 | - |
1346 | -Collapse whitespace in queries to a single space. This might make it easier to |
1347 | -inspect on the command line or in a query. By default, whitespace is collapsed |
1348 | -when printing with L<"--print">, but not modified when storing to L<"--dest">. |
1349 | -(That is, the default is different for each action). |
1350 | - |
1351 | -=item --columns |
1352 | - |
1353 | -type: hash |
1354 | - |
1355 | -Output only this comma-separated list of columns. See L<"OUTPUT"> for more |
1356 | -details on columns. |
1357 | - |
1358 | =item --config |
1359 | |
1360 | type: Array |
1361 | @@ -4617,12 +5012,12 @@ |
1362 | can usually omit most parts of this argument if you're storing deadlocks on the |
1363 | same server on which they happen. |
1364 | |
1365 | -By default, whitespace in the query column is left intact; |
1366 | -use L<"--[no]collapse"> if you want whitespace collapsed. |
1367 | - |
1368 | -The following MAGIC_dest_table is suggested if you want to store all the |
1369 | +The following table structure is suggested if you want to store all the |
1370 | information pt-deadlock-logger can extract about deadlocks: |
1371 | |
1372 | +=for comment ignore-pt-internal-value |
1373 | +MAGIC_dest_table |
1374 | + |
1375 | CREATE TABLE deadlocks ( |
1376 | server char(20) NOT NULL, |
1377 | ts datetime NOT NULL, |
1378 | @@ -4658,12 +5053,23 @@ |
1379 | |
1380 | =item --interval |
1381 | |
1382 | -type: time |
1383 | +type: time; default: 30 |
1384 | |
1385 | How often to check for deadlocks. If no L<"--run-time"> is specified, |
1386 | pt-deadlock-logger runs forever, checking for deadlocks at every interval. |
1387 | See also L<"--run-time">. |
1388 | |
1389 | +=item --iterations |
1390 | + |
1391 | +type: int |
1392 | + |
1393 | +How many times to check for deadlocks. By default, this option |
1394 | +is undefined which means an infinite number of iterations. The tool always |
1395 | +exits for L<"--run-time">, regardless of the value specified for this option. |
1396 | +For example, the tool will exit after 1 minute with |
1397 | +C<--run-time 1m --iterations 4 --interval 30> because 4 iterations at 30 |
1398 | +second intervals would take 2 minutes, longer than the 1 mintue run-time. |
1399 | + |
1400 | =item --log |
1401 | |
1402 | type: string |
1403 | @@ -4684,10 +5090,11 @@ |
1404 | |
1405 | type: string |
1406 | |
1407 | -Create the given PID file when daemonized. The file contains the process ID of |
1408 | -the daemonized instance. The PID file is removed when the daemonized instance |
1409 | -exits. The program checks for the existence of the PID file when starting; if |
1410 | -it exists and the process with the matching PID exists, the program exits. |
1411 | +Create the given PID file. The tool won't start if the PID file already |
1412 | +exists and the PID it contains is different than the current PID. However, |
1413 | +if the PID file exists and the PID it contains is no longer running, the |
1414 | +tool will overwrite the PID file with the current PID. The PID file is |
1415 | +removed automatically when the tool exits. |
1416 | |
1417 | =item --port |
1418 | |
1419 | @@ -4695,16 +5102,9 @@ |
1420 | |
1421 | Port number to use for connection. |
1422 | |
1423 | -=item --print |
1424 | - |
1425 | -Print results on standard output. See L<"OUTPUT"> for more. By default, |
1426 | -enables L<"--[no]collapse"> unless you explicitly disable it. |
1427 | - |
1428 | -If L<"--interval"> or L<"--run-time"> is specified, only new deadlocks are |
1429 | -printed at each interval. A fingerprint for each deadlock is created using |
1430 | -L<"--columns"> server, ts and thread (even if those columns were not specified |
1431 | -by L<"--columns">) and if the current deadlock's fingerprint is different from |
1432 | -the last deadlock's fingerprint, then it is printed. |
1433 | +=item --quiet |
1434 | + |
1435 | +Do not deadlocks; only print errors and warnings to C<STDERR>. |
1436 | |
1437 | =item --run-time |
1438 | |
1439 | @@ -4729,7 +5129,7 @@ |
1440 | |
1441 | =item --tab |
1442 | |
1443 | -Print tab-separated columns, instead of aligned. |
1444 | +Use tabs to separate columns instead of spaces. |
1445 | |
1446 | =item --user |
1447 | |
1448 | @@ -4886,7 +5286,7 @@ |
1449 | |
1450 | =head1 AUTHORS |
1451 | |
1452 | -Baron Schwartz |
1453 | +Baron Schwartz and Daniel Nichter |
1454 | |
1455 | =head1 ABOUT PERCONA TOOLKIT |
1456 | |
1457 | |
1458 | === modified file 'bin/pt-duplicate-key-checker' |
1459 | --- bin/pt-duplicate-key-checker 2013-02-22 17:47:57 +0000 |
1460 | +++ bin/pt-duplicate-key-checker 2013-02-27 23:41:26 +0000 |
1461 | @@ -5130,14 +5130,11 @@ |
1462 | |
1463 | type: string |
1464 | |
1465 | -Create the given PID file. The file contains the process ID of the script. |
1466 | -The PID file is removed when the script exits. Before starting, the script |
1467 | -checks if the PID file already exists. If it does not, then the script creates |
1468 | -and writes its own PID to it. If it does, then the script checks the following: |
1469 | -if the file contains a PID and a process is running with that PID, then |
1470 | -the script dies; or, if there is no process running with that PID, then the |
1471 | -script overwrites the file with its own PID and starts; else, if the file |
1472 | -contains no PID, then the script dies. |
1473 | +Create the given PID file. The tool won't start if the PID file already |
1474 | +exists and the PID it contains is different than the current PID. However, |
1475 | +if the PID file exists and the PID it contains is no longer running, the |
1476 | +tool will overwrite the PID file with the current PID. The PID file is |
1477 | +removed automatically when the tool exits. |
1478 | |
1479 | =item --port |
1480 | |
1481 | |
1482 | === modified file 'bin/pt-fifo-split' |
1483 | --- bin/pt-fifo-split 2013-01-03 00:54:18 +0000 |
1484 | +++ bin/pt-fifo-split 2013-02-27 23:41:26 +0000 |
1485 | @@ -1456,14 +1456,11 @@ |
1486 | |
1487 | type: string |
1488 | |
1489 | -Create the given PID file. The file contains the process ID of the script. |
1490 | -The PID file is removed when the script exits. Before starting, the script |
1491 | -checks if the PID file already exists. If it does not, then the script creates |
1492 | -and writes its own PID to it. If it does, then the script checks the following: |
1493 | -if the file contains a PID and a process is running with that PID, then |
1494 | -the script dies; or, if there is no process running with that PID, then the |
1495 | -script overwrites the file with its own PID and starts; else, if the file |
1496 | -contains no PID, then the script dies. |
1497 | +Create the given PID file. The tool won't start if the PID file already |
1498 | +exists and the PID it contains is different than the current PID. However, |
1499 | +if the PID file exists and the PID it contains is no longer running, the |
1500 | +tool will overwrite the PID file with the current PID. The PID file is |
1501 | +removed automatically when the tool exits. |
1502 | |
1503 | =item --statistics |
1504 | |
1505 | |
1506 | === modified file 'bin/pt-find' |
1507 | --- bin/pt-find 2013-02-22 17:47:57 +0000 |
1508 | +++ bin/pt-find 2013-02-27 23:41:26 +0000 |
1509 | @@ -4241,14 +4241,11 @@ |
1510 | |
1511 | type: string |
1512 | |
1513 | -Create the given PID file. The file contains the process ID of the script. |
1514 | -The PID file is removed when the script exits. Before starting, the script |
1515 | -checks if the PID file already exists. If it does not, then the script creates |
1516 | -and writes its own PID to it. If it does, then the script checks the following: |
1517 | -if the file contains a PID and a process is running with that PID, then |
1518 | -the script dies; or, if there is no process running with that PID, then the |
1519 | -script overwrites the file with its own PID and starts; else, if the file |
1520 | -contains no PID, then the script dies. |
1521 | +Create the given PID file. The tool won't start if the PID file already |
1522 | +exists and the PID it contains is different than the current PID. However, |
1523 | +if the PID file exists and the PID it contains is no longer running, the |
1524 | +tool will overwrite the PID file with the current PID. The PID file is |
1525 | +removed automatically when the tool exits. |
1526 | |
1527 | =item --port |
1528 | |
1529 | |
1530 | === modified file 'bin/pt-fk-error-logger' |
1531 | --- bin/pt-fk-error-logger 2013-02-22 17:47:57 +0000 |
1532 | +++ bin/pt-fk-error-logger 2013-02-27 23:41:26 +0000 |
1533 | @@ -17,10 +17,12 @@ |
1534 | OptionParser |
1535 | Quoter |
1536 | DSNParser |
1537 | + Cxn |
1538 | Daemon |
1539 | Transformers |
1540 | HTTPMicro |
1541 | VersionCheck |
1542 | + Runtime |
1543 | )); |
1544 | } |
1545 | |
1546 | @@ -1596,6 +1598,181 @@ |
1547 | # ########################################################################### |
1548 | |
1549 | # ########################################################################### |
1550 | +# Cxn package |
1551 | +# This package is a copy without comments from the original. The original |
1552 | +# with comments and its test file can be found in the Bazaar repository at, |
1553 | +# lib/Cxn.pm |
1554 | +# t/lib/Cxn.t |
1555 | +# See https://launchpad.net/percona-toolkit for more information. |
1556 | +# ########################################################################### |
1557 | +{ |
1558 | +package Cxn; |
1559 | + |
1560 | +use strict; |
1561 | +use warnings FATAL => 'all'; |
1562 | +use English qw(-no_match_vars); |
1563 | +use Scalar::Util qw(blessed); |
1564 | +use constant { |
1565 | + PTDEBUG => $ENV{PTDEBUG} || 0, |
1566 | + PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0, |
1567 | +}; |
1568 | + |
1569 | +sub new { |
1570 | + my ( $class, %args ) = @_; |
1571 | + my @required_args = qw(DSNParser OptionParser); |
1572 | + foreach my $arg ( @required_args ) { |
1573 | + die "I need a $arg argument" unless $args{$arg}; |
1574 | + }; |
1575 | + my ($dp, $o) = @args{@required_args}; |
1576 | + |
1577 | + my $dsn_defaults = $dp->parse_options($o); |
1578 | + my $prev_dsn = $args{prev_dsn}; |
1579 | + my $dsn = $args{dsn}; |
1580 | + if ( !$dsn ) { |
1581 | + $args{dsn_string} ||= 'h=' . ($dsn_defaults->{h} || 'localhost'); |
1582 | + |
1583 | + $dsn = $dp->parse( |
1584 | + $args{dsn_string}, $prev_dsn, $dsn_defaults); |
1585 | + } |
1586 | + elsif ( $prev_dsn ) { |
1587 | + $dsn = $dp->copy($prev_dsn, $dsn); |
1588 | + } |
1589 | + |
1590 | + my $self = { |
1591 | + dsn => $dsn, |
1592 | + dbh => $args{dbh}, |
1593 | + dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
1594 | + hostname => '', |
1595 | + set => $args{set}, |
1596 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
1597 | + dbh_set => 0, |
1598 | + OptionParser => $o, |
1599 | + DSNParser => $dp, |
1600 | + is_cluster_node => undef, |
1601 | + parent => $args{parent}, |
1602 | + }; |
1603 | + |
1604 | + return bless $self, $class; |
1605 | +} |
1606 | + |
1607 | +sub connect { |
1608 | + my ( $self, %opts ) = @_; |
1609 | + my $dsn = $self->{dsn}; |
1610 | + my $dp = $self->{DSNParser}; |
1611 | + my $o = $self->{OptionParser}; |
1612 | + |
1613 | + my $dbh = $self->{dbh}; |
1614 | + if ( !$dbh || !$dbh->ping() ) { |
1615 | + if ( $o->get('ask-pass') && !$self->{asked_for_pass} ) { |
1616 | + $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); |
1617 | + $self->{asked_for_pass} = 1; |
1618 | + } |
1619 | + $dbh = $dp->get_dbh( |
1620 | + $dp->get_cxn_params($dsn), |
1621 | + { |
1622 | + AutoCommit => 1, |
1623 | + %opts, |
1624 | + }, |
1625 | + ); |
1626 | + } |
1627 | + |
1628 | + $dbh = $self->set_dbh($dbh); |
1629 | + PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name}); |
1630 | + return $dbh; |
1631 | +} |
1632 | + |
1633 | +sub set_dbh { |
1634 | + my ($self, $dbh) = @_; |
1635 | + |
1636 | + if ( $self->{dbh} && $self->{dbh} == $dbh && $self->{dbh_set} ) { |
1637 | + PTDEBUG && _d($dbh, 'Already set dbh'); |
1638 | + return $dbh; |
1639 | + } |
1640 | + |
1641 | + PTDEBUG && _d($dbh, 'Setting dbh'); |
1642 | + |
1643 | + $dbh->{FetchHashKeyName} = 'NAME_lc' if $self->{NAME_lc}; |
1644 | + |
1645 | + my $sql = 'SELECT @@hostname, @@server_id'; |
1646 | + PTDEBUG && _d($dbh, $sql); |
1647 | + my ($hostname, $server_id) = $dbh->selectrow_array($sql); |
1648 | + PTDEBUG && _d($dbh, 'hostname:', $hostname, $server_id); |
1649 | + if ( $hostname ) { |
1650 | + $self->{hostname} = $hostname; |
1651 | + } |
1652 | + |
1653 | + if ( $self->{parent} ) { |
1654 | + PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent'); |
1655 | + $dbh->{InactiveDestroy} = 1; |
1656 | + } |
1657 | + |
1658 | + if ( my $set = $self->{set}) { |
1659 | + $set->($dbh); |
1660 | + } |
1661 | + |
1662 | + $self->{dbh} = $dbh; |
1663 | + $self->{dbh_set} = 1; |
1664 | + return $dbh; |
1665 | +} |
1666 | + |
1667 | +sub lost_connection { |
1668 | + my ($self, $e) = @_; |
1669 | + return 0 unless $e; |
1670 | + return $e =~ m/MySQL server has gone away/ |
1671 | + || $e =~ m/Lost connection to MySQL server/; |
1672 | +} |
1673 | + |
1674 | +sub dbh { |
1675 | + my ($self) = @_; |
1676 | + return $self->{dbh}; |
1677 | +} |
1678 | + |
1679 | +sub dsn { |
1680 | + my ($self) = @_; |
1681 | + return $self->{dsn}; |
1682 | +} |
1683 | + |
1684 | +sub name { |
1685 | + my ($self) = @_; |
1686 | + return $self->{dsn_name} if PERCONA_TOOLKIT_TEST_USE_DSN_NAMES; |
1687 | + return $self->{hostname} || $self->{dsn_name} || 'unknown host'; |
1688 | +} |
1689 | + |
1690 | +sub DESTROY { |
1691 | + my ($self) = @_; |
1692 | + |
1693 | + PTDEBUG && _d('Destroying cxn'); |
1694 | + |
1695 | + if ( $self->{parent} ) { |
1696 | + PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent'); |
1697 | + } |
1698 | + elsif ( $self->{dbh} |
1699 | + && blessed($self->{dbh}) |
1700 | + && $self->{dbh}->can("disconnect") ) |
1701 | + { |
1702 | + PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname}, |
1703 | + $self->{dsn_name}); |
1704 | + $self->{dbh}->disconnect(); |
1705 | + } |
1706 | + |
1707 | + return; |
1708 | +} |
1709 | + |
1710 | +sub _d { |
1711 | + my ($package, undef, $line) = caller 0; |
1712 | + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
1713 | + map { defined $_ ? $_ : 'undef' } |
1714 | + @_; |
1715 | + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; |
1716 | +} |
1717 | + |
1718 | +1; |
1719 | +} |
1720 | +# ########################################################################### |
1721 | +# End Cxn package |
1722 | +# ########################################################################### |
1723 | + |
1724 | +# ########################################################################### |
1725 | # Daemon package |
1726 | # This package is a copy without comments from the original. The original |
1727 | # with comments and its test file can be found in the Bazaar repository at, |
1728 | @@ -3375,6 +3552,139 @@ |
1729 | # ########################################################################### |
1730 | |
1731 | # ########################################################################### |
1732 | +# Runtime package |
1733 | +# This package is a copy without comments from the original. The original |
1734 | +# with comments and its test file can be found in the Bazaar repository at, |
1735 | +# lib/Runtime.pm |
1736 | +# t/lib/Runtime.t |
1737 | +# See https://launchpad.net/percona-toolkit for more information. |
1738 | +# ########################################################################### |
1739 | +{ |
1740 | +package Runtime; |
1741 | + |
1742 | +use strict; |
1743 | +use warnings FATAL => 'all'; |
1744 | +use English qw(-no_match_vars); |
1745 | +use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
1746 | + |
1747 | +sub new { |
1748 | + my ( $class, %args ) = @_; |
1749 | + my @required_args = qw(run_time now); |
1750 | + foreach my $arg ( @required_args ) { |
1751 | + die "I need a $arg argument" unless exists $args{$arg}; |
1752 | + } |
1753 | + |
1754 | + my $run_time = $args{run_time}; |
1755 | + if ( defined $run_time ) { |
1756 | + die "run_time must be > 0" if $run_time <= 0; |
1757 | + } |
1758 | + |
1759 | + my $now = $args{now}; |
1760 | + die "now must be a callback" unless ref $now eq 'CODE'; |
1761 | + |
1762 | + my $self = { |
1763 | + run_time => $run_time, |
1764 | + now => $now, |
1765 | + start_time => undef, |
1766 | + end_time => undef, |
1767 | + time_left => undef, |
1768 | + stop => 0, |
1769 | + }; |
1770 | + |
1771 | + return bless $self, $class; |
1772 | +} |
1773 | + |
1774 | +sub time_left { |
1775 | + my ( $self, %args ) = @_; |
1776 | + |
1777 | + if ( $self->{stop} ) { |
1778 | + PTDEBUG && _d("No time left because stop was called"); |
1779 | + return 0; |
1780 | + } |
1781 | + |
1782 | + my $now = $self->{now}->(%args); |
1783 | + PTDEBUG && _d("Current time:", $now); |
1784 | + |
1785 | + if ( !defined $self->{start_time} ) { |
1786 | + $self->{start_time} = $now; |
1787 | + } |
1788 | + |
1789 | + return unless defined $now; |
1790 | + |
1791 | + my $run_time = $self->{run_time}; |
1792 | + return unless defined $run_time; |
1793 | + |
1794 | + if ( !$self->{end_time} ) { |
1795 | + $self->{end_time} = $now + $run_time; |
1796 | + PTDEBUG && _d("End time:", $self->{end_time}); |
1797 | + } |
1798 | + |
1799 | + $self->{time_left} = $self->{end_time} - $now; |
1800 | + PTDEBUG && _d("Time left:", $self->{time_left}); |
1801 | + return $self->{time_left}; |
1802 | +} |
1803 | + |
1804 | +sub have_time { |
1805 | + my ( $self, %args ) = @_; |
1806 | + my $time_left = $self->time_left(%args); |
1807 | + return 1 if !defined $time_left; # run forever |
1808 | + return $time_left <= 0 ? 0 : 1; # <=0s means run time has elapsed |
1809 | +} |
1810 | + |
1811 | +sub time_elapsed { |
1812 | + my ( $self, %args ) = @_; |
1813 | + |
1814 | + my $start_time = $self->{start_time}; |
1815 | + return 0 unless $start_time; |
1816 | + |
1817 | + my $now = $self->{now}->(%args); |
1818 | + PTDEBUG && _d("Current time:", $now); |
1819 | + |
1820 | + my $time_elapsed = $now - $start_time; |
1821 | + PTDEBUG && _d("Time elapsed:", $time_elapsed); |
1822 | + if ( $time_elapsed < 0 ) { |
1823 | + warn "Current time $now is earlier than start time $start_time"; |
1824 | + } |
1825 | + return $time_elapsed; |
1826 | +} |
1827 | + |
1828 | +sub reset { |
1829 | + my ( $self ) = @_; |
1830 | + $self->{start_time} = undef; |
1831 | + $self->{end_time} = undef; |
1832 | + $self->{time_left} = undef; |
1833 | + $self->{stop} = 0; |
1834 | + PTDEBUG && _d("Reset run time"); |
1835 | + return; |
1836 | +} |
1837 | + |
1838 | +sub stop { |
1839 | + my ( $self ) = @_; |
1840 | + $self->{stop} = 1; |
1841 | + return; |
1842 | +} |
1843 | + |
1844 | +sub start { |
1845 | + my ( $self ) = @_; |
1846 | + $self->{stop} = 0; |
1847 | + return; |
1848 | +} |
1849 | + |
1850 | +sub _d { |
1851 | + my ($package, undef, $line) = caller 0; |
1852 | + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
1853 | + map { defined $_ ? $_ : 'undef' } |
1854 | + @_; |
1855 | + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; |
1856 | +} |
1857 | + |
1858 | +1; |
1859 | +} |
1860 | +# ########################################################################### |
1861 | +# End Runtime package |
1862 | +# ########################################################################### |
1863 | + |
1864 | +# ########################################################################### |
1865 | # This is a combination of modules and programs in one -- a runnable module. |
1866 | # http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last |
1867 | # Or, look it up in the Camel book on pages 642 and 643 in the 3rd edition. |
1868 | @@ -3387,18 +3697,21 @@ |
1869 | use strict; |
1870 | use warnings FATAL => 'all'; |
1871 | use English qw(-no_match_vars); |
1872 | -use sigtrap qw(handler finish untrapped normal-signals); |
1873 | + |
1874 | +use sigtrap 'handler', \&sig_int, 'normal-signals'; |
1875 | |
1876 | use Percona::Toolkit; |
1877 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
1878 | |
1879 | Transformers->import(qw(parse_timestamp)); |
1880 | |
1881 | -my $oktorun; |
1882 | +my $oktorun = 1; |
1883 | +my $exit_status = 0; |
1884 | |
1885 | sub main { |
1886 | - local @ARGV = @_; # set global ARGV for this package |
1887 | - $oktorun = 1; |
1888 | + local @ARGV = @_; # set global ARGV for this package |
1889 | + $oktorun = 1; |
1890 | + $exit_status = 0; |
1891 | |
1892 | # ######################################################################## |
1893 | # Get configuration information. |
1894 | @@ -3409,69 +3722,56 @@ |
1895 | |
1896 | my $dp = $o->DSNParser(); |
1897 | $dp->prop('set-vars', $o->get('set-vars')); |
1898 | - my $dsn_defaults = $dp->parse_options($o); |
1899 | - my $src_dsn = @ARGV ? $dp->parse(shift @ARGV, $dsn_defaults) : $dsn_defaults; |
1900 | - my $dst_dsn = $o->get('dest'); |
1901 | - |
1902 | - # The source dsn is not an option so --dest cannot use OptionParser |
1903 | - # to inherit values from it. Thus, we do it manually. --dest will |
1904 | - # inherit from --user, --port, etc. |
1905 | - if ( $src_dsn && $dst_dsn ) { |
1906 | - # If dest DSN only has D and t, this will copy h, P, S, etc. |
1907 | - # from the source DSN. |
1908 | - $dst_dsn = $dp->copy($src_dsn, $dst_dsn); |
1909 | + |
1910 | + my $src; |
1911 | + if ( my $src_dsn_string = shift @ARGV ) { |
1912 | + $src = Cxn->new( |
1913 | + dsn_string => $src_dsn_string, |
1914 | + parent => $o->get('daemonize'), |
1915 | + DSNParser => $dp, |
1916 | + OptionParser => $o, |
1917 | + ); |
1918 | + } |
1919 | + |
1920 | + my $dst; |
1921 | + if ( my $dst_dsn = $o->get('dest') ) { |
1922 | + $dst = Cxn->new( |
1923 | + dsn => $dst_dsn, |
1924 | + prev_dsn => ($src ? $src->dsn : undef), |
1925 | + parent => $o->get('daemonize'), |
1926 | + DSNParser => $dp, |
1927 | + OptionParser => $o, |
1928 | + ); |
1929 | } |
1930 | |
1931 | if ( !$o->get('help') ) { |
1932 | - if ( !$src_dsn ) { |
1933 | - $o->save_error('Missing or invalid source host'); |
1934 | - } |
1935 | - if ( $dst_dsn && !$dst_dsn->{D} ) { |
1936 | - $o->save_error("--dest requires a 'D' (database) part"); |
1937 | - } |
1938 | - if ( $dst_dsn && !$dst_dsn->{t} ) { |
1939 | - $o->save_error("--dest requires a 't' (table) part"); |
1940 | + if ( !$src ) { |
1941 | + $o->save_error('No DSN was specified.'); |
1942 | + } |
1943 | + if ( $dst && !$dst->dsn->{D} ) { |
1944 | + $o->save_error("--dest requires a 'D' (database) part."); |
1945 | + } |
1946 | + if ( $dst && !$dst->dsn->{t} ) { |
1947 | + $o->save_error("--dest requires a 't' (table) part."); |
1948 | } |
1949 | } |
1950 | |
1951 | $o->usage_or_errors(); |
1952 | |
1953 | # ######################################################################## |
1954 | - # Make common modules. |
1955 | - # ######################################################################## |
1956 | - my $q = new Quoter(); |
1957 | - my %modules = ( |
1958 | - o => $o, |
1959 | - dp => $dp, |
1960 | - q => $q, |
1961 | - ); |
1962 | - |
1963 | - # ######################################################################## |
1964 | - # Start working. |
1965 | - # ######################################################################## |
1966 | - my $dbh = get_cxn($src_dsn, 1, %modules); |
1967 | - my $start = time(); |
1968 | - my $end = $start + ($o->get('run-time') || 0); # When we should exit |
1969 | - my $now = $start; |
1970 | - my $dst_dbh; |
1971 | + # Connect to MySQL. |
1972 | + # ######################################################################## |
1973 | + my $q = Quoter->new(); |
1974 | + |
1975 | + $src->connect(); |
1976 | + |
1977 | my $ins_sth; |
1978 | - |
1979 | - # Since the user might not have specified a hostname for the connection, |
1980 | - # try to extract it from the $dbh |
1981 | - if ( !$src_dsn->{h} ) { |
1982 | - ($src_dsn->{h}) = $dbh->{mysql_hostinfo} =~ m/(\w+) via/; |
1983 | - PTDEBUG && _d('Got source host from dbh:', $src_dsn->{h}); |
1984 | - } |
1985 | - |
1986 | - if ( $dst_dsn ) { |
1987 | - my $db_tbl = join('.', |
1988 | - map { $q->quote($_) } |
1989 | - grep { $_ } |
1990 | - ( $dst_dsn->{D}, $dst_dsn->{t} )); |
1991 | - $dst_dbh = get_cxn($dst_dsn, 1, %modules); |
1992 | - my $sql = "INSERT IGNORE INTO $db_tbl VALUES (?, ?)"; |
1993 | - PTDEBUG && _d('insert sql:', $sql); |
1994 | - $ins_sth = $dst_dbh->prepare($sql); |
1995 | + if ( $dst ) { |
1996 | + $dst->connect(); |
1997 | + my $db_tbl = $q->join_quote($dst->dsn->{D}, $dst->dsn->{t}); |
1998 | + my $sql = "INSERT IGNORE INTO $db_tbl VALUES (?, ?)"; |
1999 | + PTDEBUG && _d('--dest INSERT SQL:', $sql); |
2000 | + $ins_sth = $dst->dbh->prepare($sql); |
2001 | } |
2002 | |
2003 | # ######################################################################## |
2004 | @@ -3489,6 +3789,16 @@ |
2005 | $daemon->make_PID_file(); |
2006 | } |
2007 | |
2008 | + # If we daemonized, the parent has already exited and we're the child. |
2009 | + # We shared a copy of every Cxn with the parent, and the parent's copies |
2010 | + # were destroyed but the dbhs were not disconnected because the parent |
2011 | + # attrib was true. Now, as the child, set it false so the dbhs will be |
2012 | + # disconnected when our Cxn copies are destroyed. If we didn't daemonize, |
2013 | + # then we're not really a parent (since we have no children), so set it |
2014 | + # false to auto-disconnect the dbhs when our Cxns are destroyed. |
2015 | + $src->{parent} = 0; |
2016 | + $dst->{parent} = 0 if $dst; |
2017 | + |
2018 | # ######################################################################## |
2019 | # Do the version-check |
2020 | # ######################################################################## |
2021 | @@ -3496,8 +3806,8 @@ |
2022 | VersionCheck::version_check( |
2023 | force => $o->got('version-check'), |
2024 | instances => [ |
2025 | - { dbh => $dbh, dsn => $src_dsn }, |
2026 | - ($dst_dbh ? { dbh => $dst_dbh, dsn => $dst_dsn } : ()) |
2027 | + { dbh => $src->dbh, dsn => $src->dsn }, |
2028 | + ($dst ? { dbh => $dst->dbh, dsn => $dst->dsn } : ()) |
2029 | ], |
2030 | ); |
2031 | } |
2032 | @@ -3505,43 +3815,77 @@ |
2033 | # ######################################################################## |
2034 | # Start finding and logging foreign key errors. |
2035 | # ######################################################################## |
2036 | - while ( # Quit if: |
2037 | - ($start == $end || $now < $end) # time is exceeded |
2038 | - && $oktorun # or instructed to quit |
2039 | - ) |
2040 | - { |
2041 | - my $text = $dbh->selectrow_hashref("SHOW /*!40100 ENGINE*/ INNODB STATUS")->{Status}; |
2042 | - my ($ts, $fk_error) = get_fk_error($text); |
2043 | - PTDEBUG && _d('ts:', $ts, 'fk error:', $fk_error); |
2044 | - |
2045 | - if ( $ts && $fk_error ) { |
2046 | - # Save and/or print the foreign key error. |
2047 | - if ( $ins_sth ) { |
2048 | - my $fk_ts = parse_timestamp($ts); |
2049 | - PTDEBUG && _d('Saving fk error', $ts, $fk_error); |
2050 | - eval { |
2051 | - $ins_sth->execute($fk_ts, $fk_error); |
2052 | - }; |
2053 | + my $run_time = Runtime->new( |
2054 | + run_time => $o->get('run-time'), |
2055 | + now => sub { return time }, |
2056 | + ); |
2057 | + |
2058 | + my $interval = $o->get('interval'); |
2059 | + my $iters = $o->get('iterations'); |
2060 | + PTDEBUG && _d('iterations:', $iters, 'interval:', $interval); |
2061 | + |
2062 | + ITERATION: |
2063 | + while ( |
2064 | + $oktorun |
2065 | + && $run_time->have_time() |
2066 | + && (!defined $iters || $iters--) |
2067 | + ) { |
2068 | + my ($ts, $fk_error); |
2069 | + eval { |
2070 | + my $sql = "SHOW /*!40100 ENGINE*/ INNODB STATUS " |
2071 | + . "/* pt-fk-error-logger */"; |
2072 | + PTDEBUG && _d($sql); |
2073 | + my $text = $src->dbh->selectrow_hashref($sql)->{status}; |
2074 | + ($ts, $fk_error) = get_fk_error($text); |
2075 | + }; |
2076 | + if ( my $e = $EVAL_ERROR ) { |
2077 | + PTDEBUG && _d('Error getting InnoDB status:', $e); |
2078 | + if ( $src->lost_connection($e) ) { |
2079 | + eval { $src->connect() }; |
2080 | if ( $EVAL_ERROR ) { |
2081 | - warn $EVAL_ERROR; |
2082 | - PTDEBUG && _d($EVAL_ERROR); |
2083 | - } |
2084 | - } |
2085 | - print "$ts $fk_error\n\n" if $o->get('print') || !$o->got('dest'); |
2086 | - } |
2087 | - |
2088 | - # If there's an --interval argument, run forever or till specified. |
2089 | - # Otherwise just run once. |
2090 | - if ( $o->get('interval') ) { |
2091 | - sleep($o->get('interval')); |
2092 | - $now = time(); |
2093 | + warn "Lost connection to MySQL. Will try to reconnect " |
2094 | + . "in the next iteration.\n"; |
2095 | + } |
2096 | + else { |
2097 | + PTDEBUG && _d('Reconnected to MySQL'); |
2098 | + redo ITERATION; |
2099 | + } |
2100 | + } |
2101 | + else { |
2102 | + warn "Error parsing SHOW ENGINE INNODB STATUS: $EVAL_ERROR"; |
2103 | + $exit_status |= 1; |
2104 | + } |
2105 | } |
2106 | else { |
2107 | - $oktorun = 0; |
2108 | + if ( $ts && $fk_error ) { |
2109 | + # Save and/or print the foreign key error. |
2110 | + if ( $ins_sth ) { |
2111 | + my $fk_ts = parse_timestamp($ts); |
2112 | + PTDEBUG && _d('Saving fk error', $ts, $fk_error); |
2113 | + eval { |
2114 | + $ins_sth->execute($fk_ts, $fk_error); |
2115 | + }; |
2116 | + if ( $EVAL_ERROR ) { |
2117 | + warn $EVAL_ERROR; |
2118 | + PTDEBUG && _d($EVAL_ERROR); |
2119 | + } |
2120 | + } |
2121 | + |
2122 | + if ( !$o->get('quiet') ) { |
2123 | + print "$ts $fk_error\n\n"; |
2124 | + } |
2125 | + } |
2126 | + } |
2127 | + |
2128 | + # Sleep if there's an --iteration left. |
2129 | + if ( !defined $iters || $iters ) { |
2130 | + PTDEBUG && _d('Sleeping', $interval, 'seconds'); |
2131 | + sleep $interval; |
2132 | } |
2133 | } |
2134 | |
2135 | - return 0; |
2136 | + PTDEBUG && _d('Done running, exiting', $exit_status); |
2137 | + return $exit_status; |
2138 | } |
2139 | |
2140 | # ############################################################################ |
2141 | @@ -3550,9 +3894,13 @@ |
2142 | |
2143 | sub get_fk_error { |
2144 | my ( $text ) = @_; |
2145 | + PTDEBUG && _d($text); |
2146 | |
2147 | # Quick check if text even has a foreign key error. |
2148 | - return unless $text =~ m/LATEST FOREIGN KEY ERROR/; |
2149 | + if ( $text !~ m/LATEST FOREIGN KEY ERROR/ ) { |
2150 | + PTDEBUG && _d('No fk error'); |
2151 | + return; |
2152 | + } |
2153 | |
2154 | # InnoDB timestamp |
2155 | my $idb_ts = qr/((?:\d{6}|\d{4}-\d\d-\d\d) .\d:\d\d:\d\d)/; |
2156 | @@ -3564,24 +3912,11 @@ |
2157 | return $ts, $fke; |
2158 | } |
2159 | |
2160 | -# Catches signals so the program can exit gracefully. |
2161 | -sub finish { |
2162 | - my ($signal) = @_; |
2163 | - print STDERR "Exiting on SIG$signal.\n"; |
2164 | +sub sig_int { |
2165 | + my ( $signal ) = @_; |
2166 | $oktorun = 0; |
2167 | -} |
2168 | - |
2169 | -sub get_cxn { |
2170 | - my ( $dsn, $ac, %args ) = @_; |
2171 | - my $o = $args{o}; |
2172 | - my $dp = $args{dp}; |
2173 | - |
2174 | - if ( $o->get('ask-pass') ) { |
2175 | - $dsn->{p} = OptionParser::prompt_noecho("Enter password: "); |
2176 | - } |
2177 | - my $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), {AutoCommit => $ac}); |
2178 | - $dbh->{InactiveDestroy} = 1; # Because of forking. |
2179 | - return $dbh; |
2180 | + print STDERR "# Caught SIG$signal. Use 'kill -ABRT $PID' if " |
2181 | + . "the tool does not exit normally in a few seconds.\n"; |
2182 | } |
2183 | |
2184 | sub _d { |
2185 | @@ -3606,22 +3941,28 @@ |
2186 | |
2187 | =head1 NAME |
2188 | |
2189 | -pt-fk-error-logger - Extract and log MySQL foreign key errors. |
2190 | +pt-fk-error-logger - Log MySQL foreign key errors. |
2191 | |
2192 | =head1 SYNOPSIS |
2193 | |
2194 | -Usage: pt-fk-error-logger [OPTION...] SOURCE_DSN |
2195 | +Usage: pt-fk-error-logger [OPTIONS] DSN |
2196 | |
2197 | -pt-fk-error-logger extracts and saves information about the most recent foreign |
2198 | -key errors in a MySQL server. |
2199 | +pt-fk-error-logger logs information about foreign key errors on the given |
2200 | +DSN. Information is printed to C<STDOUT>, and it can also be saved to a |
2201 | +table by specifying L<"--dest">. The tool runs for forever unless |
2202 | +L<"--run-time"> or L<"--iterations"> is specified. |
2203 | |
2204 | Print foreign key errors on host1: |
2205 | |
2206 | pt-fk-error-logger h=host1 |
2207 | |
2208 | -Save foreign key errors on host1 to db.foreign_key_errors table on host2: |
2209 | - |
2210 | - pt-fk-error-logger h=host1 --dest h=host1,D=db,t=foreign_key_errors |
2211 | +Print foreign key errors on host1 once then exit: |
2212 | + |
2213 | + pt-fk-error-logger h=host1 --iterations 1 |
2214 | + |
2215 | +Save foreign key errors on host1 to percona_schema.fke on host2: |
2216 | + |
2217 | + pt-fk-error-logger h=host1 --dest h=host2,D=percona_schema,t=fke |
2218 | |
2219 | =head1 RISKS |
2220 | |
2221 | @@ -3650,11 +3991,15 @@ |
2222 | way. Foreign key errors are uniquely identified by their timestamp. |
2223 | Only new (more recent) errors are printed or saved. |
2224 | |
2225 | +By default the tool runs forever, checking every L<"--interval"> seconds |
2226 | +for new foreign key errors. Specify L<"--run-time"> and/or L<"--iterations"> |
2227 | +to limit how long the tool runs. |
2228 | + |
2229 | =head1 OUTPUT |
2230 | |
2231 | -If L<"--print"> is given or no L<"--dest"> is given, then pt-fk-error-logger |
2232 | -prints the foreign key error text to STDOUT exactly as it appeared in |
2233 | -C<SHOW INNODB STATUS>. |
2234 | +The foreign key error text from C<SHOW ENGINE INNODB STATUS> is printed |
2235 | +to C<STDOUT>, unless L<"--quiet"> is specified. Errors and warnings |
2236 | +are printed to C<STDERR>. |
2237 | |
2238 | =head1 OPTIONS |
2239 | |
2240 | @@ -3698,11 +4043,12 @@ |
2241 | |
2242 | type: DSN |
2243 | |
2244 | -DSN for where to store foreign key errors; specify at least a database (D) and table (t). |
2245 | +Save foreign key errors in this table. The DSN must specify a database (D) |
2246 | +and table (t). |
2247 | |
2248 | -Missing values are filled in with the same values from the source host, so you |
2249 | -can usually omit most parts of this argument if you're storing foreign key |
2250 | -errors on the same server on which they happen. |
2251 | +Missing DSN values are inherited from the DSN being monitored, so you |
2252 | +can omit most values if you're saving foreign key errors on the same |
2253 | +host. |
2254 | |
2255 | The following table is suggested: |
2256 | |
2257 | @@ -3726,10 +4072,21 @@ |
2258 | |
2259 | =item --interval |
2260 | |
2261 | -type: time; default: 0 |
2262 | +type: time; default: 30 |
2263 | |
2264 | How often to check for foreign key errors. |
2265 | |
2266 | +=item --iterations |
2267 | + |
2268 | +type: int |
2269 | + |
2270 | +How many times to check for foreign key errors. By default, this option |
2271 | +is undefined which means an infinite number of iterations. The tool always |
2272 | +exits for L<"--run-time">, regardless of the value specified for this option. |
2273 | +For example, the tool will exit after 1 minute with |
2274 | +C<--run-time 1m --iterations 4 --interval 30> because 4 iterations at 30 |
2275 | +second intervals would take 2 minutes, longer than the 1 mintue run-time. |
2276 | + |
2277 | =item --log |
2278 | |
2279 | type: string |
2280 | @@ -3746,10 +4103,11 @@ |
2281 | |
2282 | type: string |
2283 | |
2284 | -Create the given PID file when daemonized. The file contains the process ID of |
2285 | -the daemonized instance. The PID file is removed when the daemonized instance |
2286 | -exits. The program checks for the existence of the PID file when starting; if |
2287 | -it exists and the process with the matching PID exists, the program exits. |
2288 | +Create the given PID file. The tool won't start if the PID file already |
2289 | +exists and the PID it contains is different than the current PID. However, |
2290 | +if the PID file exists and the PID it contains is no longer running, the |
2291 | +tool will overwrite the PID file with the current PID. The PID file is |
2292 | +removed automatically when the tool exits. |
2293 | |
2294 | =item --port |
2295 | |
2296 | @@ -3757,15 +4115,15 @@ |
2297 | |
2298 | Port number to use for connection. |
2299 | |
2300 | -=item --print |
2301 | +=item --quiet |
2302 | |
2303 | -Print results on standard output. See L<"OUTPUT"> for more. |
2304 | +Do not print foreign key errors; only print errors and warnings to C<STDERR>. |
2305 | |
2306 | =item --run-time |
2307 | |
2308 | type: time |
2309 | |
2310 | -How long to run before exiting. |
2311 | +How long to run before exiting. By default, the tool runs forever. |
2312 | |
2313 | =item --set-vars |
2314 | |
2315 | @@ -3948,7 +4306,7 @@ |
2316 | |
2317 | =head1 COPYRIGHT, LICENSE, AND WARRANTY |
2318 | |
2319 | -This program is copyright 2011-2012 Percona Ireland Ltd. |
2320 | +This program is copyright 2011-2013 Percona Ireland Ltd. |
2321 | |
2322 | THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED |
2323 | WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF |
2324 | |
2325 | === modified file 'bin/pt-heartbeat' |
2326 | --- bin/pt-heartbeat 2013-02-22 17:47:57 +0000 |
2327 | +++ bin/pt-heartbeat 2013-02-27 23:41:26 +0000 |
2328 | @@ -5664,10 +5664,11 @@ |
2329 | |
2330 | type: string |
2331 | |
2332 | -Create the given PID file when daemonized. The file contains the process ID of |
2333 | -the daemonized instance. The PID file is removed when the daemonized instance |
2334 | -exits. The program checks for the existence of the PID file when starting; if |
2335 | -it exists and the process with the matching PID exists, the program exits. |
2336 | +Create the given PID file. The tool won't start if the PID file already |
2337 | +exists and the PID it contains is different than the current PID. However, |
2338 | +if the PID file exists and the PID it contains is no longer running, the |
2339 | +tool will overwrite the PID file with the current PID. The PID file is |
2340 | +removed automatically when the tool exits. |
2341 | |
2342 | =item --port |
2343 | |
2344 | |
2345 | === modified file 'bin/pt-kill' |
2346 | --- bin/pt-kill 2013-02-22 17:47:57 +0000 |
2347 | +++ bin/pt-kill 2013-02-27 23:41:26 +0000 |
2348 | @@ -4969,23 +4969,24 @@ |
2349 | } |
2350 | |
2351 | my $self = { |
2352 | - dsn => $dsn, |
2353 | - dbh => $args{dbh}, |
2354 | - dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
2355 | - hostname => '', |
2356 | - set => $args{set}, |
2357 | - NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
2358 | - dbh_set => 0, |
2359 | - OptionParser => $o, |
2360 | - DSNParser => $dp, |
2361 | + dsn => $dsn, |
2362 | + dbh => $args{dbh}, |
2363 | + dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
2364 | + hostname => '', |
2365 | + set => $args{set}, |
2366 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
2367 | + dbh_set => 0, |
2368 | + OptionParser => $o, |
2369 | + DSNParser => $dp, |
2370 | is_cluster_node => undef, |
2371 | + parent => $args{parent}, |
2372 | }; |
2373 | |
2374 | return bless $self, $class; |
2375 | } |
2376 | |
2377 | sub connect { |
2378 | - my ( $self ) = @_; |
2379 | + my ( $self, %opts ) = @_; |
2380 | my $dsn = $self->{dsn}; |
2381 | my $dp = $self->{DSNParser}; |
2382 | my $o = $self->{OptionParser}; |
2383 | @@ -4996,11 +4997,18 @@ |
2384 | $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); |
2385 | $self->{asked_for_pass} = 1; |
2386 | } |
2387 | - $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 }); |
2388 | + $dbh = $dp->get_dbh( |
2389 | + $dp->get_cxn_params($dsn), |
2390 | + { |
2391 | + AutoCommit => 1, |
2392 | + %opts, |
2393 | + }, |
2394 | + ); |
2395 | } |
2396 | - PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name}); |
2397 | |
2398 | - return $self->set_dbh($dbh); |
2399 | + $dbh = $self->set_dbh($dbh); |
2400 | + PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name}); |
2401 | + return $dbh; |
2402 | } |
2403 | |
2404 | sub set_dbh { |
2405 | @@ -5023,6 +5031,11 @@ |
2406 | $self->{hostname} = $hostname; |
2407 | } |
2408 | |
2409 | + if ( $self->{parent} ) { |
2410 | + PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent'); |
2411 | + $dbh->{InactiveDestroy} = 1; |
2412 | + } |
2413 | + |
2414 | if ( my $set = $self->{set}) { |
2415 | $set->($dbh); |
2416 | } |
2417 | @@ -5032,6 +5045,13 @@ |
2418 | return $dbh; |
2419 | } |
2420 | |
2421 | +sub lost_connection { |
2422 | + my ($self, $e) = @_; |
2423 | + return 0 unless $e; |
2424 | + return $e =~ m/MySQL server has gone away/ |
2425 | + || $e =~ m/Lost connection to MySQL server/; |
2426 | +} |
2427 | + |
2428 | sub dbh { |
2429 | my ($self) = @_; |
2430 | return $self->{dbh}; |
2431 | @@ -5050,12 +5070,21 @@ |
2432 | |
2433 | sub DESTROY { |
2434 | my ($self) = @_; |
2435 | - if ( $self->{dbh} |
2436 | - && blessed($self->{dbh}) |
2437 | - && $self->{dbh}->can("disconnect") ) { |
2438 | - PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name}); |
2439 | + |
2440 | + PTDEBUG && _d('Destroying cxn'); |
2441 | + |
2442 | + if ( $self->{parent} ) { |
2443 | + PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent'); |
2444 | + } |
2445 | + elsif ( $self->{dbh} |
2446 | + && blessed($self->{dbh}) |
2447 | + && $self->{dbh}->can("disconnect") ) |
2448 | + { |
2449 | + PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname}, |
2450 | + $self->{dsn_name}); |
2451 | $self->{dbh}->disconnect(); |
2452 | } |
2453 | + |
2454 | return; |
2455 | } |
2456 | |
2457 | @@ -6464,6 +6493,7 @@ |
2458 | $cxn = Cxn->new( |
2459 | dsn_string => shift @ARGV, |
2460 | NAME_lc => 0, |
2461 | + parent => $o->get('daemonize'), |
2462 | DSNParser => $dp, |
2463 | OptionParser => $o, |
2464 | ); |
2465 | @@ -6643,6 +6673,15 @@ |
2466 | $daemon->make_PID_file(); |
2467 | } |
2468 | |
2469 | + # If we daemonized, the parent has already exited and we're the child. |
2470 | + # We shared a copy of every Cxn with the parent, and the parent's copies |
2471 | + # were destroyed but the dbhs were not disconnected because the parent |
2472 | + # attrib was true. Now, as the child, set it false so the dbhs will be |
2473 | + # disconnected when our Cxn copies are destroyed. If we didn't daemonize, |
2474 | + # then we're not really a parent (since we have no children), so set it |
2475 | + # false to auto-disconnect the dbhs when our Cxns are destroyed. |
2476 | + $cxn->{parent} = 0 if $cxn; |
2477 | + |
2478 | # ######################################################################## |
2479 | # Do the version-check |
2480 | # ######################################################################## |
2481 | @@ -7311,10 +7350,11 @@ |
2482 | |
2483 | type: string |
2484 | |
2485 | -Create the given PID file when daemonized. The file contains the process ID of |
2486 | -the daemonized instance. The PID file is removed when the daemonized instance |
2487 | -exits. The program checks for the existence of the PID file when starting; if |
2488 | -it exists and the process with the matching PID exists, the program exits. |
2489 | +Create the given PID file. The tool won't start if the PID file already |
2490 | +exists and the PID it contains is different than the current PID. However, |
2491 | +if the PID file exists and the PID it contains is no longer running, the |
2492 | +tool will overwrite the PID file with the current PID. The PID file is |
2493 | +removed automatically when the tool exits. |
2494 | |
2495 | =item --port |
2496 | |
2497 | |
2498 | === modified file 'bin/pt-online-schema-change' |
2499 | --- bin/pt-online-schema-change 2013-02-22 17:47:57 +0000 |
2500 | +++ bin/pt-online-schema-change 2013-02-27 23:41:26 +0000 |
2501 | @@ -4018,23 +4018,24 @@ |
2502 | } |
2503 | |
2504 | my $self = { |
2505 | - dsn => $dsn, |
2506 | - dbh => $args{dbh}, |
2507 | - dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
2508 | - hostname => '', |
2509 | - set => $args{set}, |
2510 | - NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
2511 | - dbh_set => 0, |
2512 | - OptionParser => $o, |
2513 | - DSNParser => $dp, |
2514 | + dsn => $dsn, |
2515 | + dbh => $args{dbh}, |
2516 | + dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
2517 | + hostname => '', |
2518 | + set => $args{set}, |
2519 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
2520 | + dbh_set => 0, |
2521 | + OptionParser => $o, |
2522 | + DSNParser => $dp, |
2523 | is_cluster_node => undef, |
2524 | + parent => $args{parent}, |
2525 | }; |
2526 | |
2527 | return bless $self, $class; |
2528 | } |
2529 | |
2530 | sub connect { |
2531 | - my ( $self ) = @_; |
2532 | + my ( $self, %opts ) = @_; |
2533 | my $dsn = $self->{dsn}; |
2534 | my $dp = $self->{DSNParser}; |
2535 | my $o = $self->{OptionParser}; |
2536 | @@ -4045,11 +4046,18 @@ |
2537 | $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); |
2538 | $self->{asked_for_pass} = 1; |
2539 | } |
2540 | - $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 }); |
2541 | + $dbh = $dp->get_dbh( |
2542 | + $dp->get_cxn_params($dsn), |
2543 | + { |
2544 | + AutoCommit => 1, |
2545 | + %opts, |
2546 | + }, |
2547 | + ); |
2548 | } |
2549 | - PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name}); |
2550 | |
2551 | - return $self->set_dbh($dbh); |
2552 | + $dbh = $self->set_dbh($dbh); |
2553 | + PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name}); |
2554 | + return $dbh; |
2555 | } |
2556 | |
2557 | sub set_dbh { |
2558 | @@ -4072,6 +4080,11 @@ |
2559 | $self->{hostname} = $hostname; |
2560 | } |
2561 | |
2562 | + if ( $self->{parent} ) { |
2563 | + PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent'); |
2564 | + $dbh->{InactiveDestroy} = 1; |
2565 | + } |
2566 | + |
2567 | if ( my $set = $self->{set}) { |
2568 | $set->($dbh); |
2569 | } |
2570 | @@ -4081,6 +4094,13 @@ |
2571 | return $dbh; |
2572 | } |
2573 | |
2574 | +sub lost_connection { |
2575 | + my ($self, $e) = @_; |
2576 | + return 0 unless $e; |
2577 | + return $e =~ m/MySQL server has gone away/ |
2578 | + || $e =~ m/Lost connection to MySQL server/; |
2579 | +} |
2580 | + |
2581 | sub dbh { |
2582 | my ($self) = @_; |
2583 | return $self->{dbh}; |
2584 | @@ -4099,12 +4119,21 @@ |
2585 | |
2586 | sub DESTROY { |
2587 | my ($self) = @_; |
2588 | - if ( $self->{dbh} |
2589 | - && blessed($self->{dbh}) |
2590 | - && $self->{dbh}->can("disconnect") ) { |
2591 | - PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name}); |
2592 | + |
2593 | + PTDEBUG && _d('Destroying cxn'); |
2594 | + |
2595 | + if ( $self->{parent} ) { |
2596 | + PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent'); |
2597 | + } |
2598 | + elsif ( $self->{dbh} |
2599 | + && blessed($self->{dbh}) |
2600 | + && $self->{dbh}->can("disconnect") ) |
2601 | + { |
2602 | + PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname}, |
2603 | + $self->{dsn_name}); |
2604 | $self->{dbh}->disconnect(); |
2605 | } |
2606 | + |
2607 | return; |
2608 | } |
2609 | |
2610 | @@ -10839,10 +10868,11 @@ |
2611 | |
2612 | type: string |
2613 | |
2614 | -Create the given PID file. The file contains the process ID of the tool's |
2615 | -instance. The PID file is removed when the tool exits. The tool checks for |
2616 | -the existence of the PID file when starting; if it exists and the process with |
2617 | -the matching PID exists, the tool exits. |
2618 | +Create the given PID file. The tool won't start if the PID file already |
2619 | +exists and the PID it contains is different than the current PID. However, |
2620 | +if the PID file exists and the PID it contains is no longer running, the |
2621 | +tool will overwrite the PID file with the current PID. The PID file is |
2622 | +removed automatically when the tool exits. |
2623 | |
2624 | =item --port |
2625 | |
2626 | |
2627 | === modified file 'bin/pt-query-advisor' |
2628 | --- bin/pt-query-advisor 2013-02-22 17:47:57 +0000 |
2629 | +++ bin/pt-query-advisor 2013-02-27 23:41:26 +0000 |
2630 | @@ -9092,11 +9092,11 @@ |
2631 | |
2632 | type: string |
2633 | |
2634 | -Create the given PID file when daemonized. The file contains the process |
2635 | -ID of the daemonized instance. The PID file is removed when the |
2636 | -daemonized instance exits. The program checks for the existence of the |
2637 | -PID file when starting; if it exists and the process with the matching PID |
2638 | -exists, the program exits. |
2639 | +Create the given PID file. The tool won't start if the PID file already |
2640 | +exists and the PID it contains is different than the current PID. However, |
2641 | +if the PID file exists and the PID it contains is no longer running, the |
2642 | +tool will overwrite the PID file with the current PID. The PID file is |
2643 | +removed automatically when the tool exits. |
2644 | |
2645 | =item --port |
2646 | |
2647 | |
2648 | === modified file 'bin/pt-query-digest' |
2649 | --- bin/pt-query-digest 2013-02-25 15:13:35 +0000 |
2650 | +++ bin/pt-query-digest 2013-02-27 23:41:26 +0000 |
2651 | @@ -14967,11 +14967,11 @@ |
2652 | |
2653 | type: string |
2654 | |
2655 | -Create the given PID file when daemonized. The file contains the process |
2656 | -ID of the daemonized instance. The PID file is removed when the |
2657 | -daemonized instance exits. The program checks for the existence of the |
2658 | -PID file when starting; if it exists and the process with the matching PID |
2659 | -exists, the program exits. |
2660 | +Create the given PID file. The tool won't start if the PID file already |
2661 | +exists and the PID it contains is different than the current PID. However, |
2662 | +if the PID file exists and the PID it contains is no longer running, the |
2663 | +tool will overwrite the PID file with the current PID. The PID file is |
2664 | +removed automatically when the tool exits. |
2665 | |
2666 | =item --port |
2667 | |
2668 | |
2669 | === modified file 'bin/pt-show-grants' |
2670 | --- bin/pt-show-grants 2013-01-03 00:54:18 +0000 |
2671 | +++ bin/pt-show-grants 2013-02-27 23:41:26 +0000 |
2672 | @@ -2093,14 +2093,11 @@ |
2673 | |
2674 | type: string |
2675 | |
2676 | -Create the given PID file. The file contains the process ID of the script. |
2677 | -The PID file is removed when the script exits. Before starting, the script |
2678 | -checks if the PID file already exists. If it does not, then the script creates |
2679 | -and writes its own PID to it. If it does, then the script checks the following: |
2680 | -if the file contains a PID and a process is running with that PID, then |
2681 | -the script dies; or, if there is no process running with that PID, then the |
2682 | -script overwrites the file with its own PID and starts; else, if the file |
2683 | -contains no PID, then the script dies. |
2684 | +Create the given PID file. The tool won't start if the PID file already |
2685 | +exists and the PID it contains is different than the current PID. However, |
2686 | +if the PID file exists and the PID it contains is no longer running, the |
2687 | +tool will overwrite the PID file with the current PID. The PID file is |
2688 | +removed automatically when the tool exits. |
2689 | |
2690 | =item --port |
2691 | |
2692 | |
2693 | === modified file 'bin/pt-slave-delay' |
2694 | --- bin/pt-slave-delay 2013-02-22 15:00:55 +0000 |
2695 | +++ bin/pt-slave-delay 2013-02-27 23:41:26 +0000 |
2696 | @@ -4478,11 +4478,11 @@ |
2697 | |
2698 | type: string |
2699 | |
2700 | -Create the given PID file when daemonized. The file contains the process |
2701 | -ID of the daemonized instance. The PID file is removed when the |
2702 | -daemonized instance exits. The program checks for the existence of the |
2703 | -PID file when starting; if it exists and the process with the matching PID |
2704 | -exists, the program exits. |
2705 | +Create the given PID file. The tool won't start if the PID file already |
2706 | +exists and the PID it contains is different than the current PID. However, |
2707 | +if the PID file exists and the PID it contains is no longer running, the |
2708 | +tool will overwrite the PID file with the current PID. The PID file is |
2709 | +removed automatically when the tool exits. |
2710 | |
2711 | =item --port |
2712 | |
2713 | |
2714 | === modified file 'bin/pt-slave-find' |
2715 | --- bin/pt-slave-find 2013-02-01 17:06:13 +0000 |
2716 | +++ bin/pt-slave-find 2013-02-27 23:41:26 +0000 |
2717 | @@ -3960,14 +3960,11 @@ |
2718 | |
2719 | type: string |
2720 | |
2721 | -Create the given PID file. The file contains the process ID of the script. |
2722 | -The PID file is removed when the script exits. Before starting, the script |
2723 | -checks if the PID file already exists. If it does not, then the script creates |
2724 | -and writes its own PID to it. If it does, then the script checks the following: |
2725 | -if the file contains a PID and a process is running with that PID, then |
2726 | -the script dies; or, if there is no process running with that PID, then the |
2727 | -script overwrites the file with its own PID and starts; else, if the file |
2728 | -contains no PID, then the script dies. |
2729 | +Create the given PID file. The tool won't start if the PID file already |
2730 | +exists and the PID it contains is different than the current PID. However, |
2731 | +if the PID file exists and the PID it contains is no longer running, the |
2732 | +tool will overwrite the PID file with the current PID. The PID file is |
2733 | +removed automatically when the tool exits. |
2734 | |
2735 | =item --port |
2736 | |
2737 | |
2738 | === modified file 'bin/pt-slave-restart' |
2739 | --- bin/pt-slave-restart 2013-02-22 17:47:57 +0000 |
2740 | +++ bin/pt-slave-restart 2013-02-27 23:41:26 +0000 |
2741 | @@ -5312,11 +5312,11 @@ |
2742 | |
2743 | type: string |
2744 | |
2745 | -Create the given PID file when daemonized. The file contains the process |
2746 | -ID of the daemonized instance. The PID file is removed when the |
2747 | -daemonized instance exits. The program checks for the existence of the |
2748 | -PID file when starting; if it exists and the process with the matching PID |
2749 | -exists, the program exits. |
2750 | +Create the given PID file. The tool won't start if the PID file already |
2751 | +exists and the PID it contains is different than the current PID. However, |
2752 | +if the PID file exists and the PID it contains is no longer running, the |
2753 | +tool will overwrite the PID file with the current PID. The PID file is |
2754 | +removed automatically when the tool exits. |
2755 | |
2756 | =item --port |
2757 | |
2758 | |
2759 | === modified file 'bin/pt-stalk' |
2760 | --- bin/pt-stalk 2013-01-24 19:08:09 +0000 |
2761 | +++ bin/pt-stalk 2013-02-27 23:41:26 +0000 |
2762 | @@ -1723,7 +1723,11 @@ |
2763 | |
2764 | type: string; default: /var/run/pt-stalk.pid |
2765 | |
2766 | -Create a PID file when daemonized. |
2767 | +Create the given PID file. The tool won't start if the PID file already |
2768 | +exists and the PID it contains is different than the current PID. However, |
2769 | +if the PID file exists and the PID it contains is no longer running, the |
2770 | +tool will overwrite the PID file with the current PID. The PID file is |
2771 | +removed automatically when the tool exits. |
2772 | |
2773 | =item --plugin |
2774 | |
2775 | |
2776 | === modified file 'bin/pt-table-checksum' |
2777 | --- bin/pt-table-checksum 2013-02-22 17:47:57 +0000 |
2778 | +++ bin/pt-table-checksum 2013-02-27 23:41:26 +0000 |
2779 | @@ -3376,23 +3376,24 @@ |
2780 | } |
2781 | |
2782 | my $self = { |
2783 | - dsn => $dsn, |
2784 | - dbh => $args{dbh}, |
2785 | - dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
2786 | - hostname => '', |
2787 | - set => $args{set}, |
2788 | - NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
2789 | - dbh_set => 0, |
2790 | - OptionParser => $o, |
2791 | - DSNParser => $dp, |
2792 | + dsn => $dsn, |
2793 | + dbh => $args{dbh}, |
2794 | + dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
2795 | + hostname => '', |
2796 | + set => $args{set}, |
2797 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
2798 | + dbh_set => 0, |
2799 | + OptionParser => $o, |
2800 | + DSNParser => $dp, |
2801 | is_cluster_node => undef, |
2802 | + parent => $args{parent}, |
2803 | }; |
2804 | |
2805 | return bless $self, $class; |
2806 | } |
2807 | |
2808 | sub connect { |
2809 | - my ( $self ) = @_; |
2810 | + my ( $self, %opts ) = @_; |
2811 | my $dsn = $self->{dsn}; |
2812 | my $dp = $self->{DSNParser}; |
2813 | my $o = $self->{OptionParser}; |
2814 | @@ -3403,11 +3404,18 @@ |
2815 | $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); |
2816 | $self->{asked_for_pass} = 1; |
2817 | } |
2818 | - $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 }); |
2819 | + $dbh = $dp->get_dbh( |
2820 | + $dp->get_cxn_params($dsn), |
2821 | + { |
2822 | + AutoCommit => 1, |
2823 | + %opts, |
2824 | + }, |
2825 | + ); |
2826 | } |
2827 | - PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name}); |
2828 | |
2829 | - return $self->set_dbh($dbh); |
2830 | + $dbh = $self->set_dbh($dbh); |
2831 | + PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name}); |
2832 | + return $dbh; |
2833 | } |
2834 | |
2835 | sub set_dbh { |
2836 | @@ -3430,6 +3438,11 @@ |
2837 | $self->{hostname} = $hostname; |
2838 | } |
2839 | |
2840 | + if ( $self->{parent} ) { |
2841 | + PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent'); |
2842 | + $dbh->{InactiveDestroy} = 1; |
2843 | + } |
2844 | + |
2845 | if ( my $set = $self->{set}) { |
2846 | $set->($dbh); |
2847 | } |
2848 | @@ -3439,6 +3452,13 @@ |
2849 | return $dbh; |
2850 | } |
2851 | |
2852 | +sub lost_connection { |
2853 | + my ($self, $e) = @_; |
2854 | + return 0 unless $e; |
2855 | + return $e =~ m/MySQL server has gone away/ |
2856 | + || $e =~ m/Lost connection to MySQL server/; |
2857 | +} |
2858 | + |
2859 | sub dbh { |
2860 | my ($self) = @_; |
2861 | return $self->{dbh}; |
2862 | @@ -3457,12 +3477,21 @@ |
2863 | |
2864 | sub DESTROY { |
2865 | my ($self) = @_; |
2866 | - if ( $self->{dbh} |
2867 | - && blessed($self->{dbh}) |
2868 | - && $self->{dbh}->can("disconnect") ) { |
2869 | - PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name}); |
2870 | + |
2871 | + PTDEBUG && _d('Destroying cxn'); |
2872 | + |
2873 | + if ( $self->{parent} ) { |
2874 | + PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent'); |
2875 | + } |
2876 | + elsif ( $self->{dbh} |
2877 | + && blessed($self->{dbh}) |
2878 | + && $self->{dbh}->can("disconnect") ) |
2879 | + { |
2880 | + PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname}, |
2881 | + $self->{dsn_name}); |
2882 | $self->{dbh}->disconnect(); |
2883 | } |
2884 | + |
2885 | return; |
2886 | } |
2887 | |
2888 | @@ -11590,14 +11619,11 @@ |
2889 | |
2890 | type: string |
2891 | |
2892 | -Create the given PID file. The file contains the process ID of the script. |
2893 | -The PID file is removed when the script exits. Before starting, the script |
2894 | -checks if the PID file already exists. If it does not, then the script creates |
2895 | -and writes its own PID to it. If it does, then the script checks the following: |
2896 | -if the file contains a PID and a process is running with that PID, then |
2897 | -the script dies; or, if there is no process running with that PID, then the |
2898 | -script overwrites the file with its own PID and starts; else, if the file |
2899 | -contains no PID, then the script dies. |
2900 | +Create the given PID file. The tool won't start if the PID file already |
2901 | +exists and the PID it contains is different than the current PID. However, |
2902 | +if the PID file exists and the PID it contains is no longer running, the |
2903 | +tool will overwrite the PID file with the current PID. The PID file is |
2904 | +removed automatically when the tool exits. |
2905 | |
2906 | =item --port |
2907 | |
2908 | |
2909 | === modified file 'bin/pt-table-sync' |
2910 | --- bin/pt-table-sync 2013-02-22 17:47:57 +0000 |
2911 | +++ bin/pt-table-sync 2013-02-27 23:41:26 +0000 |
2912 | @@ -12051,14 +12051,11 @@ |
2913 | |
2914 | type: string |
2915 | |
2916 | -Create the given PID file. The file contains the process ID of the script. |
2917 | -The PID file is removed when the script exits. Before starting, the script |
2918 | -checks if the PID file already exists. If it does not, then the script creates |
2919 | -and writes its own PID to it. If it does, then the script checks the following: |
2920 | -if the file contains a PID and a process is running with that PID, then |
2921 | -the script dies; or, if there is no process running with that PID, then the |
2922 | -script overwrites the file with its own PID and starts; else, if the file |
2923 | -contains no PID, then the script dies. |
2924 | +Create the given PID file. The tool won't start if the PID file already |
2925 | +exists and the PID it contains is different than the current PID. However, |
2926 | +if the PID file exists and the PID it contains is no longer running, the |
2927 | +tool will overwrite the PID file with the current PID. The PID file is |
2928 | +removed automatically when the tool exits. |
2929 | |
2930 | =item --port |
2931 | |
2932 | |
2933 | === modified file 'bin/pt-table-usage' |
2934 | --- bin/pt-table-usage 2013-02-19 20:01:58 +0000 |
2935 | +++ bin/pt-table-usage 2013-02-27 23:41:26 +0000 |
2936 | @@ -7192,11 +7192,11 @@ |
2937 | |
2938 | type: string |
2939 | |
2940 | -Create the given PID file when running. The file contains the process |
2941 | -ID of the daemonized instance. The PID file is removed when the |
2942 | -daemonized instance exits. The program checks for the existence of the |
2943 | -PID file when starting; if it exists and the process with the matching PID |
2944 | -exists, the program exits. |
2945 | +Create the given PID file. The tool won't start if the PID file already |
2946 | +exists and the PID it contains is different than the current PID. However, |
2947 | +if the PID file exists and the PID it contains is no longer running, the |
2948 | +tool will overwrite the PID file with the current PID. The PID file is |
2949 | +removed automatically when the tool exits. |
2950 | |
2951 | =item --port |
2952 | |
2953 | |
2954 | === modified file 'bin/pt-upgrade' |
2955 | --- bin/pt-upgrade 2013-02-22 17:47:57 +0000 |
2956 | +++ bin/pt-upgrade 2013-02-27 23:41:26 +0000 |
2957 | @@ -13483,11 +13483,11 @@ |
2958 | |
2959 | type: string |
2960 | |
2961 | -Create the given PID file when daemonized. The file contains the process |
2962 | -ID of the daemonized instance. The PID file is removed when the |
2963 | -daemonized instance exits. The program checks for the existence of the |
2964 | -PID file when starting; if it exists and the process with the matching PID |
2965 | -exists, the program exits. |
2966 | +Create the given PID file. The tool won't start if the PID file already |
2967 | +exists and the PID it contains is different than the current PID. However, |
2968 | +if the PID file exists and the PID it contains is no longer running, the |
2969 | +tool will overwrite the PID file with the current PID. The PID file is |
2970 | +removed automatically when the tool exits. |
2971 | |
2972 | =item --port |
2973 | |
2974 | |
2975 | === modified file 'bin/pt-variable-advisor' |
2976 | --- bin/pt-variable-advisor 2013-02-22 15:00:55 +0000 |
2977 | +++ bin/pt-variable-advisor 2013-02-27 23:41:26 +0000 |
2978 | @@ -5752,11 +5752,11 @@ |
2979 | |
2980 | type: string |
2981 | |
2982 | -Create the given PID file when daemonized. The file contains the process |
2983 | -ID of the daemonized instance. The PID file is removed when the |
2984 | -daemonized instance exits. The program checks for the existence of the |
2985 | -PID file when starting; if it exists and the process with the matching PID |
2986 | -exists, the program exits. |
2987 | +Create the given PID file. The tool won't start if the PID file already |
2988 | +exists and the PID it contains is different than the current PID. However, |
2989 | +if the PID file exists and the PID it contains is no longer running, the |
2990 | +tool will overwrite the PID file with the current PID. The PID file is |
2991 | +removed automatically when the tool exits. |
2992 | |
2993 | =item --port |
2994 | |
2995 | |
2996 | === modified file 'bin/pt-visual-explain' |
2997 | --- bin/pt-visual-explain 2013-02-19 20:01:58 +0000 |
2998 | +++ bin/pt-visual-explain 2013-02-27 23:41:26 +0000 |
2999 | @@ -2952,14 +2952,11 @@ |
3000 | |
3001 | type: string |
3002 | |
3003 | -Create the given PID file. The file contains the process ID of the script. |
3004 | -The PID file is removed when the script exits. Before starting, the script |
3005 | -checks if the PID file already exists. If it does not, then the script creates |
3006 | -and writes its own PID to it. If it does, then the script checks the following: |
3007 | -if the file contains a PID and a process is running with that PID, then |
3008 | -the script dies; or, if there is no process running with that PID, then the |
3009 | -script overwrites the file with its own PID and starts; else, if the file |
3010 | -contains no PID, then the script dies. |
3011 | +Create the given PID file. The tool won't start if the PID file already |
3012 | +exists and the PID it contains is different than the current PID. However, |
3013 | +if the PID file exists and the PID it contains is no longer running, the |
3014 | +tool will overwrite the PID file with the current PID. The PID file is |
3015 | +removed automatically when the tool exits. |
3016 | |
3017 | =item --port |
3018 | |
3019 | |
3020 | === modified file 'docs/percona-toolkit.pod' |
3021 | --- docs/percona-toolkit.pod 2013-01-03 00:54:18 +0000 |
3022 | +++ docs/percona-toolkit.pod 2013-02-27 23:41:26 +0000 |
3023 | @@ -175,6 +175,34 @@ |
3024 | For more free, open-source software developed Percona, visit |
3025 | L<http://www.percona.com/software/>. |
3026 | |
3027 | +=head1 SPECIAL OPTION TYPES |
3028 | + |
3029 | +=over |
3030 | + |
3031 | +=item time |
3032 | + |
3033 | +Time values are seconds by default. For example, C<--run-time 60> means |
3034 | +60 seconds. Time values support an optional suffix: s (seconds), |
3035 | +m (minutes), h (hours), d (days). C<--run-time 1m> means 1 minute |
3036 | +(the same as 60 seconds). |
3037 | + |
3038 | +=item size |
3039 | + |
3040 | +Size values are bytes by default. For example, C<--disk-space-free 1024> |
3041 | +means 1 Kibibyte. Size values support an optional suffix: k (Kibibyte), |
3042 | +M (Mebibyte), G (Gibibyte). |
3043 | + |
3044 | +=item DSN |
3045 | + |
3046 | +See L<"DSN (DATA SOURCE NAME) SPECIFICATIONS">. |
3047 | + |
3048 | +=item Hash, hash, Array, array |
3049 | + |
3050 | +Hash, hash, Array, and array values are comma-separated lists of values. |
3051 | +For example, C<--ignore-tables foo,bar> ignores tables C<foo> and C<bar>. |
3052 | + |
3053 | +=back |
3054 | + |
3055 | =head1 CONFIGURATION FILES |
3056 | |
3057 | Percona Toolkit tools can read options from configuration files. The |
3058 | |
3059 | === modified file 'lib/Cxn.pm' |
3060 | --- lib/Cxn.pm 2013-01-03 00:19:16 +0000 |
3061 | +++ lib/Cxn.pm 2013-02-27 23:41:26 +0000 |
3062 | @@ -98,23 +98,24 @@ |
3063 | } |
3064 | |
3065 | my $self = { |
3066 | - dsn => $dsn, |
3067 | - dbh => $args{dbh}, |
3068 | - dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
3069 | - hostname => '', |
3070 | - set => $args{set}, |
3071 | - NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
3072 | - dbh_set => 0, |
3073 | - OptionParser => $o, |
3074 | - DSNParser => $dp, |
3075 | + dsn => $dsn, |
3076 | + dbh => $args{dbh}, |
3077 | + dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
3078 | + hostname => '', |
3079 | + set => $args{set}, |
3080 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
3081 | + dbh_set => 0, |
3082 | + OptionParser => $o, |
3083 | + DSNParser => $dp, |
3084 | is_cluster_node => undef, |
3085 | + parent => $args{parent}, |
3086 | }; |
3087 | |
3088 | return bless $self, $class; |
3089 | } |
3090 | |
3091 | sub connect { |
3092 | - my ( $self ) = @_; |
3093 | + my ( $self, %opts ) = @_; |
3094 | my $dsn = $self->{dsn}; |
3095 | my $dp = $self->{DSNParser}; |
3096 | my $o = $self->{OptionParser}; |
3097 | @@ -126,11 +127,18 @@ |
3098 | $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); |
3099 | $self->{asked_for_pass} = 1; |
3100 | } |
3101 | - $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 }); |
3102 | + $dbh = $dp->get_dbh( |
3103 | + $dp->get_cxn_params($dsn), |
3104 | + { |
3105 | + AutoCommit => 1, |
3106 | + %opts, |
3107 | + }, |
3108 | + ); |
3109 | } |
3110 | - PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name}); |
3111 | |
3112 | - return $self->set_dbh($dbh); |
3113 | + $dbh = $self->set_dbh($dbh); |
3114 | + PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name}); |
3115 | + return $dbh; |
3116 | } |
3117 | |
3118 | sub set_dbh { |
3119 | @@ -163,6 +171,11 @@ |
3120 | $self->{hostname} = $hostname; |
3121 | } |
3122 | |
3123 | + if ( $self->{parent} ) { |
3124 | + PTDEBUG && _d($dbh, 'Setting InactiveDestroy=1 in parent'); |
3125 | + $dbh->{InactiveDestroy} = 1; |
3126 | + } |
3127 | + |
3128 | # Call the set callback to let the caller SET any MySQL variables. |
3129 | if ( my $set = $self->{set}) { |
3130 | $set->($dbh); |
3131 | @@ -173,6 +186,15 @@ |
3132 | return $dbh; |
3133 | } |
3134 | |
3135 | +sub lost_connection { |
3136 | + my ($self, $e) = @_; |
3137 | + return 0 unless $e; |
3138 | + return $e =~ m/MySQL server has gone away/ |
3139 | + || $e =~ m/Lost connection to MySQL server/; |
3140 | + # The 1st pattern means that MySQL itself died or was stopped. |
3141 | + # The 2nd pattern means that our cxn was killed (KILL <id>). |
3142 | +} |
3143 | + |
3144 | # Sub: dbh |
3145 | # Return the cxn's dbh. |
3146 | sub dbh { |
3147 | @@ -197,12 +219,21 @@ |
3148 | |
3149 | sub DESTROY { |
3150 | my ($self) = @_; |
3151 | - if ( $self->{dbh} |
3152 | - && blessed($self->{dbh}) |
3153 | - && $self->{dbh}->can("disconnect") ) { |
3154 | - PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name}); |
3155 | + |
3156 | + PTDEBUG && _d('Destroying cxn'); |
3157 | + |
3158 | + if ( $self->{parent} ) { |
3159 | + PTDEBUG && _d($self->{dbh}, 'Not disconnecting dbh in parent'); |
3160 | + } |
3161 | + elsif ( $self->{dbh} |
3162 | + && blessed($self->{dbh}) |
3163 | + && $self->{dbh}->can("disconnect") ) |
3164 | + { |
3165 | + PTDEBUG && _d($self->{dbh}, 'Disconnecting dbh on', $self->{hostname}, |
3166 | + $self->{dsn_name}); |
3167 | $self->{dbh}->disconnect(); |
3168 | } |
3169 | + |
3170 | return; |
3171 | } |
3172 | |
3173 | |
3174 | === modified file 'lib/Runtime.pm' |
3175 | --- lib/Runtime.pm 2013-01-03 00:19:16 +0000 |
3176 | +++ lib/Runtime.pm 2013-02-27 23:41:26 +0000 |
3177 | @@ -18,13 +18,6 @@ |
3178 | # Runtime package |
3179 | # ########################################################################### |
3180 | { |
3181 | -# Package: Runtime |
3182 | -# Runtime keeps track of time to control how long a tool's main loop runs. |
3183 | -# This package was created to handle mk-query-digest --run-time-mode event. |
3184 | -# In essence, we abstract time so that the tool doesn't know/care whether |
3185 | -# now() comes from a clock, a log timestamp, or wherever. The creator of |
3186 | -# Runtime object determines how, or from where, time is gotten so that the |
3187 | -# caller of the object can simply ask, "What time is it?". |
3188 | package Runtime; |
3189 | |
3190 | use strict; |
3191 | @@ -32,30 +25,24 @@ |
3192 | use English qw(-no_match_vars); |
3193 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
3194 | |
3195 | -# Sub: new |
3196 | -# |
3197 | -# Parameters: |
3198 | -# %args - Arguments |
3199 | -# |
3200 | -# Required Arguments: |
3201 | -# now - Callback that sets current time. |
3202 | -# runtime - Amount of time to run in seconds, or undef for forever. |
3203 | -# |
3204 | -# Returns: |
3205 | -# Runtime object |
3206 | sub new { |
3207 | my ( $class, %args ) = @_; |
3208 | - my @required_args = qw(now); |
3209 | + my @required_args = qw(run_time now); |
3210 | foreach my $arg ( @required_args ) { |
3211 | - die "I need a $arg argument" unless $args{$arg}; |
3212 | - } |
3213 | - |
3214 | - if ( ($args{runtime} || 0) < 0 ) { |
3215 | - die "runtime argument must be greater than zero" |
3216 | - } |
3217 | + die "I need a $arg argument" unless exists $args{$arg}; |
3218 | + } |
3219 | + |
3220 | + my $run_time = $args{run_time}; |
3221 | + if ( defined $run_time ) { |
3222 | + die "run_time must be > 0" if $run_time <= 0; |
3223 | + } |
3224 | + |
3225 | + my $now = $args{now}; |
3226 | + die "now must be a callback" unless ref $now eq 'CODE'; |
3227 | |
3228 | my $self = { |
3229 | - %args, |
3230 | + run_time => $run_time, |
3231 | + now => $now, |
3232 | start_time => undef, |
3233 | end_time => undef, |
3234 | time_left => undef, |
3235 | @@ -66,8 +53,8 @@ |
3236 | } |
3237 | |
3238 | # Sub: time_left |
3239 | -# Return the number of runtime seconds left or undef for forever. |
3240 | -# The return may be less than zero if the runtime has been exceeded. |
3241 | +# Return the number of run time seconds left or undef for forever. |
3242 | +# The return may be less than zero if the run time has been exceeded. |
3243 | # The first call to this subroutine "starts the clock", so to speak, |
3244 | # if the now callbackup returns a defined value. |
3245 | # |
3246 | @@ -75,7 +62,7 @@ |
3247 | # %args - Arguments passed to now callback. |
3248 | # |
3249 | # Returns: |
3250 | -# Number of runtime seconds left, possibly less than zero, or undef |
3251 | +# Number of run time seconds left, possibly less than zero, or undef |
3252 | # if running forever. |
3253 | sub time_left { |
3254 | my ( $self, %args ) = @_; |
3255 | @@ -99,14 +86,14 @@ |
3256 | # we know the current time. |
3257 | return unless defined $now; |
3258 | |
3259 | - # If runtime is also defined, then we can determine time left. |
3260 | + # If run_time is also defined, then we can determine time left. |
3261 | # If it's not defined, then we're running forever. |
3262 | - my $runtime = $self->{runtime}; |
3263 | - return unless defined $runtime; |
3264 | + my $run_time = $self->{run_time}; |
3265 | + return unless defined $run_time; |
3266 | |
3267 | # Set the end time once. |
3268 | if ( !$self->{end_time} ) { |
3269 | - $self->{end_time} = $now + $runtime; |
3270 | + $self->{end_time} = $now + $run_time; |
3271 | PTDEBUG && _d("End time:", $self->{end_time}); |
3272 | } |
3273 | |
3274 | @@ -118,7 +105,7 @@ |
3275 | } |
3276 | |
3277 | # Sub: have_time |
3278 | -# Return true or false if there's runtime left. This sub is a simpler |
3279 | +# Return true or false if there's run time left. This sub is a simpler |
3280 | # wrapper around <time_left()> which returns true (1) if time left is |
3281 | # defined and greater than zero or undef, else returns false. |
3282 | # |
3283 | @@ -131,7 +118,7 @@ |
3284 | my ( $self, %args ) = @_; |
3285 | my $time_left = $self->time_left(%args); |
3286 | return 1 if !defined $time_left; # run forever |
3287 | - return $time_left <= 0 ? 0 : 1; # <=0s means runtime has elapsed |
3288 | + return $time_left <= 0 ? 0 : 1; # <=0s means run time has elapsed |
3289 | } |
3290 | |
3291 | # Sub: time_elapsed |
3292 | @@ -173,7 +160,7 @@ |
3293 | $self->{end_time} = undef; |
3294 | $self->{time_left} = undef; |
3295 | $self->{stop} = 0; |
3296 | - PTDEBUG && _d("Reset runtime"); |
3297 | + PTDEBUG && _d("Reset run time"); |
3298 | return; |
3299 | } |
3300 | |
3301 | |
3302 | === modified file 't/lib/Cxn.t' |
3303 | --- t/lib/Cxn.t 2012-11-08 20:47:00 +0000 |
3304 | +++ t/lib/Cxn.t 2013-02-27 23:41:26 +0000 |
3305 | @@ -9,7 +9,7 @@ |
3306 | use strict; |
3307 | use warnings FATAL => 'all'; |
3308 | use English qw(-no_match_vars); |
3309 | -use Test::More tests => 19; |
3310 | +use Test::More; |
3311 | |
3312 | use Sandbox; |
3313 | use OptionParser; |
3314 | @@ -252,8 +252,64 @@ |
3315 | $o->get_opts(); |
3316 | |
3317 | # ############################################################################# |
3318 | +# The parent of a forked Cxn should not disconnect the dbh in DESTORY |
3319 | +# because the child still has access to it. |
3320 | +# ############################################################################# |
3321 | + |
3322 | +my $sync_file = "/tmp/pt-cxn-sync.$PID"; |
3323 | +my $outfile = "/tmp/pt-cxn-outfile.$PID"; |
3324 | + |
3325 | +my $pid; |
3326 | +{ |
3327 | + my $parent_cxn = make_cxn( |
3328 | + dsn_string => 'h=127.1,P=12345,u=msandbox,p=msandbox', |
3329 | + parent => 1, |
3330 | + ); |
3331 | + $parent_cxn->connect(); |
3332 | + |
3333 | + $pid = fork(); |
3334 | + if ( defined($pid) && $pid == 0 ) { |
3335 | + # I am the child. |
3336 | + # Wait for the parent to leave this code block which will cause |
3337 | + # the $parent_cxn to be destroyed. |
3338 | + PerconaTest::wait_for_files($sync_file); |
3339 | + $parent_cxn->{parent} = 0; |
3340 | + eval { |
3341 | + $parent_cxn->dbh->do("SELECT 123 INTO OUTFILE '$outfile'"); |
3342 | + $parent_cxn->dbh->disconnect(); |
3343 | + }; |
3344 | + warn $EVAL_ERROR if $EVAL_ERROR; |
3345 | + exit; |
3346 | + } |
3347 | +} |
3348 | + |
3349 | +# Let the child know that we (the parent) have left that ^ code block, |
3350 | +# so our copy of $parent_cxn has been destroyed, but hopefully the child's |
3351 | +# copy is still alive, i.e. has an active/not-disconnected dbh. |
3352 | +diag(`touch $sync_file`); |
3353 | + |
3354 | +# Wait for the child. |
3355 | +waitpid($pid, 0); |
3356 | + |
3357 | +ok( |
3358 | + -f $outfile, |
3359 | + "Child created outfile" |
3360 | +); |
3361 | + |
3362 | +my $output = `cat $outfile 2>/dev/null`; |
3363 | + |
3364 | +is( |
3365 | + $output, |
3366 | + "123\n", |
3367 | + "Child executed query" |
3368 | +); |
3369 | + |
3370 | +unlink $sync_file if -f $sync_file; |
3371 | +unlink $outfile if -f $outfile; |
3372 | + |
3373 | +# ############################################################################# |
3374 | # Done. |
3375 | # ############################################################################# |
3376 | $master_dbh->disconnect() if $master_dbh; |
3377 | ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
3378 | -exit; |
3379 | +done_testing; |
3380 | |
3381 | === modified file 't/pt-deadlock-logger/basics.t' |
3382 | --- t/pt-deadlock-logger/basics.t 2012-10-22 21:30:29 +0000 |
3383 | +++ t/pt-deadlock-logger/basics.t 2013-02-27 23:41:26 +0000 |
3384 | @@ -11,6 +11,8 @@ |
3385 | use English qw(-no_match_vars); |
3386 | use Test::More; |
3387 | |
3388 | +$ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} = 1; |
3389 | + |
3390 | use PerconaTest; |
3391 | use Sandbox; |
3392 | require "$trunk/bin/pt-deadlock-logger"; |
3393 | @@ -25,8 +27,8 @@ |
3394 | } |
3395 | |
3396 | my $output; |
3397 | -my $cnf = "/tmp/12345/my.sandbox.cnf"; |
3398 | -my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1"; |
3399 | +my $dsn = $sb->dsn_for('master'); |
3400 | +my @args = ($dsn, qw(--iterations 1)); |
3401 | |
3402 | $dbh1->commit; |
3403 | $dbh2->commit; |
3404 | @@ -90,21 +92,30 @@ |
3405 | $output = $dbh1->selectrow_hashref('show /*!40101 engine*/ innodb status')->{status}; |
3406 | like($output, qr/WE ROLL BACK/, 'There was a deadlock'); |
3407 | |
3408 | -$output = `$cmd --print`; |
3409 | +$output = output( |
3410 | + sub { |
3411 | + pt_deadlock_logger::main(@args); |
3412 | + } |
3413 | +); |
3414 | + |
3415 | like( |
3416 | $output, |
3417 | qr/127\.1.+msandbox.+GEN_CLUST_INDEX/, |
3418 | 'Deadlock logger prints the output' |
3419 | ); |
3420 | |
3421 | -$output = `$cmd`; |
3422 | -like( |
3423 | +$output = output( |
3424 | + sub { |
3425 | + pt_deadlock_logger::main(@args, qw(--quiet)); |
3426 | + } |
3427 | +); |
3428 | + |
3429 | +is( |
3430 | $output, |
3431 | - qr/127\.1.+msandbox.+GEN_CLUST_INDEX/, |
3432 | - '--print is implicit' |
3433 | + "", |
3434 | + "No output with --quiet" |
3435 | ); |
3436 | |
3437 | - |
3438 | # ############################################################################# |
3439 | # Issue 943: mk-deadlock-logger reports the same deadlock with --interval |
3440 | # ############################################################################# |
3441 | @@ -112,55 +123,59 @@ |
3442 | # The deadlock from above won't be re-printed so even after running for |
3443 | # 3 seconds and checking multiple times only the single, 3 line deadlock |
3444 | # should be reported. |
3445 | -chomp($output = `$cmd --run-time 3 | wc -l`); |
3446 | + |
3447 | +$output = output( |
3448 | + sub { |
3449 | + pt_deadlock_logger::main(@args, qw(--run-time 3)); |
3450 | + } |
3451 | +); |
3452 | $output =~ s/^\s+//; |
3453 | +my @lines = split("\n", $output); |
3454 | is( |
3455 | - $output, |
3456 | + scalar @lines, |
3457 | 3, |
3458 | "Doesn't re-print same deadlock (issue 943)" |
3459 | -); |
3460 | +) or diag($output); |
3461 | |
3462 | # ############################################################################# |
3463 | # Check that deadlocks from previous test were stored in table. |
3464 | # ############################################################################# |
3465 | -`$cmd --dest D=test,t=deadlocks --create-dest-table`; |
3466 | +$output = output( |
3467 | + sub { |
3468 | + pt_deadlock_logger::main(@args, '--dest', 'D=test,t=deadlocks', |
3469 | + qw(--create-dest-table)) |
3470 | + } |
3471 | +); |
3472 | + |
3473 | my $res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks'); |
3474 | ok( |
3475 | scalar @$res, |
3476 | - 'Deadlocks recorded in --dest table' |
3477 | -); |
3478 | - |
3479 | -# ############################################################################# |
3480 | -# Check that --dest suppress --print output unless --print is explicit. |
3481 | -# ############################################################################# |
3482 | -$output = 'foo'; |
3483 | -$dbh1->do('TRUNCATE TABLE test.deadlocks'); |
3484 | -$output = `$cmd --dest D=test,t=deadlocks`; |
3485 | -is( |
3486 | - $output, |
3487 | - '', |
3488 | - 'No output with --dest' |
3489 | -); |
3490 | - |
3491 | -$res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks'); |
3492 | -ok( |
3493 | - scalar @$res, |
3494 | - 'Deadlocks still recorded in table' |
3495 | -); |
3496 | - |
3497 | + 'Deadlock saved in --dest table' |
3498 | +) or diag($output); |
3499 | + |
3500 | +# ############################################################################# |
3501 | +# In 2.1, --dest suppressed output (--print). In 2.2, output is only |
3502 | +# suppressed by --quiet. |
3503 | +# ############################################################################# |
3504 | $output = ''; |
3505 | $dbh1->do('TRUNCATE TABLE test.deadlocks'); |
3506 | -$output = `$trunk/bin/pt-deadlock-logger --print --dest D=test,t=deadlocks --host 127.1 --port 12345 --user msandbox --password msandbox`; |
3507 | -like( |
3508 | +$output = output( |
3509 | + sub { |
3510 | + pt_deadlock_logger::main(@args, '--dest', 'D=test,t=deadlocks', |
3511 | + qw(--quiet)) |
3512 | + } |
3513 | +); |
3514 | + |
3515 | +is( |
3516 | $output, |
3517 | - qr/127\.1.+msandbox.+GEN_CLUST_INDEX/, |
3518 | - 'Prints output with --dest and explicit --print' |
3519 | + "", |
3520 | + "No output with --dest and --quiet" |
3521 | ); |
3522 | |
3523 | $res = $dbh1->selectall_arrayref('SELECT * FROM test.deadlocks'); |
3524 | ok( |
3525 | scalar @$res, |
3526 | - 'Deadlocks recorded in table again' |
3527 | + "... deadlock still saved in the table" |
3528 | ); |
3529 | |
3530 | # ############################################################################# |
3531 | @@ -180,9 +195,7 @@ |
3532 | make_deadlock(); |
3533 | |
3534 | $output = output( |
3535 | - sub { pt_deadlock_logger::main("F=/tmp/12345/my.sandbox.cnf", |
3536 | - qw(--print) ); |
3537 | - } |
3538 | + sub { pt_deadlock_logger::main(@args) } |
3539 | ); |
3540 | |
3541 | like( |
3542 | @@ -200,4 +213,3 @@ |
3543 | $sb->wipe_clean($dbh1); |
3544 | ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
3545 | done_testing; |
3546 | -exit; |
3547 | |
3548 | === modified file 't/pt-deadlock-logger/clear_deadlocks.t' |
3549 | --- t/pt-deadlock-logger/clear_deadlocks.t 2012-06-03 19:14:30 +0000 |
3550 | +++ t/pt-deadlock-logger/clear_deadlocks.t 2013-02-27 23:41:26 +0000 |
3551 | @@ -22,9 +22,6 @@ |
3552 | if ( !$dbh1 ) { |
3553 | plan skip_all => 'Cannot connect to sandbox master'; |
3554 | } |
3555 | -else { |
3556 | - plan tests => 4; |
3557 | -} |
3558 | |
3559 | my $output; |
3560 | my $cnf = "/tmp/12345/my.sandbox.cnf"; |
3561 | @@ -39,7 +36,7 @@ |
3562 | |
3563 | # The clear-deadlocks table comes and goes quickly so we can really |
3564 | # only search the debug output for evidence that it was created. |
3565 | -$output = `PTDEBUG=1 $trunk/bin/pt-deadlock-logger F=$cnf,D=test --clear-deadlocks test.make_deadlock 2>&1`; |
3566 | +$output = `PTDEBUG=1 $trunk/bin/pt-deadlock-logger F=$cnf,D=test --clear-deadlocks test.make_deadlock --iterations 1 2>&1`; |
3567 | like( |
3568 | $output, |
3569 | qr/INSERT INTO test.make_deadlock/, |
3570 | @@ -67,4 +64,4 @@ |
3571 | # ############################################################################# |
3572 | $sb->wipe_clean($dbh1); |
3573 | ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
3574 | -exit; |
3575 | +done_testing; |
3576 | |
3577 | === modified file 't/pt-deadlock-logger/create_dest_table.t' |
3578 | --- t/pt-deadlock-logger/create_dest_table.t 2012-06-09 18:43:33 +0000 |
3579 | +++ t/pt-deadlock-logger/create_dest_table.t 2013-02-27 23:41:26 +0000 |
3580 | @@ -22,15 +22,10 @@ |
3581 | if ( !$dbh1 ) { |
3582 | plan skip_all => 'Cannot connect to sandbox master'; |
3583 | } |
3584 | -else { |
3585 | - plan tests => 3; |
3586 | -} |
3587 | |
3588 | my $output; |
3589 | -my $cnf = "/tmp/12345/my.sandbox.cnf"; |
3590 | -my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1"; |
3591 | +my $dsn = $sb->dsn_for('master'); |
3592 | |
3593 | -$sb->wipe_clean($dbh1); |
3594 | $sb->create_dbs($dbh1, ['test']); |
3595 | |
3596 | # ############################################################################# |
3597 | @@ -42,17 +37,25 @@ |
3598 | 'Deadlocks table does not exit (issue 386)' |
3599 | ); |
3600 | |
3601 | -`$cmd --dest D=test,t=issue_386 --run-time 1s --interval 1s --create-dest-table`; |
3602 | +$output = output( |
3603 | + sub { |
3604 | + pt_deadlock_logger::main($dsn, |
3605 | + '--dest', 'D=test,t=issue_386', |
3606 | + qw(--iterations 1 --create-dest-table) |
3607 | + ) |
3608 | + }, |
3609 | + stderr => 1, |
3610 | +); |
3611 | |
3612 | is_deeply( |
3613 | $dbh1->selectall_arrayref(q{show tables from `test` like 'issue_386'}), |
3614 | [['issue_386']], |
3615 | 'Deadlocks table created with --create-dest-table (issue 386)' |
3616 | -); |
3617 | +) or diag($output); |
3618 | |
3619 | # ############################################################################# |
3620 | # Done. |
3621 | # ############################################################################# |
3622 | $sb->wipe_clean($dbh1); |
3623 | ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
3624 | -exit; |
3625 | +done_testing; |
3626 | |
3627 | === modified file 't/pt-deadlock-logger/option_sanity.t' |
3628 | --- t/pt-deadlock-logger/option_sanity.t 2012-10-22 18:16:42 +0000 |
3629 | +++ t/pt-deadlock-logger/option_sanity.t 2013-02-27 23:41:26 +0000 |
3630 | @@ -21,7 +21,7 @@ |
3631 | $output = `$trunk/bin/pt-deadlock-logger --dest D=test,t=deadlocks 2>&1`; |
3632 | like( |
3633 | $output, |
3634 | - qr/Missing or invalid source host/, |
3635 | + qr/No DSN was specified/, |
3636 | 'Requires source host' |
3637 | ); |
3638 | |
3639 | |
3640 | === renamed file 't/pt-deadlock-logger/deadlocks_tbl.sql' => 't/pt-deadlock-logger/samples/deadlocks_tbl.sql' |
3641 | === modified file 't/pt-deadlock-logger/standard_options.t' |
3642 | --- t/pt-deadlock-logger/standard_options.t 2012-07-23 04:52:41 +0000 |
3643 | +++ t/pt-deadlock-logger/standard_options.t 2013-02-27 23:41:26 +0000 |
3644 | @@ -17,69 +17,96 @@ |
3645 | |
3646 | my $dp = new DSNParser(opts=>$dsn_opts); |
3647 | my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); |
3648 | -my $dbh1 = $sb->get_dbh_for('master'); |
3649 | +my $dbh = $sb->get_dbh_for('master'); |
3650 | |
3651 | -if ( !$dbh1 ) { |
3652 | +if ( !$dbh ) { |
3653 | plan skip_all => 'Cannot connect to sandbox master'; |
3654 | } |
3655 | -else { |
3656 | - plan tests => 10; |
3657 | -} |
3658 | |
3659 | my $output; |
3660 | -my $cnf = "/tmp/12345/my.sandbox.cnf"; |
3661 | -my $cmd = "$trunk/bin/pt-deadlock-logger -F $cnf h=127.1"; |
3662 | +my $dsn = $sb->dsn_for('master'); |
3663 | +my @args = ($dsn, qw(--iterations 1)); |
3664 | |
3665 | -$sb->wipe_clean($dbh1); |
3666 | -$sb->create_dbs($dbh1, ['test']); |
3667 | +$sb->wipe_clean($dbh); |
3668 | +$sb->create_dbs($dbh, ['test']); |
3669 | |
3670 | # ############################################################################# |
3671 | # Issue 248: Add --user, --pass, --host, etc to all tools |
3672 | # ############################################################################# |
3673 | |
3674 | # Test that source DSN inherits from --user, etc. |
3675 | -$output = `$trunk/bin/pt-deadlock-logger h=127.1,D=test,u=msandbox,p=msandbox --clear-deadlocks test.make_deadlock --port 12345 2>&1`; |
3676 | +$output = output( |
3677 | + sub { |
3678 | + pt_deadlock_logger::main( |
3679 | + "h=127.1,D=test,u=msandbox,p=msandbox", |
3680 | + qw(--clear-deadlocks test.make_deadlock --port 12345), |
3681 | + qw(--iterations 1) |
3682 | + ) |
3683 | + } |
3684 | +); |
3685 | + |
3686 | unlike( |
3687 | $output, |
3688 | qr/failed/, |
3689 | 'Source DSN inherits from standard connection options (issue 248)' |
3690 | ); |
3691 | |
3692 | -# ######################################################################### |
3693 | +# ############################################################################# |
3694 | # Issue 391: Add --pid option to all scripts |
3695 | -# ######################################################################### |
3696 | -`touch /tmp/mk-script.pid`; |
3697 | -$output = `$cmd --clear-deadlocks test.make_deadlock --port 12345 --pid /tmp/mk-script.pid 2>&1`; |
3698 | +# ############################################################################# |
3699 | + |
3700 | +my $pid_file = "/tmp/pt-deadlock-logger-test.pid.$PID"; |
3701 | +diag(`touch $pid_file`); |
3702 | + |
3703 | +$output = output( |
3704 | + sub { |
3705 | + pt_deadlock_logger::main(@args, '--pid', $pid_file) |
3706 | + }, |
3707 | + stderr => 1, |
3708 | +); |
3709 | + |
3710 | like( |
3711 | $output, |
3712 | - qr{PID file /tmp/mk-script.pid already exists}, |
3713 | + qr{PID file $pid_file already exists}, |
3714 | 'Dies if PID file already exists (--pid without --daemonize) (issue 391)' |
3715 | ); |
3716 | -`rm -rf /tmp/mk-script.pid`; |
3717 | + |
3718 | +unlink $pid_file if -f $pid_file; |
3719 | |
3720 | # ############################################################################# |
3721 | # Check daemonization |
3722 | # ############################################################################# |
3723 | -my $deadlocks_tbl = load_file('t/pt-deadlock-logger/deadlocks_tbl.sql'); |
3724 | -$dbh1->do('USE test'); |
3725 | -$dbh1->do('DROP TABLE IF EXISTS deadlocks'); |
3726 | -$dbh1->do("$deadlocks_tbl"); |
3727 | - |
3728 | -my $pid_file = '/tmp/mk-deadlock-logger.pid'; |
3729 | -unlink $pid_file |
3730 | - and diag("Unlinked existing $pid_file"); |
3731 | - |
3732 | -`$cmd --dest D=test,t=deadlocks --daemonize --run-time 6s --interval 1s --pid $pid_file 1>/dev/null 2>/dev/null`; |
3733 | -$output = `ps -eaf | grep '$cmd \-\-dest '`; |
3734 | -like($output, qr/\Q$cmd/, 'It lives daemonized'); |
3735 | +$dbh->do('USE test'); |
3736 | +$dbh->do('DROP TABLE IF EXISTS deadlocks'); |
3737 | +$sb->load_file('master', 't/pt-deadlock-logger/samples/deadlocks_tbl.sql', 'test'); |
3738 | + |
3739 | +$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`; |
3740 | |
3741 | PerconaTest::wait_for_files($pid_file); |
3742 | -ok(-f $pid_file, 'PID file created'); |
3743 | -my ($pid) = $output =~ /\s+(\d+)\s+/; |
3744 | + |
3745 | +$output = `ps x | grep 'pt-deadlock-logger $dsn' | grep -v grep`; |
3746 | +like( |
3747 | + $output, |
3748 | + qr/\Qpt-deadlock-logger $dsn/, |
3749 | + 'It lives daemonized' |
3750 | +) or diag($output); |
3751 | + |
3752 | +my ($pid) = $output =~ /(\d+)/; |
3753 | + |
3754 | +ok( |
3755 | + -f $pid_file, |
3756 | + 'PID file created' |
3757 | +) or diag($output); |
3758 | + |
3759 | chomp($output = slurp_file($pid_file)); |
3760 | -is($output, $pid, 'PID file has correct PID'); |
3761 | +is( |
3762 | + $output, |
3763 | + $pid, |
3764 | + 'PID file has correct PID' |
3765 | +); |
3766 | |
3767 | # Kill it |
3768 | +kill 2, $pid; |
3769 | PerconaTest::wait_until(sub { !kill 0, $pid }); |
3770 | ok(! -f $pid_file, 'PID file removed'); |
3771 | |
3772 | @@ -90,17 +117,25 @@ |
3773 | 'PID file already exists' |
3774 | ); |
3775 | |
3776 | -$output = `$cmd --dest D=test,t=deadlocks --daemonize --run-time 1s --interval 1s --pid $pid_file 2>&1`; |
3777 | +$output = output( |
3778 | + sub { |
3779 | + pt_deadlock_logger::main(@args, '--pid', $pid_file, |
3780 | + qw(--daemonize)) |
3781 | + }, |
3782 | + stderr => 1, |
3783 | +); |
3784 | + |
3785 | like( |
3786 | $output, |
3787 | - qr/PID file .+ already exists/, |
3788 | + qr/PID file $pid_file already exists/, |
3789 | 'Does not run if PID file already exists' |
3790 | ); |
3791 | |
3792 | -$output = `ps -eaf | grep 'pt-deadlock-logger \-\-dest '`; |
3793 | -unlike( |
3794 | +$output = `ps x | grep 'pt-deadlock-logger $dsn' | grep -v grep`; |
3795 | + |
3796 | +is( |
3797 | $output, |
3798 | - qr/$cmd/, |
3799 | + "", |
3800 | 'It does not lived daemonized' |
3801 | ); |
3802 | |
3803 | @@ -109,6 +144,6 @@ |
3804 | # ############################################################################# |
3805 | # Done. |
3806 | # ############################################################################# |
3807 | -$sb->wipe_clean($dbh1); |
3808 | +$sb->wipe_clean($dbh); |
3809 | ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
3810 | -exit; |
3811 | +done_testing; |
3812 | |
3813 | === modified file 't/pt-fk-error-logger/basics.t' |
3814 | --- t/pt-fk-error-logger/basics.t 2012-11-07 18:38:09 +0000 |
3815 | +++ t/pt-fk-error-logger/basics.t 2013-02-27 23:41:26 +0000 |
3816 | @@ -27,8 +27,9 @@ |
3817 | $sb->create_dbs($dbh, [qw(test)]); |
3818 | |
3819 | my $output; |
3820 | -my $cnf = '/tmp/12345/my.sandbox.cnf'; |
3821 | -my $cmd = "$trunk/bin/pt-fk-error-logger -F $cnf "; |
3822 | +my $cnf = '/tmp/12345/my.sandbox.cnf'; |
3823 | +my $cmd = "$trunk/bin/pt-fk-error-logger -F $cnf "; |
3824 | +my @args = qw(--iterations 1); |
3825 | |
3826 | $sb->load_file('master', 't/pt-fk-error-logger/samples/fke_tbl.sql', 'test'); |
3827 | |
3828 | @@ -39,8 +40,45 @@ |
3829 | # First, create a foreign key error. |
3830 | `/tmp/12345/use -D test < $trunk/t/pt-fk-error-logger/samples/fke.sql 1>/dev/null 2>/dev/null`; |
3831 | |
3832 | -# Then get and save that fke. |
3833 | -output(sub { pt_fk_error_logger::main('h=127.1,P=12345,u=msandbox,p=msandbox', '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors'); } ); |
3834 | +$output = output( |
3835 | + sub { |
3836 | + pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox'), |
3837 | + } |
3838 | +); |
3839 | + |
3840 | +like( |
3841 | + $output, |
3842 | + qr/Foreign key constraint fails/, |
3843 | + "Prints fk error by default" |
3844 | +); |
3845 | + |
3846 | +$output = output( |
3847 | + sub { |
3848 | + pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox', |
3849 | + qw(--quiet)) |
3850 | + } |
3851 | +); |
3852 | + |
3853 | +is( |
3854 | + $output, |
3855 | + "", |
3856 | + "No output with --quiet" |
3857 | +); |
3858 | + |
3859 | + |
3860 | +# ############################################################################# |
3861 | +# --dest |
3862 | +# ############################################################################# |
3863 | + |
3864 | +$output = output( |
3865 | + sub { |
3866 | + pt_fk_error_logger::main(@args, |
3867 | + 'h=127.1,P=12345,u=msandbox,p=msandbox', |
3868 | + '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors', |
3869 | + ) |
3870 | + } |
3871 | +); |
3872 | + |
3873 | sleep 0.1; |
3874 | |
3875 | # And then test that it was actually saved. |
3876 | @@ -61,7 +99,7 @@ |
3877 | |
3878 | # Check again to make sure that the same fke isn't saved twice. |
3879 | my $first_ts = $fke->[0]->[0]; |
3880 | -output(sub { pt_fk_error_logger::main('h=127.1,P=12345,u=msandbox,p=msandbox', '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors'); } ); |
3881 | +output(sub { pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox', '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors'); } ); |
3882 | sleep 0.1; |
3883 | $fke = $dbh->selectall_arrayref('SELECT * FROM test.foreign_key_errors'); |
3884 | is( |
3885 | @@ -82,7 +120,7 @@ |
3886 | eval { |
3887 | $dbh->do('DELETE FROM parent WHERE id = 2'); # Causes foreign key error. |
3888 | }; |
3889 | -output( sub { pt_fk_error_logger::main('h=127.1,P=12345,u=msandbox,p=msandbox', '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors'); } ); |
3890 | +output( sub { pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox', '--dest', 'h=127.1,P=12345,D=test,t=foreign_key_errors'); } ); |
3891 | sleep 0.1; |
3892 | $fke = $dbh->selectall_arrayref('SELECT * FROM test.foreign_key_errors'); |
3893 | like( |
3894 | @@ -99,11 +137,14 @@ |
3895 | # ########################################################################## |
3896 | # Test printing the errors. |
3897 | # ########################################################################## |
3898 | + |
3899 | $dbh->do('USE test'); |
3900 | eval { |
3901 | $dbh->do('DELETE FROM parent WHERE id = 2'); # Causes foreign key error. |
3902 | }; |
3903 | -$output = output(sub { pt_fk_error_logger::main('h=127.1,P=12345,u=msandbox,p=msandbox'); }); |
3904 | + |
3905 | +$output = output(sub { pt_fk_error_logger::main(@args, 'h=127.1,P=12345,u=msandbox,p=msandbox'); }); |
3906 | + |
3907 | like( |
3908 | $output, |
3909 | qr/DELETE FROM parent WHERE id = 2/, |
3910 | @@ -127,7 +168,7 @@ |
3911 | |
3912 | $output = output( |
3913 | sub { |
3914 | - pt_fk_error_logger::main('h=127.1,P=12348,u=msandbox,p=msandbox', |
3915 | + pt_fk_error_logger::main(@args, 'h=127.1,P=12348,u=msandbox,p=msandbox', |
3916 | '--dest', 'h=127.1,P=12348,D=test,t=foreign_key_errors') |
3917 | }, |
3918 | stderr => 1, |
3919 | @@ -142,6 +183,23 @@ |
3920 | diag(`$trunk/sandbox/stop-sandbox 12348 >/dev/null`); |
3921 | |
3922 | # ############################################################################# |
3923 | +# Test --pid |
3924 | +# ############################################################################# |
3925 | + |
3926 | +my $pid_file = "/tmp/pt-fk-error-log-test-$PID.pid"; |
3927 | +diag(`touch $pid_file`); |
3928 | + |
3929 | +$output = `$trunk/bin/pt-fk-error-logger h=127.1,P=12345,u=msandbox,p=msandbox --pid $pid_file --iterations 1 2>&1`; |
3930 | + |
3931 | +like( |
3932 | + $output, |
3933 | + qr{PID file $pid_file already exists}, |
3934 | + 'Dies if PID file already exists (--pid without --daemonize) (issue 391)' |
3935 | +); |
3936 | + |
3937 | +unlink $pid_file; |
3938 | + |
3939 | +# ############################################################################# |
3940 | # Done. |
3941 | # ############################################################################# |
3942 | $sb->wipe_clean($dbh); |
3943 | |
3944 | === modified file 't/pt-fk-error-logger/get_fk_error.t' |
3945 | --- t/pt-fk-error-logger/get_fk_error.t 2011-07-12 22:56:55 +0000 |
3946 | +++ t/pt-fk-error-logger/get_fk_error.t 2013-02-27 23:41:26 +0000 |
3947 | @@ -9,7 +9,7 @@ |
3948 | use strict; |
3949 | use warnings FATAL => 'all'; |
3950 | use English qw(-no_match_vars); |
3951 | -use Test::More tests => 10; |
3952 | +use Test::More; |
3953 | |
3954 | use PerconaTest; |
3955 | require "$trunk/bin/pt-fk-error-logger"; |
3956 | @@ -70,4 +70,4 @@ |
3957 | # ############################################################################# |
3958 | # Done. |
3959 | # ############################################################################# |
3960 | -exit; |
3961 | +done_testing; |
3962 | |
3963 | === removed file 't/pt-fk-error-logger/standard_options.t' |
3964 | --- t/pt-fk-error-logger/standard_options.t 2011-07-12 22:56:55 +0000 |
3965 | +++ t/pt-fk-error-logger/standard_options.t 1970-01-01 00:00:00 +0000 |
3966 | @@ -1,32 +0,0 @@ |
3967 | -#!/usr/bin/env perl |
3968 | - |
3969 | -BEGIN { |
3970 | - die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n" |
3971 | - unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH}; |
3972 | - unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib"; |
3973 | -}; |
3974 | - |
3975 | -use strict; |
3976 | -use warnings FATAL => 'all'; |
3977 | -use English qw(-no_match_vars); |
3978 | -use Test::More tests => 1; |
3979 | - |
3980 | -use PerconaTest; |
3981 | -require "$trunk/bin/pt-fk-error-logger"; |
3982 | - |
3983 | -# ######################################################################### |
3984 | -# Issue 391: Add --pid option to all scripts |
3985 | -# ######################################################################### |
3986 | -`touch /tmp/mk-script.pid`; |
3987 | -my $output = `$trunk/bin/pt-fk-error-logger h=127.1,P=12345,u=msandbox,p=msandbox --print --pid /tmp/mk-script.pid 2>&1`; |
3988 | -like( |
3989 | - $output, |
3990 | - qr{PID file /tmp/mk-script.pid already exists}, |
3991 | - 'Dies if PID file already exists (--pid without --daemonize) (issue 391)' |
3992 | -); |
3993 | -`rm -rf /tmp/mk-script.pid`; |
3994 | - |
3995 | -# ############################################################################# |
3996 | -# Done. |
3997 | -# ############################################################################# |
3998 | -exit; |
See my comment #2 in bug 1056838.
All tests pass: https:/ /refute. testnoir. com/percona- toolkit/ jobs/percona- toolkit- all-platforms/ 63