Merge lp:~percona-toolkit-dev/percona-toolkit/v-c-update into lp:percona-toolkit/2.1

Proposed by Daniel Nichter
Status: Merged
Merged at revision: 384
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/v-c-update
Merge into: lp:percona-toolkit/2.1
Diff against target: 7594 lines (+3196/-1018)
28 files modified
bin/pt-archiver (+146/-47)
bin/pt-config-diff (+148/-47)
bin/pt-deadlock-logger (+146/-47)
bin/pt-diskstats (+142/-46)
bin/pt-duplicate-key-checker (+143/-47)
bin/pt-find (+143/-47)
bin/pt-fk-error-logger (+146/-47)
bin/pt-heartbeat (+143/-47)
bin/pt-index-usage (+150/-50)
bin/pt-kill (+145/-47)
bin/pt-online-schema-change (+152/-53)
bin/pt-query-advisor (+149/-49)
bin/pt-query-digest (+151/-49)
bin/pt-slave-delay (+149/-47)
bin/pt-slave-restart (+151/-53)
bin/pt-table-checksum (+152/-53)
bin/pt-table-sync (+162/-47)
bin/pt-upgrade (+145/-47)
bin/pt-variable-advisor (+147/-49)
lib/Percona/Toolkit.pm (+1/-0)
lib/PerconaTest.pm (+2/-0)
lib/Pingback.pm (+130/-30)
lib/VersionCheck.pm (+33/-29)
t/lib/Pingback.t (+193/-28)
t/lib/VersionCheck.t (+23/-9)
t/pt-archiver/version_check.t (+1/-1)
t/pt-heartbeat/basics.t (+2/-1)
t/pt-query-digest/version_check.t (+1/-1)
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/v-c-update
Reviewer Review Type Date Requested Status
Daniel Nichter Approve
Review via email: mp+121465@code.launchpad.net
To post a comment you must log in.
369. By Daniel Nichter

Add code comments. Make Pingback.t tests more explicity. Don't '1 while unlink file'. Remove get_mysql_status() stub.

370. By Daniel Nichter

Simplify how Pingback handles MySQL instances. Remove Percona::Toolkit::slurp_file() because it was causing 'sub redefined' errors, probably due to PerconaTest::slurp_file(). Add more PTVCDEBUG statements.

371. By Brian Fraser

Update the rest of the modules to use the updated version_check

372. By Daniel Nichter

Fix version check instances in pt-config-diff and pt-table-sync.

373. By Daniel Nichter

Add back PerconaTest::slurp_file(). Put in scope in pt-index-usage. Sort MySQL instance IDs for testing. Wait for pid file in pt-heartbeat/basics.t.

Revision history for this message
Daniel Nichter (daniel-nichter) :
review: Approve

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 2012-08-24 23:26:04 +0000
3+++ bin/pt-archiver 2012-08-28 21:31:19 +0000
4@@ -40,6 +40,7 @@
5 {
6 package Percona::Toolkit;
7 our $VERSION = '2.1.3';
8+
9 1;
10 }
11 # ###########################################################################
12@@ -3910,20 +3911,19 @@
13 die "I need a $arg arugment" unless $args{$arg};
14 }
15 my ($items) = @args{@required_args};
16- my $dbh = $args{dbh}; # optional
17
18 my %versions;
19 foreach my $item ( values %$items ) {
20 next unless $self->valid_item($item);
21-
22+
23 eval {
24 my $func = 'get_' . $item->{type};
25 my $version = $self->$func(
26- item => $item,
27- dbh => $dbh,
28+ item => $item,
29+ instances => $args{instances},
30 );
31 if ( $version ) {
32- chomp $version;
33+ chomp $version unless ref($version);
34 $versions{$item->{item}} = $version;
35 }
36 };
37@@ -4048,28 +4048,42 @@
38 );
39 }
40
41-
42 sub _get_from_mysql {
43 my ($self, %args) = @_;
44- my $show = $args{show};
45- my $item = $args{item};
46- my $dbh = $args{dbh};
47- return unless $show && $item && $dbh;
48+ my $show = $args{show};
49+ my $item = $args{item};
50+ my $instances = $args{instances};
51+ return unless $show && $item;
52
53- local $dbh->{FetchHashKeyName} = 'NAME_lc';
54- my $sql = qq/SHOW $show/;
55- PTDEBUG && _d($sql);
56- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
57+ if ( !$instances || !@$instances ) {
58+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
59+ _d('Cannot check', $item, 'because there are no MySQL instances');
60+ }
61+ return;
62+ }
63
64 my @versions;
65- foreach my $var ( @{$item->{vars}} ) {
66- $var = lc($var);
67- my $version = $rows->{$var}->{value};
68- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
69- push @versions, $version;
70+ my %version_for;
71+ foreach my $instance ( @$instances ) {
72+ my $dbh = $instance->{dbh};
73+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
74+ my $sql = qq/SHOW $show/;
75+ PTDEBUG && _d($sql);
76+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
77+
78+ my @versions;
79+ foreach my $var ( @{$item->{vars}} ) {
80+ $var = lc($var);
81+ my $version = $rows->{$var}->{value};
82+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
83+ 'on', $instance->{name});
84+ push @versions, $version;
85+ }
86+
87+ $version_for{ $instance->{id} } = join(' ', @versions);
88 }
89
90- return join(' ', @versions);
91+ return \%version_for;
92 }
93
94 sub get_bin_version {
95@@ -4567,14 +4581,15 @@
96
97 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
98
99-use File::Basename qw();
100 use Data::Dumper qw();
101+use Digest::MD5 qw(md5_hex);
102+use Sys::Hostname qw(hostname);
103 use Fcntl qw(:DEFAULT);
104-
105+use File::Basename qw();
106 use File::Spec;
107
108-my $dir = File::Spec->tmpdir();
109-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
110+my $dir = File::Spec->tmpdir();
111+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
112 my $check_time_limit = 60 * 60 * 24; # one day
113
114 sub Dumper {
115@@ -4587,11 +4602,13 @@
116
117 local $EVAL_ERROR;
118 eval {
119+ require Percona::Toolkit;
120 require HTTPMicro;
121 require VersionCheck;
122 };
123
124 sub version_check {
125+ my @instances = @_;
126 eval {
127 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
128 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
129@@ -4601,19 +4618,26 @@
130 return;
131 }
132
133- if ( !time_to_check($check_time_file) ) {
134+ foreach my $instance ( @instances ) {
135+ my ($name, $id) = _generate_identifier($instance);
136+ $instance->{name} = $name;
137+ $instance->{id} = $id;
138+ }
139+
140+ my ($time_to_check, $instances_to_check)
141+ = time_to_check($check_time_file, \@instances);
142+ if ( !$time_to_check ) {
143 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
144- _d('It is not time to --version-checka again;',
145+ _d('It is not time to --version-check again;',
146 'only 1 check per', $check_time_limit, 'seconds, and the last',
147 'check was performed on the modified time of', $check_time_file);
148 }
149 return;
150 }
151
152- my $dbh = shift; # optional
153 my $advice = pingback(
154- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
155- dbh => $dbh,
156+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
157+ instances => $instances_to_check,
158 );
159 if ( $advice ) {
160 print "# Percona suggests these upgrades:\n";
161@@ -4641,7 +4665,7 @@
162 }
163 my ($url) = @args{@required_args};
164
165- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
166+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
167
168 $ua ||= HTTPMicro->new( timeout => 2 );
169 $vc ||= VersionCheck->new();
170@@ -4660,24 +4684,27 @@
171 );
172 die "Failed to parse server requested programs: $response->{content}"
173 if !scalar keys %$items;
174-
175+
176 my $versions = $vc->get_versions(
177- items => $items,
178- dbh => $dbh,
179+ items => $items,
180+ instances => $instances,
181 );
182 die "Failed to get any program versions; should have at least gotten Perl"
183 if !scalar keys %$versions;
184
185 my $client_content = encode_client_response(
186- items => $items,
187- versions => $versions,
188+ items => $items,
189+ versions => $versions,
190+ general_id => md5_hex( hostname() ),
191 );
192
193 my $client_response = {
194 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
195 content => $client_content,
196 };
197- PTDEBUG && _d('Client response:', Dumper($client_response));
198+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
199+ _d('Client response:', Dumper($client_response));
200+ }
201
202 $response = $ua->request('POST', $url, $client_response);
203 PTDEBUG && _d('Server suggestions:', Dumper($response));
204@@ -4702,22 +4729,35 @@
205 }
206
207 sub time_to_check {
208- my ($file) = @_;
209+ my ($file, $instances) = @_;
210 die "I need a file argument" unless $file;
211
212+ my $created_file = 0;
213 if ( !-f $file ) {
214- PTDEBUG && _d('Creating', $file);
215+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
216+ _d('Creating time limit file', $file);
217+ }
218 _touch($file);
219- return 1;
220- }
221+ $created_file = 1;
222+ }
223+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
224+ _d('Time limit file already exists:', $file);
225+ }
226+
227+ my $time = int(time()); # current time
228+
229+ if ( @$instances ) {
230+ my $instances_to_check = instances_to_check($file, $instances, $time);
231+ return scalar @$instances_to_check, $instances_to_check;
232+ }
233+
234+ return 1 if $created_file;
235
236 my $mtime = (stat $file)[9];
237 if ( !defined $mtime ) {
238 PTDEBUG && _d('Error getting modified time of', $file);
239 return 0;
240 }
241-
242- my $time = int(time());
243 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
244 if ( ($time - $mtime) > $check_time_limit ) {
245 _touch($file);
246@@ -4727,6 +4767,36 @@
247 return 0;
248 }
249
250+sub instances_to_check {
251+ my ($file, $instances, $time) = @_;
252+
253+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
254+ my $file_contents = do { local $/ = undef; <$fh> };
255+ close $fh;
256+ chomp($file_contents);
257+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
258+
259+ my @instances_to_check;
260+ foreach my $instance ( @$instances ) {
261+ my $mtime = $cached_instances{ $instance->{id} };
262+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
263+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
264+ _d('Time to check MySQL instance', $instance->{name});
265+ }
266+ push @instances_to_check, $instance;
267+ $cached_instances{ $instance->{id} } = $time;
268+ }
269+ }
270+
271+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
272+ while ( my ($id, $time) = each %cached_instances ) {
273+ print { $fh } "$id,$time\n";
274+ }
275+ close $fh or die "Cannot close $file: $OS_ERROR";
276+
277+ return \@instances_to_check;
278+}
279+
280 sub _touch {
281 my ($file) = @_;
282 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
283@@ -4735,22 +4805,48 @@
284 utime(undef, undef, $file);
285 }
286
287+sub _generate_identifier {
288+ my $instance = shift;
289+ my $dbh = $instance->{dbh};
290+ my $dsn = $instance->{dsn};
291+
292+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
293+ my ($name) = eval { $dbh->selectrow_array($sql) };
294+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
295+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
296+ }
297+ my $id = md5_hex($name);
298+
299+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
300+ _d('MySQL instance', $name, 'is', $id);
301+ }
302+
303+ return $name, $id;
304+}
305+
306 sub encode_client_response {
307 my (%args) = @_;
308- my @required_args = qw(items versions);
309+ my @required_args = qw(items versions general_id);
310 foreach my $arg ( @required_args ) {
311 die "I need a $arg arugment" unless $args{$arg};
312 }
313- my ($items, $versions) = @args{@required_args};
314+ my ($items, $versions, $general_id) = @args{@required_args};
315
316 my @lines;
317 foreach my $item ( sort keys %$items ) {
318 next unless exists $versions->{$item};
319- push @lines, join(';', $item, $versions->{$item});
320+ if ( ref($versions->{$item}) eq 'HASH' ) {
321+ my $mysql_versions = $versions->{$item};
322+ for my $id ( keys %$mysql_versions ) {
323+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
324+ }
325+ }
326+ else {
327+ push @lines, join(';', $general_id, $item, $versions->{$item});
328+ }
329 }
330
331 my $client_response = join("\n", @lines) . "\n";
332- PTDEBUG && _d('Client response:', $client_response);
333 return $client_response;
334 }
335
336@@ -5296,7 +5392,10 @@
337 # Do the version-check
338 # ########################################################################
339 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
340- Pingback::version_check($src->{dbh});
341+ Pingback::version_check(
342+ { dbh => $src->{dbh}, dsn => $src->{dsn} },
343+ ( $dst ? { dbh => $dst->{dbh}, dsn => $dst->{dsn} } : () ),
344+ );
345 }
346
347 # ########################################################################
348
349=== modified file 'bin/pt-config-diff'
350--- bin/pt-config-diff 2012-08-23 16:22:43 +0000
351+++ bin/pt-config-diff 2012-08-28 21:31:19 +0000
352@@ -39,6 +39,7 @@
353 {
354 package Percona::Toolkit;
355 our $VERSION = '2.1.3';
356+
357 1;
358 }
359 # ###########################################################################
360@@ -3011,20 +3012,19 @@
361 die "I need a $arg arugment" unless $args{$arg};
362 }
363 my ($items) = @args{@required_args};
364- my $dbh = $args{dbh}; # optional
365
366 my %versions;
367 foreach my $item ( values %$items ) {
368 next unless $self->valid_item($item);
369-
370+
371 eval {
372 my $func = 'get_' . $item->{type};
373 my $version = $self->$func(
374- item => $item,
375- dbh => $dbh,
376+ item => $item,
377+ instances => $args{instances},
378 );
379 if ( $version ) {
380- chomp $version;
381+ chomp $version unless ref($version);
382 $versions{$item->{item}} = $version;
383 }
384 };
385@@ -3149,28 +3149,42 @@
386 );
387 }
388
389-
390 sub _get_from_mysql {
391 my ($self, %args) = @_;
392- my $show = $args{show};
393- my $item = $args{item};
394- my $dbh = $args{dbh};
395- return unless $show && $item && $dbh;
396+ my $show = $args{show};
397+ my $item = $args{item};
398+ my $instances = $args{instances};
399+ return unless $show && $item;
400
401- local $dbh->{FetchHashKeyName} = 'NAME_lc';
402- my $sql = qq/SHOW $show/;
403- PTDEBUG && _d($sql);
404- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
405+ if ( !$instances || !@$instances ) {
406+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
407+ _d('Cannot check', $item, 'because there are no MySQL instances');
408+ }
409+ return;
410+ }
411
412 my @versions;
413- foreach my $var ( @{$item->{vars}} ) {
414- $var = lc($var);
415- my $version = $rows->{$var}->{value};
416- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
417- push @versions, $version;
418+ my %version_for;
419+ foreach my $instance ( @$instances ) {
420+ my $dbh = $instance->{dbh};
421+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
422+ my $sql = qq/SHOW $show/;
423+ PTDEBUG && _d($sql);
424+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
425+
426+ my @versions;
427+ foreach my $var ( @{$item->{vars}} ) {
428+ $var = lc($var);
429+ my $version = $rows->{$var}->{value};
430+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
431+ 'on', $instance->{name});
432+ push @versions, $version;
433+ }
434+
435+ $version_for{ $instance->{id} } = join(' ', @versions);
436 }
437
438- return join(' ', @versions);
439+ return \%version_for;
440 }
441
442 sub get_bin_version {
443@@ -3668,14 +3682,15 @@
444
445 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
446
447-use File::Basename qw();
448 use Data::Dumper qw();
449+use Digest::MD5 qw(md5_hex);
450+use Sys::Hostname qw(hostname);
451 use Fcntl qw(:DEFAULT);
452-
453+use File::Basename qw();
454 use File::Spec;
455
456-my $dir = File::Spec->tmpdir();
457-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
458+my $dir = File::Spec->tmpdir();
459+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
460 my $check_time_limit = 60 * 60 * 24; # one day
461
462 sub Dumper {
463@@ -3688,11 +3703,13 @@
464
465 local $EVAL_ERROR;
466 eval {
467+ require Percona::Toolkit;
468 require HTTPMicro;
469 require VersionCheck;
470 };
471
472 sub version_check {
473+ my @instances = @_;
474 eval {
475 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
476 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
477@@ -3702,19 +3719,26 @@
478 return;
479 }
480
481- if ( !time_to_check($check_time_file) ) {
482+ foreach my $instance ( @instances ) {
483+ my ($name, $id) = _generate_identifier($instance);
484+ $instance->{name} = $name;
485+ $instance->{id} = $id;
486+ }
487+
488+ my ($time_to_check, $instances_to_check)
489+ = time_to_check($check_time_file, \@instances);
490+ if ( !$time_to_check ) {
491 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
492- _d('It is not time to --version-checka again;',
493+ _d('It is not time to --version-check again;',
494 'only 1 check per', $check_time_limit, 'seconds, and the last',
495 'check was performed on the modified time of', $check_time_file);
496 }
497 return;
498 }
499
500- my $dbh = shift; # optional
501 my $advice = pingback(
502- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
503- dbh => $dbh,
504+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
505+ instances => $instances_to_check,
506 );
507 if ( $advice ) {
508 print "# Percona suggests these upgrades:\n";
509@@ -3742,7 +3766,7 @@
510 }
511 my ($url) = @args{@required_args};
512
513- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
514+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
515
516 $ua ||= HTTPMicro->new( timeout => 2 );
517 $vc ||= VersionCheck->new();
518@@ -3761,24 +3785,27 @@
519 );
520 die "Failed to parse server requested programs: $response->{content}"
521 if !scalar keys %$items;
522-
523+
524 my $versions = $vc->get_versions(
525- items => $items,
526- dbh => $dbh,
527+ items => $items,
528+ instances => $instances,
529 );
530 die "Failed to get any program versions; should have at least gotten Perl"
531 if !scalar keys %$versions;
532
533 my $client_content = encode_client_response(
534- items => $items,
535- versions => $versions,
536+ items => $items,
537+ versions => $versions,
538+ general_id => md5_hex( hostname() ),
539 );
540
541 my $client_response = {
542 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
543 content => $client_content,
544 };
545- PTDEBUG && _d('Client response:', Dumper($client_response));
546+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
547+ _d('Client response:', Dumper($client_response));
548+ }
549
550 $response = $ua->request('POST', $url, $client_response);
551 PTDEBUG && _d('Server suggestions:', Dumper($response));
552@@ -3803,22 +3830,35 @@
553 }
554
555 sub time_to_check {
556- my ($file) = @_;
557+ my ($file, $instances) = @_;
558 die "I need a file argument" unless $file;
559
560+ my $created_file = 0;
561 if ( !-f $file ) {
562- PTDEBUG && _d('Creating', $file);
563+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
564+ _d('Creating time limit file', $file);
565+ }
566 _touch($file);
567- return 1;
568- }
569+ $created_file = 1;
570+ }
571+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
572+ _d('Time limit file already exists:', $file);
573+ }
574+
575+ my $time = int(time()); # current time
576+
577+ if ( @$instances ) {
578+ my $instances_to_check = instances_to_check($file, $instances, $time);
579+ return scalar @$instances_to_check, $instances_to_check;
580+ }
581+
582+ return 1 if $created_file;
583
584 my $mtime = (stat $file)[9];
585 if ( !defined $mtime ) {
586 PTDEBUG && _d('Error getting modified time of', $file);
587 return 0;
588 }
589-
590- my $time = int(time());
591 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
592 if ( ($time - $mtime) > $check_time_limit ) {
593 _touch($file);
594@@ -3828,6 +3868,36 @@
595 return 0;
596 }
597
598+sub instances_to_check {
599+ my ($file, $instances, $time) = @_;
600+
601+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
602+ my $file_contents = do { local $/ = undef; <$fh> };
603+ close $fh;
604+ chomp($file_contents);
605+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
606+
607+ my @instances_to_check;
608+ foreach my $instance ( @$instances ) {
609+ my $mtime = $cached_instances{ $instance->{id} };
610+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
611+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
612+ _d('Time to check MySQL instance', $instance->{name});
613+ }
614+ push @instances_to_check, $instance;
615+ $cached_instances{ $instance->{id} } = $time;
616+ }
617+ }
618+
619+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
620+ while ( my ($id, $time) = each %cached_instances ) {
621+ print { $fh } "$id,$time\n";
622+ }
623+ close $fh or die "Cannot close $file: $OS_ERROR";
624+
625+ return \@instances_to_check;
626+}
627+
628 sub _touch {
629 my ($file) = @_;
630 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
631@@ -3836,22 +3906,48 @@
632 utime(undef, undef, $file);
633 }
634
635+sub _generate_identifier {
636+ my $instance = shift;
637+ my $dbh = $instance->{dbh};
638+ my $dsn = $instance->{dsn};
639+
640+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
641+ my ($name) = eval { $dbh->selectrow_array($sql) };
642+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
643+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
644+ }
645+ my $id = md5_hex($name);
646+
647+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
648+ _d('MySQL instance', $name, 'is', $id);
649+ }
650+
651+ return $name, $id;
652+}
653+
654 sub encode_client_response {
655 my (%args) = @_;
656- my @required_args = qw(items versions);
657+ my @required_args = qw(items versions general_id);
658 foreach my $arg ( @required_args ) {
659 die "I need a $arg arugment" unless $args{$arg};
660 }
661- my ($items, $versions) = @args{@required_args};
662+ my ($items, $versions, $general_id) = @args{@required_args};
663
664 my @lines;
665 foreach my $item ( sort keys %$items ) {
666 next unless exists $versions->{$item};
667- push @lines, join(';', $item, $versions->{$item});
668+ if ( ref($versions->{$item}) eq 'HASH' ) {
669+ my $mysql_versions = $versions->{$item};
670+ for my $id ( keys %$mysql_versions ) {
671+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
672+ }
673+ }
674+ else {
675+ push @lines, join(';', $general_id, $item, $versions->{$item});
676+ }
677 }
678
679 my $client_response = join("\n", @lines) . "\n";
680- PTDEBUG && _d('Client response:', $client_response);
681 return $client_response;
682 }
683
684@@ -3932,6 +4028,7 @@
685 my $last_dsn;
686 my @configs; # MySQLConfig objects
687 my @config_names; # Human-readable names for those ^ objs
688+ my @cxn;
689 foreach my $config_src ( @ARGV ) {
690 if ( -f $config_src ) {
691 PTDEBUG && _d('Config source', $config_src, 'is a file');
692@@ -3954,9 +4051,11 @@
693
694 push @configs, new MySQLConfig(
695 dbh => $cxn->dbh(),
696+ dsn => $cxn->dsn(),
697 %common_modules,
698 );
699 push @config_names, $cxn->name();
700+ push @cxn, $cxn;
701 }
702 }
703
704@@ -3979,7 +4078,9 @@
705 # Do the version-check
706 # ########################################################################
707 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
708- Pingback::version_check($configs[0]->{dbh});
709+ Pingback::version_check(
710+ map { +{ dbh => $_->dbh, dsn => $_->dsn } } @cxn
711+ );
712 }
713
714 # #########################################################################
715
716=== modified file 'bin/pt-deadlock-logger'
717--- bin/pt-deadlock-logger 2012-08-24 23:26:04 +0000
718+++ bin/pt-deadlock-logger 2012-08-28 21:31:19 +0000
719@@ -37,6 +37,7 @@
720 {
721 package Percona::Toolkit;
722 our $VERSION = '2.1.3';
723+
724 1;
725 }
726 # ###########################################################################
727@@ -2469,20 +2470,19 @@
728 die "I need a $arg arugment" unless $args{$arg};
729 }
730 my ($items) = @args{@required_args};
731- my $dbh = $args{dbh}; # optional
732
733 my %versions;
734 foreach my $item ( values %$items ) {
735 next unless $self->valid_item($item);
736-
737+
738 eval {
739 my $func = 'get_' . $item->{type};
740 my $version = $self->$func(
741- item => $item,
742- dbh => $dbh,
743+ item => $item,
744+ instances => $args{instances},
745 );
746 if ( $version ) {
747- chomp $version;
748+ chomp $version unless ref($version);
749 $versions{$item->{item}} = $version;
750 }
751 };
752@@ -2607,28 +2607,42 @@
753 );
754 }
755
756-
757 sub _get_from_mysql {
758 my ($self, %args) = @_;
759- my $show = $args{show};
760- my $item = $args{item};
761- my $dbh = $args{dbh};
762- return unless $show && $item && $dbh;
763+ my $show = $args{show};
764+ my $item = $args{item};
765+ my $instances = $args{instances};
766+ return unless $show && $item;
767
768- local $dbh->{FetchHashKeyName} = 'NAME_lc';
769- my $sql = qq/SHOW $show/;
770- PTDEBUG && _d($sql);
771- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
772+ if ( !$instances || !@$instances ) {
773+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
774+ _d('Cannot check', $item, 'because there are no MySQL instances');
775+ }
776+ return;
777+ }
778
779 my @versions;
780- foreach my $var ( @{$item->{vars}} ) {
781- $var = lc($var);
782- my $version = $rows->{$var}->{value};
783- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
784- push @versions, $version;
785+ my %version_for;
786+ foreach my $instance ( @$instances ) {
787+ my $dbh = $instance->{dbh};
788+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
789+ my $sql = qq/SHOW $show/;
790+ PTDEBUG && _d($sql);
791+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
792+
793+ my @versions;
794+ foreach my $var ( @{$item->{vars}} ) {
795+ $var = lc($var);
796+ my $version = $rows->{$var}->{value};
797+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
798+ 'on', $instance->{name});
799+ push @versions, $version;
800+ }
801+
802+ $version_for{ $instance->{id} } = join(' ', @versions);
803 }
804
805- return join(' ', @versions);
806+ return \%version_for;
807 }
808
809 sub get_bin_version {
810@@ -3126,14 +3140,15 @@
811
812 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
813
814-use File::Basename qw();
815 use Data::Dumper qw();
816+use Digest::MD5 qw(md5_hex);
817+use Sys::Hostname qw(hostname);
818 use Fcntl qw(:DEFAULT);
819-
820+use File::Basename qw();
821 use File::Spec;
822
823-my $dir = File::Spec->tmpdir();
824-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
825+my $dir = File::Spec->tmpdir();
826+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
827 my $check_time_limit = 60 * 60 * 24; # one day
828
829 sub Dumper {
830@@ -3146,11 +3161,13 @@
831
832 local $EVAL_ERROR;
833 eval {
834+ require Percona::Toolkit;
835 require HTTPMicro;
836 require VersionCheck;
837 };
838
839 sub version_check {
840+ my @instances = @_;
841 eval {
842 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
843 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
844@@ -3160,19 +3177,26 @@
845 return;
846 }
847
848- if ( !time_to_check($check_time_file) ) {
849+ foreach my $instance ( @instances ) {
850+ my ($name, $id) = _generate_identifier($instance);
851+ $instance->{name} = $name;
852+ $instance->{id} = $id;
853+ }
854+
855+ my ($time_to_check, $instances_to_check)
856+ = time_to_check($check_time_file, \@instances);
857+ if ( !$time_to_check ) {
858 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
859- _d('It is not time to --version-checka again;',
860+ _d('It is not time to --version-check again;',
861 'only 1 check per', $check_time_limit, 'seconds, and the last',
862 'check was performed on the modified time of', $check_time_file);
863 }
864 return;
865 }
866
867- my $dbh = shift; # optional
868 my $advice = pingback(
869- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
870- dbh => $dbh,
871+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
872+ instances => $instances_to_check,
873 );
874 if ( $advice ) {
875 print "# Percona suggests these upgrades:\n";
876@@ -3200,7 +3224,7 @@
877 }
878 my ($url) = @args{@required_args};
879
880- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
881+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
882
883 $ua ||= HTTPMicro->new( timeout => 2 );
884 $vc ||= VersionCheck->new();
885@@ -3219,24 +3243,27 @@
886 );
887 die "Failed to parse server requested programs: $response->{content}"
888 if !scalar keys %$items;
889-
890+
891 my $versions = $vc->get_versions(
892- items => $items,
893- dbh => $dbh,
894+ items => $items,
895+ instances => $instances,
896 );
897 die "Failed to get any program versions; should have at least gotten Perl"
898 if !scalar keys %$versions;
899
900 my $client_content = encode_client_response(
901- items => $items,
902- versions => $versions,
903+ items => $items,
904+ versions => $versions,
905+ general_id => md5_hex( hostname() ),
906 );
907
908 my $client_response = {
909 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
910 content => $client_content,
911 };
912- PTDEBUG && _d('Client response:', Dumper($client_response));
913+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
914+ _d('Client response:', Dumper($client_response));
915+ }
916
917 $response = $ua->request('POST', $url, $client_response);
918 PTDEBUG && _d('Server suggestions:', Dumper($response));
919@@ -3261,22 +3288,35 @@
920 }
921
922 sub time_to_check {
923- my ($file) = @_;
924+ my ($file, $instances) = @_;
925 die "I need a file argument" unless $file;
926
927+ my $created_file = 0;
928 if ( !-f $file ) {
929- PTDEBUG && _d('Creating', $file);
930+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
931+ _d('Creating time limit file', $file);
932+ }
933 _touch($file);
934- return 1;
935- }
936+ $created_file = 1;
937+ }
938+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
939+ _d('Time limit file already exists:', $file);
940+ }
941+
942+ my $time = int(time()); # current time
943+
944+ if ( @$instances ) {
945+ my $instances_to_check = instances_to_check($file, $instances, $time);
946+ return scalar @$instances_to_check, $instances_to_check;
947+ }
948+
949+ return 1 if $created_file;
950
951 my $mtime = (stat $file)[9];
952 if ( !defined $mtime ) {
953 PTDEBUG && _d('Error getting modified time of', $file);
954 return 0;
955 }
956-
957- my $time = int(time());
958 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
959 if ( ($time - $mtime) > $check_time_limit ) {
960 _touch($file);
961@@ -3286,6 +3326,36 @@
962 return 0;
963 }
964
965+sub instances_to_check {
966+ my ($file, $instances, $time) = @_;
967+
968+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
969+ my $file_contents = do { local $/ = undef; <$fh> };
970+ close $fh;
971+ chomp($file_contents);
972+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
973+
974+ my @instances_to_check;
975+ foreach my $instance ( @$instances ) {
976+ my $mtime = $cached_instances{ $instance->{id} };
977+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
978+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
979+ _d('Time to check MySQL instance', $instance->{name});
980+ }
981+ push @instances_to_check, $instance;
982+ $cached_instances{ $instance->{id} } = $time;
983+ }
984+ }
985+
986+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
987+ while ( my ($id, $time) = each %cached_instances ) {
988+ print { $fh } "$id,$time\n";
989+ }
990+ close $fh or die "Cannot close $file: $OS_ERROR";
991+
992+ return \@instances_to_check;
993+}
994+
995 sub _touch {
996 my ($file) = @_;
997 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
998@@ -3294,22 +3364,48 @@
999 utime(undef, undef, $file);
1000 }
1001
1002+sub _generate_identifier {
1003+ my $instance = shift;
1004+ my $dbh = $instance->{dbh};
1005+ my $dsn = $instance->{dsn};
1006+
1007+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
1008+ my ($name) = eval { $dbh->selectrow_array($sql) };
1009+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
1010+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
1011+ }
1012+ my $id = md5_hex($name);
1013+
1014+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1015+ _d('MySQL instance', $name, 'is', $id);
1016+ }
1017+
1018+ return $name, $id;
1019+}
1020+
1021 sub encode_client_response {
1022 my (%args) = @_;
1023- my @required_args = qw(items versions);
1024+ my @required_args = qw(items versions general_id);
1025 foreach my $arg ( @required_args ) {
1026 die "I need a $arg arugment" unless $args{$arg};
1027 }
1028- my ($items, $versions) = @args{@required_args};
1029+ my ($items, $versions, $general_id) = @args{@required_args};
1030
1031 my @lines;
1032 foreach my $item ( sort keys %$items ) {
1033 next unless exists $versions->{$item};
1034- push @lines, join(';', $item, $versions->{$item});
1035+ if ( ref($versions->{$item}) eq 'HASH' ) {
1036+ my $mysql_versions = $versions->{$item};
1037+ for my $id ( keys %$mysql_versions ) {
1038+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
1039+ }
1040+ }
1041+ else {
1042+ push @lines, join(';', $general_id, $item, $versions->{$item});
1043+ }
1044 }
1045
1046 my $client_response = join("\n", @lines) . "\n";
1047- PTDEBUG && _d('Client response:', $client_response);
1048 return $client_response;
1049 }
1050
1051@@ -3505,7 +3601,10 @@
1052 # Do the version-check
1053 # ########################################################################
1054 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
1055- Pingback::version_check($dbh);
1056+ Pingback::version_check(
1057+ { dbh => $dbh, dsn => $source_dsn },
1058+ ($dest_dsn ? { dbh => $dest_dsn, dsn => $dest_dsn } : ()),
1059+ );
1060 }
1061
1062 # ########################################################################
1063
1064=== modified file 'bin/pt-diskstats'
1065--- bin/pt-diskstats 2012-08-24 22:57:01 +0000
1066+++ bin/pt-diskstats 2012-08-28 21:31:19 +0000
1067@@ -39,6 +39,7 @@
1068 {
1069 package Percona::Toolkit;
1070 our $VERSION = '2.1.3';
1071+
1072 1;
1073 }
1074 # ###########################################################################
1075@@ -3580,20 +3581,19 @@
1076 die "I need a $arg arugment" unless $args{$arg};
1077 }
1078 my ($items) = @args{@required_args};
1079- my $dbh = $args{dbh}; # optional
1080
1081 my %versions;
1082 foreach my $item ( values %$items ) {
1083 next unless $self->valid_item($item);
1084-
1085+
1086 eval {
1087 my $func = 'get_' . $item->{type};
1088 my $version = $self->$func(
1089- item => $item,
1090- dbh => $dbh,
1091+ item => $item,
1092+ instances => $args{instances},
1093 );
1094 if ( $version ) {
1095- chomp $version;
1096+ chomp $version unless ref($version);
1097 $versions{$item->{item}} = $version;
1098 }
1099 };
1100@@ -3718,28 +3718,42 @@
1101 );
1102 }
1103
1104-
1105 sub _get_from_mysql {
1106 my ($self, %args) = @_;
1107- my $show = $args{show};
1108- my $item = $args{item};
1109- my $dbh = $args{dbh};
1110- return unless $show && $item && $dbh;
1111+ my $show = $args{show};
1112+ my $item = $args{item};
1113+ my $instances = $args{instances};
1114+ return unless $show && $item;
1115
1116- local $dbh->{FetchHashKeyName} = 'NAME_lc';
1117- my $sql = qq/SHOW $show/;
1118- PTDEBUG && _d($sql);
1119- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
1120+ if ( !$instances || !@$instances ) {
1121+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1122+ _d('Cannot check', $item, 'because there are no MySQL instances');
1123+ }
1124+ return;
1125+ }
1126
1127 my @versions;
1128- foreach my $var ( @{$item->{vars}} ) {
1129- $var = lc($var);
1130- my $version = $rows->{$var}->{value};
1131- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
1132- push @versions, $version;
1133+ my %version_for;
1134+ foreach my $instance ( @$instances ) {
1135+ my $dbh = $instance->{dbh};
1136+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
1137+ my $sql = qq/SHOW $show/;
1138+ PTDEBUG && _d($sql);
1139+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
1140+
1141+ my @versions;
1142+ foreach my $var ( @{$item->{vars}} ) {
1143+ $var = lc($var);
1144+ my $version = $rows->{$var}->{value};
1145+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
1146+ 'on', $instance->{name});
1147+ push @versions, $version;
1148+ }
1149+
1150+ $version_for{ $instance->{id} } = join(' ', @versions);
1151 }
1152
1153- return join(' ', @versions);
1154+ return \%version_for;
1155 }
1156
1157 sub get_bin_version {
1158@@ -4237,14 +4251,15 @@
1159
1160 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1161
1162-use File::Basename qw();
1163 use Data::Dumper qw();
1164+use Digest::MD5 qw(md5_hex);
1165+use Sys::Hostname qw(hostname);
1166 use Fcntl qw(:DEFAULT);
1167-
1168+use File::Basename qw();
1169 use File::Spec;
1170
1171-my $dir = File::Spec->tmpdir();
1172-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
1173+my $dir = File::Spec->tmpdir();
1174+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
1175 my $check_time_limit = 60 * 60 * 24; # one day
1176
1177 sub Dumper {
1178@@ -4257,11 +4272,13 @@
1179
1180 local $EVAL_ERROR;
1181 eval {
1182+ require Percona::Toolkit;
1183 require HTTPMicro;
1184 require VersionCheck;
1185 };
1186
1187 sub version_check {
1188+ my @instances = @_;
1189 eval {
1190 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
1191 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1192@@ -4271,19 +4288,26 @@
1193 return;
1194 }
1195
1196- if ( !time_to_check($check_time_file) ) {
1197+ foreach my $instance ( @instances ) {
1198+ my ($name, $id) = _generate_identifier($instance);
1199+ $instance->{name} = $name;
1200+ $instance->{id} = $id;
1201+ }
1202+
1203+ my ($time_to_check, $instances_to_check)
1204+ = time_to_check($check_time_file, \@instances);
1205+ if ( !$time_to_check ) {
1206 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1207- _d('It is not time to --version-checka again;',
1208+ _d('It is not time to --version-check again;',
1209 'only 1 check per', $check_time_limit, 'seconds, and the last',
1210 'check was performed on the modified time of', $check_time_file);
1211 }
1212 return;
1213 }
1214
1215- my $dbh = shift; # optional
1216 my $advice = pingback(
1217- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
1218- dbh => $dbh,
1219+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
1220+ instances => $instances_to_check,
1221 );
1222 if ( $advice ) {
1223 print "# Percona suggests these upgrades:\n";
1224@@ -4311,7 +4335,7 @@
1225 }
1226 my ($url) = @args{@required_args};
1227
1228- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
1229+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
1230
1231 $ua ||= HTTPMicro->new( timeout => 2 );
1232 $vc ||= VersionCheck->new();
1233@@ -4330,24 +4354,27 @@
1234 );
1235 die "Failed to parse server requested programs: $response->{content}"
1236 if !scalar keys %$items;
1237-
1238+
1239 my $versions = $vc->get_versions(
1240- items => $items,
1241- dbh => $dbh,
1242+ items => $items,
1243+ instances => $instances,
1244 );
1245 die "Failed to get any program versions; should have at least gotten Perl"
1246 if !scalar keys %$versions;
1247
1248 my $client_content = encode_client_response(
1249- items => $items,
1250- versions => $versions,
1251+ items => $items,
1252+ versions => $versions,
1253+ general_id => md5_hex( hostname() ),
1254 );
1255
1256 my $client_response = {
1257 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
1258 content => $client_content,
1259 };
1260- PTDEBUG && _d('Client response:', Dumper($client_response));
1261+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1262+ _d('Client response:', Dumper($client_response));
1263+ }
1264
1265 $response = $ua->request('POST', $url, $client_response);
1266 PTDEBUG && _d('Server suggestions:', Dumper($response));
1267@@ -4372,22 +4399,35 @@
1268 }
1269
1270 sub time_to_check {
1271- my ($file) = @_;
1272+ my ($file, $instances) = @_;
1273 die "I need a file argument" unless $file;
1274
1275+ my $created_file = 0;
1276 if ( !-f $file ) {
1277- PTDEBUG && _d('Creating', $file);
1278+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1279+ _d('Creating time limit file', $file);
1280+ }
1281 _touch($file);
1282- return 1;
1283- }
1284+ $created_file = 1;
1285+ }
1286+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1287+ _d('Time limit file already exists:', $file);
1288+ }
1289+
1290+ my $time = int(time()); # current time
1291+
1292+ if ( @$instances ) {
1293+ my $instances_to_check = instances_to_check($file, $instances, $time);
1294+ return scalar @$instances_to_check, $instances_to_check;
1295+ }
1296+
1297+ return 1 if $created_file;
1298
1299 my $mtime = (stat $file)[9];
1300 if ( !defined $mtime ) {
1301 PTDEBUG && _d('Error getting modified time of', $file);
1302 return 0;
1303 }
1304-
1305- my $time = int(time());
1306 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
1307 if ( ($time - $mtime) > $check_time_limit ) {
1308 _touch($file);
1309@@ -4397,6 +4437,36 @@
1310 return 0;
1311 }
1312
1313+sub instances_to_check {
1314+ my ($file, $instances, $time) = @_;
1315+
1316+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
1317+ my $file_contents = do { local $/ = undef; <$fh> };
1318+ close $fh;
1319+ chomp($file_contents);
1320+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
1321+
1322+ my @instances_to_check;
1323+ foreach my $instance ( @$instances ) {
1324+ my $mtime = $cached_instances{ $instance->{id} };
1325+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
1326+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1327+ _d('Time to check MySQL instance', $instance->{name});
1328+ }
1329+ push @instances_to_check, $instance;
1330+ $cached_instances{ $instance->{id} } = $time;
1331+ }
1332+ }
1333+
1334+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
1335+ while ( my ($id, $time) = each %cached_instances ) {
1336+ print { $fh } "$id,$time\n";
1337+ }
1338+ close $fh or die "Cannot close $file: $OS_ERROR";
1339+
1340+ return \@instances_to_check;
1341+}
1342+
1343 sub _touch {
1344 my ($file) = @_;
1345 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
1346@@ -4405,22 +4475,48 @@
1347 utime(undef, undef, $file);
1348 }
1349
1350+sub _generate_identifier {
1351+ my $instance = shift;
1352+ my $dbh = $instance->{dbh};
1353+ my $dsn = $instance->{dsn};
1354+
1355+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
1356+ my ($name) = eval { $dbh->selectrow_array($sql) };
1357+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
1358+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
1359+ }
1360+ my $id = md5_hex($name);
1361+
1362+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1363+ _d('MySQL instance', $name, 'is', $id);
1364+ }
1365+
1366+ return $name, $id;
1367+}
1368+
1369 sub encode_client_response {
1370 my (%args) = @_;
1371- my @required_args = qw(items versions);
1372+ my @required_args = qw(items versions general_id);
1373 foreach my $arg ( @required_args ) {
1374 die "I need a $arg arugment" unless $args{$arg};
1375 }
1376- my ($items, $versions) = @args{@required_args};
1377+ my ($items, $versions, $general_id) = @args{@required_args};
1378
1379 my @lines;
1380 foreach my $item ( sort keys %$items ) {
1381 next unless exists $versions->{$item};
1382- push @lines, join(';', $item, $versions->{$item});
1383+ if ( ref($versions->{$item}) eq 'HASH' ) {
1384+ my $mysql_versions = $versions->{$item};
1385+ for my $id ( keys %$mysql_versions ) {
1386+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
1387+ }
1388+ }
1389+ else {
1390+ push @lines, join(';', $general_id, $item, $versions->{$item});
1391+ }
1392 }
1393
1394 my $client_response = join("\n", @lines) . "\n";
1395- PTDEBUG && _d('Client response:', $client_response);
1396 return $client_response;
1397 }
1398
1399
1400=== modified file 'bin/pt-duplicate-key-checker'
1401--- bin/pt-duplicate-key-checker 2012-08-24 23:26:04 +0000
1402+++ bin/pt-duplicate-key-checker 2012-08-28 21:31:19 +0000
1403@@ -40,6 +40,7 @@
1404 {
1405 package Percona::Toolkit;
1406 our $VERSION = '2.1.3';
1407+
1408 1;
1409 }
1410 # ###########################################################################
1411@@ -3426,20 +3427,19 @@
1412 die "I need a $arg arugment" unless $args{$arg};
1413 }
1414 my ($items) = @args{@required_args};
1415- my $dbh = $args{dbh}; # optional
1416
1417 my %versions;
1418 foreach my $item ( values %$items ) {
1419 next unless $self->valid_item($item);
1420-
1421+
1422 eval {
1423 my $func = 'get_' . $item->{type};
1424 my $version = $self->$func(
1425- item => $item,
1426- dbh => $dbh,
1427+ item => $item,
1428+ instances => $args{instances},
1429 );
1430 if ( $version ) {
1431- chomp $version;
1432+ chomp $version unless ref($version);
1433 $versions{$item->{item}} = $version;
1434 }
1435 };
1436@@ -3564,28 +3564,42 @@
1437 );
1438 }
1439
1440-
1441 sub _get_from_mysql {
1442 my ($self, %args) = @_;
1443- my $show = $args{show};
1444- my $item = $args{item};
1445- my $dbh = $args{dbh};
1446- return unless $show && $item && $dbh;
1447+ my $show = $args{show};
1448+ my $item = $args{item};
1449+ my $instances = $args{instances};
1450+ return unless $show && $item;
1451
1452- local $dbh->{FetchHashKeyName} = 'NAME_lc';
1453- my $sql = qq/SHOW $show/;
1454- PTDEBUG && _d($sql);
1455- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
1456+ if ( !$instances || !@$instances ) {
1457+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1458+ _d('Cannot check', $item, 'because there are no MySQL instances');
1459+ }
1460+ return;
1461+ }
1462
1463 my @versions;
1464- foreach my $var ( @{$item->{vars}} ) {
1465- $var = lc($var);
1466- my $version = $rows->{$var}->{value};
1467- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
1468- push @versions, $version;
1469+ my %version_for;
1470+ foreach my $instance ( @$instances ) {
1471+ my $dbh = $instance->{dbh};
1472+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
1473+ my $sql = qq/SHOW $show/;
1474+ PTDEBUG && _d($sql);
1475+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
1476+
1477+ my @versions;
1478+ foreach my $var ( @{$item->{vars}} ) {
1479+ $var = lc($var);
1480+ my $version = $rows->{$var}->{value};
1481+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
1482+ 'on', $instance->{name});
1483+ push @versions, $version;
1484+ }
1485+
1486+ $version_for{ $instance->{id} } = join(' ', @versions);
1487 }
1488
1489- return join(' ', @versions);
1490+ return \%version_for;
1491 }
1492
1493 sub get_bin_version {
1494@@ -4083,14 +4097,15 @@
1495
1496 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1497
1498-use File::Basename qw();
1499 use Data::Dumper qw();
1500+use Digest::MD5 qw(md5_hex);
1501+use Sys::Hostname qw(hostname);
1502 use Fcntl qw(:DEFAULT);
1503-
1504+use File::Basename qw();
1505 use File::Spec;
1506
1507-my $dir = File::Spec->tmpdir();
1508-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
1509+my $dir = File::Spec->tmpdir();
1510+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
1511 my $check_time_limit = 60 * 60 * 24; # one day
1512
1513 sub Dumper {
1514@@ -4103,11 +4118,13 @@
1515
1516 local $EVAL_ERROR;
1517 eval {
1518+ require Percona::Toolkit;
1519 require HTTPMicro;
1520 require VersionCheck;
1521 };
1522
1523 sub version_check {
1524+ my @instances = @_;
1525 eval {
1526 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
1527 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1528@@ -4117,19 +4134,26 @@
1529 return;
1530 }
1531
1532- if ( !time_to_check($check_time_file) ) {
1533+ foreach my $instance ( @instances ) {
1534+ my ($name, $id) = _generate_identifier($instance);
1535+ $instance->{name} = $name;
1536+ $instance->{id} = $id;
1537+ }
1538+
1539+ my ($time_to_check, $instances_to_check)
1540+ = time_to_check($check_time_file, \@instances);
1541+ if ( !$time_to_check ) {
1542 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1543- _d('It is not time to --version-checka again;',
1544+ _d('It is not time to --version-check again;',
1545 'only 1 check per', $check_time_limit, 'seconds, and the last',
1546 'check was performed on the modified time of', $check_time_file);
1547 }
1548 return;
1549 }
1550
1551- my $dbh = shift; # optional
1552 my $advice = pingback(
1553- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
1554- dbh => $dbh,
1555+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
1556+ instances => $instances_to_check,
1557 );
1558 if ( $advice ) {
1559 print "# Percona suggests these upgrades:\n";
1560@@ -4157,7 +4181,7 @@
1561 }
1562 my ($url) = @args{@required_args};
1563
1564- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
1565+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
1566
1567 $ua ||= HTTPMicro->new( timeout => 2 );
1568 $vc ||= VersionCheck->new();
1569@@ -4176,24 +4200,27 @@
1570 );
1571 die "Failed to parse server requested programs: $response->{content}"
1572 if !scalar keys %$items;
1573-
1574+
1575 my $versions = $vc->get_versions(
1576- items => $items,
1577- dbh => $dbh,
1578+ items => $items,
1579+ instances => $instances,
1580 );
1581 die "Failed to get any program versions; should have at least gotten Perl"
1582 if !scalar keys %$versions;
1583
1584 my $client_content = encode_client_response(
1585- items => $items,
1586- versions => $versions,
1587+ items => $items,
1588+ versions => $versions,
1589+ general_id => md5_hex( hostname() ),
1590 );
1591
1592 my $client_response = {
1593 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
1594 content => $client_content,
1595 };
1596- PTDEBUG && _d('Client response:', Dumper($client_response));
1597+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1598+ _d('Client response:', Dumper($client_response));
1599+ }
1600
1601 $response = $ua->request('POST', $url, $client_response);
1602 PTDEBUG && _d('Server suggestions:', Dumper($response));
1603@@ -4218,22 +4245,35 @@
1604 }
1605
1606 sub time_to_check {
1607- my ($file) = @_;
1608+ my ($file, $instances) = @_;
1609 die "I need a file argument" unless $file;
1610
1611+ my $created_file = 0;
1612 if ( !-f $file ) {
1613- PTDEBUG && _d('Creating', $file);
1614+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1615+ _d('Creating time limit file', $file);
1616+ }
1617 _touch($file);
1618- return 1;
1619- }
1620+ $created_file = 1;
1621+ }
1622+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1623+ _d('Time limit file already exists:', $file);
1624+ }
1625+
1626+ my $time = int(time()); # current time
1627+
1628+ if ( @$instances ) {
1629+ my $instances_to_check = instances_to_check($file, $instances, $time);
1630+ return scalar @$instances_to_check, $instances_to_check;
1631+ }
1632+
1633+ return 1 if $created_file;
1634
1635 my $mtime = (stat $file)[9];
1636 if ( !defined $mtime ) {
1637 PTDEBUG && _d('Error getting modified time of', $file);
1638 return 0;
1639 }
1640-
1641- my $time = int(time());
1642 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
1643 if ( ($time - $mtime) > $check_time_limit ) {
1644 _touch($file);
1645@@ -4243,6 +4283,36 @@
1646 return 0;
1647 }
1648
1649+sub instances_to_check {
1650+ my ($file, $instances, $time) = @_;
1651+
1652+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
1653+ my $file_contents = do { local $/ = undef; <$fh> };
1654+ close $fh;
1655+ chomp($file_contents);
1656+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
1657+
1658+ my @instances_to_check;
1659+ foreach my $instance ( @$instances ) {
1660+ my $mtime = $cached_instances{ $instance->{id} };
1661+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
1662+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1663+ _d('Time to check MySQL instance', $instance->{name});
1664+ }
1665+ push @instances_to_check, $instance;
1666+ $cached_instances{ $instance->{id} } = $time;
1667+ }
1668+ }
1669+
1670+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
1671+ while ( my ($id, $time) = each %cached_instances ) {
1672+ print { $fh } "$id,$time\n";
1673+ }
1674+ close $fh or die "Cannot close $file: $OS_ERROR";
1675+
1676+ return \@instances_to_check;
1677+}
1678+
1679 sub _touch {
1680 my ($file) = @_;
1681 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
1682@@ -4251,22 +4321,48 @@
1683 utime(undef, undef, $file);
1684 }
1685
1686+sub _generate_identifier {
1687+ my $instance = shift;
1688+ my $dbh = $instance->{dbh};
1689+ my $dsn = $instance->{dsn};
1690+
1691+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
1692+ my ($name) = eval { $dbh->selectrow_array($sql) };
1693+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
1694+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
1695+ }
1696+ my $id = md5_hex($name);
1697+
1698+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1699+ _d('MySQL instance', $name, 'is', $id);
1700+ }
1701+
1702+ return $name, $id;
1703+}
1704+
1705 sub encode_client_response {
1706 my (%args) = @_;
1707- my @required_args = qw(items versions);
1708+ my @required_args = qw(items versions general_id);
1709 foreach my $arg ( @required_args ) {
1710 die "I need a $arg arugment" unless $args{$arg};
1711 }
1712- my ($items, $versions) = @args{@required_args};
1713+ my ($items, $versions, $general_id) = @args{@required_args};
1714
1715 my @lines;
1716 foreach my $item ( sort keys %$items ) {
1717 next unless exists $versions->{$item};
1718- push @lines, join(';', $item, $versions->{$item});
1719+ if ( ref($versions->{$item}) eq 'HASH' ) {
1720+ my $mysql_versions = $versions->{$item};
1721+ for my $id ( keys %$mysql_versions ) {
1722+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
1723+ }
1724+ }
1725+ else {
1726+ push @lines, join(';', $general_id, $item, $versions->{$item});
1727+ }
1728 }
1729
1730 my $client_response = join("\n", @lines) . "\n";
1731- PTDEBUG && _d('Client response:', $client_response);
1732 return $client_response;
1733 }
1734
1735@@ -4358,7 +4454,7 @@
1736 # Do the version-check
1737 # ########################################################################
1738 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
1739- Pingback::version_check($dbh);
1740+ Pingback::version_check({dbh => $dbh, dsn => $dsn});
1741 }
1742
1743 # #######################################################################
1744
1745=== modified file 'bin/pt-find'
1746--- bin/pt-find 2012-08-24 23:26:04 +0000
1747+++ bin/pt-find 2012-08-28 21:31:19 +0000
1748@@ -36,6 +36,7 @@
1749 {
1750 package Percona::Toolkit;
1751 our $VERSION = '2.1.3';
1752+
1753 1;
1754 }
1755 # ###########################################################################
1756@@ -2266,20 +2267,19 @@
1757 die "I need a $arg arugment" unless $args{$arg};
1758 }
1759 my ($items) = @args{@required_args};
1760- my $dbh = $args{dbh}; # optional
1761
1762 my %versions;
1763 foreach my $item ( values %$items ) {
1764 next unless $self->valid_item($item);
1765-
1766+
1767 eval {
1768 my $func = 'get_' . $item->{type};
1769 my $version = $self->$func(
1770- item => $item,
1771- dbh => $dbh,
1772+ item => $item,
1773+ instances => $args{instances},
1774 );
1775 if ( $version ) {
1776- chomp $version;
1777+ chomp $version unless ref($version);
1778 $versions{$item->{item}} = $version;
1779 }
1780 };
1781@@ -2404,28 +2404,42 @@
1782 );
1783 }
1784
1785-
1786 sub _get_from_mysql {
1787 my ($self, %args) = @_;
1788- my $show = $args{show};
1789- my $item = $args{item};
1790- my $dbh = $args{dbh};
1791- return unless $show && $item && $dbh;
1792+ my $show = $args{show};
1793+ my $item = $args{item};
1794+ my $instances = $args{instances};
1795+ return unless $show && $item;
1796
1797- local $dbh->{FetchHashKeyName} = 'NAME_lc';
1798- my $sql = qq/SHOW $show/;
1799- PTDEBUG && _d($sql);
1800- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
1801+ if ( !$instances || !@$instances ) {
1802+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1803+ _d('Cannot check', $item, 'because there are no MySQL instances');
1804+ }
1805+ return;
1806+ }
1807
1808 my @versions;
1809- foreach my $var ( @{$item->{vars}} ) {
1810- $var = lc($var);
1811- my $version = $rows->{$var}->{value};
1812- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
1813- push @versions, $version;
1814+ my %version_for;
1815+ foreach my $instance ( @$instances ) {
1816+ my $dbh = $instance->{dbh};
1817+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
1818+ my $sql = qq/SHOW $show/;
1819+ PTDEBUG && _d($sql);
1820+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
1821+
1822+ my @versions;
1823+ foreach my $var ( @{$item->{vars}} ) {
1824+ $var = lc($var);
1825+ my $version = $rows->{$var}->{value};
1826+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
1827+ 'on', $instance->{name});
1828+ push @versions, $version;
1829+ }
1830+
1831+ $version_for{ $instance->{id} } = join(' ', @versions);
1832 }
1833
1834- return join(' ', @versions);
1835+ return \%version_for;
1836 }
1837
1838 sub get_bin_version {
1839@@ -2923,14 +2937,15 @@
1840
1841 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
1842
1843-use File::Basename qw();
1844 use Data::Dumper qw();
1845+use Digest::MD5 qw(md5_hex);
1846+use Sys::Hostname qw(hostname);
1847 use Fcntl qw(:DEFAULT);
1848-
1849+use File::Basename qw();
1850 use File::Spec;
1851
1852-my $dir = File::Spec->tmpdir();
1853-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
1854+my $dir = File::Spec->tmpdir();
1855+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
1856 my $check_time_limit = 60 * 60 * 24; # one day
1857
1858 sub Dumper {
1859@@ -2943,11 +2958,13 @@
1860
1861 local $EVAL_ERROR;
1862 eval {
1863+ require Percona::Toolkit;
1864 require HTTPMicro;
1865 require VersionCheck;
1866 };
1867
1868 sub version_check {
1869+ my @instances = @_;
1870 eval {
1871 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
1872 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1873@@ -2957,19 +2974,26 @@
1874 return;
1875 }
1876
1877- if ( !time_to_check($check_time_file) ) {
1878+ foreach my $instance ( @instances ) {
1879+ my ($name, $id) = _generate_identifier($instance);
1880+ $instance->{name} = $name;
1881+ $instance->{id} = $id;
1882+ }
1883+
1884+ my ($time_to_check, $instances_to_check)
1885+ = time_to_check($check_time_file, \@instances);
1886+ if ( !$time_to_check ) {
1887 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1888- _d('It is not time to --version-checka again;',
1889+ _d('It is not time to --version-check again;',
1890 'only 1 check per', $check_time_limit, 'seconds, and the last',
1891 'check was performed on the modified time of', $check_time_file);
1892 }
1893 return;
1894 }
1895
1896- my $dbh = shift; # optional
1897 my $advice = pingback(
1898- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
1899- dbh => $dbh,
1900+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
1901+ instances => $instances_to_check,
1902 );
1903 if ( $advice ) {
1904 print "# Percona suggests these upgrades:\n";
1905@@ -2997,7 +3021,7 @@
1906 }
1907 my ($url) = @args{@required_args};
1908
1909- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
1910+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
1911
1912 $ua ||= HTTPMicro->new( timeout => 2 );
1913 $vc ||= VersionCheck->new();
1914@@ -3016,24 +3040,27 @@
1915 );
1916 die "Failed to parse server requested programs: $response->{content}"
1917 if !scalar keys %$items;
1918-
1919+
1920 my $versions = $vc->get_versions(
1921- items => $items,
1922- dbh => $dbh,
1923+ items => $items,
1924+ instances => $instances,
1925 );
1926 die "Failed to get any program versions; should have at least gotten Perl"
1927 if !scalar keys %$versions;
1928
1929 my $client_content = encode_client_response(
1930- items => $items,
1931- versions => $versions,
1932+ items => $items,
1933+ versions => $versions,
1934+ general_id => md5_hex( hostname() ),
1935 );
1936
1937 my $client_response = {
1938 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
1939 content => $client_content,
1940 };
1941- PTDEBUG && _d('Client response:', Dumper($client_response));
1942+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1943+ _d('Client response:', Dumper($client_response));
1944+ }
1945
1946 $response = $ua->request('POST', $url, $client_response);
1947 PTDEBUG && _d('Server suggestions:', Dumper($response));
1948@@ -3058,22 +3085,35 @@
1949 }
1950
1951 sub time_to_check {
1952- my ($file) = @_;
1953+ my ($file, $instances) = @_;
1954 die "I need a file argument" unless $file;
1955
1956+ my $created_file = 0;
1957 if ( !-f $file ) {
1958- PTDEBUG && _d('Creating', $file);
1959+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1960+ _d('Creating time limit file', $file);
1961+ }
1962 _touch($file);
1963- return 1;
1964- }
1965+ $created_file = 1;
1966+ }
1967+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
1968+ _d('Time limit file already exists:', $file);
1969+ }
1970+
1971+ my $time = int(time()); # current time
1972+
1973+ if ( @$instances ) {
1974+ my $instances_to_check = instances_to_check($file, $instances, $time);
1975+ return scalar @$instances_to_check, $instances_to_check;
1976+ }
1977+
1978+ return 1 if $created_file;
1979
1980 my $mtime = (stat $file)[9];
1981 if ( !defined $mtime ) {
1982 PTDEBUG && _d('Error getting modified time of', $file);
1983 return 0;
1984 }
1985-
1986- my $time = int(time());
1987 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
1988 if ( ($time - $mtime) > $check_time_limit ) {
1989 _touch($file);
1990@@ -3083,6 +3123,36 @@
1991 return 0;
1992 }
1993
1994+sub instances_to_check {
1995+ my ($file, $instances, $time) = @_;
1996+
1997+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
1998+ my $file_contents = do { local $/ = undef; <$fh> };
1999+ close $fh;
2000+ chomp($file_contents);
2001+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
2002+
2003+ my @instances_to_check;
2004+ foreach my $instance ( @$instances ) {
2005+ my $mtime = $cached_instances{ $instance->{id} };
2006+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
2007+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2008+ _d('Time to check MySQL instance', $instance->{name});
2009+ }
2010+ push @instances_to_check, $instance;
2011+ $cached_instances{ $instance->{id} } = $time;
2012+ }
2013+ }
2014+
2015+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
2016+ while ( my ($id, $time) = each %cached_instances ) {
2017+ print { $fh } "$id,$time\n";
2018+ }
2019+ close $fh or die "Cannot close $file: $OS_ERROR";
2020+
2021+ return \@instances_to_check;
2022+}
2023+
2024 sub _touch {
2025 my ($file) = @_;
2026 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
2027@@ -3091,22 +3161,48 @@
2028 utime(undef, undef, $file);
2029 }
2030
2031+sub _generate_identifier {
2032+ my $instance = shift;
2033+ my $dbh = $instance->{dbh};
2034+ my $dsn = $instance->{dsn};
2035+
2036+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
2037+ my ($name) = eval { $dbh->selectrow_array($sql) };
2038+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
2039+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
2040+ }
2041+ my $id = md5_hex($name);
2042+
2043+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2044+ _d('MySQL instance', $name, 'is', $id);
2045+ }
2046+
2047+ return $name, $id;
2048+}
2049+
2050 sub encode_client_response {
2051 my (%args) = @_;
2052- my @required_args = qw(items versions);
2053+ my @required_args = qw(items versions general_id);
2054 foreach my $arg ( @required_args ) {
2055 die "I need a $arg arugment" unless $args{$arg};
2056 }
2057- my ($items, $versions) = @args{@required_args};
2058+ my ($items, $versions, $general_id) = @args{@required_args};
2059
2060 my @lines;
2061 foreach my $item ( sort keys %$items ) {
2062 next unless exists $versions->{$item};
2063- push @lines, join(';', $item, $versions->{$item});
2064+ if ( ref($versions->{$item}) eq 'HASH' ) {
2065+ my $mysql_versions = $versions->{$item};
2066+ for my $id ( keys %$mysql_versions ) {
2067+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
2068+ }
2069+ }
2070+ else {
2071+ push @lines, join(';', $general_id, $item, $versions->{$item});
2072+ }
2073 }
2074
2075 my $client_response = join("\n", @lines) . "\n";
2076- PTDEBUG && _d('Client response:', $client_response);
2077 return $client_response;
2078 }
2079
2080@@ -3526,7 +3622,7 @@
2081 # Do the version-check
2082 # ########################################################################
2083 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
2084- Pingback::version_check($dbh);
2085+ Pingback::version_check({dbh => $dbh, dsn => $dsn});
2086 }
2087
2088 # ########################################################################
2089
2090=== modified file 'bin/pt-fk-error-logger'
2091--- bin/pt-fk-error-logger 2012-08-24 23:26:04 +0000
2092+++ bin/pt-fk-error-logger 2012-08-28 21:31:19 +0000
2093@@ -36,6 +36,7 @@
2094 {
2095 package Percona::Toolkit;
2096 our $VERSION = '2.1.3';
2097+
2098 1;
2099 }
2100 # ###########################################################################
2101@@ -2173,20 +2174,19 @@
2102 die "I need a $arg arugment" unless $args{$arg};
2103 }
2104 my ($items) = @args{@required_args};
2105- my $dbh = $args{dbh}; # optional
2106
2107 my %versions;
2108 foreach my $item ( values %$items ) {
2109 next unless $self->valid_item($item);
2110-
2111+
2112 eval {
2113 my $func = 'get_' . $item->{type};
2114 my $version = $self->$func(
2115- item => $item,
2116- dbh => $dbh,
2117+ item => $item,
2118+ instances => $args{instances},
2119 );
2120 if ( $version ) {
2121- chomp $version;
2122+ chomp $version unless ref($version);
2123 $versions{$item->{item}} = $version;
2124 }
2125 };
2126@@ -2311,28 +2311,42 @@
2127 );
2128 }
2129
2130-
2131 sub _get_from_mysql {
2132 my ($self, %args) = @_;
2133- my $show = $args{show};
2134- my $item = $args{item};
2135- my $dbh = $args{dbh};
2136- return unless $show && $item && $dbh;
2137+ my $show = $args{show};
2138+ my $item = $args{item};
2139+ my $instances = $args{instances};
2140+ return unless $show && $item;
2141
2142- local $dbh->{FetchHashKeyName} = 'NAME_lc';
2143- my $sql = qq/SHOW $show/;
2144- PTDEBUG && _d($sql);
2145- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
2146+ if ( !$instances || !@$instances ) {
2147+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2148+ _d('Cannot check', $item, 'because there are no MySQL instances');
2149+ }
2150+ return;
2151+ }
2152
2153 my @versions;
2154- foreach my $var ( @{$item->{vars}} ) {
2155- $var = lc($var);
2156- my $version = $rows->{$var}->{value};
2157- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
2158- push @versions, $version;
2159+ my %version_for;
2160+ foreach my $instance ( @$instances ) {
2161+ my $dbh = $instance->{dbh};
2162+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
2163+ my $sql = qq/SHOW $show/;
2164+ PTDEBUG && _d($sql);
2165+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
2166+
2167+ my @versions;
2168+ foreach my $var ( @{$item->{vars}} ) {
2169+ $var = lc($var);
2170+ my $version = $rows->{$var}->{value};
2171+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
2172+ 'on', $instance->{name});
2173+ push @versions, $version;
2174+ }
2175+
2176+ $version_for{ $instance->{id} } = join(' ', @versions);
2177 }
2178
2179- return join(' ', @versions);
2180+ return \%version_for;
2181 }
2182
2183 sub get_bin_version {
2184@@ -2830,14 +2844,15 @@
2185
2186 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
2187
2188-use File::Basename qw();
2189 use Data::Dumper qw();
2190+use Digest::MD5 qw(md5_hex);
2191+use Sys::Hostname qw(hostname);
2192 use Fcntl qw(:DEFAULT);
2193-
2194+use File::Basename qw();
2195 use File::Spec;
2196
2197-my $dir = File::Spec->tmpdir();
2198-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
2199+my $dir = File::Spec->tmpdir();
2200+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
2201 my $check_time_limit = 60 * 60 * 24; # one day
2202
2203 sub Dumper {
2204@@ -2850,11 +2865,13 @@
2205
2206 local $EVAL_ERROR;
2207 eval {
2208+ require Percona::Toolkit;
2209 require HTTPMicro;
2210 require VersionCheck;
2211 };
2212
2213 sub version_check {
2214+ my @instances = @_;
2215 eval {
2216 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
2217 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2218@@ -2864,19 +2881,26 @@
2219 return;
2220 }
2221
2222- if ( !time_to_check($check_time_file) ) {
2223+ foreach my $instance ( @instances ) {
2224+ my ($name, $id) = _generate_identifier($instance);
2225+ $instance->{name} = $name;
2226+ $instance->{id} = $id;
2227+ }
2228+
2229+ my ($time_to_check, $instances_to_check)
2230+ = time_to_check($check_time_file, \@instances);
2231+ if ( !$time_to_check ) {
2232 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2233- _d('It is not time to --version-checka again;',
2234+ _d('It is not time to --version-check again;',
2235 'only 1 check per', $check_time_limit, 'seconds, and the last',
2236 'check was performed on the modified time of', $check_time_file);
2237 }
2238 return;
2239 }
2240
2241- my $dbh = shift; # optional
2242 my $advice = pingback(
2243- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
2244- dbh => $dbh,
2245+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
2246+ instances => $instances_to_check,
2247 );
2248 if ( $advice ) {
2249 print "# Percona suggests these upgrades:\n";
2250@@ -2904,7 +2928,7 @@
2251 }
2252 my ($url) = @args{@required_args};
2253
2254- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
2255+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
2256
2257 $ua ||= HTTPMicro->new( timeout => 2 );
2258 $vc ||= VersionCheck->new();
2259@@ -2923,24 +2947,27 @@
2260 );
2261 die "Failed to parse server requested programs: $response->{content}"
2262 if !scalar keys %$items;
2263-
2264+
2265 my $versions = $vc->get_versions(
2266- items => $items,
2267- dbh => $dbh,
2268+ items => $items,
2269+ instances => $instances,
2270 );
2271 die "Failed to get any program versions; should have at least gotten Perl"
2272 if !scalar keys %$versions;
2273
2274 my $client_content = encode_client_response(
2275- items => $items,
2276- versions => $versions,
2277+ items => $items,
2278+ versions => $versions,
2279+ general_id => md5_hex( hostname() ),
2280 );
2281
2282 my $client_response = {
2283 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
2284 content => $client_content,
2285 };
2286- PTDEBUG && _d('Client response:', Dumper($client_response));
2287+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2288+ _d('Client response:', Dumper($client_response));
2289+ }
2290
2291 $response = $ua->request('POST', $url, $client_response);
2292 PTDEBUG && _d('Server suggestions:', Dumper($response));
2293@@ -2965,22 +2992,35 @@
2294 }
2295
2296 sub time_to_check {
2297- my ($file) = @_;
2298+ my ($file, $instances) = @_;
2299 die "I need a file argument" unless $file;
2300
2301+ my $created_file = 0;
2302 if ( !-f $file ) {
2303- PTDEBUG && _d('Creating', $file);
2304+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2305+ _d('Creating time limit file', $file);
2306+ }
2307 _touch($file);
2308- return 1;
2309- }
2310+ $created_file = 1;
2311+ }
2312+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2313+ _d('Time limit file already exists:', $file);
2314+ }
2315+
2316+ my $time = int(time()); # current time
2317+
2318+ if ( @$instances ) {
2319+ my $instances_to_check = instances_to_check($file, $instances, $time);
2320+ return scalar @$instances_to_check, $instances_to_check;
2321+ }
2322+
2323+ return 1 if $created_file;
2324
2325 my $mtime = (stat $file)[9];
2326 if ( !defined $mtime ) {
2327 PTDEBUG && _d('Error getting modified time of', $file);
2328 return 0;
2329 }
2330-
2331- my $time = int(time());
2332 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
2333 if ( ($time - $mtime) > $check_time_limit ) {
2334 _touch($file);
2335@@ -2990,6 +3030,36 @@
2336 return 0;
2337 }
2338
2339+sub instances_to_check {
2340+ my ($file, $instances, $time) = @_;
2341+
2342+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
2343+ my $file_contents = do { local $/ = undef; <$fh> };
2344+ close $fh;
2345+ chomp($file_contents);
2346+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
2347+
2348+ my @instances_to_check;
2349+ foreach my $instance ( @$instances ) {
2350+ my $mtime = $cached_instances{ $instance->{id} };
2351+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
2352+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2353+ _d('Time to check MySQL instance', $instance->{name});
2354+ }
2355+ push @instances_to_check, $instance;
2356+ $cached_instances{ $instance->{id} } = $time;
2357+ }
2358+ }
2359+
2360+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
2361+ while ( my ($id, $time) = each %cached_instances ) {
2362+ print { $fh } "$id,$time\n";
2363+ }
2364+ close $fh or die "Cannot close $file: $OS_ERROR";
2365+
2366+ return \@instances_to_check;
2367+}
2368+
2369 sub _touch {
2370 my ($file) = @_;
2371 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
2372@@ -2998,22 +3068,48 @@
2373 utime(undef, undef, $file);
2374 }
2375
2376+sub _generate_identifier {
2377+ my $instance = shift;
2378+ my $dbh = $instance->{dbh};
2379+ my $dsn = $instance->{dsn};
2380+
2381+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
2382+ my ($name) = eval { $dbh->selectrow_array($sql) };
2383+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
2384+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
2385+ }
2386+ my $id = md5_hex($name);
2387+
2388+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2389+ _d('MySQL instance', $name, 'is', $id);
2390+ }
2391+
2392+ return $name, $id;
2393+}
2394+
2395 sub encode_client_response {
2396 my (%args) = @_;
2397- my @required_args = qw(items versions);
2398+ my @required_args = qw(items versions general_id);
2399 foreach my $arg ( @required_args ) {
2400 die "I need a $arg arugment" unless $args{$arg};
2401 }
2402- my ($items, $versions) = @args{@required_args};
2403+ my ($items, $versions, $general_id) = @args{@required_args};
2404
2405 my @lines;
2406 foreach my $item ( sort keys %$items ) {
2407 next unless exists $versions->{$item};
2408- push @lines, join(';', $item, $versions->{$item});
2409+ if ( ref($versions->{$item}) eq 'HASH' ) {
2410+ my $mysql_versions = $versions->{$item};
2411+ for my $id ( keys %$mysql_versions ) {
2412+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
2413+ }
2414+ }
2415+ else {
2416+ push @lines, join(';', $general_id, $item, $versions->{$item});
2417+ }
2418 }
2419
2420 my $client_response = join("\n", @lines) . "\n";
2421- PTDEBUG && _d('Client response:', $client_response);
2422 return $client_response;
2423 }
2424
2425@@ -3150,7 +3246,10 @@
2426 # Do the version-check
2427 # ########################################################################
2428 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
2429- Pingback::version_check($dbh);
2430+ Pingback::version_check(
2431+ { dbh => $dbh, dsn => $src_dsn },
2432+ ($dst_dbh ? { dbh => $dst_dbh, dsn => $dst_dsn } : ()),
2433+ );
2434 }
2435
2436 # ########################################################################
2437
2438=== modified file 'bin/pt-heartbeat'
2439--- bin/pt-heartbeat 2012-08-24 23:26:04 +0000
2440+++ bin/pt-heartbeat 2012-08-28 21:31:19 +0000
2441@@ -38,6 +38,7 @@
2442 {
2443 package Percona::Toolkit;
2444 our $VERSION = '2.1.3';
2445+
2446 1;
2447 }
2448 # ###########################################################################
2449@@ -3350,20 +3351,19 @@
2450 die "I need a $arg arugment" unless $args{$arg};
2451 }
2452 my ($items) = @args{@required_args};
2453- my $dbh = $args{dbh}; # optional
2454
2455 my %versions;
2456 foreach my $item ( values %$items ) {
2457 next unless $self->valid_item($item);
2458-
2459+
2460 eval {
2461 my $func = 'get_' . $item->{type};
2462 my $version = $self->$func(
2463- item => $item,
2464- dbh => $dbh,
2465+ item => $item,
2466+ instances => $args{instances},
2467 );
2468 if ( $version ) {
2469- chomp $version;
2470+ chomp $version unless ref($version);
2471 $versions{$item->{item}} = $version;
2472 }
2473 };
2474@@ -3488,28 +3488,42 @@
2475 );
2476 }
2477
2478-
2479 sub _get_from_mysql {
2480 my ($self, %args) = @_;
2481- my $show = $args{show};
2482- my $item = $args{item};
2483- my $dbh = $args{dbh};
2484- return unless $show && $item && $dbh;
2485+ my $show = $args{show};
2486+ my $item = $args{item};
2487+ my $instances = $args{instances};
2488+ return unless $show && $item;
2489
2490- local $dbh->{FetchHashKeyName} = 'NAME_lc';
2491- my $sql = qq/SHOW $show/;
2492- PTDEBUG && _d($sql);
2493- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
2494+ if ( !$instances || !@$instances ) {
2495+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2496+ _d('Cannot check', $item, 'because there are no MySQL instances');
2497+ }
2498+ return;
2499+ }
2500
2501 my @versions;
2502- foreach my $var ( @{$item->{vars}} ) {
2503- $var = lc($var);
2504- my $version = $rows->{$var}->{value};
2505- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
2506- push @versions, $version;
2507+ my %version_for;
2508+ foreach my $instance ( @$instances ) {
2509+ my $dbh = $instance->{dbh};
2510+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
2511+ my $sql = qq/SHOW $show/;
2512+ PTDEBUG && _d($sql);
2513+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
2514+
2515+ my @versions;
2516+ foreach my $var ( @{$item->{vars}} ) {
2517+ $var = lc($var);
2518+ my $version = $rows->{$var}->{value};
2519+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
2520+ 'on', $instance->{name});
2521+ push @versions, $version;
2522+ }
2523+
2524+ $version_for{ $instance->{id} } = join(' ', @versions);
2525 }
2526
2527- return join(' ', @versions);
2528+ return \%version_for;
2529 }
2530
2531 sub get_bin_version {
2532@@ -4007,14 +4021,15 @@
2533
2534 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
2535
2536-use File::Basename qw();
2537 use Data::Dumper qw();
2538+use Digest::MD5 qw(md5_hex);
2539+use Sys::Hostname qw(hostname);
2540 use Fcntl qw(:DEFAULT);
2541-
2542+use File::Basename qw();
2543 use File::Spec;
2544
2545-my $dir = File::Spec->tmpdir();
2546-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
2547+my $dir = File::Spec->tmpdir();
2548+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
2549 my $check_time_limit = 60 * 60 * 24; # one day
2550
2551 sub Dumper {
2552@@ -4027,11 +4042,13 @@
2553
2554 local $EVAL_ERROR;
2555 eval {
2556+ require Percona::Toolkit;
2557 require HTTPMicro;
2558 require VersionCheck;
2559 };
2560
2561 sub version_check {
2562+ my @instances = @_;
2563 eval {
2564 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
2565 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2566@@ -4041,19 +4058,26 @@
2567 return;
2568 }
2569
2570- if ( !time_to_check($check_time_file) ) {
2571+ foreach my $instance ( @instances ) {
2572+ my ($name, $id) = _generate_identifier($instance);
2573+ $instance->{name} = $name;
2574+ $instance->{id} = $id;
2575+ }
2576+
2577+ my ($time_to_check, $instances_to_check)
2578+ = time_to_check($check_time_file, \@instances);
2579+ if ( !$time_to_check ) {
2580 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2581- _d('It is not time to --version-checka again;',
2582+ _d('It is not time to --version-check again;',
2583 'only 1 check per', $check_time_limit, 'seconds, and the last',
2584 'check was performed on the modified time of', $check_time_file);
2585 }
2586 return;
2587 }
2588
2589- my $dbh = shift; # optional
2590 my $advice = pingback(
2591- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
2592- dbh => $dbh,
2593+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
2594+ instances => $instances_to_check,
2595 );
2596 if ( $advice ) {
2597 print "# Percona suggests these upgrades:\n";
2598@@ -4081,7 +4105,7 @@
2599 }
2600 my ($url) = @args{@required_args};
2601
2602- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
2603+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
2604
2605 $ua ||= HTTPMicro->new( timeout => 2 );
2606 $vc ||= VersionCheck->new();
2607@@ -4100,24 +4124,27 @@
2608 );
2609 die "Failed to parse server requested programs: $response->{content}"
2610 if !scalar keys %$items;
2611-
2612+
2613 my $versions = $vc->get_versions(
2614- items => $items,
2615- dbh => $dbh,
2616+ items => $items,
2617+ instances => $instances,
2618 );
2619 die "Failed to get any program versions; should have at least gotten Perl"
2620 if !scalar keys %$versions;
2621
2622 my $client_content = encode_client_response(
2623- items => $items,
2624- versions => $versions,
2625+ items => $items,
2626+ versions => $versions,
2627+ general_id => md5_hex( hostname() ),
2628 );
2629
2630 my $client_response = {
2631 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
2632 content => $client_content,
2633 };
2634- PTDEBUG && _d('Client response:', Dumper($client_response));
2635+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2636+ _d('Client response:', Dumper($client_response));
2637+ }
2638
2639 $response = $ua->request('POST', $url, $client_response);
2640 PTDEBUG && _d('Server suggestions:', Dumper($response));
2641@@ -4142,22 +4169,35 @@
2642 }
2643
2644 sub time_to_check {
2645- my ($file) = @_;
2646+ my ($file, $instances) = @_;
2647 die "I need a file argument" unless $file;
2648
2649+ my $created_file = 0;
2650 if ( !-f $file ) {
2651- PTDEBUG && _d('Creating', $file);
2652+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2653+ _d('Creating time limit file', $file);
2654+ }
2655 _touch($file);
2656- return 1;
2657- }
2658+ $created_file = 1;
2659+ }
2660+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2661+ _d('Time limit file already exists:', $file);
2662+ }
2663+
2664+ my $time = int(time()); # current time
2665+
2666+ if ( @$instances ) {
2667+ my $instances_to_check = instances_to_check($file, $instances, $time);
2668+ return scalar @$instances_to_check, $instances_to_check;
2669+ }
2670+
2671+ return 1 if $created_file;
2672
2673 my $mtime = (stat $file)[9];
2674 if ( !defined $mtime ) {
2675 PTDEBUG && _d('Error getting modified time of', $file);
2676 return 0;
2677 }
2678-
2679- my $time = int(time());
2680 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
2681 if ( ($time - $mtime) > $check_time_limit ) {
2682 _touch($file);
2683@@ -4167,6 +4207,36 @@
2684 return 0;
2685 }
2686
2687+sub instances_to_check {
2688+ my ($file, $instances, $time) = @_;
2689+
2690+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
2691+ my $file_contents = do { local $/ = undef; <$fh> };
2692+ close $fh;
2693+ chomp($file_contents);
2694+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
2695+
2696+ my @instances_to_check;
2697+ foreach my $instance ( @$instances ) {
2698+ my $mtime = $cached_instances{ $instance->{id} };
2699+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
2700+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2701+ _d('Time to check MySQL instance', $instance->{name});
2702+ }
2703+ push @instances_to_check, $instance;
2704+ $cached_instances{ $instance->{id} } = $time;
2705+ }
2706+ }
2707+
2708+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
2709+ while ( my ($id, $time) = each %cached_instances ) {
2710+ print { $fh } "$id,$time\n";
2711+ }
2712+ close $fh or die "Cannot close $file: $OS_ERROR";
2713+
2714+ return \@instances_to_check;
2715+}
2716+
2717 sub _touch {
2718 my ($file) = @_;
2719 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
2720@@ -4175,22 +4245,48 @@
2721 utime(undef, undef, $file);
2722 }
2723
2724+sub _generate_identifier {
2725+ my $instance = shift;
2726+ my $dbh = $instance->{dbh};
2727+ my $dsn = $instance->{dsn};
2728+
2729+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
2730+ my ($name) = eval { $dbh->selectrow_array($sql) };
2731+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
2732+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
2733+ }
2734+ my $id = md5_hex($name);
2735+
2736+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2737+ _d('MySQL instance', $name, 'is', $id);
2738+ }
2739+
2740+ return $name, $id;
2741+}
2742+
2743 sub encode_client_response {
2744 my (%args) = @_;
2745- my @required_args = qw(items versions);
2746+ my @required_args = qw(items versions general_id);
2747 foreach my $arg ( @required_args ) {
2748 die "I need a $arg arugment" unless $args{$arg};
2749 }
2750- my ($items, $versions) = @args{@required_args};
2751+ my ($items, $versions, $general_id) = @args{@required_args};
2752
2753 my @lines;
2754 foreach my $item ( sort keys %$items ) {
2755 next unless exists $versions->{$item};
2756- push @lines, join(';', $item, $versions->{$item});
2757+ if ( ref($versions->{$item}) eq 'HASH' ) {
2758+ my $mysql_versions = $versions->{$item};
2759+ for my $id ( keys %$mysql_versions ) {
2760+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
2761+ }
2762+ }
2763+ else {
2764+ push @lines, join(';', $general_id, $item, $versions->{$item});
2765+ }
2766 }
2767
2768 my $client_response = join("\n", @lines) . "\n";
2769- PTDEBUG && _d('Client response:', $client_response);
2770 return $client_response;
2771 }
2772
2773@@ -4646,7 +4742,7 @@
2774 # Do the version-check
2775 # ########################################################################
2776 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
2777- Pingback::version_check($dbh);
2778+ Pingback::version_check({dbh => $dbh, dsn => $dsn});
2779 }
2780
2781 # ########################################################################
2782
2783=== modified file 'bin/pt-index-usage'
2784--- bin/pt-index-usage 2012-08-24 23:26:04 +0000
2785+++ bin/pt-index-usage 2012-08-28 21:31:19 +0000
2786@@ -46,6 +46,7 @@
2787 {
2788 package Percona::Toolkit;
2789 our $VERSION = '2.1.3';
2790+
2791 1;
2792 }
2793 # ###########################################################################
2794@@ -4932,20 +4933,19 @@
2795 die "I need a $arg arugment" unless $args{$arg};
2796 }
2797 my ($items) = @args{@required_args};
2798- my $dbh = $args{dbh}; # optional
2799
2800 my %versions;
2801 foreach my $item ( values %$items ) {
2802 next unless $self->valid_item($item);
2803-
2804+
2805 eval {
2806 my $func = 'get_' . $item->{type};
2807 my $version = $self->$func(
2808- item => $item,
2809- dbh => $dbh,
2810+ item => $item,
2811+ instances => $args{instances},
2812 );
2813 if ( $version ) {
2814- chomp $version;
2815+ chomp $version unless ref($version);
2816 $versions{$item->{item}} = $version;
2817 }
2818 };
2819@@ -5070,28 +5070,42 @@
2820 );
2821 }
2822
2823-
2824 sub _get_from_mysql {
2825 my ($self, %args) = @_;
2826- my $show = $args{show};
2827- my $item = $args{item};
2828- my $dbh = $args{dbh};
2829- return unless $show && $item && $dbh;
2830+ my $show = $args{show};
2831+ my $item = $args{item};
2832+ my $instances = $args{instances};
2833+ return unless $show && $item;
2834
2835- local $dbh->{FetchHashKeyName} = 'NAME_lc';
2836- my $sql = qq/SHOW $show/;
2837- PTDEBUG && _d($sql);
2838- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
2839+ if ( !$instances || !@$instances ) {
2840+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2841+ _d('Cannot check', $item, 'because there are no MySQL instances');
2842+ }
2843+ return;
2844+ }
2845
2846 my @versions;
2847- foreach my $var ( @{$item->{vars}} ) {
2848- $var = lc($var);
2849- my $version = $rows->{$var}->{value};
2850- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
2851- push @versions, $version;
2852+ my %version_for;
2853+ foreach my $instance ( @$instances ) {
2854+ my $dbh = $instance->{dbh};
2855+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
2856+ my $sql = qq/SHOW $show/;
2857+ PTDEBUG && _d($sql);
2858+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
2859+
2860+ my @versions;
2861+ foreach my $var ( @{$item->{vars}} ) {
2862+ $var = lc($var);
2863+ my $version = $rows->{$var}->{value};
2864+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
2865+ 'on', $instance->{name});
2866+ push @versions, $version;
2867+ }
2868+
2869+ $version_for{ $instance->{id} } = join(' ', @versions);
2870 }
2871
2872- return join(' ', @versions);
2873+ return \%version_for;
2874 }
2875
2876 sub get_bin_version {
2877@@ -5589,14 +5603,15 @@
2878
2879 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
2880
2881-use File::Basename qw();
2882 use Data::Dumper qw();
2883+use Digest::MD5 qw(md5_hex);
2884+use Sys::Hostname qw(hostname);
2885 use Fcntl qw(:DEFAULT);
2886-
2887+use File::Basename qw();
2888 use File::Spec;
2889
2890-my $dir = File::Spec->tmpdir();
2891-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
2892+my $dir = File::Spec->tmpdir();
2893+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
2894 my $check_time_limit = 60 * 60 * 24; # one day
2895
2896 sub Dumper {
2897@@ -5609,11 +5624,13 @@
2898
2899 local $EVAL_ERROR;
2900 eval {
2901+ require Percona::Toolkit;
2902 require HTTPMicro;
2903 require VersionCheck;
2904 };
2905
2906 sub version_check {
2907+ my @instances = @_;
2908 eval {
2909 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
2910 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2911@@ -5623,19 +5640,26 @@
2912 return;
2913 }
2914
2915- if ( !time_to_check($check_time_file) ) {
2916+ foreach my $instance ( @instances ) {
2917+ my ($name, $id) = _generate_identifier($instance);
2918+ $instance->{name} = $name;
2919+ $instance->{id} = $id;
2920+ }
2921+
2922+ my ($time_to_check, $instances_to_check)
2923+ = time_to_check($check_time_file, \@instances);
2924+ if ( !$time_to_check ) {
2925 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2926- _d('It is not time to --version-checka again;',
2927+ _d('It is not time to --version-check again;',
2928 'only 1 check per', $check_time_limit, 'seconds, and the last',
2929 'check was performed on the modified time of', $check_time_file);
2930 }
2931 return;
2932 }
2933
2934- my $dbh = shift; # optional
2935 my $advice = pingback(
2936- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
2937- dbh => $dbh,
2938+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
2939+ instances => $instances_to_check,
2940 );
2941 if ( $advice ) {
2942 print "# Percona suggests these upgrades:\n";
2943@@ -5663,7 +5687,7 @@
2944 }
2945 my ($url) = @args{@required_args};
2946
2947- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
2948+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
2949
2950 $ua ||= HTTPMicro->new( timeout => 2 );
2951 $vc ||= VersionCheck->new();
2952@@ -5682,24 +5706,27 @@
2953 );
2954 die "Failed to parse server requested programs: $response->{content}"
2955 if !scalar keys %$items;
2956-
2957+
2958 my $versions = $vc->get_versions(
2959- items => $items,
2960- dbh => $dbh,
2961+ items => $items,
2962+ instances => $instances,
2963 );
2964 die "Failed to get any program versions; should have at least gotten Perl"
2965 if !scalar keys %$versions;
2966
2967 my $client_content = encode_client_response(
2968- items => $items,
2969- versions => $versions,
2970+ items => $items,
2971+ versions => $versions,
2972+ general_id => md5_hex( hostname() ),
2973 );
2974
2975 my $client_response = {
2976 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
2977 content => $client_content,
2978 };
2979- PTDEBUG && _d('Client response:', Dumper($client_response));
2980+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2981+ _d('Client response:', Dumper($client_response));
2982+ }
2983
2984 $response = $ua->request('POST', $url, $client_response);
2985 PTDEBUG && _d('Server suggestions:', Dumper($response));
2986@@ -5724,22 +5751,35 @@
2987 }
2988
2989 sub time_to_check {
2990- my ($file) = @_;
2991+ my ($file, $instances) = @_;
2992 die "I need a file argument" unless $file;
2993
2994+ my $created_file = 0;
2995 if ( !-f $file ) {
2996- PTDEBUG && _d('Creating', $file);
2997+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
2998+ _d('Creating time limit file', $file);
2999+ }
3000 _touch($file);
3001- return 1;
3002- }
3003+ $created_file = 1;
3004+ }
3005+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3006+ _d('Time limit file already exists:', $file);
3007+ }
3008+
3009+ my $time = int(time()); # current time
3010+
3011+ if ( @$instances ) {
3012+ my $instances_to_check = instances_to_check($file, $instances, $time);
3013+ return scalar @$instances_to_check, $instances_to_check;
3014+ }
3015+
3016+ return 1 if $created_file;
3017
3018 my $mtime = (stat $file)[9];
3019 if ( !defined $mtime ) {
3020 PTDEBUG && _d('Error getting modified time of', $file);
3021 return 0;
3022 }
3023-
3024- my $time = int(time());
3025 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
3026 if ( ($time - $mtime) > $check_time_limit ) {
3027 _touch($file);
3028@@ -5749,6 +5789,36 @@
3029 return 0;
3030 }
3031
3032+sub instances_to_check {
3033+ my ($file, $instances, $time) = @_;
3034+
3035+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
3036+ my $file_contents = do { local $/ = undef; <$fh> };
3037+ close $fh;
3038+ chomp($file_contents);
3039+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
3040+
3041+ my @instances_to_check;
3042+ foreach my $instance ( @$instances ) {
3043+ my $mtime = $cached_instances{ $instance->{id} };
3044+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
3045+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3046+ _d('Time to check MySQL instance', $instance->{name});
3047+ }
3048+ push @instances_to_check, $instance;
3049+ $cached_instances{ $instance->{id} } = $time;
3050+ }
3051+ }
3052+
3053+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
3054+ while ( my ($id, $time) = each %cached_instances ) {
3055+ print { $fh } "$id,$time\n";
3056+ }
3057+ close $fh or die "Cannot close $file: $OS_ERROR";
3058+
3059+ return \@instances_to_check;
3060+}
3061+
3062 sub _touch {
3063 my ($file) = @_;
3064 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
3065@@ -5757,22 +5827,48 @@
3066 utime(undef, undef, $file);
3067 }
3068
3069+sub _generate_identifier {
3070+ my $instance = shift;
3071+ my $dbh = $instance->{dbh};
3072+ my $dsn = $instance->{dsn};
3073+
3074+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
3075+ my ($name) = eval { $dbh->selectrow_array($sql) };
3076+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
3077+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
3078+ }
3079+ my $id = md5_hex($name);
3080+
3081+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3082+ _d('MySQL instance', $name, 'is', $id);
3083+ }
3084+
3085+ return $name, $id;
3086+}
3087+
3088 sub encode_client_response {
3089 my (%args) = @_;
3090- my @required_args = qw(items versions);
3091+ my @required_args = qw(items versions general_id);
3092 foreach my $arg ( @required_args ) {
3093 die "I need a $arg arugment" unless $args{$arg};
3094 }
3095- my ($items, $versions) = @args{@required_args};
3096+ my ($items, $versions, $general_id) = @args{@required_args};
3097
3098 my @lines;
3099 foreach my $item ( sort keys %$items ) {
3100 next unless exists $versions->{$item};
3101- push @lines, join(';', $item, $versions->{$item});
3102+ if ( ref($versions->{$item}) eq 'HASH' ) {
3103+ my $mysql_versions = $versions->{$item};
3104+ for my $id ( keys %$mysql_versions ) {
3105+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
3106+ }
3107+ }
3108+ else {
3109+ push @lines, join(';', $general_id, $item, $versions->{$item});
3110+ }
3111 }
3112
3113 my $client_response = join("\n", @lines) . "\n";
3114- PTDEBUG && _d('Client response:', $client_response);
3115 return $client_response;
3116 }
3117
3118@@ -5854,10 +5950,11 @@
3119 # are given on the cmd line then parse_options() will return undef,
3120 # but get_cxn() required a defined dsn arg so use an empty hashref.
3121 # ##########################################################################
3122- my ($dbh, $si_dbh, $res_dbh);
3123+ my ($dsn, $dbh, $si_dbh, $res_dbh);
3124+ my $res_dsn;
3125 my $res_db;
3126 eval {
3127- my $dsn = $dp->parse_options($o) || {};
3128+ $dsn = $dp->parse_options($o) || {};
3129
3130 # dbh for EXPLAIN-ing.
3131 $dbh = get_cxn(
3132@@ -5875,7 +5972,7 @@
3133 );
3134
3135 # dbh for --save-results-database
3136- if ( my $res_dsn = $o->get('save-results-database') ) {
3137+ if ( $res_dsn = $o->get('save-results-database') ) {
3138
3139 # To make --create-save-results-database work we have to
3140 # temporarily remove the D from the DSN to avoid the error
3141@@ -5988,7 +6085,10 @@
3142 # Do the version-check
3143 # ########################################################################
3144 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
3145- Pingback::version_check($dbh);
3146+ Pingback::version_check(
3147+ { dbh => $dbh, dsn => $dsn },
3148+ ($res_dbh ? { dbh => $res_dbh, dsn => $res_dsn } : ()),
3149+ );
3150 }
3151
3152 # ########################################################################
3153
3154=== modified file 'bin/pt-kill'
3155--- bin/pt-kill 2012-08-24 23:26:04 +0000
3156+++ bin/pt-kill 2012-08-28 21:31:19 +0000
3157@@ -44,6 +44,7 @@
3158 {
3159 package Percona::Toolkit;
3160 our $VERSION = '2.1.3';
3161+
3162 1;
3163 }
3164 # ###########################################################################
3165@@ -4927,20 +4928,19 @@
3166 die "I need a $arg arugment" unless $args{$arg};
3167 }
3168 my ($items) = @args{@required_args};
3169- my $dbh = $args{dbh}; # optional
3170
3171 my %versions;
3172 foreach my $item ( values %$items ) {
3173 next unless $self->valid_item($item);
3174-
3175+
3176 eval {
3177 my $func = 'get_' . $item->{type};
3178 my $version = $self->$func(
3179- item => $item,
3180- dbh => $dbh,
3181+ item => $item,
3182+ instances => $args{instances},
3183 );
3184 if ( $version ) {
3185- chomp $version;
3186+ chomp $version unless ref($version);
3187 $versions{$item->{item}} = $version;
3188 }
3189 };
3190@@ -5065,28 +5065,42 @@
3191 );
3192 }
3193
3194-
3195 sub _get_from_mysql {
3196 my ($self, %args) = @_;
3197- my $show = $args{show};
3198- my $item = $args{item};
3199- my $dbh = $args{dbh};
3200- return unless $show && $item && $dbh;
3201+ my $show = $args{show};
3202+ my $item = $args{item};
3203+ my $instances = $args{instances};
3204+ return unless $show && $item;
3205
3206- local $dbh->{FetchHashKeyName} = 'NAME_lc';
3207- my $sql = qq/SHOW $show/;
3208- PTDEBUG && _d($sql);
3209- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
3210+ if ( !$instances || !@$instances ) {
3211+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3212+ _d('Cannot check', $item, 'because there are no MySQL instances');
3213+ }
3214+ return;
3215+ }
3216
3217 my @versions;
3218- foreach my $var ( @{$item->{vars}} ) {
3219- $var = lc($var);
3220- my $version = $rows->{$var}->{value};
3221- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
3222- push @versions, $version;
3223+ my %version_for;
3224+ foreach my $instance ( @$instances ) {
3225+ my $dbh = $instance->{dbh};
3226+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
3227+ my $sql = qq/SHOW $show/;
3228+ PTDEBUG && _d($sql);
3229+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
3230+
3231+ my @versions;
3232+ foreach my $var ( @{$item->{vars}} ) {
3233+ $var = lc($var);
3234+ my $version = $rows->{$var}->{value};
3235+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
3236+ 'on', $instance->{name});
3237+ push @versions, $version;
3238+ }
3239+
3240+ $version_for{ $instance->{id} } = join(' ', @versions);
3241 }
3242
3243- return join(' ', @versions);
3244+ return \%version_for;
3245 }
3246
3247 sub get_bin_version {
3248@@ -5584,14 +5598,15 @@
3249
3250 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
3251
3252-use File::Basename qw();
3253 use Data::Dumper qw();
3254+use Digest::MD5 qw(md5_hex);
3255+use Sys::Hostname qw(hostname);
3256 use Fcntl qw(:DEFAULT);
3257-
3258+use File::Basename qw();
3259 use File::Spec;
3260
3261-my $dir = File::Spec->tmpdir();
3262-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
3263+my $dir = File::Spec->tmpdir();
3264+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
3265 my $check_time_limit = 60 * 60 * 24; # one day
3266
3267 sub Dumper {
3268@@ -5604,11 +5619,13 @@
3269
3270 local $EVAL_ERROR;
3271 eval {
3272+ require Percona::Toolkit;
3273 require HTTPMicro;
3274 require VersionCheck;
3275 };
3276
3277 sub version_check {
3278+ my @instances = @_;
3279 eval {
3280 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
3281 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3282@@ -5618,19 +5635,26 @@
3283 return;
3284 }
3285
3286- if ( !time_to_check($check_time_file) ) {
3287+ foreach my $instance ( @instances ) {
3288+ my ($name, $id) = _generate_identifier($instance);
3289+ $instance->{name} = $name;
3290+ $instance->{id} = $id;
3291+ }
3292+
3293+ my ($time_to_check, $instances_to_check)
3294+ = time_to_check($check_time_file, \@instances);
3295+ if ( !$time_to_check ) {
3296 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3297- _d('It is not time to --version-checka again;',
3298+ _d('It is not time to --version-check again;',
3299 'only 1 check per', $check_time_limit, 'seconds, and the last',
3300 'check was performed on the modified time of', $check_time_file);
3301 }
3302 return;
3303 }
3304
3305- my $dbh = shift; # optional
3306 my $advice = pingback(
3307- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
3308- dbh => $dbh,
3309+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
3310+ instances => $instances_to_check,
3311 );
3312 if ( $advice ) {
3313 print "# Percona suggests these upgrades:\n";
3314@@ -5658,7 +5682,7 @@
3315 }
3316 my ($url) = @args{@required_args};
3317
3318- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
3319+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
3320
3321 $ua ||= HTTPMicro->new( timeout => 2 );
3322 $vc ||= VersionCheck->new();
3323@@ -5677,24 +5701,27 @@
3324 );
3325 die "Failed to parse server requested programs: $response->{content}"
3326 if !scalar keys %$items;
3327-
3328+
3329 my $versions = $vc->get_versions(
3330- items => $items,
3331- dbh => $dbh,
3332+ items => $items,
3333+ instances => $instances,
3334 );
3335 die "Failed to get any program versions; should have at least gotten Perl"
3336 if !scalar keys %$versions;
3337
3338 my $client_content = encode_client_response(
3339- items => $items,
3340- versions => $versions,
3341+ items => $items,
3342+ versions => $versions,
3343+ general_id => md5_hex( hostname() ),
3344 );
3345
3346 my $client_response = {
3347 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
3348 content => $client_content,
3349 };
3350- PTDEBUG && _d('Client response:', Dumper($client_response));
3351+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3352+ _d('Client response:', Dumper($client_response));
3353+ }
3354
3355 $response = $ua->request('POST', $url, $client_response);
3356 PTDEBUG && _d('Server suggestions:', Dumper($response));
3357@@ -5719,22 +5746,35 @@
3358 }
3359
3360 sub time_to_check {
3361- my ($file) = @_;
3362+ my ($file, $instances) = @_;
3363 die "I need a file argument" unless $file;
3364
3365+ my $created_file = 0;
3366 if ( !-f $file ) {
3367- PTDEBUG && _d('Creating', $file);
3368+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3369+ _d('Creating time limit file', $file);
3370+ }
3371 _touch($file);
3372- return 1;
3373- }
3374+ $created_file = 1;
3375+ }
3376+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3377+ _d('Time limit file already exists:', $file);
3378+ }
3379+
3380+ my $time = int(time()); # current time
3381+
3382+ if ( @$instances ) {
3383+ my $instances_to_check = instances_to_check($file, $instances, $time);
3384+ return scalar @$instances_to_check, $instances_to_check;
3385+ }
3386+
3387+ return 1 if $created_file;
3388
3389 my $mtime = (stat $file)[9];
3390 if ( !defined $mtime ) {
3391 PTDEBUG && _d('Error getting modified time of', $file);
3392 return 0;
3393 }
3394-
3395- my $time = int(time());
3396 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
3397 if ( ($time - $mtime) > $check_time_limit ) {
3398 _touch($file);
3399@@ -5744,6 +5784,36 @@
3400 return 0;
3401 }
3402
3403+sub instances_to_check {
3404+ my ($file, $instances, $time) = @_;
3405+
3406+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
3407+ my $file_contents = do { local $/ = undef; <$fh> };
3408+ close $fh;
3409+ chomp($file_contents);
3410+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
3411+
3412+ my @instances_to_check;
3413+ foreach my $instance ( @$instances ) {
3414+ my $mtime = $cached_instances{ $instance->{id} };
3415+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
3416+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3417+ _d('Time to check MySQL instance', $instance->{name});
3418+ }
3419+ push @instances_to_check, $instance;
3420+ $cached_instances{ $instance->{id} } = $time;
3421+ }
3422+ }
3423+
3424+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
3425+ while ( my ($id, $time) = each %cached_instances ) {
3426+ print { $fh } "$id,$time\n";
3427+ }
3428+ close $fh or die "Cannot close $file: $OS_ERROR";
3429+
3430+ return \@instances_to_check;
3431+}
3432+
3433 sub _touch {
3434 my ($file) = @_;
3435 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
3436@@ -5752,22 +5822,48 @@
3437 utime(undef, undef, $file);
3438 }
3439
3440+sub _generate_identifier {
3441+ my $instance = shift;
3442+ my $dbh = $instance->{dbh};
3443+ my $dsn = $instance->{dsn};
3444+
3445+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
3446+ my ($name) = eval { $dbh->selectrow_array($sql) };
3447+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
3448+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
3449+ }
3450+ my $id = md5_hex($name);
3451+
3452+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3453+ _d('MySQL instance', $name, 'is', $id);
3454+ }
3455+
3456+ return $name, $id;
3457+}
3458+
3459 sub encode_client_response {
3460 my (%args) = @_;
3461- my @required_args = qw(items versions);
3462+ my @required_args = qw(items versions general_id);
3463 foreach my $arg ( @required_args ) {
3464 die "I need a $arg arugment" unless $args{$arg};
3465 }
3466- my ($items, $versions) = @args{@required_args};
3467+ my ($items, $versions, $general_id) = @args{@required_args};
3468
3469 my @lines;
3470 foreach my $item ( sort keys %$items ) {
3471 next unless exists $versions->{$item};
3472- push @lines, join(';', $item, $versions->{$item});
3473+ if ( ref($versions->{$item}) eq 'HASH' ) {
3474+ my $mysql_versions = $versions->{$item};
3475+ for my $id ( keys %$mysql_versions ) {
3476+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
3477+ }
3478+ }
3479+ else {
3480+ push @lines, join(';', $general_id, $item, $versions->{$item});
3481+ }
3482 }
3483
3484 my $client_response = join("\n", @lines) . "\n";
3485- PTDEBUG && _d('Client response:', $client_response);
3486 return $client_response;
3487 }
3488
3489@@ -6135,7 +6231,9 @@
3490 # Do the version-check
3491 # ########################################################################
3492 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
3493- Pingback::version_check($dbh);
3494+ Pingback::version_check(
3495+ ($dbh ? { dbh => $dbh, dsn => $cxn->dsn() } : ())
3496+ );
3497 }
3498
3499 # ########################################################################
3500
3501=== modified file 'bin/pt-online-schema-change'
3502--- bin/pt-online-schema-change 2012-08-24 23:26:04 +0000
3503+++ bin/pt-online-schema-change 2012-08-28 21:31:19 +0000
3504@@ -50,6 +50,7 @@
3505 {
3506 package Percona::Toolkit;
3507 our $VERSION = '2.1.3';
3508+
3509 1;
3510 }
3511 # ###########################################################################
3512@@ -5952,20 +5953,19 @@
3513 die "I need a $arg arugment" unless $args{$arg};
3514 }
3515 my ($items) = @args{@required_args};
3516- my $dbh = $args{dbh}; # optional
3517
3518 my %versions;
3519 foreach my $item ( values %$items ) {
3520 next unless $self->valid_item($item);
3521-
3522+
3523 eval {
3524 my $func = 'get_' . $item->{type};
3525 my $version = $self->$func(
3526- item => $item,
3527- dbh => $dbh,
3528+ item => $item,
3529+ instances => $args{instances},
3530 );
3531 if ( $version ) {
3532- chomp $version;
3533+ chomp $version unless ref($version);
3534 $versions{$item->{item}} = $version;
3535 }
3536 };
3537@@ -6090,28 +6090,42 @@
3538 );
3539 }
3540
3541-
3542 sub _get_from_mysql {
3543 my ($self, %args) = @_;
3544- my $show = $args{show};
3545- my $item = $args{item};
3546- my $dbh = $args{dbh};
3547- return unless $show && $item && $dbh;
3548+ my $show = $args{show};
3549+ my $item = $args{item};
3550+ my $instances = $args{instances};
3551+ return unless $show && $item;
3552
3553- local $dbh->{FetchHashKeyName} = 'NAME_lc';
3554- my $sql = qq/SHOW $show/;
3555- PTDEBUG && _d($sql);
3556- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
3557+ if ( !$instances || !@$instances ) {
3558+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3559+ _d('Cannot check', $item, 'because there are no MySQL instances');
3560+ }
3561+ return;
3562+ }
3563
3564 my @versions;
3565- foreach my $var ( @{$item->{vars}} ) {
3566- $var = lc($var);
3567- my $version = $rows->{$var}->{value};
3568- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
3569- push @versions, $version;
3570+ my %version_for;
3571+ foreach my $instance ( @$instances ) {
3572+ my $dbh = $instance->{dbh};
3573+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
3574+ my $sql = qq/SHOW $show/;
3575+ PTDEBUG && _d($sql);
3576+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
3577+
3578+ my @versions;
3579+ foreach my $var ( @{$item->{vars}} ) {
3580+ $var = lc($var);
3581+ my $version = $rows->{$var}->{value};
3582+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
3583+ 'on', $instance->{name});
3584+ push @versions, $version;
3585+ }
3586+
3587+ $version_for{ $instance->{id} } = join(' ', @versions);
3588 }
3589
3590- return join(' ', @versions);
3591+ return \%version_for;
3592 }
3593
3594 sub get_bin_version {
3595@@ -6609,14 +6623,15 @@
3596
3597 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
3598
3599-use File::Basename qw();
3600 use Data::Dumper qw();
3601+use Digest::MD5 qw(md5_hex);
3602+use Sys::Hostname qw(hostname);
3603 use Fcntl qw(:DEFAULT);
3604-
3605+use File::Basename qw();
3606 use File::Spec;
3607
3608-my $dir = File::Spec->tmpdir();
3609-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
3610+my $dir = File::Spec->tmpdir();
3611+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
3612 my $check_time_limit = 60 * 60 * 24; # one day
3613
3614 sub Dumper {
3615@@ -6629,11 +6644,13 @@
3616
3617 local $EVAL_ERROR;
3618 eval {
3619+ require Percona::Toolkit;
3620 require HTTPMicro;
3621 require VersionCheck;
3622 };
3623
3624 sub version_check {
3625+ my @instances = @_;
3626 eval {
3627 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
3628 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3629@@ -6643,19 +6660,26 @@
3630 return;
3631 }
3632
3633- if ( !time_to_check($check_time_file) ) {
3634+ foreach my $instance ( @instances ) {
3635+ my ($name, $id) = _generate_identifier($instance);
3636+ $instance->{name} = $name;
3637+ $instance->{id} = $id;
3638+ }
3639+
3640+ my ($time_to_check, $instances_to_check)
3641+ = time_to_check($check_time_file, \@instances);
3642+ if ( !$time_to_check ) {
3643 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3644- _d('It is not time to --version-checka again;',
3645+ _d('It is not time to --version-check again;',
3646 'only 1 check per', $check_time_limit, 'seconds, and the last',
3647 'check was performed on the modified time of', $check_time_file);
3648 }
3649 return;
3650 }
3651
3652- my $dbh = shift; # optional
3653 my $advice = pingback(
3654- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
3655- dbh => $dbh,
3656+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
3657+ instances => $instances_to_check,
3658 );
3659 if ( $advice ) {
3660 print "# Percona suggests these upgrades:\n";
3661@@ -6683,7 +6707,7 @@
3662 }
3663 my ($url) = @args{@required_args};
3664
3665- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
3666+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
3667
3668 $ua ||= HTTPMicro->new( timeout => 2 );
3669 $vc ||= VersionCheck->new();
3670@@ -6702,24 +6726,27 @@
3671 );
3672 die "Failed to parse server requested programs: $response->{content}"
3673 if !scalar keys %$items;
3674-
3675+
3676 my $versions = $vc->get_versions(
3677- items => $items,
3678- dbh => $dbh,
3679+ items => $items,
3680+ instances => $instances,
3681 );
3682 die "Failed to get any program versions; should have at least gotten Perl"
3683 if !scalar keys %$versions;
3684
3685 my $client_content = encode_client_response(
3686- items => $items,
3687- versions => $versions,
3688+ items => $items,
3689+ versions => $versions,
3690+ general_id => md5_hex( hostname() ),
3691 );
3692
3693 my $client_response = {
3694 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
3695 content => $client_content,
3696 };
3697- PTDEBUG && _d('Client response:', Dumper($client_response));
3698+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3699+ _d('Client response:', Dumper($client_response));
3700+ }
3701
3702 $response = $ua->request('POST', $url, $client_response);
3703 PTDEBUG && _d('Server suggestions:', Dumper($response));
3704@@ -6744,22 +6771,35 @@
3705 }
3706
3707 sub time_to_check {
3708- my ($file) = @_;
3709+ my ($file, $instances) = @_;
3710 die "I need a file argument" unless $file;
3711
3712+ my $created_file = 0;
3713 if ( !-f $file ) {
3714- PTDEBUG && _d('Creating', $file);
3715+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3716+ _d('Creating time limit file', $file);
3717+ }
3718 _touch($file);
3719- return 1;
3720- }
3721+ $created_file = 1;
3722+ }
3723+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3724+ _d('Time limit file already exists:', $file);
3725+ }
3726+
3727+ my $time = int(time()); # current time
3728+
3729+ if ( @$instances ) {
3730+ my $instances_to_check = instances_to_check($file, $instances, $time);
3731+ return scalar @$instances_to_check, $instances_to_check;
3732+ }
3733+
3734+ return 1 if $created_file;
3735
3736 my $mtime = (stat $file)[9];
3737 if ( !defined $mtime ) {
3738 PTDEBUG && _d('Error getting modified time of', $file);
3739 return 0;
3740 }
3741-
3742- my $time = int(time());
3743 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
3744 if ( ($time - $mtime) > $check_time_limit ) {
3745 _touch($file);
3746@@ -6769,6 +6809,36 @@
3747 return 0;
3748 }
3749
3750+sub instances_to_check {
3751+ my ($file, $instances, $time) = @_;
3752+
3753+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
3754+ my $file_contents = do { local $/ = undef; <$fh> };
3755+ close $fh;
3756+ chomp($file_contents);
3757+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
3758+
3759+ my @instances_to_check;
3760+ foreach my $instance ( @$instances ) {
3761+ my $mtime = $cached_instances{ $instance->{id} };
3762+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
3763+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3764+ _d('Time to check MySQL instance', $instance->{name});
3765+ }
3766+ push @instances_to_check, $instance;
3767+ $cached_instances{ $instance->{id} } = $time;
3768+ }
3769+ }
3770+
3771+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
3772+ while ( my ($id, $time) = each %cached_instances ) {
3773+ print { $fh } "$id,$time\n";
3774+ }
3775+ close $fh or die "Cannot close $file: $OS_ERROR";
3776+
3777+ return \@instances_to_check;
3778+}
3779+
3780 sub _touch {
3781 my ($file) = @_;
3782 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
3783@@ -6777,22 +6847,48 @@
3784 utime(undef, undef, $file);
3785 }
3786
3787+sub _generate_identifier {
3788+ my $instance = shift;
3789+ my $dbh = $instance->{dbh};
3790+ my $dsn = $instance->{dsn};
3791+
3792+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
3793+ my ($name) = eval { $dbh->selectrow_array($sql) };
3794+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
3795+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
3796+ }
3797+ my $id = md5_hex($name);
3798+
3799+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3800+ _d('MySQL instance', $name, 'is', $id);
3801+ }
3802+
3803+ return $name, $id;
3804+}
3805+
3806 sub encode_client_response {
3807 my (%args) = @_;
3808- my @required_args = qw(items versions);
3809+ my @required_args = qw(items versions general_id);
3810 foreach my $arg ( @required_args ) {
3811 die "I need a $arg arugment" unless $args{$arg};
3812 }
3813- my ($items, $versions) = @args{@required_args};
3814+ my ($items, $versions, $general_id) = @args{@required_args};
3815
3816 my @lines;
3817 foreach my $item ( sort keys %$items ) {
3818 next unless exists $versions->{$item};
3819- push @lines, join(';', $item, $versions->{$item});
3820+ if ( ref($versions->{$item}) eq 'HASH' ) {
3821+ my $mysql_versions = $versions->{$item};
3822+ for my $id ( keys %$mysql_versions ) {
3823+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
3824+ }
3825+ }
3826+ else {
3827+ push @lines, join(';', $general_id, $item, $versions->{$item});
3828+ }
3829 }
3830
3831 my $client_response = join("\n", @lines) . "\n";
3832- PTDEBUG && _d('Client response:', $client_response);
3833 return $client_response;
3834 }
3835
3836@@ -7029,13 +7125,6 @@
3837 }
3838
3839 # ########################################################################
3840- # Do the version-check
3841- # ########################################################################
3842- if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
3843- Pingback::version_check($cxn->dbh());
3844- }
3845-
3846- # ########################################################################
3847 # Setup lag and load monitors.
3848 # ########################################################################
3849 my $slaves; # all slaves that are found or specified
3850@@ -7203,6 +7292,16 @@
3851 }
3852
3853 # ########################################################################
3854+ # Do the version-check
3855+ # ########################################################################
3856+ if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
3857+ Pingback::version_check(
3858+ map { +{ dbh => $_->dbh(), dsn => $_->dsn() } }
3859+ $cxn, ($slaves ? @$slaves : ())
3860+ );
3861+ }
3862+
3863+ # ########################################################################
3864 # Setup and check the original table.
3865 # ########################################################################
3866 my $tp = new TableParser(Quoter => $q);
3867
3868=== modified file 'bin/pt-query-advisor'
3869--- bin/pt-query-advisor 2012-08-24 23:26:04 +0000
3870+++ bin/pt-query-advisor 2012-08-28 21:31:19 +0000
3871@@ -47,6 +47,7 @@
3872 {
3873 package Percona::Toolkit;
3874 our $VERSION = '2.1.3';
3875+
3876 1;
3877 }
3878 # ###########################################################################
3879@@ -6114,20 +6115,19 @@
3880 die "I need a $arg arugment" unless $args{$arg};
3881 }
3882 my ($items) = @args{@required_args};
3883- my $dbh = $args{dbh}; # optional
3884
3885 my %versions;
3886 foreach my $item ( values %$items ) {
3887 next unless $self->valid_item($item);
3888-
3889+
3890 eval {
3891 my $func = 'get_' . $item->{type};
3892 my $version = $self->$func(
3893- item => $item,
3894- dbh => $dbh,
3895+ item => $item,
3896+ instances => $args{instances},
3897 );
3898 if ( $version ) {
3899- chomp $version;
3900+ chomp $version unless ref($version);
3901 $versions{$item->{item}} = $version;
3902 }
3903 };
3904@@ -6252,28 +6252,42 @@
3905 );
3906 }
3907
3908-
3909 sub _get_from_mysql {
3910 my ($self, %args) = @_;
3911- my $show = $args{show};
3912- my $item = $args{item};
3913- my $dbh = $args{dbh};
3914- return unless $show && $item && $dbh;
3915+ my $show = $args{show};
3916+ my $item = $args{item};
3917+ my $instances = $args{instances};
3918+ return unless $show && $item;
3919
3920- local $dbh->{FetchHashKeyName} = 'NAME_lc';
3921- my $sql = qq/SHOW $show/;
3922- PTDEBUG && _d($sql);
3923- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
3924+ if ( !$instances || !@$instances ) {
3925+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3926+ _d('Cannot check', $item, 'because there are no MySQL instances');
3927+ }
3928+ return;
3929+ }
3930
3931 my @versions;
3932- foreach my $var ( @{$item->{vars}} ) {
3933- $var = lc($var);
3934- my $version = $rows->{$var}->{value};
3935- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
3936- push @versions, $version;
3937+ my %version_for;
3938+ foreach my $instance ( @$instances ) {
3939+ my $dbh = $instance->{dbh};
3940+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
3941+ my $sql = qq/SHOW $show/;
3942+ PTDEBUG && _d($sql);
3943+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
3944+
3945+ my @versions;
3946+ foreach my $var ( @{$item->{vars}} ) {
3947+ $var = lc($var);
3948+ my $version = $rows->{$var}->{value};
3949+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
3950+ 'on', $instance->{name});
3951+ push @versions, $version;
3952+ }
3953+
3954+ $version_for{ $instance->{id} } = join(' ', @versions);
3955 }
3956
3957- return join(' ', @versions);
3958+ return \%version_for;
3959 }
3960
3961 sub get_bin_version {
3962@@ -6771,14 +6785,15 @@
3963
3964 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
3965
3966-use File::Basename qw();
3967 use Data::Dumper qw();
3968+use Digest::MD5 qw(md5_hex);
3969+use Sys::Hostname qw(hostname);
3970 use Fcntl qw(:DEFAULT);
3971-
3972+use File::Basename qw();
3973 use File::Spec;
3974
3975-my $dir = File::Spec->tmpdir();
3976-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
3977+my $dir = File::Spec->tmpdir();
3978+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
3979 my $check_time_limit = 60 * 60 * 24; # one day
3980
3981 sub Dumper {
3982@@ -6791,11 +6806,13 @@
3983
3984 local $EVAL_ERROR;
3985 eval {
3986+ require Percona::Toolkit;
3987 require HTTPMicro;
3988 require VersionCheck;
3989 };
3990
3991 sub version_check {
3992+ my @instances = @_;
3993 eval {
3994 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
3995 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
3996@@ -6805,19 +6822,26 @@
3997 return;
3998 }
3999
4000- if ( !time_to_check($check_time_file) ) {
4001+ foreach my $instance ( @instances ) {
4002+ my ($name, $id) = _generate_identifier($instance);
4003+ $instance->{name} = $name;
4004+ $instance->{id} = $id;
4005+ }
4006+
4007+ my ($time_to_check, $instances_to_check)
4008+ = time_to_check($check_time_file, \@instances);
4009+ if ( !$time_to_check ) {
4010 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4011- _d('It is not time to --version-checka again;',
4012+ _d('It is not time to --version-check again;',
4013 'only 1 check per', $check_time_limit, 'seconds, and the last',
4014 'check was performed on the modified time of', $check_time_file);
4015 }
4016 return;
4017 }
4018
4019- my $dbh = shift; # optional
4020 my $advice = pingback(
4021- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
4022- dbh => $dbh,
4023+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
4024+ instances => $instances_to_check,
4025 );
4026 if ( $advice ) {
4027 print "# Percona suggests these upgrades:\n";
4028@@ -6845,7 +6869,7 @@
4029 }
4030 my ($url) = @args{@required_args};
4031
4032- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
4033+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
4034
4035 $ua ||= HTTPMicro->new( timeout => 2 );
4036 $vc ||= VersionCheck->new();
4037@@ -6864,24 +6888,27 @@
4038 );
4039 die "Failed to parse server requested programs: $response->{content}"
4040 if !scalar keys %$items;
4041-
4042+
4043 my $versions = $vc->get_versions(
4044- items => $items,
4045- dbh => $dbh,
4046+ items => $items,
4047+ instances => $instances,
4048 );
4049 die "Failed to get any program versions; should have at least gotten Perl"
4050 if !scalar keys %$versions;
4051
4052 my $client_content = encode_client_response(
4053- items => $items,
4054- versions => $versions,
4055+ items => $items,
4056+ versions => $versions,
4057+ general_id => md5_hex( hostname() ),
4058 );
4059
4060 my $client_response = {
4061 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
4062 content => $client_content,
4063 };
4064- PTDEBUG && _d('Client response:', Dumper($client_response));
4065+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4066+ _d('Client response:', Dumper($client_response));
4067+ }
4068
4069 $response = $ua->request('POST', $url, $client_response);
4070 PTDEBUG && _d('Server suggestions:', Dumper($response));
4071@@ -6906,22 +6933,35 @@
4072 }
4073
4074 sub time_to_check {
4075- my ($file) = @_;
4076+ my ($file, $instances) = @_;
4077 die "I need a file argument" unless $file;
4078
4079+ my $created_file = 0;
4080 if ( !-f $file ) {
4081- PTDEBUG && _d('Creating', $file);
4082+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4083+ _d('Creating time limit file', $file);
4084+ }
4085 _touch($file);
4086- return 1;
4087- }
4088+ $created_file = 1;
4089+ }
4090+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4091+ _d('Time limit file already exists:', $file);
4092+ }
4093+
4094+ my $time = int(time()); # current time
4095+
4096+ if ( @$instances ) {
4097+ my $instances_to_check = instances_to_check($file, $instances, $time);
4098+ return scalar @$instances_to_check, $instances_to_check;
4099+ }
4100+
4101+ return 1 if $created_file;
4102
4103 my $mtime = (stat $file)[9];
4104 if ( !defined $mtime ) {
4105 PTDEBUG && _d('Error getting modified time of', $file);
4106 return 0;
4107 }
4108-
4109- my $time = int(time());
4110 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
4111 if ( ($time - $mtime) > $check_time_limit ) {
4112 _touch($file);
4113@@ -6931,6 +6971,36 @@
4114 return 0;
4115 }
4116
4117+sub instances_to_check {
4118+ my ($file, $instances, $time) = @_;
4119+
4120+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
4121+ my $file_contents = do { local $/ = undef; <$fh> };
4122+ close $fh;
4123+ chomp($file_contents);
4124+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
4125+
4126+ my @instances_to_check;
4127+ foreach my $instance ( @$instances ) {
4128+ my $mtime = $cached_instances{ $instance->{id} };
4129+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
4130+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4131+ _d('Time to check MySQL instance', $instance->{name});
4132+ }
4133+ push @instances_to_check, $instance;
4134+ $cached_instances{ $instance->{id} } = $time;
4135+ }
4136+ }
4137+
4138+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
4139+ while ( my ($id, $time) = each %cached_instances ) {
4140+ print { $fh } "$id,$time\n";
4141+ }
4142+ close $fh or die "Cannot close $file: $OS_ERROR";
4143+
4144+ return \@instances_to_check;
4145+}
4146+
4147 sub _touch {
4148 my ($file) = @_;
4149 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
4150@@ -6939,22 +7009,48 @@
4151 utime(undef, undef, $file);
4152 }
4153
4154+sub _generate_identifier {
4155+ my $instance = shift;
4156+ my $dbh = $instance->{dbh};
4157+ my $dsn = $instance->{dsn};
4158+
4159+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
4160+ my ($name) = eval { $dbh->selectrow_array($sql) };
4161+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
4162+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
4163+ }
4164+ my $id = md5_hex($name);
4165+
4166+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4167+ _d('MySQL instance', $name, 'is', $id);
4168+ }
4169+
4170+ return $name, $id;
4171+}
4172+
4173 sub encode_client_response {
4174 my (%args) = @_;
4175- my @required_args = qw(items versions);
4176+ my @required_args = qw(items versions general_id);
4177 foreach my $arg ( @required_args ) {
4178 die "I need a $arg arugment" unless $args{$arg};
4179 }
4180- my ($items, $versions) = @args{@required_args};
4181+ my ($items, $versions, $general_id) = @args{@required_args};
4182
4183 my @lines;
4184 foreach my $item ( sort keys %$items ) {
4185 next unless exists $versions->{$item};
4186- push @lines, join(';', $item, $versions->{$item});
4187+ if ( ref($versions->{$item}) eq 'HASH' ) {
4188+ my $mysql_versions = $versions->{$item};
4189+ for my $id ( keys %$mysql_versions ) {
4190+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
4191+ }
4192+ }
4193+ else {
4194+ push @lines, join(';', $general_id, $item, $versions->{$item});
4195+ }
4196 }
4197
4198 my $client_response = join("\n", @lines) . "\n";
4199- PTDEBUG && _d('Client response:', $client_response);
4200 return $client_response;
4201 }
4202
4203@@ -7091,10 +7187,11 @@
4204 # #########################################################################
4205 # Try to connect to MySQL.
4206 # #########################################################################
4207- my $dbh;
4208+ my ($dbh, $dsn);
4209 eval {
4210+ $dsn = $dp->parse_options($o);
4211 $dbh = get_cxn(
4212- dsn => $dp->parse_options($o),
4213+ dsn => $dsn,
4214 %common_modules
4215 );
4216 };
4217@@ -7364,7 +7461,10 @@
4218 # Do the version-check
4219 # ########################################################################
4220 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
4221- Pingback::version_check($dbh);
4222+ Pingback::version_check(
4223+ ($review_dbh ? { dbh => $review_dbh, dsn => $review_dsn } : ()),
4224+ ($dbh ? { dbh => $dbh, dsn => $dsn } : ()),
4225+ );
4226 }
4227
4228 # #########################################################################
4229
4230=== modified file 'bin/pt-query-digest'
4231--- bin/pt-query-digest 2012-08-24 23:26:04 +0000
4232+++ bin/pt-query-digest 2012-08-28 21:31:19 +0000
4233@@ -64,6 +64,7 @@
4234 {
4235 package Percona::Toolkit;
4236 our $VERSION = '2.1.3';
4237+
4238 1;
4239 }
4240 # ###########################################################################
4241@@ -11966,20 +11967,19 @@
4242 die "I need a $arg arugment" unless $args{$arg};
4243 }
4244 my ($items) = @args{@required_args};
4245- my $dbh = $args{dbh}; # optional
4246
4247 my %versions;
4248 foreach my $item ( values %$items ) {
4249 next unless $self->valid_item($item);
4250-
4251+
4252 eval {
4253 my $func = 'get_' . $item->{type};
4254 my $version = $self->$func(
4255- item => $item,
4256- dbh => $dbh,
4257+ item => $item,
4258+ instances => $args{instances},
4259 );
4260 if ( $version ) {
4261- chomp $version;
4262+ chomp $version unless ref($version);
4263 $versions{$item->{item}} = $version;
4264 }
4265 };
4266@@ -12104,28 +12104,42 @@
4267 );
4268 }
4269
4270-
4271 sub _get_from_mysql {
4272 my ($self, %args) = @_;
4273- my $show = $args{show};
4274- my $item = $args{item};
4275- my $dbh = $args{dbh};
4276- return unless $show && $item && $dbh;
4277+ my $show = $args{show};
4278+ my $item = $args{item};
4279+ my $instances = $args{instances};
4280+ return unless $show && $item;
4281
4282- local $dbh->{FetchHashKeyName} = 'NAME_lc';
4283- my $sql = qq/SHOW $show/;
4284- PTDEBUG && _d($sql);
4285- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
4286+ if ( !$instances || !@$instances ) {
4287+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4288+ _d('Cannot check', $item, 'because there are no MySQL instances');
4289+ }
4290+ return;
4291+ }
4292
4293 my @versions;
4294- foreach my $var ( @{$item->{vars}} ) {
4295- $var = lc($var);
4296- my $version = $rows->{$var}->{value};
4297- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
4298- push @versions, $version;
4299+ my %version_for;
4300+ foreach my $instance ( @$instances ) {
4301+ my $dbh = $instance->{dbh};
4302+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
4303+ my $sql = qq/SHOW $show/;
4304+ PTDEBUG && _d($sql);
4305+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
4306+
4307+ my @versions;
4308+ foreach my $var ( @{$item->{vars}} ) {
4309+ $var = lc($var);
4310+ my $version = $rows->{$var}->{value};
4311+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
4312+ 'on', $instance->{name});
4313+ push @versions, $version;
4314+ }
4315+
4316+ $version_for{ $instance->{id} } = join(' ', @versions);
4317 }
4318
4319- return join(' ', @versions);
4320+ return \%version_for;
4321 }
4322
4323 sub get_bin_version {
4324@@ -12623,14 +12637,15 @@
4325
4326 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
4327
4328-use File::Basename qw();
4329 use Data::Dumper qw();
4330+use Digest::MD5 qw(md5_hex);
4331+use Sys::Hostname qw(hostname);
4332 use Fcntl qw(:DEFAULT);
4333-
4334+use File::Basename qw();
4335 use File::Spec;
4336
4337-my $dir = File::Spec->tmpdir();
4338-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
4339+my $dir = File::Spec->tmpdir();
4340+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
4341 my $check_time_limit = 60 * 60 * 24; # one day
4342
4343 sub Dumper {
4344@@ -12643,11 +12658,13 @@
4345
4346 local $EVAL_ERROR;
4347 eval {
4348+ require Percona::Toolkit;
4349 require HTTPMicro;
4350 require VersionCheck;
4351 };
4352
4353 sub version_check {
4354+ my @instances = @_;
4355 eval {
4356 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
4357 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4358@@ -12657,19 +12674,26 @@
4359 return;
4360 }
4361
4362- if ( !time_to_check($check_time_file) ) {
4363+ foreach my $instance ( @instances ) {
4364+ my ($name, $id) = _generate_identifier($instance);
4365+ $instance->{name} = $name;
4366+ $instance->{id} = $id;
4367+ }
4368+
4369+ my ($time_to_check, $instances_to_check)
4370+ = time_to_check($check_time_file, \@instances);
4371+ if ( !$time_to_check ) {
4372 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4373- _d('It is not time to --version-checka again;',
4374+ _d('It is not time to --version-check again;',
4375 'only 1 check per', $check_time_limit, 'seconds, and the last',
4376 'check was performed on the modified time of', $check_time_file);
4377 }
4378 return;
4379 }
4380
4381- my $dbh = shift; # optional
4382 my $advice = pingback(
4383- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
4384- dbh => $dbh,
4385+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
4386+ instances => $instances_to_check,
4387 );
4388 if ( $advice ) {
4389 print "# Percona suggests these upgrades:\n";
4390@@ -12697,7 +12721,7 @@
4391 }
4392 my ($url) = @args{@required_args};
4393
4394- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
4395+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
4396
4397 $ua ||= HTTPMicro->new( timeout => 2 );
4398 $vc ||= VersionCheck->new();
4399@@ -12716,24 +12740,27 @@
4400 );
4401 die "Failed to parse server requested programs: $response->{content}"
4402 if !scalar keys %$items;
4403-
4404+
4405 my $versions = $vc->get_versions(
4406- items => $items,
4407- dbh => $dbh,
4408+ items => $items,
4409+ instances => $instances,
4410 );
4411 die "Failed to get any program versions; should have at least gotten Perl"
4412 if !scalar keys %$versions;
4413
4414 my $client_content = encode_client_response(
4415- items => $items,
4416- versions => $versions,
4417+ items => $items,
4418+ versions => $versions,
4419+ general_id => md5_hex( hostname() ),
4420 );
4421
4422 my $client_response = {
4423 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
4424 content => $client_content,
4425 };
4426- PTDEBUG && _d('Client response:', Dumper($client_response));
4427+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4428+ _d('Client response:', Dumper($client_response));
4429+ }
4430
4431 $response = $ua->request('POST', $url, $client_response);
4432 PTDEBUG && _d('Server suggestions:', Dumper($response));
4433@@ -12758,22 +12785,35 @@
4434 }
4435
4436 sub time_to_check {
4437- my ($file) = @_;
4438+ my ($file, $instances) = @_;
4439 die "I need a file argument" unless $file;
4440
4441+ my $created_file = 0;
4442 if ( !-f $file ) {
4443- PTDEBUG && _d('Creating', $file);
4444+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4445+ _d('Creating time limit file', $file);
4446+ }
4447 _touch($file);
4448- return 1;
4449- }
4450+ $created_file = 1;
4451+ }
4452+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4453+ _d('Time limit file already exists:', $file);
4454+ }
4455+
4456+ my $time = int(time()); # current time
4457+
4458+ if ( @$instances ) {
4459+ my $instances_to_check = instances_to_check($file, $instances, $time);
4460+ return scalar @$instances_to_check, $instances_to_check;
4461+ }
4462+
4463+ return 1 if $created_file;
4464
4465 my $mtime = (stat $file)[9];
4466 if ( !defined $mtime ) {
4467 PTDEBUG && _d('Error getting modified time of', $file);
4468 return 0;
4469 }
4470-
4471- my $time = int(time());
4472 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
4473 if ( ($time - $mtime) > $check_time_limit ) {
4474 _touch($file);
4475@@ -12783,6 +12823,36 @@
4476 return 0;
4477 }
4478
4479+sub instances_to_check {
4480+ my ($file, $instances, $time) = @_;
4481+
4482+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
4483+ my $file_contents = do { local $/ = undef; <$fh> };
4484+ close $fh;
4485+ chomp($file_contents);
4486+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
4487+
4488+ my @instances_to_check;
4489+ foreach my $instance ( @$instances ) {
4490+ my $mtime = $cached_instances{ $instance->{id} };
4491+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
4492+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4493+ _d('Time to check MySQL instance', $instance->{name});
4494+ }
4495+ push @instances_to_check, $instance;
4496+ $cached_instances{ $instance->{id} } = $time;
4497+ }
4498+ }
4499+
4500+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
4501+ while ( my ($id, $time) = each %cached_instances ) {
4502+ print { $fh } "$id,$time\n";
4503+ }
4504+ close $fh or die "Cannot close $file: $OS_ERROR";
4505+
4506+ return \@instances_to_check;
4507+}
4508+
4509 sub _touch {
4510 my ($file) = @_;
4511 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
4512@@ -12791,22 +12861,48 @@
4513 utime(undef, undef, $file);
4514 }
4515
4516+sub _generate_identifier {
4517+ my $instance = shift;
4518+ my $dbh = $instance->{dbh};
4519+ my $dsn = $instance->{dsn};
4520+
4521+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
4522+ my ($name) = eval { $dbh->selectrow_array($sql) };
4523+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
4524+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
4525+ }
4526+ my $id = md5_hex($name);
4527+
4528+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4529+ _d('MySQL instance', $name, 'is', $id);
4530+ }
4531+
4532+ return $name, $id;
4533+}
4534+
4535 sub encode_client_response {
4536 my (%args) = @_;
4537- my @required_args = qw(items versions);
4538+ my @required_args = qw(items versions general_id);
4539 foreach my $arg ( @required_args ) {
4540 die "I need a $arg arugment" unless $args{$arg};
4541 }
4542- my ($items, $versions) = @args{@required_args};
4543+ my ($items, $versions, $general_id) = @args{@required_args};
4544
4545 my @lines;
4546 foreach my $item ( sort keys %$items ) {
4547 next unless exists $versions->{$item};
4548- push @lines, join(';', $item, $versions->{$item});
4549+ if ( ref($versions->{$item}) eq 'HASH' ) {
4550+ my $mysql_versions = $versions->{$item};
4551+ for my $id ( keys %$mysql_versions ) {
4552+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
4553+ }
4554+ }
4555+ else {
4556+ push @lines, join(';', $general_id, $item, $versions->{$item});
4557+ }
4558 }
4559
4560 my $client_response = join("\n", @lines) . "\n";
4561- PTDEBUG && _d('Client response:', $client_response);
4562 return $client_response;
4563 }
4564
4565@@ -13231,9 +13327,10 @@
4566 );
4567 } # input
4568
4569+ my $ps_dsn;
4570 { # event
4571 my $misc;
4572- if ( my $ps_dsn = $o->get('processlist') ) {
4573+ if ( $ps_dsn = $o->get('processlist') ) {
4574 my $ms = new MasterSlave(
4575 OptionParser => $o,
4576 DSNParser => $dp,
4577@@ -13929,6 +14026,7 @@
4578 }
4579 } # sample
4580
4581+ my $ex_dsn;
4582 { # execute throttle and execute
4583 my $et;
4584 if ( my $et_args = $o->get('execute-throttle') ) {
4585@@ -13967,7 +14065,7 @@
4586 );
4587 } # execute throttle
4588
4589- if ( my $ex_dsn = $o->get('execute') ) {
4590+ if ( $ex_dsn = $o->get('execute') ) {
4591 if ( $o->get('ask-pass') ) {
4592 $ex_dsn->{p} = OptionParser::prompt_noecho("Enter password for "
4593 . "--execute: ");
4594@@ -14165,7 +14263,11 @@
4595 # Do the version-check
4596 # ########################################################################
4597 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
4598- Pingback::version_check($qv_dbh || $ps_dbh || $ex_dbh);
4599+ Pingback::version_check(
4600+ ($qv_dbh ? { dbh => $qv_dbh, dsn => $review_dsn } : ()),
4601+ ($ps_dbh ? { dbh => $ps_dbh, dsn => $ps_dsn } : ()),
4602+ ($ex_dbh ? { dbh => $ex_dbh, dsn => $ex_dsn } : ()),
4603+ );
4604 }
4605
4606 # ##########################################################################
4607
4608=== modified file 'bin/pt-slave-delay'
4609--- bin/pt-slave-delay 2012-08-23 16:22:43 +0000
4610+++ bin/pt-slave-delay 2012-08-28 21:31:19 +0000
4611@@ -37,6 +37,7 @@
4612 {
4613 package Percona::Toolkit;
4614 our $VERSION = '2.1.3';
4615+
4616 1;
4617 }
4618 # ###########################################################################
4619@@ -2582,20 +2583,19 @@
4620 die "I need a $arg arugment" unless $args{$arg};
4621 }
4622 my ($items) = @args{@required_args};
4623- my $dbh = $args{dbh}; # optional
4624
4625 my %versions;
4626 foreach my $item ( values %$items ) {
4627 next unless $self->valid_item($item);
4628-
4629+
4630 eval {
4631 my $func = 'get_' . $item->{type};
4632 my $version = $self->$func(
4633- item => $item,
4634- dbh => $dbh,
4635+ item => $item,
4636+ instances => $args{instances},
4637 );
4638 if ( $version ) {
4639- chomp $version;
4640+ chomp $version unless ref($version);
4641 $versions{$item->{item}} = $version;
4642 }
4643 };
4644@@ -2720,28 +2720,42 @@
4645 );
4646 }
4647
4648-
4649 sub _get_from_mysql {
4650 my ($self, %args) = @_;
4651- my $show = $args{show};
4652- my $item = $args{item};
4653- my $dbh = $args{dbh};
4654- return unless $show && $item && $dbh;
4655+ my $show = $args{show};
4656+ my $item = $args{item};
4657+ my $instances = $args{instances};
4658+ return unless $show && $item;
4659
4660- local $dbh->{FetchHashKeyName} = 'NAME_lc';
4661- my $sql = qq/SHOW $show/;
4662- PTDEBUG && _d($sql);
4663- my $rows = $dbh->selectall_hashref($sql, 'variable_name');
4664+ if ( !$instances || !@$instances ) {
4665+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4666+ _d('Cannot check', $item, 'because there are no MySQL instances');
4667+ }
4668+ return;
4669+ }
4670
4671 my @versions;
4672- foreach my $var ( @{$item->{vars}} ) {
4673- $var = lc($var);
4674- my $version = $rows->{$var}->{value};
4675- PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version);
4676- push @versions, $version;
4677+ my %version_for;
4678+ foreach my $instance ( @$instances ) {
4679+ my $dbh = $instance->{dbh};
4680+ local $dbh->{FetchHashKeyName} = 'NAME_lc';
4681+ my $sql = qq/SHOW $show/;
4682+ PTDEBUG && _d($sql);
4683+ my $rows = $dbh->selectall_hashref($sql, 'variable_name');
4684+
4685+ my @versions;
4686+ foreach my $var ( @{$item->{vars}} ) {
4687+ $var = lc($var);
4688+ my $version = $rows->{$var}->{value};
4689+ PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version,
4690+ 'on', $instance->{name});
4691+ push @versions, $version;
4692+ }
4693+
4694+ $version_for{ $instance->{id} } = join(' ', @versions);
4695 }
4696
4697- return join(' ', @versions);
4698+ return \%version_for;
4699 }
4700
4701 sub get_bin_version {
4702@@ -3239,14 +3253,15 @@
4703
4704 use constant PTDEBUG => $ENV{PTDEBUG} || 0;
4705
4706-use File::Basename qw();
4707 use Data::Dumper qw();
4708+use Digest::MD5 qw(md5_hex);
4709+use Sys::Hostname qw(hostname);
4710 use Fcntl qw(:DEFAULT);
4711-
4712+use File::Basename qw();
4713 use File::Spec;
4714
4715-my $dir = File::Spec->tmpdir();
4716-my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
4717+my $dir = File::Spec->tmpdir();
4718+my $check_time_file = File::Spec->catfile($dir,'percona-toolkit-version-check');
4719 my $check_time_limit = 60 * 60 * 24; # one day
4720
4721 sub Dumper {
4722@@ -3259,11 +3274,13 @@
4723
4724 local $EVAL_ERROR;
4725 eval {
4726+ require Percona::Toolkit;
4727 require HTTPMicro;
4728 require VersionCheck;
4729 };
4730
4731 sub version_check {
4732+ my @instances = @_;
4733 eval {
4734 if (exists $ENV{PERCONA_VERSION_CHECK} && !$ENV{PERCONA_VERSION_CHECK}) {
4735 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4736@@ -3273,19 +3290,26 @@
4737 return;
4738 }
4739
4740- if ( !time_to_check($check_time_file) ) {
4741+ foreach my $instance ( @instances ) {
4742+ my ($name, $id) = _generate_identifier($instance);
4743+ $instance->{name} = $name;
4744+ $instance->{id} = $id;
4745+ }
4746+
4747+ my ($time_to_check, $instances_to_check)
4748+ = time_to_check($check_time_file, \@instances);
4749+ if ( !$time_to_check ) {
4750 if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4751- _d('It is not time to --version-checka again;',
4752+ _d('It is not time to --version-check again;',
4753 'only 1 check per', $check_time_limit, 'seconds, and the last',
4754 'check was performed on the modified time of', $check_time_file);
4755 }
4756 return;
4757 }
4758
4759- my $dbh = shift; # optional
4760 my $advice = pingback(
4761- url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
4762- dbh => $dbh,
4763+ url => $ENV{PERCONA_VERSION_CHECK_URL} || 'http://v.percona.com',
4764+ instances => $instances_to_check,
4765 );
4766 if ( $advice ) {
4767 print "# Percona suggests these upgrades:\n";
4768@@ -3313,7 +3337,7 @@
4769 }
4770 my ($url) = @args{@required_args};
4771
4772- my ($dbh, $ua, $vc) = @args{qw(dbh ua VersionCheck)};
4773+ my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)};
4774
4775 $ua ||= HTTPMicro->new( timeout => 2 );
4776 $vc ||= VersionCheck->new();
4777@@ -3332,24 +3356,27 @@
4778 );
4779 die "Failed to parse server requested programs: $response->{content}"
4780 if !scalar keys %$items;
4781-
4782+
4783 my $versions = $vc->get_versions(
4784- items => $items,
4785- dbh => $dbh,
4786+ items => $items,
4787+ instances => $instances,
4788 );
4789 die "Failed to get any program versions; should have at least gotten Perl"
4790 if !scalar keys %$versions;
4791
4792 my $client_content = encode_client_response(
4793- items => $items,
4794- versions => $versions,
4795+ items => $items,
4796+ versions => $versions,
4797+ general_id => md5_hex( hostname() ),
4798 );
4799
4800 my $client_response = {
4801 headers => { "X-Percona-Toolkit-Tool" => File::Basename::basename($0) },
4802 content => $client_content,
4803 };
4804- PTDEBUG && _d('Client response:', Dumper($client_response));
4805+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4806+ _d('Client response:', Dumper($client_response));
4807+ }
4808
4809 $response = $ua->request('POST', $url, $client_response);
4810 PTDEBUG && _d('Server suggestions:', Dumper($response));
4811@@ -3374,22 +3401,35 @@
4812 }
4813
4814 sub time_to_check {
4815- my ($file) = @_;
4816+ my ($file, $instances) = @_;
4817 die "I need a file argument" unless $file;
4818
4819+ my $created_file = 0;
4820 if ( !-f $file ) {
4821- PTDEBUG && _d('Creating', $file);
4822+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4823+ _d('Creating time limit file', $file);
4824+ }
4825 _touch($file);
4826- return 1;
4827- }
4828+ $created_file = 1;
4829+ }
4830+ elsif ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4831+ _d('Time limit file already exists:', $file);
4832+ }
4833+
4834+ my $time = int(time()); # current time
4835+
4836+ if ( @$instances ) {
4837+ my $instances_to_check = instances_to_check($file, $instances, $time);
4838+ return scalar @$instances_to_check, $instances_to_check;
4839+ }
4840+
4841+ return 1 if $created_file;
4842
4843 my $mtime = (stat $file)[9];
4844 if ( !defined $mtime ) {
4845 PTDEBUG && _d('Error getting modified time of', $file);
4846 return 0;
4847 }
4848-
4849- my $time = int(time());
4850 PTDEBUG && _d('time=', $time, 'mtime=', $mtime);
4851 if ( ($time - $mtime) > $check_time_limit ) {
4852 _touch($file);
4853@@ -3399,6 +3439,36 @@
4854 return 0;
4855 }
4856
4857+sub instances_to_check {
4858+ my ($file, $instances, $time) = @_;
4859+
4860+ open my $fh, '<', $file or die "Cannot open $file: $OS_ERROR";
4861+ my $file_contents = do { local $/ = undef; <$fh> };
4862+ close $fh;
4863+ chomp($file_contents);
4864+ my %cached_instances = $file_contents =~ /^([^,]+),(.+)$/mg;
4865+
4866+ my @instances_to_check;
4867+ foreach my $instance ( @$instances ) {
4868+ my $mtime = $cached_instances{ $instance->{id} };
4869+ if ( !$mtime || (($time - $mtime) > $check_time_limit) ) {
4870+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4871+ _d('Time to check MySQL instance', $instance->{name});
4872+ }
4873+ push @instances_to_check, $instance;
4874+ $cached_instances{ $instance->{id} } = $time;
4875+ }
4876+ }
4877+
4878+ open $fh, '>', $file or die "Cannot open $file for writing: $OS_ERROR";
4879+ while ( my ($id, $time) = each %cached_instances ) {
4880+ print { $fh } "$id,$time\n";
4881+ }
4882+ close $fh or die "Cannot close $file: $OS_ERROR";
4883+
4884+ return \@instances_to_check;
4885+}
4886+
4887 sub _touch {
4888 my ($file) = @_;
4889 sysopen my $fh, $file, O_WRONLY|O_CREAT|O_NONBLOCK
4890@@ -3407,22 +3477,48 @@
4891 utime(undef, undef, $file);
4892 }
4893
4894+sub _generate_identifier {
4895+ my $instance = shift;
4896+ my $dbh = $instance->{dbh};
4897+ my $dsn = $instance->{dsn};
4898+
4899+ my $sql = q{SELECT CONCAT(@@hostname, @@port)};
4900+ my ($name) = eval { $dbh->selectrow_array($sql) };
4901+ if ( $EVAL_ERROR ) { # assume that it's MySQL 4.x
4902+ $name = ($dsn->{h} || 'localhost') . ($dsn->{P} || 3306);
4903+ }
4904+ my $id = md5_hex($name);
4905+
4906+ if ( $ENV{PTVCDEBUG} || PTDEBUG ) {
4907+ _d('MySQL instance', $name, 'is', $id);
4908+ }
4909+
4910+ return $name, $id;
4911+}
4912+
4913 sub encode_client_response {
4914 my (%args) = @_;
4915- my @required_args = qw(items versions);
4916+ my @required_args = qw(items versions general_id);
4917 foreach my $arg ( @required_args ) {
4918 die "I need a $arg arugment" unless $args{$arg};
4919 }
4920- my ($items, $versions) = @args{@required_args};
4921+ my ($items, $versions, $general_id) = @args{@required_args};
4922
4923 my @lines;
4924 foreach my $item ( sort keys %$items ) {
4925 next unless exists $versions->{$item};
4926- push @lines, join(';', $item, $versions->{$item});
4927+ if ( ref($versions->{$item}) eq 'HASH' ) {
4928+ my $mysql_versions = $versions->{$item};
4929+ for my $id ( keys %$mysql_versions ) {
4930+ push @lines, join(';', $id, $item, $mysql_versions->{$id});
4931+ }
4932+ }
4933+ else {
4934+ push @lines, join(';', $general_id, $item, $versions->{$item});
4935+ }
4936 }
4937
4938 my $client_response = join("\n", @lines) . "\n";
4939- PTDEBUG && _d('Client response:', $client_response);
4940 return $client_response;
4941 }
4942
4943@@ -3543,7 +3639,13 @@
4944 # Do the version-check
4945 # ########################################################################
4946 if ( $o->get('version-check') && (!$o->has('quiet') || !$o->get('quiet')) ) {
4947- Pingback::version_check($slave_dbh);
4948+ my $tmp_master_dsn = $master_dsn
4949+ ? $master_dsn
4950+ : { h => $status->{master_host}, P => $status->{master_port} };
4951+ Pingback::version_check(
4952+ { dbh => $slave_dbh, dsn => $slave_dsn },
4953+ { dbh => $master_dbh, dsn => $tmp_master_dsn },
4954+ );
4955 }
4956
4957 # If the I/O thread isn't running when the program starts,
4958
4959=== modified file 'bin/pt-slave-restart'
4960--- bin/pt-slave-restart 2012-08-24 23:26:04 +0000
4961+++ bin/pt-slave-restart 2012-08-28 21:31:19 +0000
4962@@ -38,6 +38,7 @@
4963 {
4964 package Percona::Toolkit;
4965 our $VERSION = '2.1.3';
4966+
4967 1;
4968 }
4969 # ###########################################################################
4970@@ -3205,20 +3206,19 @@
4971 die "I need a $arg arugment" unless $args{$arg};
4972 }
4973 my ($items) = @args{@required_args};
4974- my $dbh = $args{dbh}; # optional
4975
4976 my %versions;
4977 foreach my $item ( values %$items ) {
4978 next unless $self->valid_item($item);
4979-
4980+
4981 eval {
4982 my $func = 'get_' . $item->{type};
4983 my $version = $self->$func(
4984- item => $item,
4985- dbh => $dbh,
4986+ item => $item,
4987+ instances => $args{instances},
4988 );
4989 if ( $version ) {
4990- chomp $version;
4991+ chomp $version unless ref($version);
4992 $versions{$item->{item}} = $version;
4993 }
4994 };
4995@@ -3343,28 +3343,42 @@
4996 );
4997 }
4998
4999-
5000 sub _get_from_mysql {
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches