Merge lp:~percona-toolkit-dev/percona-toolkit/validate-load-options-bug-996915-2.0 into lp:percona-toolkit/2.0

Proposed by Daniel Nichter
Status: Merged
Merged at revision: 239
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/validate-load-options-bug-996915-2.0
Merge into: lp:percona-toolkit/2.0
Diff against target: 559 lines (+291/-61)
4 files modified
bin/pt-table-checksum (+111/-31)
lib/MySQLStatusWaiter.pm (+82/-24)
t/lib/MySQLStatusWaiter.t (+86/-5)
t/pt-table-checksum/option_sanity.t (+12/-1)
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/validate-load-options-bug-996915-2.0
Reviewer Review Type Date Requested Status
Daniel Nichter Approve
Review via email: mp+107484@code.launchpad.net
To post a comment you must log in.
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-table-checksum'
2--- bin/pt-table-checksum 2012-05-24 17:52:01 +0000
3+++ bin/pt-table-checksum 2012-05-25 21:51:20 +0000
4@@ -5603,47 +5603,66 @@
5
6 sub new {
7 my ( $class, %args ) = @_;
8- my @required_args = qw(spec get_status sleep oktorun);
9+ my @required_args = qw(max_spec get_status sleep oktorun);
10 foreach my $arg ( @required_args ) {
11 die "I need a $arg argument" unless defined $args{$arg};
12 }
13
14- my $max_val_for = _parse_spec(%args);
15+ PTDEBUG && _d('Parsing spec for max thresholds');
16+ my $max_val_for = _parse_spec($args{max_spec});
17+ if ( $max_val_for ) {
18+ _check_and_set_vals(
19+ vars => $max_val_for,
20+ get_status => $args{get_status},
21+ threshold_factor => 0.2, # +20%
22+ );
23+ }
24+
25+ PTDEBUG && _d('Parsing spec for critical thresholds');
26+ my $critical_val_for = _parse_spec($args{critical_spec} || []);
27+ if ( $critical_val_for ) {
28+ _check_and_set_vals(
29+ vars => $critical_val_for,
30+ get_status => $args{get_status},
31+ threshold_factor => 1.0, # double (x2; +100%)
32+ );
33+ }
34
35 my $self = {
36- get_status => $args{get_status},
37- sleep => $args{sleep},
38- oktorun => $args{oktorun},
39- max_val_for => $max_val_for,
40+ get_status => $args{get_status},
41+ sleep => $args{sleep},
42+ oktorun => $args{oktorun},
43+ max_val_for => $max_val_for,
44+ critical_val_for => $critical_val_for,
45 };
46
47 return bless $self, $class;
48 }
49
50 sub _parse_spec {
51- my ( %args ) = @_;
52- my @required_args = qw(spec get_status);
53- foreach my $arg ( @required_args ) {
54- die "I need a $arg argument" unless defined $args{$arg};
55- }
56- my ($spec, $get_status) = @args{@required_args};
57+ my ($spec) = @_;
58
59- if ( !@$spec ) {
60- PTDEBUG && _d('No spec, disabling status var waits');
61- return;
62- }
63+ return unless $spec && scalar @$spec;
64
65 my %max_val_for;
66 foreach my $var_val ( @$spec ) {
67+ die "Empty or undefined spec\n" unless $var_val;
68+ $var_val =~ s/^\s+//;
69+ $var_val =~ s/\s+$//g;
70+
71 my ($var, $val) = split /[:=]/, $var_val;
72- die "Invalid spec: $var_val" unless $var;
73+ die "$var_val does not contain a variable\n" unless $var;
74+ die "$var is not a variable name\n" unless $var =~ m/^[a-zA-Z_]+$/;
75+
76 if ( !$val ) {
77- my $init_val = $get_status->($var);
78- PTDEBUG && _d('Initial', $var, 'value:', $init_val);
79- $val = int(($init_val * .20) + $init_val);
80- }
81- PTDEBUG && _d('Wait if', $var, '>=', $val);
82- $max_val_for{$var} = $val;
83+ PTDEBUG && _d('Will get intial value for', $var, 'later');
84+ $max_val_for{$var} = undef;
85+ }
86+ else {
87+ die "The value for $var must be a number\n"
88+ unless $val =~ m/^[\d\.]+$/;
89+ $max_val_for{$var} = $val;
90+ }
91 }
92
93 return \%max_val_for;
94@@ -5654,6 +5673,11 @@
95 return $self->{max_val_for};
96 }
97
98+sub critical_values {
99+ my ($self) = @_;
100+ return $self->{critical_val_for};
101+}
102+
103 sub wait {
104 my ( $self, %args ) = @_;
105
106@@ -5664,7 +5688,7 @@
107 my $oktorun = $self->{oktorun};
108 my $get_status = $self->{get_status};
109 my $sleep = $self->{sleep};
110-
111+
112 my %vals_too_high = %{$self->{max_val_for}};
113 my $pr_callback;
114 if ( $pr ) {
115@@ -5688,6 +5712,12 @@
116 foreach my $var ( sort keys %vals_too_high ) {
117 my $val = $get_status->($var);
118 PTDEBUG && _d($var, '=', $val);
119+ if ( $val
120+ && exists $self->{critical_val_for}->{$var}
121+ && $val >= $self->{critical_val_for}->{$var} ) {
122+ die "$var=$val exceeds its critical threshold "
123+ . "$self->{critical_val_for}->{$var}\n";
124+ }
125 if ( !$val || $val >= $self->{max_val_for}->{$var} ) {
126 $vals_too_high{$var} = $val;
127 }
128@@ -5712,6 +5742,34 @@
129 return;
130 }
131
132+sub _check_and_set_vals {
133+ my (%args) = @_;
134+ my @required_args = qw(vars get_status threshold_factor);
135+ foreach my $arg ( @required_args ) {
136+ die "I need a $arg argument" unless defined $args{$arg};
137+ }
138+ my ($vars, $get_status, $threshold_factor) = @args{@required_args};
139+
140+ PTDEBUG && _d('Checking and setting values');
141+ return unless $vars && scalar %$vars;
142+
143+ foreach my $var ( keys %$vars ) {
144+ my $init_val = $get_status->($var);
145+ die "Variable $var does not exist or its value is undefined\n"
146+ unless defined $init_val;
147+ my $val;
148+ if ( defined $vars->{$var} ) {
149+ $val = $vars->{$var};
150+ }
151+ else {
152+ PTDEBUG && _d('Initial', $var, 'value:', $init_val);
153+ $val = int(($init_val * $threshold_factor) + $init_val);
154+ $vars->{$var} = $val;
155+ }
156+ PTDEBUG && _d('Wait if', $var, '>=', $val);
157+ }
158+}
159+
160 sub _d {
161 my ($package, undef, $line) = caller 0;
162 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
163@@ -5874,6 +5932,18 @@
164
165 $o->set('chunk-time', 0) if $o->got('chunk-size');
166
167+ foreach my $opt ( qw(max-load critical-load) ) {
168+ next unless $o->has($opt);
169+ my $spec = $o->get($opt);
170+ eval {
171+ MySQLStatusWaiter::_parse_spec($o->get($opt));
172+ };
173+ if ( $EVAL_ERROR ) {
174+ chomp $EVAL_ERROR;
175+ $o->save_error("Invalid --$opt: $EVAL_ERROR");
176+ }
177+ }
178+
179 if ( !$o->get('help') ) {
180 if ( @ARGV > 1 ) {
181 $o->save_error("More than one host specified; only one allowed");
182@@ -6233,13 +6303,23 @@
183 };
184 }
185
186- $sys_load = new MySQLStatusWaiter(
187- spec => $o->get('max-load'),
188- get_status => $get_status,
189- oktorun => sub { return $oktorun },
190- sleep => $sleep,
191- );
192-
193+ eval {
194+ $sys_load = new MySQLStatusWaiter(
195+ max_spec => $o->get('max-load'),
196+ get_status => $get_status,
197+ oktorun => sub { return $oktorun },
198+ sleep => $sleep,
199+ );
200+ };
201+ if ( $EVAL_ERROR ) {
202+ chomp $EVAL_ERROR;
203+ die "Error checking --max-load: $EVAL_ERROR. "
204+ . "Check that the variables specified for --max-load "
205+ . "are spelled correctly and exist in "
206+ . "SHOW GLOBAL STATUS. Current value for this option is:\n"
207+ . " --max-load " . (join(',', @{$o->get('max-load')})) . "\n";
208+ }
209+
210 if ( $o->get('progress') ) {
211 $replica_lag_pr = new Progress(
212 jobsize => scalar @$slaves,
213
214=== modified file 'lib/MySQLStatusWaiter.pm'
215--- lib/MySQLStatusWaiter.pm 2012-01-19 19:46:56 +0000
216+++ lib/MySQLStatusWaiter.pm 2012-05-25 21:51:20 +0000
217@@ -39,18 +39,37 @@
218 # MySQLStatusWaiter object
219 sub new {
220 my ( $class, %args ) = @_;
221- my @required_args = qw(spec get_status sleep oktorun);
222+ my @required_args = qw(max_spec get_status sleep oktorun);
223 foreach my $arg ( @required_args ) {
224 die "I need a $arg argument" unless defined $args{$arg};
225 }
226
227- my $max_val_for = _parse_spec(%args);
228+ PTDEBUG && _d('Parsing spec for max thresholds');
229+ my $max_val_for = _parse_spec($args{max_spec});
230+ if ( $max_val_for ) {
231+ _check_and_set_vals(
232+ vars => $max_val_for,
233+ get_status => $args{get_status},
234+ threshold_factor => 0.2, # +20%
235+ );
236+ }
237+
238+ PTDEBUG && _d('Parsing spec for critical thresholds');
239+ my $critical_val_for = _parse_spec($args{critical_spec} || []);
240+ if ( $critical_val_for ) {
241+ _check_and_set_vals(
242+ vars => $critical_val_for,
243+ get_status => $args{get_status},
244+ threshold_factor => 1.0, # double (x2; +100%)
245+ );
246+ }
247
248 my $self = {
249- get_status => $args{get_status},
250- sleep => $args{sleep},
251- oktorun => $args{oktorun},
252- max_val_for => $max_val_for,
253+ get_status => $args{get_status},
254+ sleep => $args{sleep},
255+ oktorun => $args{oktorun},
256+ max_val_for => $max_val_for,
257+ critical_val_for => $critical_val_for,
258 };
259
260 return bless $self, $class;
261@@ -66,29 +85,29 @@
262 # Returns:
263 # Hashref with each variable's maximum permitted value.
264 sub _parse_spec {
265- my ( %args ) = @_;
266- my @required_args = qw(spec get_status);
267- foreach my $arg ( @required_args ) {
268- die "I need a $arg argument" unless defined $args{$arg};
269- }
270- my ($spec, $get_status) = @args{@required_args};
271+ my ($spec) = @_;
272
273- if ( !@$spec ) {
274- PTDEBUG && _d('No spec, disabling status var waits');
275- return;
276- }
277+ return unless $spec && scalar @$spec;
278
279 my %max_val_for;
280 foreach my $var_val ( @$spec ) {
281+ die "Empty or undefined spec\n" unless $var_val;
282+ $var_val =~ s/^\s+//;
283+ $var_val =~ s/\s+$//g;
284+
285 my ($var, $val) = split /[:=]/, $var_val;
286- die "Invalid spec: $var_val" unless $var;
287+ die "$var_val does not contain a variable\n" unless $var;
288+ die "$var is not a variable name\n" unless $var =~ m/^[a-zA-Z_]+$/;
289+
290 if ( !$val ) {
291- my $init_val = $get_status->($var);
292- PTDEBUG && _d('Initial', $var, 'value:', $init_val);
293- $val = int(($init_val * .20) + $init_val);
294- }
295- PTDEBUG && _d('Wait if', $var, '>=', $val);
296- $max_val_for{$var} = $val;
297+ PTDEBUG && _d('Will get intial value for', $var, 'later');
298+ $max_val_for{$var} = undef;
299+ }
300+ else {
301+ die "The value for $var must be a number\n"
302+ unless $val =~ m/^[\d\.]+$/;
303+ $max_val_for{$var} = $val;
304+ }
305 }
306
307 return \%max_val_for;
308@@ -101,6 +120,11 @@
309 return $self->{max_val_for};
310 }
311
312+sub critical_values {
313+ my ($self) = @_;
314+ return $self->{critical_val_for};
315+}
316+
317 # Sub: wait
318 # Wait until all variables' values are less than their permitted maximums.
319 #
320@@ -117,7 +141,7 @@
321 my $oktorun = $self->{oktorun};
322 my $get_status = $self->{get_status};
323 my $sleep = $self->{sleep};
324-
325+
326 my %vals_too_high = %{$self->{max_val_for}};
327 my $pr_callback;
328 if ( $pr ) {
329@@ -144,6 +168,12 @@
330 foreach my $var ( sort keys %vals_too_high ) {
331 my $val = $get_status->($var);
332 PTDEBUG && _d($var, '=', $val);
333+ if ( $val
334+ && exists $self->{critical_val_for}->{$var}
335+ && $val >= $self->{critical_val_for}->{$var} ) {
336+ die "$var=$val exceeds its critical threshold "
337+ . "$self->{critical_val_for}->{$var}\n";
338+ }
339 if ( !$val || $val >= $self->{max_val_for}->{$var} ) {
340 $vals_too_high{$var} = $val;
341 }
342@@ -170,6 +200,34 @@
343 return;
344 }
345
346+sub _check_and_set_vals {
347+ my (%args) = @_;
348+ my @required_args = qw(vars get_status threshold_factor);
349+ foreach my $arg ( @required_args ) {
350+ die "I need a $arg argument" unless defined $args{$arg};
351+ }
352+ my ($vars, $get_status, $threshold_factor) = @args{@required_args};
353+
354+ PTDEBUG && _d('Checking and setting values');
355+ return unless $vars && scalar %$vars;
356+
357+ foreach my $var ( keys %$vars ) {
358+ my $init_val = $get_status->($var);
359+ die "Variable $var does not exist or its value is undefined\n"
360+ unless defined $init_val;
361+ my $val;
362+ if ( defined $vars->{$var} ) {
363+ $val = $vars->{$var};
364+ }
365+ else {
366+ PTDEBUG && _d('Initial', $var, 'value:', $init_val);
367+ $val = int(($init_val * $threshold_factor) + $init_val);
368+ $vars->{$var} = $val;
369+ }
370+ PTDEBUG && _d('Wait if', $var, '>=', $val);
371+ }
372+}
373+
374 sub _d {
375 my ($package, undef, $line) = caller 0;
376 @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
377
378=== modified file 't/lib/MySQLStatusWaiter.t'
379--- t/lib/MySQLStatusWaiter.t 2011-10-20 02:07:02 +0000
380+++ t/lib/MySQLStatusWaiter.t 2012-05-25 21:51:20 +0000
381@@ -9,7 +9,7 @@
382 use strict;
383 use warnings FATAL => 'all';
384 use English qw(-no_match_vars);
385-use Test::More tests => 12;
386+use Test::More tests => 17;
387
388 use MySQLStatusWaiter;
389 use PerconaTest;
390@@ -39,11 +39,48 @@
391 return;
392 }
393
394+# #############################################################################
395+# _parse_spec()
396+# #############################################################################
397+
398+throws_ok(
399+ sub { new MySQLStatusWaiter(
400+ max_spec => [qw(100)],
401+ get_status => \&get_status,
402+ sleep => \&sleep,
403+ oktorun => \&oktorun,
404+ ) },
405+ qr/100 is not a variable name/,
406+ "Catch non-variable name"
407+);
408+
409+throws_ok(
410+ sub { new MySQLStatusWaiter(
411+ max_spec => [qw(foo=bar)],
412+ get_status => \&get_status,
413+ sleep => \&sleep,
414+ oktorun => \&oktorun,
415+ ) },
416+ qr/value for foo must be a number/,
417+ "Catch non-number value"
418+);
419+
420+throws_ok(
421+ sub { new MySQLStatusWaiter(
422+ max_spec => [qw(foo)],
423+ get_status => \&get_status,
424+ sleep => \&sleep,
425+ oktorun => \&oktorun,
426+ ) },
427+ qr/foo does not exist/,
428+ "Catch non-existent variable"
429+);
430+
431 # ############################################################################
432 # Use initial vals + 20%.
433 # ############################################################################
434 @vals = (
435- # initial values
436+ # initial check for existence
437 { Threads_connected => 10, },
438 { Threads_running => 5, },
439
440@@ -68,11 +105,13 @@
441 { Threads_running => 5, },
442 );
443
444+$oktorun = 1;
445+
446 my $sw = new MySQLStatusWaiter(
447 oktorun => \&oktorun,
448 get_status => \&get_status,
449 sleep => \&sleep,
450- spec => [qw(Threads_connected Threads_running)],
451+ max_spec => [qw(Threads_connected Threads_running)],
452 );
453
454 is_deeply(
455@@ -127,6 +166,10 @@
456 # Use static vals.
457 # ############################################################################
458 @vals = (
459+ # initial check for existence
460+ { Threads_connected => 1, },
461+ { Threads_running => 1, },
462+
463 # first check, no wait
464 { Threads_connected => 1, },
465 { Threads_running => 1, },
466@@ -136,7 +179,7 @@
467 oktorun => \&oktorun,
468 get_status => \&get_status,
469 sleep => \&sleep,
470- spec => [qw(Threads_connected=5 Threads_running=5)],
471+ max_spec => [qw(Threads_connected=5 Threads_running=5)],
472 );
473
474 is_deeply(
475@@ -178,7 +221,7 @@
476 oktorun => \&oktorun,
477 get_status => \&get_status,
478 sleep => \&sleep,
479- spec => [],
480+ max_spec => [],
481 );
482
483 is(
484@@ -204,6 +247,44 @@
485 "No spec, no sleep"
486 );
487
488+# ############################################################################
489+# Critical thresholds (with static vals).
490+# ############################################################################
491+@vals = (
492+ # initial check for existence
493+ { Threads_running => 1, },
494+ { Threads_running => 9, },
495+
496+ # first check, no wait
497+ { Threads_running => 1, },
498+ { Threads_running => 9, },
499+);
500+
501+$sw = new MySQLStatusWaiter(
502+ oktorun => \&oktorun,
503+ get_status => \&get_status,
504+ sleep => \&sleep,
505+ max_spec => [qw(Threads_running=4)],
506+ critical_spec => [qw(Threads_running=8)],
507+
508+);
509+
510+@checked = ();
511+$slept = 0;
512+$sw->wait();
513+
514+is(
515+ $slept,
516+ 0,
517+ "Vals not critical, did not sleep"
518+);
519+
520+throws_ok(
521+ sub { $sw->wait(); },
522+ qr/Threads_running=9 exceeds its critical threshold 8/,
523+ "Die on critical threshold"
524+);
525+
526 # #############################################################################
527 # Done.
528 # #############################################################################
529
530=== modified file 't/pt-table-checksum/option_sanity.t'
531--- t/pt-table-checksum/option_sanity.t 2011-12-27 17:24:35 +0000
532+++ t/pt-table-checksum/option_sanity.t 2012-05-25 21:51:20 +0000
533@@ -9,7 +9,7 @@
534 use strict;
535 use warnings FATAL => 'all';
536 use English qw(-no_match_vars);
537-use Test::More tests => 18;
538+use Test::More tests => 19;
539
540 use PerconaTest;
541 shift @INC; # our unshift (above)
542@@ -157,6 +157,17 @@
543 );
544
545 # #############################################################################
546+# --max-load
547+# #############################################################################
548+
549+$output = `$trunk/bin/pt-table-checksum h=127.1,P=12345 --max-load 100`;
550+like(
551+ $output,
552+ qr/Invalid --max-load/,
553+ "Validates --max-load"
554+);
555+
556+# #############################################################################
557 # Done.
558 # #############################################################################
559 exit;

Subscribers

People subscribed via source and target branches