Merge lp:~percona-toolkit-dev/percona-toolkit/validate-load-options-bug-996915-2.0 into lp:percona-toolkit/2.0
- validate-load-options-bug-996915-2.0
- Merge into 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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel Nichter | Approve | ||
Review via email: mp+107484@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Daniel Nichter (daniel-nichter) : | # |
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; |