Merge lp:~percona-toolkit-dev/percona-toolkit/fix-953141-recursion-method-array into lp:percona-toolkit/2.1
- fix-953141-recursion-method-array
- Merge into 2.1
Status: | Merged |
---|---|
Merged at revision: | 345 |
Proposed branch: | lp:~percona-toolkit-dev/percona-toolkit/fix-953141-recursion-method-array |
Merge into: | lp:percona-toolkit/2.1 |
Diff against target: |
2941 lines (+985/-554) 12 files modified
bin/pt-archiver (+76/-41) bin/pt-heartbeat (+99/-54) bin/pt-kill (+79/-44) bin/pt-online-schema-change (+100/-54) bin/pt-query-digest (+76/-41) bin/pt-slave-find (+97/-56) bin/pt-slave-restart (+97/-56) bin/pt-table-checksum (+102/-57) bin/pt-table-sync (+94/-56) lib/MasterSlave.pm (+80/-44) t/lib/MasterSlave.t (+71/-49) t/pt-table-checksum/option_sanity.t (+14/-2) |
To merge this branch: | bzr merge lp:~percona-toolkit-dev/percona-toolkit/fix-953141-recursion-method-array |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel Nichter | Approve | ||
Review via email: mp+117792@code.launchpad.net |
This proposal supersedes a proposal from 2012-07-31.
Commit message
Description of the change
Daniel Nichter (daniel-nichter) wrote : Posted in a previous version of this proposal | # |
Daniel Nichter (daniel-nichter) wrote : Posted in a previous version of this proposal | # |
61 + die("get_slaves() shoudn't get here; An unexpected "
62 + . "recursion method was specified: ", $methods);
Since $methods is an arrayref, it will print as ARRAY(0x100804ed0). That die can just be die "Unexpected recursion methods: @$methods";
Daniel Nichter (daniel-nichter) wrote : Posted in a previous version of this proposal | # |
Also: $o->save_
Daniel Nichter (daniel-nichter) wrote : Posted in a previous version of this proposal | # |
Also:
"""
=item --recursion-method
type: array; default: hosts,processlist
...
The processlist method is the default, because SHOW SLAVE HOSTS is not
reliable. ...
"""
So I made the wrong call: I told you (Brian) to default to "hosts,
But see original MasterSlave:
my @methods = qw(processlist hosts);
if ( $method ) {
@methods = grep { $_ ne $method } @methods;
unshift @methods, $method;
}
else {
if ( ($dsn->{P} || 3306) != 3306 ) {
PTDEBUG && _d('Port number is non-standard; using only hosts method');
@methods = qw(hosts);
}
}
So proc is default unless non-standard port, then only hosts is used. But we also need to respect the user, so altogether:
- If $o->got(
- Else, default=
-- If standard port (3306), use default
-- Else use only hosts
Daniel Nichter (daniel-nichter) wrote : | # |
Running t/lib/MasterSla
ok 5 - No privs needed for --recursion-
Can't use string ("none") as an ARRAY ref while "strict refs" in use at /Users/
That line is in recurse_
if ( $args->{method} && lc($args-
# https:/
PTDEBUG && _d('Not recursing to slaves');
return;
}
- 334. By Brian Fraser
-
t/lib/MasterSla
ve.t: A call to ->recurse_ to_slaves( ) still used a string instead of an arrayref - 335. By Daniel Nichter
-
Rewrite how MasterSlave handles --recursion-
methods.
Daniel Nichter (daniel-nichter) wrote : | # |
I decided to take this a new direction. The code wasn't clean or logical imho. It's still not, but that's because we probably need to overhaul the whole module. In any case, the major sticking point for me was the ambiguity of:
203 - method => $o->get(
204 + method => $o->got(
205 + ? $o->get(
206 + : [],
That effectively threw away the "default: ..." from the option's spec and created a magical, hard-coded default in _resolve_
Daniel Nichter (daniel-nichter) wrote : | # |
Correction: "1) defeats the original purpose of _not_ having a hard-coded default"
- 336. By Daniel Nichter
-
Create MasterSlave with required args.
- 337. By Daniel Nichter
-
Pass a placeholder for Quoter since it's not needed.
Daniel Nichter (daniel-nichter) : | # |
Preview Diff
1 | === modified file 'bin/pt-archiver' |
2 | --- bin/pt-archiver 2012-07-31 21:37:02 +0000 |
3 | +++ bin/pt-archiver 2012-08-02 17:46:19 +0000 |
4 | @@ -3078,8 +3078,31 @@ |
5 | use English qw(-no_match_vars); |
6 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
7 | |
8 | +sub check_recursion_method { |
9 | + my ($methods) = @_; |
10 | + |
11 | + if ( @$methods != 1 ) { |
12 | + if ( grep({ !m/processlist|hosts/i } @$methods) |
13 | + && $methods->[0] !~ /^dsn=/i ) |
14 | + { |
15 | + die "Invalid combination of recursion methods: " |
16 | + . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
17 | + . "Only hosts and processlist may be combined.\n" |
18 | + } |
19 | + } |
20 | + else { |
21 | + my ($method) = @$methods; |
22 | + die "Invalid recursion method: " . ( $method || 'undef' ) |
23 | + unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
24 | + } |
25 | +} |
26 | + |
27 | sub new { |
28 | my ( $class, %args ) = @_; |
29 | + my @required_args = qw(OptionParser DSNParser Quoter); |
30 | + foreach my $arg ( @required_args ) { |
31 | + die "I need a $arg argument" unless $args{$arg}; |
32 | + } |
33 | my $self = { |
34 | %args, |
35 | replication_thread => {}, |
36 | @@ -3089,28 +3112,27 @@ |
37 | |
38 | sub get_slaves { |
39 | my ($self, %args) = @_; |
40 | - my @required_args = qw(make_cxn OptionParser DSNParser Quoter); |
41 | + my @required_args = qw(make_cxn); |
42 | foreach my $arg ( @required_args ) { |
43 | die "I need a $arg argument" unless $args{$arg}; |
44 | } |
45 | - my ($make_cxn, $o, $dp) = @args{@required_args}; |
46 | - |
47 | - my $slaves = []; |
48 | - my $method = $o->get('recursion-method'); |
49 | - PTDEBUG && _d('Slave recursion method:', $method); |
50 | - if ( !$method || $method =~ m/processlist|hosts/i ) { |
51 | + my ($make_cxn) = @args{@required_args}; |
52 | + |
53 | + my $slaves = []; |
54 | + my $dp = $self->{DSNParser}; |
55 | + my $methods = $self->_resolve_recursion_methods($args{dsn}); |
56 | + |
57 | + if ( grep { m/processlist|hosts/i } @$methods ) { |
58 | my @required_args = qw(dbh dsn); |
59 | foreach my $arg ( @required_args ) { |
60 | die "I need a $arg argument" unless $args{$arg}; |
61 | } |
62 | my ($dbh, $dsn) = @args{@required_args}; |
63 | + |
64 | $self->recurse_to_slaves( |
65 | - { dbh => $dbh, |
66 | - dsn => $dsn, |
67 | - dsn_parser => $dp, |
68 | - recurse => $o->get('recurse'), |
69 | - method => $o->get('recursion-method'), |
70 | - callback => sub { |
71 | + { dbh => $dbh, |
72 | + dsn => $dsn, |
73 | + callback => sub { |
74 | my ( $dsn, $dbh, $level, $parent ) = @_; |
75 | return unless $level; |
76 | PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
77 | @@ -3120,31 +3142,48 @@ |
78 | } |
79 | ); |
80 | } |
81 | - elsif ( $method =~ m/^dsn=/i ) { |
82 | - my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i; |
83 | + elsif ( $methods->[0] =~ m/^dsn=/i ) { |
84 | + (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
85 | $slaves = $self->get_cxn_from_dsn_table( |
86 | %args, |
87 | dsn_table_dsn => $dsn_table_dsn, |
88 | ); |
89 | } |
90 | - elsif ( $method =~ m/none/i ) { |
91 | + elsif ( $methods->[0] =~ m/none/i ) { |
92 | PTDEBUG && _d('Not getting to slaves'); |
93 | } |
94 | else { |
95 | - die "Invalid --recursion-method: $method. Valid values are: " |
96 | - . "dsn=DSN, hosts, or processlist.\n"; |
97 | + die "Unexpected recursion methods: @$methods"; |
98 | } |
99 | - |
100 | + |
101 | return $slaves; |
102 | } |
103 | |
104 | +sub _resolve_recursion_methods { |
105 | + my ($self, $dsn) = @_; |
106 | + my $o = $self->{OptionParser}; |
107 | + if ( $o->got('recursion-method') ) { |
108 | + return $o->get('recursion-method'); |
109 | + } |
110 | + elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
111 | + PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
112 | + return [qw(hosts)]; |
113 | + } |
114 | + else { |
115 | + return $o->get('recursion-method'); |
116 | + } |
117 | +} |
118 | + |
119 | sub recurse_to_slaves { |
120 | my ( $self, $args, $level ) = @_; |
121 | $level ||= 0; |
122 | - my $dp = $args->{dsn_parser}; |
123 | - my $dsn = $args->{dsn}; |
124 | + my $dp = $self->{DSNParser}; |
125 | + my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
126 | + my $dsn = $args->{dsn}; |
127 | |
128 | - if ( lc($args->{method} || '') eq 'none' ) { |
129 | + my $methods = $self->_resolve_recursion_methods($dsn); |
130 | + PTDEBUG && _d('Recursion methods:', @$methods); |
131 | + if ( lc($methods->[0]) eq 'none' ) { |
132 | PTDEBUG && _d('Not recursing to slaves'); |
133 | return; |
134 | } |
135 | @@ -3179,11 +3218,11 @@ |
136 | |
137 | $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
138 | |
139 | - if ( !defined $args->{recurse} || $level < $args->{recurse} ) { |
140 | + if ( !defined $recurse || $level < $recurse ) { |
141 | |
142 | my @slaves = |
143 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
144 | - $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method}); |
145 | + $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
146 | |
147 | foreach my $slave ( @slaves ) { |
148 | PTDEBUG && _d('Recursing from', |
149 | @@ -3195,25 +3234,14 @@ |
150 | } |
151 | |
152 | sub find_slave_hosts { |
153 | - my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_; |
154 | + my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
155 | |
156 | - my @methods = qw(processlist hosts); |
157 | - if ( $method ) { |
158 | - @methods = grep { $_ ne $method } @methods; |
159 | - unshift @methods, $method; |
160 | - } |
161 | - else { |
162 | - if ( ($dsn->{P} || 3306) != 3306 ) { |
163 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
164 | - @methods = qw(hosts); |
165 | - } |
166 | - } |
167 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
168 | - 'using methods', @methods); |
169 | + 'using methods', @$methods); |
170 | |
171 | my @slaves; |
172 | METHOD: |
173 | - foreach my $method ( @methods ) { |
174 | + foreach my $method ( @$methods ) { |
175 | my $find_slaves = "_find_slaves_by_$method"; |
176 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
177 | @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
178 | @@ -3716,13 +3744,16 @@ |
179 | |
180 | sub get_cxn_from_dsn_table { |
181 | my ($self, %args) = @_; |
182 | - my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter); |
183 | + my @required_args = qw(dsn_table_dsn make_cxn); |
184 | foreach my $arg ( @required_args ) { |
185 | die "I need a $arg argument" unless $args{$arg}; |
186 | } |
187 | - my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args}; |
188 | + my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
189 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
190 | |
191 | + my $dp = $self->{DSNParser}; |
192 | + my $q = $self->{Quoter}; |
193 | + |
194 | my $dsn = $dp->parse($dsn_table_dsn); |
195 | my $dsn_table; |
196 | if ( $dsn->{D} && $dsn->{t} ) { |
197 | @@ -4054,7 +4085,11 @@ |
198 | my $dsn_defaults = $dp->parse_options($o); |
199 | my $dsn = $dp->parse($o->get('check-slave-lag'), $dsn_defaults); |
200 | $lag_dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 }); |
201 | - $ms = new MasterSlave(); |
202 | + $ms = new MasterSlave( |
203 | + OptionParser => $o, |
204 | + DSNParser => $dp, |
205 | + Quoter => $q, |
206 | + ); |
207 | } |
208 | |
209 | # ######################################################################## |
210 | |
211 | === modified file 'bin/pt-heartbeat' |
212 | --- bin/pt-heartbeat 2012-07-30 14:30:05 +0000 |
213 | +++ bin/pt-heartbeat 2012-08-02 17:46:19 +0000 |
214 | @@ -24,8 +24,31 @@ |
215 | use English qw(-no_match_vars); |
216 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
217 | |
218 | +sub check_recursion_method { |
219 | + my ($methods) = @_; |
220 | + |
221 | + if ( @$methods != 1 ) { |
222 | + if ( grep({ !m/processlist|hosts/i } @$methods) |
223 | + && $methods->[0] !~ /^dsn=/i ) |
224 | + { |
225 | + die "Invalid combination of recursion methods: " |
226 | + . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
227 | + . "Only hosts and processlist may be combined.\n" |
228 | + } |
229 | + } |
230 | + else { |
231 | + my ($method) = @$methods; |
232 | + die "Invalid recursion method: " . ( $method || 'undef' ) |
233 | + unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
234 | + } |
235 | +} |
236 | + |
237 | sub new { |
238 | my ( $class, %args ) = @_; |
239 | + my @required_args = qw(OptionParser DSNParser Quoter); |
240 | + foreach my $arg ( @required_args ) { |
241 | + die "I need a $arg argument" unless $args{$arg}; |
242 | + } |
243 | my $self = { |
244 | %args, |
245 | replication_thread => {}, |
246 | @@ -35,28 +58,27 @@ |
247 | |
248 | sub get_slaves { |
249 | my ($self, %args) = @_; |
250 | - my @required_args = qw(make_cxn OptionParser DSNParser Quoter); |
251 | + my @required_args = qw(make_cxn); |
252 | foreach my $arg ( @required_args ) { |
253 | die "I need a $arg argument" unless $args{$arg}; |
254 | } |
255 | - my ($make_cxn, $o, $dp) = @args{@required_args}; |
256 | - |
257 | - my $slaves = []; |
258 | - my $method = $o->get('recursion-method'); |
259 | - PTDEBUG && _d('Slave recursion method:', $method); |
260 | - if ( !$method || $method =~ m/processlist|hosts/i ) { |
261 | + my ($make_cxn) = @args{@required_args}; |
262 | + |
263 | + my $slaves = []; |
264 | + my $dp = $self->{DSNParser}; |
265 | + my $methods = $self->_resolve_recursion_methods($args{dsn}); |
266 | + |
267 | + if ( grep { m/processlist|hosts/i } @$methods ) { |
268 | my @required_args = qw(dbh dsn); |
269 | foreach my $arg ( @required_args ) { |
270 | die "I need a $arg argument" unless $args{$arg}; |
271 | } |
272 | my ($dbh, $dsn) = @args{@required_args}; |
273 | + |
274 | $self->recurse_to_slaves( |
275 | - { dbh => $dbh, |
276 | - dsn => $dsn, |
277 | - dsn_parser => $dp, |
278 | - recurse => $o->get('recurse'), |
279 | - method => $o->get('recursion-method'), |
280 | - callback => sub { |
281 | + { dbh => $dbh, |
282 | + dsn => $dsn, |
283 | + callback => sub { |
284 | my ( $dsn, $dbh, $level, $parent ) = @_; |
285 | return unless $level; |
286 | PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
287 | @@ -66,31 +88,48 @@ |
288 | } |
289 | ); |
290 | } |
291 | - elsif ( $method =~ m/^dsn=/i ) { |
292 | - my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i; |
293 | + elsif ( $methods->[0] =~ m/^dsn=/i ) { |
294 | + (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
295 | $slaves = $self->get_cxn_from_dsn_table( |
296 | %args, |
297 | dsn_table_dsn => $dsn_table_dsn, |
298 | ); |
299 | } |
300 | - elsif ( $method =~ m/none/i ) { |
301 | + elsif ( $methods->[0] =~ m/none/i ) { |
302 | PTDEBUG && _d('Not getting to slaves'); |
303 | } |
304 | else { |
305 | - die "Invalid --recursion-method: $method. Valid values are: " |
306 | - . "dsn=DSN, hosts, or processlist.\n"; |
307 | + die "Unexpected recursion methods: @$methods"; |
308 | } |
309 | - |
310 | + |
311 | return $slaves; |
312 | } |
313 | |
314 | +sub _resolve_recursion_methods { |
315 | + my ($self, $dsn) = @_; |
316 | + my $o = $self->{OptionParser}; |
317 | + if ( $o->got('recursion-method') ) { |
318 | + return $o->get('recursion-method'); |
319 | + } |
320 | + elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
321 | + PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
322 | + return [qw(hosts)]; |
323 | + } |
324 | + else { |
325 | + return $o->get('recursion-method'); |
326 | + } |
327 | +} |
328 | + |
329 | sub recurse_to_slaves { |
330 | my ( $self, $args, $level ) = @_; |
331 | $level ||= 0; |
332 | - my $dp = $args->{dsn_parser}; |
333 | - my $dsn = $args->{dsn}; |
334 | + my $dp = $self->{DSNParser}; |
335 | + my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
336 | + my $dsn = $args->{dsn}; |
337 | |
338 | - if ( lc($args->{method} || '') eq 'none' ) { |
339 | + my $methods = $self->_resolve_recursion_methods($dsn); |
340 | + PTDEBUG && _d('Recursion methods:', @$methods); |
341 | + if ( lc($methods->[0]) eq 'none' ) { |
342 | PTDEBUG && _d('Not recursing to slaves'); |
343 | return; |
344 | } |
345 | @@ -125,11 +164,11 @@ |
346 | |
347 | $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
348 | |
349 | - if ( !defined $args->{recurse} || $level < $args->{recurse} ) { |
350 | + if ( !defined $recurse || $level < $recurse ) { |
351 | |
352 | my @slaves = |
353 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
354 | - $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method}); |
355 | + $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
356 | |
357 | foreach my $slave ( @slaves ) { |
358 | PTDEBUG && _d('Recursing from', |
359 | @@ -141,25 +180,14 @@ |
360 | } |
361 | |
362 | sub find_slave_hosts { |
363 | - my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_; |
364 | + my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
365 | |
366 | - my @methods = qw(processlist hosts); |
367 | - if ( $method ) { |
368 | - @methods = grep { $_ ne $method } @methods; |
369 | - unshift @methods, $method; |
370 | - } |
371 | - else { |
372 | - if ( ($dsn->{P} || 3306) != 3306 ) { |
373 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
374 | - @methods = qw(hosts); |
375 | - } |
376 | - } |
377 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
378 | - 'using methods', @methods); |
379 | + 'using methods', @$methods); |
380 | |
381 | my @slaves; |
382 | METHOD: |
383 | - foreach my $method ( @methods ) { |
384 | + foreach my $method ( @$methods ) { |
385 | my $find_slaves = "_find_slaves_by_$method"; |
386 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
387 | @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
388 | @@ -662,13 +690,16 @@ |
389 | |
390 | sub get_cxn_from_dsn_table { |
391 | my ($self, %args) = @_; |
392 | - my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter); |
393 | + my @required_args = qw(dsn_table_dsn make_cxn); |
394 | foreach my $arg ( @required_args ) { |
395 | die "I need a $arg argument" unless $args{$arg}; |
396 | } |
397 | - my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args}; |
398 | + my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
399 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
400 | |
401 | + my $dp = $self->{DSNParser}; |
402 | + my $q = $self->{Quoter}; |
403 | + |
404 | my $dsn = $dp->parse($dsn_table_dsn); |
405 | my $dsn_table; |
406 | if ( $dsn->{D} && $dsn->{t} ) { |
407 | @@ -1994,7 +2025,7 @@ |
408 | PTDEBUG && _d($dbh, $sql); |
409 | my ($sql_mode) = eval { $dbh->selectrow_array($sql) }; |
410 | if ( $EVAL_ERROR ) { |
411 | - die $EVAL_ERROR; |
412 | + die "Error getting the current SQL_MODE: $EVAL_ERROR"; |
413 | } |
414 | |
415 | $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1' |
416 | @@ -2004,15 +2035,17 @@ |
417 | PTDEBUG && _d($dbh, $sql); |
418 | eval { $dbh->do($sql) }; |
419 | if ( $EVAL_ERROR ) { |
420 | - die $EVAL_ERROR; |
421 | + die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE" |
422 | + . ($sql_mode ? " and $sql_mode" : '') |
423 | + . ": $EVAL_ERROR"; |
424 | } |
425 | |
426 | - if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) { |
427 | - $sql = "/*!40101 SET NAMES $charset*/"; |
428 | + if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) { |
429 | + $sql = qq{/*!40101 SET NAMES "$charset"*/}; |
430 | PTDEBUG && _d($dbh, ':', $sql); |
431 | eval { $dbh->do($sql) }; |
432 | if ( $EVAL_ERROR ) { |
433 | - die $EVAL_ERROR; |
434 | + die "Error setting NAMES to $charset: $EVAL_ERROR"; |
435 | } |
436 | PTDEBUG && _d('Enabling charset for STDOUT'); |
437 | if ( $charset eq 'utf8' ) { |
438 | @@ -2024,12 +2057,12 @@ |
439 | } |
440 | } |
441 | |
442 | - if ( $self->prop('set-vars') ) { |
443 | - $sql = "SET " . $self->prop('set-vars'); |
444 | + if ( my $var = $self->prop('set-vars') ) { |
445 | + $sql = "SET $var"; |
446 | PTDEBUG && _d($dbh, ':', $sql); |
447 | eval { $dbh->do($sql) }; |
448 | if ( $EVAL_ERROR ) { |
449 | - die $EVAL_ERROR; |
450 | + die "Error setting $var: $EVAL_ERROR"; |
451 | } |
452 | } |
453 | } |
454 | @@ -3282,6 +3315,13 @@ |
455 | } |
456 | } |
457 | |
458 | + eval { |
459 | + MasterSlave::check_recursion_method($o->get('recursion-method')); |
460 | + }; |
461 | + if ( $EVAL_ERROR ) { |
462 | + $o->save_error("Invalid --recursion-method: $EVAL_ERROR") |
463 | + } |
464 | + |
465 | $o->usage_or_errors(); |
466 | |
467 | # ######################################################################## |
468 | @@ -3407,7 +3447,11 @@ |
469 | my $master_server_id = $o->get('master-server-id'); |
470 | if ( !$master_server_id ) { |
471 | eval { |
472 | - my $ms = new MasterSlave(); |
473 | + my $ms = new MasterSlave( |
474 | + OptionParser => $o, |
475 | + DSNParser => $dp, |
476 | + Quoter => $q, |
477 | + ); |
478 | my $master_dsn = $ms->get_master_dsn($dbh, $dsn, $dp) |
479 | or die "This server is not a slave"; |
480 | my $master_dbh = $dp->get_dbh($dp->get_cxn_params($master_dsn), |
481 | @@ -3734,19 +3778,20 @@ |
482 | # Collect a list of connections to the slaves. |
483 | if ( $o->get('recurse') ) { |
484 | PTDEBUG && _d('Recursing to slaves'); |
485 | - my $ms = new MasterSlave(); |
486 | + my $ms = new MasterSlave( |
487 | + OptionParser => $o, |
488 | + DSNParser => $dp, |
489 | + Quoter => "Quoter", |
490 | + ); |
491 | $ms->recurse_to_slaves( |
492 | { dbh => $dbh, |
493 | dsn => $dsn, |
494 | - dsn_parser => $dp, |
495 | - recurse => $o->get('recurse'), |
496 | callback => sub { |
497 | my ( $dsn, $dbh, $level ) = @_; |
498 | push @dbhs, $dbh; |
499 | PTDEBUG && _d("Found slave", $dp->as_string($dsn)); |
500 | push @sths, [ $dsn, $dbh->prepare($sql) ]; |
501 | }, |
502 | - method => $o->get('recursion-method'), |
503 | }, |
504 | ); |
505 | } |
506 | @@ -4235,7 +4280,7 @@ |
507 | |
508 | =item --recursion-method |
509 | |
510 | -type: string |
511 | +type: array; default: processlist,hosts |
512 | |
513 | Preferred recursion method used to find slaves. |
514 | |
515 | |
516 | === modified file 'bin/pt-kill' |
517 | --- bin/pt-kill 2012-07-30 19:48:15 +0000 |
518 | +++ bin/pt-kill 2012-08-02 17:46:19 +0000 |
519 | @@ -3345,8 +3345,31 @@ |
520 | use English qw(-no_match_vars); |
521 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
522 | |
523 | +sub check_recursion_method { |
524 | + my ($methods) = @_; |
525 | + |
526 | + if ( @$methods != 1 ) { |
527 | + if ( grep({ !m/processlist|hosts/i } @$methods) |
528 | + && $methods->[0] !~ /^dsn=/i ) |
529 | + { |
530 | + die "Invalid combination of recursion methods: " |
531 | + . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
532 | + . "Only hosts and processlist may be combined.\n" |
533 | + } |
534 | + } |
535 | + else { |
536 | + my ($method) = @$methods; |
537 | + die "Invalid recursion method: " . ( $method || 'undef' ) |
538 | + unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
539 | + } |
540 | +} |
541 | + |
542 | sub new { |
543 | my ( $class, %args ) = @_; |
544 | + my @required_args = qw(OptionParser DSNParser Quoter); |
545 | + foreach my $arg ( @required_args ) { |
546 | + die "I need a $arg argument" unless $args{$arg}; |
547 | + } |
548 | my $self = { |
549 | %args, |
550 | replication_thread => {}, |
551 | @@ -3356,28 +3379,27 @@ |
552 | |
553 | sub get_slaves { |
554 | my ($self, %args) = @_; |
555 | - my @required_args = qw(make_cxn OptionParser DSNParser Quoter); |
556 | + my @required_args = qw(make_cxn); |
557 | foreach my $arg ( @required_args ) { |
558 | die "I need a $arg argument" unless $args{$arg}; |
559 | } |
560 | - my ($make_cxn, $o, $dp) = @args{@required_args}; |
561 | - |
562 | - my $slaves = []; |
563 | - my $method = $o->get('recursion-method'); |
564 | - PTDEBUG && _d('Slave recursion method:', $method); |
565 | - if ( !$method || $method =~ m/processlist|hosts/i ) { |
566 | + my ($make_cxn) = @args{@required_args}; |
567 | + |
568 | + my $slaves = []; |
569 | + my $dp = $self->{DSNParser}; |
570 | + my $methods = $self->_resolve_recursion_methods($args{dsn}); |
571 | + |
572 | + if ( grep { m/processlist|hosts/i } @$methods ) { |
573 | my @required_args = qw(dbh dsn); |
574 | foreach my $arg ( @required_args ) { |
575 | die "I need a $arg argument" unless $args{$arg}; |
576 | } |
577 | my ($dbh, $dsn) = @args{@required_args}; |
578 | + |
579 | $self->recurse_to_slaves( |
580 | - { dbh => $dbh, |
581 | - dsn => $dsn, |
582 | - dsn_parser => $dp, |
583 | - recurse => $o->get('recurse'), |
584 | - method => $o->get('recursion-method'), |
585 | - callback => sub { |
586 | + { dbh => $dbh, |
587 | + dsn => $dsn, |
588 | + callback => sub { |
589 | my ( $dsn, $dbh, $level, $parent ) = @_; |
590 | return unless $level; |
591 | PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
592 | @@ -3387,31 +3409,48 @@ |
593 | } |
594 | ); |
595 | } |
596 | - elsif ( $method =~ m/^dsn=/i ) { |
597 | - my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i; |
598 | + elsif ( $methods->[0] =~ m/^dsn=/i ) { |
599 | + (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
600 | $slaves = $self->get_cxn_from_dsn_table( |
601 | %args, |
602 | dsn_table_dsn => $dsn_table_dsn, |
603 | ); |
604 | } |
605 | - elsif ( $method =~ m/none/i ) { |
606 | + elsif ( $methods->[0] =~ m/none/i ) { |
607 | PTDEBUG && _d('Not getting to slaves'); |
608 | } |
609 | else { |
610 | - die "Invalid --recursion-method: $method. Valid values are: " |
611 | - . "dsn=DSN, hosts, or processlist.\n"; |
612 | + die "Unexpected recursion methods: @$methods"; |
613 | } |
614 | - |
615 | + |
616 | return $slaves; |
617 | } |
618 | |
619 | +sub _resolve_recursion_methods { |
620 | + my ($self, $dsn) = @_; |
621 | + my $o = $self->{OptionParser}; |
622 | + if ( $o->got('recursion-method') ) { |
623 | + return $o->get('recursion-method'); |
624 | + } |
625 | + elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
626 | + PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
627 | + return [qw(hosts)]; |
628 | + } |
629 | + else { |
630 | + return $o->get('recursion-method'); |
631 | + } |
632 | +} |
633 | + |
634 | sub recurse_to_slaves { |
635 | my ( $self, $args, $level ) = @_; |
636 | $level ||= 0; |
637 | - my $dp = $args->{dsn_parser}; |
638 | - my $dsn = $args->{dsn}; |
639 | + my $dp = $self->{DSNParser}; |
640 | + my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
641 | + my $dsn = $args->{dsn}; |
642 | |
643 | - if ( lc($args->{method} || '') eq 'none' ) { |
644 | + my $methods = $self->_resolve_recursion_methods($dsn); |
645 | + PTDEBUG && _d('Recursion methods:', @$methods); |
646 | + if ( lc($methods->[0]) eq 'none' ) { |
647 | PTDEBUG && _d('Not recursing to slaves'); |
648 | return; |
649 | } |
650 | @@ -3446,11 +3485,11 @@ |
651 | |
652 | $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
653 | |
654 | - if ( !defined $args->{recurse} || $level < $args->{recurse} ) { |
655 | + if ( !defined $recurse || $level < $recurse ) { |
656 | |
657 | my @slaves = |
658 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
659 | - $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method}); |
660 | + $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
661 | |
662 | foreach my $slave ( @slaves ) { |
663 | PTDEBUG && _d('Recursing from', |
664 | @@ -3462,25 +3501,14 @@ |
665 | } |
666 | |
667 | sub find_slave_hosts { |
668 | - my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_; |
669 | + my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
670 | |
671 | - my @methods = qw(processlist hosts); |
672 | - if ( $method ) { |
673 | - @methods = grep { $_ ne $method } @methods; |
674 | - unshift @methods, $method; |
675 | - } |
676 | - else { |
677 | - if ( ($dsn->{P} || 3306) != 3306 ) { |
678 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
679 | - @methods = qw(hosts); |
680 | - } |
681 | - } |
682 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
683 | - 'using methods', @methods); |
684 | + 'using methods', @$methods); |
685 | |
686 | my @slaves; |
687 | METHOD: |
688 | - foreach my $method ( @methods ) { |
689 | + foreach my $method ( @$methods ) { |
690 | my $find_slaves = "_find_slaves_by_$method"; |
691 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
692 | @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
693 | @@ -3983,13 +4011,16 @@ |
694 | |
695 | sub get_cxn_from_dsn_table { |
696 | my ($self, %args) = @_; |
697 | - my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter); |
698 | + my @required_args = qw(dsn_table_dsn make_cxn); |
699 | foreach my $arg ( @required_args ) { |
700 | die "I need a $arg argument" unless $args{$arg}; |
701 | } |
702 | - my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args}; |
703 | + my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
704 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
705 | |
706 | + my $dp = $self->{DSNParser}; |
707 | + my $q = $self->{Quoter}; |
708 | + |
709 | my $dsn = $dp->parse($dsn_table_dsn); |
710 | my $dsn_table; |
711 | if ( $dsn->{D} && $dsn->{t} ) { |
712 | @@ -4802,10 +4833,6 @@ |
713 | sub main { |
714 | @ARGV = @_; # set global ARGV for this package |
715 | |
716 | - my $ms = new MasterSlave(); |
717 | - my $pl = new Processlist(MasterSlave => $ms); |
718 | - my $qr = new QueryRewriter(); |
719 | - |
720 | # ######################################################################## |
721 | # Get configuration information. |
722 | # ######################################################################## |
723 | @@ -4880,6 +4907,14 @@ |
724 | # ######################################################################## |
725 | # Make input sub that will either get processlist from MySQL or a file. |
726 | # ######################################################################## |
727 | + my $ms = new MasterSlave( |
728 | + OptionParser => $o, |
729 | + DSNParser => $dp, |
730 | + Quoter => "Quoter", |
731 | + ); |
732 | + my $pl = new Processlist(MasterSlave => $ms); |
733 | + my $qr = new QueryRewriter(); |
734 | + |
735 | my $cxn; |
736 | my $dbh; # $cxn->dbh |
737 | my $get_proclist; # callback to SHOW PROCESSLIST |
738 | |
739 | === modified file 'bin/pt-online-schema-change' |
740 | --- bin/pt-online-schema-change 2012-07-31 16:33:30 +0000 |
741 | +++ bin/pt-online-schema-change 2012-08-02 17:46:19 +0000 |
742 | @@ -1935,7 +1935,7 @@ |
743 | PTDEBUG && _d($dbh, $sql); |
744 | my ($sql_mode) = eval { $dbh->selectrow_array($sql) }; |
745 | if ( $EVAL_ERROR ) { |
746 | - die $EVAL_ERROR; |
747 | + die "Error getting the current SQL_MODE: $EVAL_ERROR"; |
748 | } |
749 | |
750 | $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1' |
751 | @@ -1945,15 +1945,17 @@ |
752 | PTDEBUG && _d($dbh, $sql); |
753 | eval { $dbh->do($sql) }; |
754 | if ( $EVAL_ERROR ) { |
755 | - die $EVAL_ERROR; |
756 | + die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE" |
757 | + . ($sql_mode ? " and $sql_mode" : '') |
758 | + . ": $EVAL_ERROR"; |
759 | } |
760 | |
761 | - if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) { |
762 | - $sql = "/*!40101 SET NAMES $charset*/"; |
763 | + if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) { |
764 | + $sql = qq{/*!40101 SET NAMES "$charset"*/}; |
765 | PTDEBUG && _d($dbh, ':', $sql); |
766 | eval { $dbh->do($sql) }; |
767 | if ( $EVAL_ERROR ) { |
768 | - die $EVAL_ERROR; |
769 | + die "Error setting NAMES to $charset: $EVAL_ERROR"; |
770 | } |
771 | PTDEBUG && _d('Enabling charset for STDOUT'); |
772 | if ( $charset eq 'utf8' ) { |
773 | @@ -1965,12 +1967,12 @@ |
774 | } |
775 | } |
776 | |
777 | - if ( $self->prop('set-vars') ) { |
778 | - $sql = "SET " . $self->prop('set-vars'); |
779 | + if ( my $var = $self->prop('set-vars') ) { |
780 | + $sql = "SET $var"; |
781 | PTDEBUG && _d($dbh, ':', $sql); |
782 | eval { $dbh->do($sql) }; |
783 | if ( $EVAL_ERROR ) { |
784 | - die $EVAL_ERROR; |
785 | + die "Error setting $var: $EVAL_ERROR"; |
786 | } |
787 | } |
788 | } |
789 | @@ -3333,6 +3335,7 @@ |
790 | dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
791 | hostname => '', |
792 | set => $args{set}, |
793 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
794 | dbh_set => 0, |
795 | OptionParser => $o, |
796 | DSNParser => $dp, |
797 | @@ -3370,7 +3373,10 @@ |
798 | |
799 | PTDEBUG && _d($dbh, 'Setting dbh'); |
800 | |
801 | - $dbh->{FetchHashKeyName} = 'NAME_lc'; |
802 | + if ( !exists $self->{NAME_lc} |
803 | + || (defined $self->{NAME_lc} && $self->{NAME_lc}) ) { |
804 | + $dbh->{FetchHashKeyName} = 'NAME_lc'; |
805 | + } |
806 | |
807 | my $sql = 'SELECT @@hostname, @@server_id'; |
808 | PTDEBUG && _d($dbh, $sql); |
809 | @@ -3446,8 +3452,31 @@ |
810 | use English qw(-no_match_vars); |
811 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
812 | |
813 | +sub check_recursion_method { |
814 | + my ($methods) = @_; |
815 | + |
816 | + if ( @$methods != 1 ) { |
817 | + if ( grep({ !m/processlist|hosts/i } @$methods) |
818 | + && $methods->[0] !~ /^dsn=/i ) |
819 | + { |
820 | + die "Invalid combination of recursion methods: " |
821 | + . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
822 | + . "Only hosts and processlist may be combined.\n" |
823 | + } |
824 | + } |
825 | + else { |
826 | + my ($method) = @$methods; |
827 | + die "Invalid recursion method: " . ( $method || 'undef' ) |
828 | + unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
829 | + } |
830 | +} |
831 | + |
832 | sub new { |
833 | my ( $class, %args ) = @_; |
834 | + my @required_args = qw(OptionParser DSNParser Quoter); |
835 | + foreach my $arg ( @required_args ) { |
836 | + die "I need a $arg argument" unless $args{$arg}; |
837 | + } |
838 | my $self = { |
839 | %args, |
840 | replication_thread => {}, |
841 | @@ -3457,28 +3486,27 @@ |
842 | |
843 | sub get_slaves { |
844 | my ($self, %args) = @_; |
845 | - my @required_args = qw(make_cxn OptionParser DSNParser Quoter); |
846 | + my @required_args = qw(make_cxn); |
847 | foreach my $arg ( @required_args ) { |
848 | die "I need a $arg argument" unless $args{$arg}; |
849 | } |
850 | - my ($make_cxn, $o, $dp) = @args{@required_args}; |
851 | - |
852 | - my $slaves = []; |
853 | - my $method = $o->get('recursion-method'); |
854 | - PTDEBUG && _d('Slave recursion method:', $method); |
855 | - if ( !$method || $method =~ m/processlist|hosts/i ) { |
856 | + my ($make_cxn) = @args{@required_args}; |
857 | + |
858 | + my $slaves = []; |
859 | + my $dp = $self->{DSNParser}; |
860 | + my $methods = $self->_resolve_recursion_methods($args{dsn}); |
861 | + |
862 | + if ( grep { m/processlist|hosts/i } @$methods ) { |
863 | my @required_args = qw(dbh dsn); |
864 | foreach my $arg ( @required_args ) { |
865 | die "I need a $arg argument" unless $args{$arg}; |
866 | } |
867 | my ($dbh, $dsn) = @args{@required_args}; |
868 | + |
869 | $self->recurse_to_slaves( |
870 | - { dbh => $dbh, |
871 | - dsn => $dsn, |
872 | - dsn_parser => $dp, |
873 | - recurse => $o->get('recurse'), |
874 | - method => $o->get('recursion-method'), |
875 | - callback => sub { |
876 | + { dbh => $dbh, |
877 | + dsn => $dsn, |
878 | + callback => sub { |
879 | my ( $dsn, $dbh, $level, $parent ) = @_; |
880 | return unless $level; |
881 | PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
882 | @@ -3488,31 +3516,48 @@ |
883 | } |
884 | ); |
885 | } |
886 | - elsif ( $method =~ m/^dsn=/i ) { |
887 | - my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i; |
888 | + elsif ( $methods->[0] =~ m/^dsn=/i ) { |
889 | + (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
890 | $slaves = $self->get_cxn_from_dsn_table( |
891 | %args, |
892 | dsn_table_dsn => $dsn_table_dsn, |
893 | ); |
894 | } |
895 | - elsif ( $method =~ m/none/i ) { |
896 | + elsif ( $methods->[0] =~ m/none/i ) { |
897 | PTDEBUG && _d('Not getting to slaves'); |
898 | } |
899 | else { |
900 | - die "Invalid --recursion-method: $method. Valid values are: " |
901 | - . "dsn=DSN, hosts, or processlist.\n"; |
902 | + die "Unexpected recursion methods: @$methods"; |
903 | } |
904 | - |
905 | + |
906 | return $slaves; |
907 | } |
908 | |
909 | +sub _resolve_recursion_methods { |
910 | + my ($self, $dsn) = @_; |
911 | + my $o = $self->{OptionParser}; |
912 | + if ( $o->got('recursion-method') ) { |
913 | + return $o->get('recursion-method'); |
914 | + } |
915 | + elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
916 | + PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
917 | + return [qw(hosts)]; |
918 | + } |
919 | + else { |
920 | + return $o->get('recursion-method'); |
921 | + } |
922 | +} |
923 | + |
924 | sub recurse_to_slaves { |
925 | my ( $self, $args, $level ) = @_; |
926 | $level ||= 0; |
927 | - my $dp = $args->{dsn_parser}; |
928 | - my $dsn = $args->{dsn}; |
929 | + my $dp = $self->{DSNParser}; |
930 | + my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
931 | + my $dsn = $args->{dsn}; |
932 | |
933 | - if ( lc($args->{method} || '') eq 'none' ) { |
934 | + my $methods = $self->_resolve_recursion_methods($dsn); |
935 | + PTDEBUG && _d('Recursion methods:', @$methods); |
936 | + if ( lc($methods->[0]) eq 'none' ) { |
937 | PTDEBUG && _d('Not recursing to slaves'); |
938 | return; |
939 | } |
940 | @@ -3547,11 +3592,11 @@ |
941 | |
942 | $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
943 | |
944 | - if ( !defined $args->{recurse} || $level < $args->{recurse} ) { |
945 | + if ( !defined $recurse || $level < $recurse ) { |
946 | |
947 | my @slaves = |
948 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
949 | - $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method}); |
950 | + $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
951 | |
952 | foreach my $slave ( @slaves ) { |
953 | PTDEBUG && _d('Recursing from', |
954 | @@ -3563,25 +3608,14 @@ |
955 | } |
956 | |
957 | sub find_slave_hosts { |
958 | - my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_; |
959 | + my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
960 | |
961 | - my @methods = qw(processlist hosts); |
962 | - if ( $method ) { |
963 | - @methods = grep { $_ ne $method } @methods; |
964 | - unshift @methods, $method; |
965 | - } |
966 | - else { |
967 | - if ( ($dsn->{P} || 3306) != 3306 ) { |
968 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
969 | - @methods = qw(hosts); |
970 | - } |
971 | - } |
972 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
973 | - 'using methods', @methods); |
974 | + 'using methods', @$methods); |
975 | |
976 | my @slaves; |
977 | METHOD: |
978 | - foreach my $method ( @methods ) { |
979 | + foreach my $method ( @$methods ) { |
980 | my $find_slaves = "_find_slaves_by_$method"; |
981 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
982 | @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
983 | @@ -4084,13 +4118,16 @@ |
984 | |
985 | sub get_cxn_from_dsn_table { |
986 | my ($self, %args) = @_; |
987 | - my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter); |
988 | + my @required_args = qw(dsn_table_dsn make_cxn); |
989 | foreach my $arg ( @required_args ) { |
990 | die "I need a $arg argument" unless $args{$arg}; |
991 | } |
992 | - my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args}; |
993 | + my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
994 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
995 | |
996 | + my $dp = $self->{DSNParser}; |
997 | + my $q = $self->{Quoter}; |
998 | + |
999 | my $dsn = $dp->parse($dsn_table_dsn); |
1000 | my $dsn_table; |
1001 | if ( $dsn->{D} && $dsn->{t} ) { |
1002 | @@ -5909,6 +5946,13 @@ |
1003 | } |
1004 | } |
1005 | |
1006 | + eval { |
1007 | + MasterSlave::check_recursion_method($o->get('recursion-method')); |
1008 | + }; |
1009 | + if ( $EVAL_ERROR ) { |
1010 | + $o->save_error("Invalid --recursion-method: $EVAL_ERROR") |
1011 | + } |
1012 | + |
1013 | $o->usage_or_errors(); |
1014 | |
1015 | if ( $o->get('quiet') ) { |
1016 | @@ -6006,13 +6050,15 @@ |
1017 | # ##################################################################### |
1018 | # Find and connect to slaves. |
1019 | # ##################################################################### |
1020 | - my $ms = new MasterSlave(); |
1021 | + my $ms = new MasterSlave( |
1022 | + OptionParser => $o, |
1023 | + DSNParser => $dp, |
1024 | + Quoter => $q, |
1025 | + ); |
1026 | + |
1027 | $slaves = $ms->get_slaves( |
1028 | dbh => $cxn->dbh(), |
1029 | dsn => $cxn->dsn(), |
1030 | - OptionParser => $o, |
1031 | - DSNParser => $dp, |
1032 | - Quoter => $q, |
1033 | make_cxn => sub { |
1034 | return $make_cxn->(@_, prev_dsn => $cxn->dsn()); |
1035 | }, |
1036 | @@ -8368,7 +8414,7 @@ |
1037 | |
1038 | =item --recursion-method |
1039 | |
1040 | -type: string |
1041 | +type: array; default: processlist,hosts |
1042 | |
1043 | Preferred recursion method for discovering replicas. Possible methods are: |
1044 | |
1045 | |
1046 | === modified file 'bin/pt-query-digest' |
1047 | --- bin/pt-query-digest 2012-07-30 19:48:15 +0000 |
1048 | +++ bin/pt-query-digest 2012-08-02 17:46:19 +0000 |
1049 | @@ -10339,8 +10339,31 @@ |
1050 | use English qw(-no_match_vars); |
1051 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
1052 | |
1053 | +sub check_recursion_method { |
1054 | + my ($methods) = @_; |
1055 | + |
1056 | + if ( @$methods != 1 ) { |
1057 | + if ( grep({ !m/processlist|hosts/i } @$methods) |
1058 | + && $methods->[0] !~ /^dsn=/i ) |
1059 | + { |
1060 | + die "Invalid combination of recursion methods: " |
1061 | + . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
1062 | + . "Only hosts and processlist may be combined.\n" |
1063 | + } |
1064 | + } |
1065 | + else { |
1066 | + my ($method) = @$methods; |
1067 | + die "Invalid recursion method: " . ( $method || 'undef' ) |
1068 | + unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
1069 | + } |
1070 | +} |
1071 | + |
1072 | sub new { |
1073 | my ( $class, %args ) = @_; |
1074 | + my @required_args = qw(OptionParser DSNParser Quoter); |
1075 | + foreach my $arg ( @required_args ) { |
1076 | + die "I need a $arg argument" unless $args{$arg}; |
1077 | + } |
1078 | my $self = { |
1079 | %args, |
1080 | replication_thread => {}, |
1081 | @@ -10350,28 +10373,27 @@ |
1082 | |
1083 | sub get_slaves { |
1084 | my ($self, %args) = @_; |
1085 | - my @required_args = qw(make_cxn OptionParser DSNParser Quoter); |
1086 | + my @required_args = qw(make_cxn); |
1087 | foreach my $arg ( @required_args ) { |
1088 | die "I need a $arg argument" unless $args{$arg}; |
1089 | } |
1090 | - my ($make_cxn, $o, $dp) = @args{@required_args}; |
1091 | - |
1092 | - my $slaves = []; |
1093 | - my $method = $o->get('recursion-method'); |
1094 | - PTDEBUG && _d('Slave recursion method:', $method); |
1095 | - if ( !$method || $method =~ m/processlist|hosts/i ) { |
1096 | + my ($make_cxn) = @args{@required_args}; |
1097 | + |
1098 | + my $slaves = []; |
1099 | + my $dp = $self->{DSNParser}; |
1100 | + my $methods = $self->_resolve_recursion_methods($args{dsn}); |
1101 | + |
1102 | + if ( grep { m/processlist|hosts/i } @$methods ) { |
1103 | my @required_args = qw(dbh dsn); |
1104 | foreach my $arg ( @required_args ) { |
1105 | die "I need a $arg argument" unless $args{$arg}; |
1106 | } |
1107 | my ($dbh, $dsn) = @args{@required_args}; |
1108 | + |
1109 | $self->recurse_to_slaves( |
1110 | - { dbh => $dbh, |
1111 | - dsn => $dsn, |
1112 | - dsn_parser => $dp, |
1113 | - recurse => $o->get('recurse'), |
1114 | - method => $o->get('recursion-method'), |
1115 | - callback => sub { |
1116 | + { dbh => $dbh, |
1117 | + dsn => $dsn, |
1118 | + callback => sub { |
1119 | my ( $dsn, $dbh, $level, $parent ) = @_; |
1120 | return unless $level; |
1121 | PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
1122 | @@ -10381,31 +10403,48 @@ |
1123 | } |
1124 | ); |
1125 | } |
1126 | - elsif ( $method =~ m/^dsn=/i ) { |
1127 | - my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i; |
1128 | + elsif ( $methods->[0] =~ m/^dsn=/i ) { |
1129 | + (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
1130 | $slaves = $self->get_cxn_from_dsn_table( |
1131 | %args, |
1132 | dsn_table_dsn => $dsn_table_dsn, |
1133 | ); |
1134 | } |
1135 | - elsif ( $method =~ m/none/i ) { |
1136 | + elsif ( $methods->[0] =~ m/none/i ) { |
1137 | PTDEBUG && _d('Not getting to slaves'); |
1138 | } |
1139 | else { |
1140 | - die "Invalid --recursion-method: $method. Valid values are: " |
1141 | - . "dsn=DSN, hosts, or processlist.\n"; |
1142 | + die "Unexpected recursion methods: @$methods"; |
1143 | } |
1144 | - |
1145 | + |
1146 | return $slaves; |
1147 | } |
1148 | |
1149 | +sub _resolve_recursion_methods { |
1150 | + my ($self, $dsn) = @_; |
1151 | + my $o = $self->{OptionParser}; |
1152 | + if ( $o->got('recursion-method') ) { |
1153 | + return $o->get('recursion-method'); |
1154 | + } |
1155 | + elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
1156 | + PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
1157 | + return [qw(hosts)]; |
1158 | + } |
1159 | + else { |
1160 | + return $o->get('recursion-method'); |
1161 | + } |
1162 | +} |
1163 | + |
1164 | sub recurse_to_slaves { |
1165 | my ( $self, $args, $level ) = @_; |
1166 | $level ||= 0; |
1167 | - my $dp = $args->{dsn_parser}; |
1168 | - my $dsn = $args->{dsn}; |
1169 | + my $dp = $self->{DSNParser}; |
1170 | + my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
1171 | + my $dsn = $args->{dsn}; |
1172 | |
1173 | - if ( lc($args->{method} || '') eq 'none' ) { |
1174 | + my $methods = $self->_resolve_recursion_methods($dsn); |
1175 | + PTDEBUG && _d('Recursion methods:', @$methods); |
1176 | + if ( lc($methods->[0]) eq 'none' ) { |
1177 | PTDEBUG && _d('Not recursing to slaves'); |
1178 | return; |
1179 | } |
1180 | @@ -10440,11 +10479,11 @@ |
1181 | |
1182 | $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
1183 | |
1184 | - if ( !defined $args->{recurse} || $level < $args->{recurse} ) { |
1185 | + if ( !defined $recurse || $level < $recurse ) { |
1186 | |
1187 | my @slaves = |
1188 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
1189 | - $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method}); |
1190 | + $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
1191 | |
1192 | foreach my $slave ( @slaves ) { |
1193 | PTDEBUG && _d('Recursing from', |
1194 | @@ -10456,25 +10495,14 @@ |
1195 | } |
1196 | |
1197 | sub find_slave_hosts { |
1198 | - my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_; |
1199 | + my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
1200 | |
1201 | - my @methods = qw(processlist hosts); |
1202 | - if ( $method ) { |
1203 | - @methods = grep { $_ ne $method } @methods; |
1204 | - unshift @methods, $method; |
1205 | - } |
1206 | - else { |
1207 | - if ( ($dsn->{P} || 3306) != 3306 ) { |
1208 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
1209 | - @methods = qw(hosts); |
1210 | - } |
1211 | - } |
1212 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
1213 | - 'using methods', @methods); |
1214 | + 'using methods', @$methods); |
1215 | |
1216 | my @slaves; |
1217 | METHOD: |
1218 | - foreach my $method ( @methods ) { |
1219 | + foreach my $method ( @$methods ) { |
1220 | my $find_slaves = "_find_slaves_by_$method"; |
1221 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
1222 | @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
1223 | @@ -10977,13 +11005,16 @@ |
1224 | |
1225 | sub get_cxn_from_dsn_table { |
1226 | my ($self, %args) = @_; |
1227 | - my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter); |
1228 | + my @required_args = qw(dsn_table_dsn make_cxn); |
1229 | foreach my $arg ( @required_args ) { |
1230 | die "I need a $arg argument" unless $args{$arg}; |
1231 | } |
1232 | - my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args}; |
1233 | + my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
1234 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
1235 | |
1236 | + my $dp = $self->{DSNParser}; |
1237 | + my $q = $self->{Quoter}; |
1238 | + |
1239 | my $dsn = $dp->parse($dsn_table_dsn); |
1240 | my $dsn_table; |
1241 | if ( $dsn->{D} && $dsn->{t} ) { |
1242 | @@ -12195,7 +12226,11 @@ |
1243 | { # event |
1244 | my $misc; |
1245 | if ( my $ps_dsn = $o->get('processlist') ) { |
1246 | - my $ms = new MasterSlave(); |
1247 | + my $ms = new MasterSlave( |
1248 | + OptionParser => $o, |
1249 | + DSNParser => $dp, |
1250 | + Quoter => $q, |
1251 | + ); |
1252 | my $pl = new Processlist( |
1253 | interval => $o->get('interval') * 1_000_000, |
1254 | MasterSlave => $ms |
1255 | |
1256 | === modified file 'bin/pt-slave-find' |
1257 | --- bin/pt-slave-find 2012-07-20 20:33:13 +0000 |
1258 | +++ bin/pt-slave-find 2012-08-02 17:46:19 +0000 |
1259 | @@ -1741,7 +1741,7 @@ |
1260 | PTDEBUG && _d($dbh, $sql); |
1261 | my ($sql_mode) = eval { $dbh->selectrow_array($sql) }; |
1262 | if ( $EVAL_ERROR ) { |
1263 | - die $EVAL_ERROR; |
1264 | + die "Error getting the current SQL_MODE: $EVAL_ERROR"; |
1265 | } |
1266 | |
1267 | $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1' |
1268 | @@ -1751,15 +1751,17 @@ |
1269 | PTDEBUG && _d($dbh, $sql); |
1270 | eval { $dbh->do($sql) }; |
1271 | if ( $EVAL_ERROR ) { |
1272 | - die $EVAL_ERROR; |
1273 | + die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE" |
1274 | + . ($sql_mode ? " and $sql_mode" : '') |
1275 | + . ": $EVAL_ERROR"; |
1276 | } |
1277 | |
1278 | - if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) { |
1279 | - $sql = "/*!40101 SET NAMES $charset*/"; |
1280 | + if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) { |
1281 | + $sql = qq{/*!40101 SET NAMES "$charset"*/}; |
1282 | PTDEBUG && _d($dbh, ':', $sql); |
1283 | eval { $dbh->do($sql) }; |
1284 | if ( $EVAL_ERROR ) { |
1285 | - die $EVAL_ERROR; |
1286 | + die "Error setting NAMES to $charset: $EVAL_ERROR"; |
1287 | } |
1288 | PTDEBUG && _d('Enabling charset for STDOUT'); |
1289 | if ( $charset eq 'utf8' ) { |
1290 | @@ -1771,12 +1773,12 @@ |
1291 | } |
1292 | } |
1293 | |
1294 | - if ( $self->prop('set-vars') ) { |
1295 | - $sql = "SET " . $self->prop('set-vars'); |
1296 | + if ( my $var = $self->prop('set-vars') ) { |
1297 | + $sql = "SET $var"; |
1298 | PTDEBUG && _d($dbh, ':', $sql); |
1299 | eval { $dbh->do($sql) }; |
1300 | if ( $EVAL_ERROR ) { |
1301 | - die $EVAL_ERROR; |
1302 | + die "Error setting $var: $EVAL_ERROR"; |
1303 | } |
1304 | } |
1305 | } |
1306 | @@ -1870,8 +1872,31 @@ |
1307 | use English qw(-no_match_vars); |
1308 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
1309 | |
1310 | +sub check_recursion_method { |
1311 | + my ($methods) = @_; |
1312 | + |
1313 | + if ( @$methods != 1 ) { |
1314 | + if ( grep({ !m/processlist|hosts/i } @$methods) |
1315 | + && $methods->[0] !~ /^dsn=/i ) |
1316 | + { |
1317 | + die "Invalid combination of recursion methods: " |
1318 | + . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
1319 | + . "Only hosts and processlist may be combined.\n" |
1320 | + } |
1321 | + } |
1322 | + else { |
1323 | + my ($method) = @$methods; |
1324 | + die "Invalid recursion method: " . ( $method || 'undef' ) |
1325 | + unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
1326 | + } |
1327 | +} |
1328 | + |
1329 | sub new { |
1330 | my ( $class, %args ) = @_; |
1331 | + my @required_args = qw(OptionParser DSNParser Quoter); |
1332 | + foreach my $arg ( @required_args ) { |
1333 | + die "I need a $arg argument" unless $args{$arg}; |
1334 | + } |
1335 | my $self = { |
1336 | %args, |
1337 | replication_thread => {}, |
1338 | @@ -1881,28 +1906,27 @@ |
1339 | |
1340 | sub get_slaves { |
1341 | my ($self, %args) = @_; |
1342 | - my @required_args = qw(make_cxn OptionParser DSNParser Quoter); |
1343 | + my @required_args = qw(make_cxn); |
1344 | foreach my $arg ( @required_args ) { |
1345 | die "I need a $arg argument" unless $args{$arg}; |
1346 | } |
1347 | - my ($make_cxn, $o, $dp) = @args{@required_args}; |
1348 | - |
1349 | - my $slaves = []; |
1350 | - my $method = $o->get('recursion-method'); |
1351 | - PTDEBUG && _d('Slave recursion method:', $method); |
1352 | - if ( !$method || $method =~ m/processlist|hosts/i ) { |
1353 | + my ($make_cxn) = @args{@required_args}; |
1354 | + |
1355 | + my $slaves = []; |
1356 | + my $dp = $self->{DSNParser}; |
1357 | + my $methods = $self->_resolve_recursion_methods($args{dsn}); |
1358 | + |
1359 | + if ( grep { m/processlist|hosts/i } @$methods ) { |
1360 | my @required_args = qw(dbh dsn); |
1361 | foreach my $arg ( @required_args ) { |
1362 | die "I need a $arg argument" unless $args{$arg}; |
1363 | } |
1364 | my ($dbh, $dsn) = @args{@required_args}; |
1365 | + |
1366 | $self->recurse_to_slaves( |
1367 | - { dbh => $dbh, |
1368 | - dsn => $dsn, |
1369 | - dsn_parser => $dp, |
1370 | - recurse => $o->get('recurse'), |
1371 | - method => $o->get('recursion-method'), |
1372 | - callback => sub { |
1373 | + { dbh => $dbh, |
1374 | + dsn => $dsn, |
1375 | + callback => sub { |
1376 | my ( $dsn, $dbh, $level, $parent ) = @_; |
1377 | return unless $level; |
1378 | PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
1379 | @@ -1912,31 +1936,48 @@ |
1380 | } |
1381 | ); |
1382 | } |
1383 | - elsif ( $method =~ m/^dsn=/i ) { |
1384 | - my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i; |
1385 | + elsif ( $methods->[0] =~ m/^dsn=/i ) { |
1386 | + (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
1387 | $slaves = $self->get_cxn_from_dsn_table( |
1388 | %args, |
1389 | dsn_table_dsn => $dsn_table_dsn, |
1390 | ); |
1391 | } |
1392 | - elsif ( $method =~ m/none/i ) { |
1393 | + elsif ( $methods->[0] =~ m/none/i ) { |
1394 | PTDEBUG && _d('Not getting to slaves'); |
1395 | } |
1396 | else { |
1397 | - die "Invalid --recursion-method: $method. Valid values are: " |
1398 | - . "dsn=DSN, hosts, or processlist.\n"; |
1399 | + die "Unexpected recursion methods: @$methods"; |
1400 | } |
1401 | - |
1402 | + |
1403 | return $slaves; |
1404 | } |
1405 | |
1406 | +sub _resolve_recursion_methods { |
1407 | + my ($self, $dsn) = @_; |
1408 | + my $o = $self->{OptionParser}; |
1409 | + if ( $o->got('recursion-method') ) { |
1410 | + return $o->get('recursion-method'); |
1411 | + } |
1412 | + elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
1413 | + PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
1414 | + return [qw(hosts)]; |
1415 | + } |
1416 | + else { |
1417 | + return $o->get('recursion-method'); |
1418 | + } |
1419 | +} |
1420 | + |
1421 | sub recurse_to_slaves { |
1422 | my ( $self, $args, $level ) = @_; |
1423 | $level ||= 0; |
1424 | - my $dp = $args->{dsn_parser}; |
1425 | - my $dsn = $args->{dsn}; |
1426 | + my $dp = $self->{DSNParser}; |
1427 | + my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
1428 | + my $dsn = $args->{dsn}; |
1429 | |
1430 | - if ( lc($args->{method} || '') eq 'none' ) { |
1431 | + my $methods = $self->_resolve_recursion_methods($dsn); |
1432 | + PTDEBUG && _d('Recursion methods:', @$methods); |
1433 | + if ( lc($methods->[0]) eq 'none' ) { |
1434 | PTDEBUG && _d('Not recursing to slaves'); |
1435 | return; |
1436 | } |
1437 | @@ -1971,11 +2012,11 @@ |
1438 | |
1439 | $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
1440 | |
1441 | - if ( !defined $args->{recurse} || $level < $args->{recurse} ) { |
1442 | + if ( !defined $recurse || $level < $recurse ) { |
1443 | |
1444 | my @slaves = |
1445 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
1446 | - $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method}); |
1447 | + $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
1448 | |
1449 | foreach my $slave ( @slaves ) { |
1450 | PTDEBUG && _d('Recursing from', |
1451 | @@ -1987,25 +2028,14 @@ |
1452 | } |
1453 | |
1454 | sub find_slave_hosts { |
1455 | - my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_; |
1456 | + my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
1457 | |
1458 | - my @methods = qw(processlist hosts); |
1459 | - if ( $method ) { |
1460 | - @methods = grep { $_ ne $method } @methods; |
1461 | - unshift @methods, $method; |
1462 | - } |
1463 | - else { |
1464 | - if ( ($dsn->{P} || 3306) != 3306 ) { |
1465 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
1466 | - @methods = qw(hosts); |
1467 | - } |
1468 | - } |
1469 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
1470 | - 'using methods', @methods); |
1471 | + 'using methods', @$methods); |
1472 | |
1473 | my @slaves; |
1474 | METHOD: |
1475 | - foreach my $method ( @methods ) { |
1476 | + foreach my $method ( @$methods ) { |
1477 | my $find_slaves = "_find_slaves_by_$method"; |
1478 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
1479 | @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
1480 | @@ -2508,13 +2538,16 @@ |
1481 | |
1482 | sub get_cxn_from_dsn_table { |
1483 | my ($self, %args) = @_; |
1484 | - my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter); |
1485 | + my @required_args = qw(dsn_table_dsn make_cxn); |
1486 | foreach my $arg ( @required_args ) { |
1487 | die "I need a $arg argument" unless $args{$arg}; |
1488 | } |
1489 | - my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args}; |
1490 | + my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
1491 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
1492 | |
1493 | + my $dp = $self->{DSNParser}; |
1494 | + my $q = $self->{Quoter}; |
1495 | + |
1496 | my $dsn = $dp->parse($dsn_table_dsn); |
1497 | my $dsn_table; |
1498 | if ( $dsn->{D} && $dsn->{t} ) { |
1499 | @@ -3330,6 +3363,13 @@ |
1500 | $o->save_error("Missing or invalid master host"); |
1501 | } |
1502 | |
1503 | + eval { |
1504 | + MasterSlave::check_recursion_method($o->get('recursion-method')); |
1505 | + }; |
1506 | + if ( $EVAL_ERROR ) { |
1507 | + $o->save_error("Invalid --recursion-method: $EVAL_ERROR") |
1508 | + } |
1509 | + |
1510 | $o->usage_or_errors(); |
1511 | |
1512 | # ######################################################################## |
1513 | @@ -3355,14 +3395,15 @@ |
1514 | |
1515 | # Despite the name, recursing to slaves actually begins at the specified |
1516 | # server, so the named server may also be included. |
1517 | - my $ms = new MasterSlave(); |
1518 | + my $ms = new MasterSlave( |
1519 | + OptionParser => $o, |
1520 | + DSNParser => $dp, |
1521 | + Quoter => "Quoter", |
1522 | + ); |
1523 | $ms->recurse_to_slaves( |
1524 | - { dbh => $dbh, |
1525 | - dsn => $master_dsn, |
1526 | - dsn_parser => $dp, |
1527 | - recurse => $o->get('recurse'), |
1528 | - method => $o->get('recursion-method'), |
1529 | - callback => sub { |
1530 | + { dbh => $dbh, |
1531 | + dsn => $master_dsn, |
1532 | + callback => sub { |
1533 | my ( $dsn, $dbh, $level, $parent ) = @_; |
1534 | if ( !$parent ) { |
1535 | $root = $dsn; |
1536 | @@ -3710,7 +3751,7 @@ |
1537 | |
1538 | =item --recursion-method |
1539 | |
1540 | -type: string |
1541 | +type: array; default: processlist,hosts |
1542 | |
1543 | Preferred recursion method used to find slaves. |
1544 | |
1545 | |
1546 | === modified file 'bin/pt-slave-restart' |
1547 | --- bin/pt-slave-restart 2012-07-20 20:33:13 +0000 |
1548 | +++ bin/pt-slave-restart 2012-08-02 17:46:19 +0000 |
1549 | @@ -2054,7 +2054,7 @@ |
1550 | PTDEBUG && _d($dbh, $sql); |
1551 | my ($sql_mode) = eval { $dbh->selectrow_array($sql) }; |
1552 | if ( $EVAL_ERROR ) { |
1553 | - die $EVAL_ERROR; |
1554 | + die "Error getting the current SQL_MODE: $EVAL_ERROR"; |
1555 | } |
1556 | |
1557 | $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1' |
1558 | @@ -2064,15 +2064,17 @@ |
1559 | PTDEBUG && _d($dbh, $sql); |
1560 | eval { $dbh->do($sql) }; |
1561 | if ( $EVAL_ERROR ) { |
1562 | - die $EVAL_ERROR; |
1563 | + die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE" |
1564 | + . ($sql_mode ? " and $sql_mode" : '') |
1565 | + . ": $EVAL_ERROR"; |
1566 | } |
1567 | |
1568 | - if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) { |
1569 | - $sql = "/*!40101 SET NAMES $charset*/"; |
1570 | + if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) { |
1571 | + $sql = qq{/*!40101 SET NAMES "$charset"*/}; |
1572 | PTDEBUG && _d($dbh, ':', $sql); |
1573 | eval { $dbh->do($sql) }; |
1574 | if ( $EVAL_ERROR ) { |
1575 | - die $EVAL_ERROR; |
1576 | + die "Error setting NAMES to $charset: $EVAL_ERROR"; |
1577 | } |
1578 | PTDEBUG && _d('Enabling charset for STDOUT'); |
1579 | if ( $charset eq 'utf8' ) { |
1580 | @@ -2084,12 +2086,12 @@ |
1581 | } |
1582 | } |
1583 | |
1584 | - if ( $self->prop('set-vars') ) { |
1585 | - $sql = "SET " . $self->prop('set-vars'); |
1586 | + if ( my $var = $self->prop('set-vars') ) { |
1587 | + $sql = "SET $var"; |
1588 | PTDEBUG && _d($dbh, ':', $sql); |
1589 | eval { $dbh->do($sql) }; |
1590 | if ( $EVAL_ERROR ) { |
1591 | - die $EVAL_ERROR; |
1592 | + die "Error setting $var: $EVAL_ERROR"; |
1593 | } |
1594 | } |
1595 | } |
1596 | @@ -2183,8 +2185,31 @@ |
1597 | use English qw(-no_match_vars); |
1598 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
1599 | |
1600 | +sub check_recursion_method { |
1601 | + my ($methods) = @_; |
1602 | + |
1603 | + if ( @$methods != 1 ) { |
1604 | + if ( grep({ !m/processlist|hosts/i } @$methods) |
1605 | + && $methods->[0] !~ /^dsn=/i ) |
1606 | + { |
1607 | + die "Invalid combination of recursion methods: " |
1608 | + . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
1609 | + . "Only hosts and processlist may be combined.\n" |
1610 | + } |
1611 | + } |
1612 | + else { |
1613 | + my ($method) = @$methods; |
1614 | + die "Invalid recursion method: " . ( $method || 'undef' ) |
1615 | + unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
1616 | + } |
1617 | +} |
1618 | + |
1619 | sub new { |
1620 | my ( $class, %args ) = @_; |
1621 | + my @required_args = qw(OptionParser DSNParser Quoter); |
1622 | + foreach my $arg ( @required_args ) { |
1623 | + die "I need a $arg argument" unless $args{$arg}; |
1624 | + } |
1625 | my $self = { |
1626 | %args, |
1627 | replication_thread => {}, |
1628 | @@ -2194,28 +2219,27 @@ |
1629 | |
1630 | sub get_slaves { |
1631 | my ($self, %args) = @_; |
1632 | - my @required_args = qw(make_cxn OptionParser DSNParser Quoter); |
1633 | + my @required_args = qw(make_cxn); |
1634 | foreach my $arg ( @required_args ) { |
1635 | die "I need a $arg argument" unless $args{$arg}; |
1636 | } |
1637 | - my ($make_cxn, $o, $dp) = @args{@required_args}; |
1638 | - |
1639 | - my $slaves = []; |
1640 | - my $method = $o->get('recursion-method'); |
1641 | - PTDEBUG && _d('Slave recursion method:', $method); |
1642 | - if ( !$method || $method =~ m/processlist|hosts/i ) { |
1643 | + my ($make_cxn) = @args{@required_args}; |
1644 | + |
1645 | + my $slaves = []; |
1646 | + my $dp = $self->{DSNParser}; |
1647 | + my $methods = $self->_resolve_recursion_methods($args{dsn}); |
1648 | + |
1649 | + if ( grep { m/processlist|hosts/i } @$methods ) { |
1650 | my @required_args = qw(dbh dsn); |
1651 | foreach my $arg ( @required_args ) { |
1652 | die "I need a $arg argument" unless $args{$arg}; |
1653 | } |
1654 | my ($dbh, $dsn) = @args{@required_args}; |
1655 | + |
1656 | $self->recurse_to_slaves( |
1657 | - { dbh => $dbh, |
1658 | - dsn => $dsn, |
1659 | - dsn_parser => $dp, |
1660 | - recurse => $o->get('recurse'), |
1661 | - method => $o->get('recursion-method'), |
1662 | - callback => sub { |
1663 | + { dbh => $dbh, |
1664 | + dsn => $dsn, |
1665 | + callback => sub { |
1666 | my ( $dsn, $dbh, $level, $parent ) = @_; |
1667 | return unless $level; |
1668 | PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
1669 | @@ -2225,31 +2249,48 @@ |
1670 | } |
1671 | ); |
1672 | } |
1673 | - elsif ( $method =~ m/^dsn=/i ) { |
1674 | - my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i; |
1675 | + elsif ( $methods->[0] =~ m/^dsn=/i ) { |
1676 | + (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
1677 | $slaves = $self->get_cxn_from_dsn_table( |
1678 | %args, |
1679 | dsn_table_dsn => $dsn_table_dsn, |
1680 | ); |
1681 | } |
1682 | - elsif ( $method =~ m/none/i ) { |
1683 | + elsif ( $methods->[0] =~ m/none/i ) { |
1684 | PTDEBUG && _d('Not getting to slaves'); |
1685 | } |
1686 | else { |
1687 | - die "Invalid --recursion-method: $method. Valid values are: " |
1688 | - . "dsn=DSN, hosts, or processlist.\n"; |
1689 | + die "Unexpected recursion methods: @$methods"; |
1690 | } |
1691 | - |
1692 | + |
1693 | return $slaves; |
1694 | } |
1695 | |
1696 | +sub _resolve_recursion_methods { |
1697 | + my ($self, $dsn) = @_; |
1698 | + my $o = $self->{OptionParser}; |
1699 | + if ( $o->got('recursion-method') ) { |
1700 | + return $o->get('recursion-method'); |
1701 | + } |
1702 | + elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
1703 | + PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
1704 | + return [qw(hosts)]; |
1705 | + } |
1706 | + else { |
1707 | + return $o->get('recursion-method'); |
1708 | + } |
1709 | +} |
1710 | + |
1711 | sub recurse_to_slaves { |
1712 | my ( $self, $args, $level ) = @_; |
1713 | $level ||= 0; |
1714 | - my $dp = $args->{dsn_parser}; |
1715 | - my $dsn = $args->{dsn}; |
1716 | + my $dp = $self->{DSNParser}; |
1717 | + my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
1718 | + my $dsn = $args->{dsn}; |
1719 | |
1720 | - if ( lc($args->{method} || '') eq 'none' ) { |
1721 | + my $methods = $self->_resolve_recursion_methods($dsn); |
1722 | + PTDEBUG && _d('Recursion methods:', @$methods); |
1723 | + if ( lc($methods->[0]) eq 'none' ) { |
1724 | PTDEBUG && _d('Not recursing to slaves'); |
1725 | return; |
1726 | } |
1727 | @@ -2284,11 +2325,11 @@ |
1728 | |
1729 | $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
1730 | |
1731 | - if ( !defined $args->{recurse} || $level < $args->{recurse} ) { |
1732 | + if ( !defined $recurse || $level < $recurse ) { |
1733 | |
1734 | my @slaves = |
1735 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
1736 | - $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method}); |
1737 | + $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
1738 | |
1739 | foreach my $slave ( @slaves ) { |
1740 | PTDEBUG && _d('Recursing from', |
1741 | @@ -2300,25 +2341,14 @@ |
1742 | } |
1743 | |
1744 | sub find_slave_hosts { |
1745 | - my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_; |
1746 | + my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
1747 | |
1748 | - my @methods = qw(processlist hosts); |
1749 | - if ( $method ) { |
1750 | - @methods = grep { $_ ne $method } @methods; |
1751 | - unshift @methods, $method; |
1752 | - } |
1753 | - else { |
1754 | - if ( ($dsn->{P} || 3306) != 3306 ) { |
1755 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
1756 | - @methods = qw(hosts); |
1757 | - } |
1758 | - } |
1759 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
1760 | - 'using methods', @methods); |
1761 | + 'using methods', @$methods); |
1762 | |
1763 | my @slaves; |
1764 | METHOD: |
1765 | - foreach my $method ( @methods ) { |
1766 | + foreach my $method ( @$methods ) { |
1767 | my $find_slaves = "_find_slaves_by_$method"; |
1768 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
1769 | @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
1770 | @@ -2821,13 +2851,16 @@ |
1771 | |
1772 | sub get_cxn_from_dsn_table { |
1773 | my ($self, %args) = @_; |
1774 | - my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter); |
1775 | + my @required_args = qw(dsn_table_dsn make_cxn); |
1776 | foreach my $arg ( @required_args ) { |
1777 | die "I need a $arg argument" unless $args{$arg}; |
1778 | } |
1779 | - my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args}; |
1780 | + my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
1781 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
1782 | |
1783 | + my $dp = $self->{DSNParser}; |
1784 | + my $q = $self->{Quoter}; |
1785 | + |
1786 | my $dsn = $dp->parse($dsn_table_dsn); |
1787 | my $dsn_table; |
1788 | if ( $dsn->{D} && $dsn->{t} ) { |
1789 | @@ -3113,6 +3146,13 @@ |
1790 | } |
1791 | } |
1792 | |
1793 | + eval { |
1794 | + MasterSlave::check_recursion_method($o->get('recursion-method')); |
1795 | + }; |
1796 | + if ( $EVAL_ERROR ) { |
1797 | + $o->save_error("Invalid --recursion-method: $EVAL_ERROR") |
1798 | + } |
1799 | + |
1800 | $o->usage_or_errors(); |
1801 | |
1802 | # ######################################################################## |
1803 | @@ -3178,13 +3218,15 @@ |
1804 | |
1805 | # Despite the name, recursing to slaves actually begins at the specified |
1806 | # server, so the named server may also be watched, if it's a slave. |
1807 | - my $ms = new MasterSlave(); |
1808 | + my $ms = new MasterSlave( |
1809 | + OptionParser => $o, |
1810 | + DSNParser => $dp, |
1811 | + Quoter => $q, |
1812 | + ); |
1813 | $ms->recurse_to_slaves( |
1814 | - { dbh => $dbh, |
1815 | - dsn => $dsn, |
1816 | - dsn_parser => $dp, |
1817 | - recurse => $o->get('recurse') || 0, |
1818 | - callback => sub { |
1819 | + { dbh => $dbh, |
1820 | + dsn => $dsn, |
1821 | + callback => sub { |
1822 | my ( $dsn, $dbh, $level ) = @_; |
1823 | # Test whether we want to watch this server. |
1824 | eval { |
1825 | @@ -3206,7 +3248,6 @@ |
1826 | my ( $dsn, $dbh, $level ) = @_; |
1827 | print STDERR "Skipping ", $dp->as_string($dsn), "\n"; |
1828 | }, |
1829 | - method => $o->get('recursion-method'), |
1830 | } |
1831 | ); |
1832 | |
1833 | @@ -3792,7 +3833,7 @@ |
1834 | |
1835 | =item --recursion-method |
1836 | |
1837 | -type: string |
1838 | +type: array; default: processlist,hosts |
1839 | |
1840 | Preferred recursion method used to find slaves. |
1841 | |
1842 | |
1843 | === modified file 'bin/pt-table-checksum' |
1844 | --- bin/pt-table-checksum 2012-08-01 20:58:52 +0000 |
1845 | +++ bin/pt-table-checksum 2012-08-02 17:46:19 +0000 |
1846 | @@ -266,7 +266,7 @@ |
1847 | PTDEBUG && _d($dbh, $sql); |
1848 | my ($sql_mode) = eval { $dbh->selectrow_array($sql) }; |
1849 | if ( $EVAL_ERROR ) { |
1850 | - die $EVAL_ERROR; |
1851 | + die "Error getting the current SQL_MODE: $EVAL_ERROR"; |
1852 | } |
1853 | |
1854 | $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1' |
1855 | @@ -276,15 +276,17 @@ |
1856 | PTDEBUG && _d($dbh, $sql); |
1857 | eval { $dbh->do($sql) }; |
1858 | if ( $EVAL_ERROR ) { |
1859 | - die $EVAL_ERROR; |
1860 | + die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE" |
1861 | + . ($sql_mode ? " and $sql_mode" : '') |
1862 | + . ": $EVAL_ERROR"; |
1863 | } |
1864 | |
1865 | - if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) { |
1866 | - $sql = "/*!40101 SET NAMES $charset*/"; |
1867 | + if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) { |
1868 | + $sql = qq{/*!40101 SET NAMES "$charset"*/}; |
1869 | PTDEBUG && _d($dbh, ':', $sql); |
1870 | eval { $dbh->do($sql) }; |
1871 | if ( $EVAL_ERROR ) { |
1872 | - die $EVAL_ERROR; |
1873 | + die "Error setting NAMES to $charset: $EVAL_ERROR"; |
1874 | } |
1875 | PTDEBUG && _d('Enabling charset for STDOUT'); |
1876 | if ( $charset eq 'utf8' ) { |
1877 | @@ -296,12 +298,12 @@ |
1878 | } |
1879 | } |
1880 | |
1881 | - if ( $self->prop('set-vars') ) { |
1882 | - $sql = "SET " . $self->prop('set-vars'); |
1883 | + if ( my $var = $self->prop('set-vars') ) { |
1884 | + $sql = "SET $var"; |
1885 | PTDEBUG && _d($dbh, ':', $sql); |
1886 | eval { $dbh->do($sql) }; |
1887 | if ( $EVAL_ERROR ) { |
1888 | - die $EVAL_ERROR; |
1889 | + die "Error setting $var: $EVAL_ERROR"; |
1890 | } |
1891 | } |
1892 | } |
1893 | @@ -1901,6 +1903,7 @@ |
1894 | dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
1895 | hostname => '', |
1896 | set => $args{set}, |
1897 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
1898 | dbh_set => 0, |
1899 | OptionParser => $o, |
1900 | DSNParser => $dp, |
1901 | @@ -1938,7 +1941,10 @@ |
1902 | |
1903 | PTDEBUG && _d($dbh, 'Setting dbh'); |
1904 | |
1905 | - $dbh->{FetchHashKeyName} = 'NAME_lc'; |
1906 | + if ( !exists $self->{NAME_lc} |
1907 | + || (defined $self->{NAME_lc} && $self->{NAME_lc}) ) { |
1908 | + $dbh->{FetchHashKeyName} = 'NAME_lc'; |
1909 | + } |
1910 | |
1911 | my $sql = 'SELECT @@hostname, @@server_id'; |
1912 | PTDEBUG && _d($dbh, $sql); |
1913 | @@ -3030,8 +3036,31 @@ |
1914 | use English qw(-no_match_vars); |
1915 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
1916 | |
1917 | +sub check_recursion_method { |
1918 | + my ($methods) = @_; |
1919 | + |
1920 | + if ( @$methods != 1 ) { |
1921 | + if ( grep({ !m/processlist|hosts/i } @$methods) |
1922 | + && $methods->[0] !~ /^dsn=/i ) |
1923 | + { |
1924 | + die "Invalid combination of recursion methods: " |
1925 | + . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
1926 | + . "Only hosts and processlist may be combined.\n" |
1927 | + } |
1928 | + } |
1929 | + else { |
1930 | + my ($method) = @$methods; |
1931 | + die "Invalid recursion method: " . ( $method || 'undef' ) |
1932 | + unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
1933 | + } |
1934 | +} |
1935 | + |
1936 | sub new { |
1937 | my ( $class, %args ) = @_; |
1938 | + my @required_args = qw(OptionParser DSNParser Quoter); |
1939 | + foreach my $arg ( @required_args ) { |
1940 | + die "I need a $arg argument" unless $args{$arg}; |
1941 | + } |
1942 | my $self = { |
1943 | %args, |
1944 | replication_thread => {}, |
1945 | @@ -3041,28 +3070,27 @@ |
1946 | |
1947 | sub get_slaves { |
1948 | my ($self, %args) = @_; |
1949 | - my @required_args = qw(make_cxn OptionParser DSNParser Quoter); |
1950 | + my @required_args = qw(make_cxn); |
1951 | foreach my $arg ( @required_args ) { |
1952 | die "I need a $arg argument" unless $args{$arg}; |
1953 | } |
1954 | - my ($make_cxn, $o, $dp) = @args{@required_args}; |
1955 | - |
1956 | - my $slaves = []; |
1957 | - my $method = $o->get('recursion-method'); |
1958 | - PTDEBUG && _d('Slave recursion method:', $method); |
1959 | - if ( !$method || $method =~ m/processlist|hosts/i ) { |
1960 | + my ($make_cxn) = @args{@required_args}; |
1961 | + |
1962 | + my $slaves = []; |
1963 | + my $dp = $self->{DSNParser}; |
1964 | + my $methods = $self->_resolve_recursion_methods($args{dsn}); |
1965 | + |
1966 | + if ( grep { m/processlist|hosts/i } @$methods ) { |
1967 | my @required_args = qw(dbh dsn); |
1968 | foreach my $arg ( @required_args ) { |
1969 | die "I need a $arg argument" unless $args{$arg}; |
1970 | } |
1971 | my ($dbh, $dsn) = @args{@required_args}; |
1972 | + |
1973 | $self->recurse_to_slaves( |
1974 | - { dbh => $dbh, |
1975 | - dsn => $dsn, |
1976 | - dsn_parser => $dp, |
1977 | - recurse => $o->get('recurse'), |
1978 | - method => $o->get('recursion-method'), |
1979 | - callback => sub { |
1980 | + { dbh => $dbh, |
1981 | + dsn => $dsn, |
1982 | + callback => sub { |
1983 | my ( $dsn, $dbh, $level, $parent ) = @_; |
1984 | return unless $level; |
1985 | PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
1986 | @@ -3072,31 +3100,48 @@ |
1987 | } |
1988 | ); |
1989 | } |
1990 | - elsif ( $method =~ m/^dsn=/i ) { |
1991 | - my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i; |
1992 | + elsif ( $methods->[0] =~ m/^dsn=/i ) { |
1993 | + (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
1994 | $slaves = $self->get_cxn_from_dsn_table( |
1995 | %args, |
1996 | dsn_table_dsn => $dsn_table_dsn, |
1997 | ); |
1998 | } |
1999 | - elsif ( $method =~ m/none/i ) { |
2000 | + elsif ( $methods->[0] =~ m/none/i ) { |
2001 | PTDEBUG && _d('Not getting to slaves'); |
2002 | } |
2003 | else { |
2004 | - die "Invalid --recursion-method: $method. Valid values are: " |
2005 | - . "dsn=DSN, hosts, or processlist.\n"; |
2006 | + die "Unexpected recursion methods: @$methods"; |
2007 | } |
2008 | - |
2009 | + |
2010 | return $slaves; |
2011 | } |
2012 | |
2013 | +sub _resolve_recursion_methods { |
2014 | + my ($self, $dsn) = @_; |
2015 | + my $o = $self->{OptionParser}; |
2016 | + if ( $o->got('recursion-method') ) { |
2017 | + return $o->get('recursion-method'); |
2018 | + } |
2019 | + elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
2020 | + PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
2021 | + return [qw(hosts)]; |
2022 | + } |
2023 | + else { |
2024 | + return $o->get('recursion-method'); |
2025 | + } |
2026 | +} |
2027 | + |
2028 | sub recurse_to_slaves { |
2029 | my ( $self, $args, $level ) = @_; |
2030 | $level ||= 0; |
2031 | - my $dp = $args->{dsn_parser}; |
2032 | - my $dsn = $args->{dsn}; |
2033 | + my $dp = $self->{DSNParser}; |
2034 | + my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
2035 | + my $dsn = $args->{dsn}; |
2036 | |
2037 | - if ( lc($args->{method} || '') eq 'none' ) { |
2038 | + my $methods = $self->_resolve_recursion_methods($dsn); |
2039 | + PTDEBUG && _d('Recursion methods:', @$methods); |
2040 | + if ( lc($methods->[0]) eq 'none' ) { |
2041 | PTDEBUG && _d('Not recursing to slaves'); |
2042 | return; |
2043 | } |
2044 | @@ -3131,11 +3176,11 @@ |
2045 | |
2046 | $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
2047 | |
2048 | - if ( !defined $args->{recurse} || $level < $args->{recurse} ) { |
2049 | + if ( !defined $recurse || $level < $recurse ) { |
2050 | |
2051 | my @slaves = |
2052 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
2053 | - $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method}); |
2054 | + $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
2055 | |
2056 | foreach my $slave ( @slaves ) { |
2057 | PTDEBUG && _d('Recursing from', |
2058 | @@ -3147,25 +3192,14 @@ |
2059 | } |
2060 | |
2061 | sub find_slave_hosts { |
2062 | - my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_; |
2063 | + my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
2064 | |
2065 | - my @methods = qw(processlist hosts); |
2066 | - if ( $method ) { |
2067 | - @methods = grep { $_ ne $method } @methods; |
2068 | - unshift @methods, $method; |
2069 | - } |
2070 | - else { |
2071 | - if ( ($dsn->{P} || 3306) != 3306 ) { |
2072 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
2073 | - @methods = qw(hosts); |
2074 | - } |
2075 | - } |
2076 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
2077 | - 'using methods', @methods); |
2078 | + 'using methods', @$methods); |
2079 | |
2080 | my @slaves; |
2081 | METHOD: |
2082 | - foreach my $method ( @methods ) { |
2083 | + foreach my $method ( @$methods ) { |
2084 | my $find_slaves = "_find_slaves_by_$method"; |
2085 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
2086 | @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
2087 | @@ -3668,13 +3702,16 @@ |
2088 | |
2089 | sub get_cxn_from_dsn_table { |
2090 | my ($self, %args) = @_; |
2091 | - my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter); |
2092 | + my @required_args = qw(dsn_table_dsn make_cxn); |
2093 | foreach my $arg ( @required_args ) { |
2094 | die "I need a $arg argument" unless $args{$arg}; |
2095 | } |
2096 | - my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args}; |
2097 | + my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
2098 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
2099 | |
2100 | + my $dp = $self->{DSNParser}; |
2101 | + my $q = $self->{Quoter}; |
2102 | + |
2103 | my $dsn = $dp->parse($dsn_table_dsn); |
2104 | my $dsn_table; |
2105 | if ( $dsn->{D} && $dsn->{t} ) { |
2106 | @@ -6856,6 +6893,13 @@ |
2107 | } |
2108 | } |
2109 | |
2110 | + eval { |
2111 | + MasterSlave::check_recursion_method($o->get('recursion-method')); |
2112 | + }; |
2113 | + if ( $EVAL_ERROR ) { |
2114 | + $o->save_error("Invalid --recursion-method: $EVAL_ERROR") |
2115 | + } |
2116 | + |
2117 | $o->usage_or_errors(); |
2118 | |
2119 | # ######################################################################## |
2120 | @@ -7006,7 +7050,11 @@ |
2121 | my $q = new Quoter(); |
2122 | my $tp = new TableParser(Quoter => $q); |
2123 | my $rc = new RowChecksum(Quoter=> $q, OptionParser => $o); |
2124 | - my $ms = new MasterSlave(); |
2125 | + my $ms = new MasterSlave( |
2126 | + OptionParser => $o, |
2127 | + DSNParser => $dp, |
2128 | + Quoter => $q, |
2129 | + ); |
2130 | |
2131 | my $slaves; # all slaves (that we can find) |
2132 | my $slave_lag_cxns; # slaves whose lag we'll check |
2133 | @@ -7026,12 +7074,9 @@ |
2134 | # Find and connect to slaves. |
2135 | # ##################################################################### |
2136 | $slaves = $ms->get_slaves( |
2137 | - dbh => $master_dbh, |
2138 | - dsn => $master_dsn, |
2139 | - OptionParser => $o, |
2140 | - DSNParser => $dp, |
2141 | - Quoter => $q, |
2142 | - make_cxn => sub { |
2143 | + dbh => $master_dbh, |
2144 | + dsn => $master_dsn, |
2145 | + make_cxn => sub { |
2146 | return $make_cxn->(@_, prev_dsn => $master_cxn->dsn()); |
2147 | }, |
2148 | ); |
2149 | @@ -9347,7 +9392,7 @@ |
2150 | |
2151 | =item --recursion-method |
2152 | |
2153 | -type: string |
2154 | +type: array; default: processlist,hosts |
2155 | |
2156 | Preferred recursion method for discovering replicas. Possible methods are: |
2157 | |
2158 | |
2159 | === modified file 'bin/pt-table-sync' |
2160 | --- bin/pt-table-sync 2012-07-31 09:46:13 +0000 |
2161 | +++ bin/pt-table-sync 2012-08-02 17:46:19 +0000 |
2162 | @@ -1860,7 +1860,7 @@ |
2163 | PTDEBUG && _d($dbh, $sql); |
2164 | my ($sql_mode) = eval { $dbh->selectrow_array($sql) }; |
2165 | if ( $EVAL_ERROR ) { |
2166 | - die $EVAL_ERROR; |
2167 | + die "Error getting the current SQL_MODE: $EVAL_ERROR"; |
2168 | } |
2169 | |
2170 | $sql = 'SET @@SQL_QUOTE_SHOW_CREATE = 1' |
2171 | @@ -1870,15 +1870,17 @@ |
2172 | PTDEBUG && _d($dbh, $sql); |
2173 | eval { $dbh->do($sql) }; |
2174 | if ( $EVAL_ERROR ) { |
2175 | - die $EVAL_ERROR; |
2176 | + die "Error setting SQL_QUOTE_SHOW_CREATE, SQL_MODE" |
2177 | + . ($sql_mode ? " and $sql_mode" : '') |
2178 | + . ": $EVAL_ERROR"; |
2179 | } |
2180 | |
2181 | - if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) { |
2182 | - $sql = "/*!40101 SET NAMES $charset*/"; |
2183 | + if ( my ($charset) = $cxn_string =~ m/charset=([\w]+)/ ) { |
2184 | + $sql = qq{/*!40101 SET NAMES "$charset"*/}; |
2185 | PTDEBUG && _d($dbh, ':', $sql); |
2186 | eval { $dbh->do($sql) }; |
2187 | if ( $EVAL_ERROR ) { |
2188 | - die $EVAL_ERROR; |
2189 | + die "Error setting NAMES to $charset: $EVAL_ERROR"; |
2190 | } |
2191 | PTDEBUG && _d('Enabling charset for STDOUT'); |
2192 | if ( $charset eq 'utf8' ) { |
2193 | @@ -1890,12 +1892,12 @@ |
2194 | } |
2195 | } |
2196 | |
2197 | - if ( $self->prop('set-vars') ) { |
2198 | - $sql = "SET " . $self->prop('set-vars'); |
2199 | + if ( my $var = $self->prop('set-vars') ) { |
2200 | + $sql = "SET $var"; |
2201 | PTDEBUG && _d($dbh, ':', $sql); |
2202 | eval { $dbh->do($sql) }; |
2203 | if ( $EVAL_ERROR ) { |
2204 | - die $EVAL_ERROR; |
2205 | + die "Error setting $var: $EVAL_ERROR"; |
2206 | } |
2207 | } |
2208 | } |
2209 | @@ -6454,8 +6456,31 @@ |
2210 | use English qw(-no_match_vars); |
2211 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
2212 | |
2213 | +sub check_recursion_method { |
2214 | + my ($methods) = @_; |
2215 | + |
2216 | + if ( @$methods != 1 ) { |
2217 | + if ( grep({ !m/processlist|hosts/i } @$methods) |
2218 | + && $methods->[0] !~ /^dsn=/i ) |
2219 | + { |
2220 | + die "Invalid combination of recursion methods: " |
2221 | + . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
2222 | + . "Only hosts and processlist may be combined.\n" |
2223 | + } |
2224 | + } |
2225 | + else { |
2226 | + my ($method) = @$methods; |
2227 | + die "Invalid recursion method: " . ( $method || 'undef' ) |
2228 | + unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
2229 | + } |
2230 | +} |
2231 | + |
2232 | sub new { |
2233 | my ( $class, %args ) = @_; |
2234 | + my @required_args = qw(OptionParser DSNParser Quoter); |
2235 | + foreach my $arg ( @required_args ) { |
2236 | + die "I need a $arg argument" unless $args{$arg}; |
2237 | + } |
2238 | my $self = { |
2239 | %args, |
2240 | replication_thread => {}, |
2241 | @@ -6465,28 +6490,27 @@ |
2242 | |
2243 | sub get_slaves { |
2244 | my ($self, %args) = @_; |
2245 | - my @required_args = qw(make_cxn OptionParser DSNParser Quoter); |
2246 | + my @required_args = qw(make_cxn); |
2247 | foreach my $arg ( @required_args ) { |
2248 | die "I need a $arg argument" unless $args{$arg}; |
2249 | } |
2250 | - my ($make_cxn, $o, $dp) = @args{@required_args}; |
2251 | - |
2252 | - my $slaves = []; |
2253 | - my $method = $o->get('recursion-method'); |
2254 | - PTDEBUG && _d('Slave recursion method:', $method); |
2255 | - if ( !$method || $method =~ m/processlist|hosts/i ) { |
2256 | + my ($make_cxn) = @args{@required_args}; |
2257 | + |
2258 | + my $slaves = []; |
2259 | + my $dp = $self->{DSNParser}; |
2260 | + my $methods = $self->_resolve_recursion_methods($args{dsn}); |
2261 | + |
2262 | + if ( grep { m/processlist|hosts/i } @$methods ) { |
2263 | my @required_args = qw(dbh dsn); |
2264 | foreach my $arg ( @required_args ) { |
2265 | die "I need a $arg argument" unless $args{$arg}; |
2266 | } |
2267 | my ($dbh, $dsn) = @args{@required_args}; |
2268 | + |
2269 | $self->recurse_to_slaves( |
2270 | - { dbh => $dbh, |
2271 | - dsn => $dsn, |
2272 | - dsn_parser => $dp, |
2273 | - recurse => $o->get('recurse'), |
2274 | - method => $o->get('recursion-method'), |
2275 | - callback => sub { |
2276 | + { dbh => $dbh, |
2277 | + dsn => $dsn, |
2278 | + callback => sub { |
2279 | my ( $dsn, $dbh, $level, $parent ) = @_; |
2280 | return unless $level; |
2281 | PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
2282 | @@ -6496,31 +6520,48 @@ |
2283 | } |
2284 | ); |
2285 | } |
2286 | - elsif ( $method =~ m/^dsn=/i ) { |
2287 | - my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i; |
2288 | + elsif ( $methods->[0] =~ m/^dsn=/i ) { |
2289 | + (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
2290 | $slaves = $self->get_cxn_from_dsn_table( |
2291 | %args, |
2292 | dsn_table_dsn => $dsn_table_dsn, |
2293 | ); |
2294 | } |
2295 | - elsif ( $method =~ m/none/i ) { |
2296 | + elsif ( $methods->[0] =~ m/none/i ) { |
2297 | PTDEBUG && _d('Not getting to slaves'); |
2298 | } |
2299 | else { |
2300 | - die "Invalid --recursion-method: $method. Valid values are: " |
2301 | - . "dsn=DSN, hosts, or processlist.\n"; |
2302 | + die "Unexpected recursion methods: @$methods"; |
2303 | } |
2304 | - |
2305 | + |
2306 | return $slaves; |
2307 | } |
2308 | |
2309 | +sub _resolve_recursion_methods { |
2310 | + my ($self, $dsn) = @_; |
2311 | + my $o = $self->{OptionParser}; |
2312 | + if ( $o->got('recursion-method') ) { |
2313 | + return $o->get('recursion-method'); |
2314 | + } |
2315 | + elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
2316 | + PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
2317 | + return [qw(hosts)]; |
2318 | + } |
2319 | + else { |
2320 | + return $o->get('recursion-method'); |
2321 | + } |
2322 | +} |
2323 | + |
2324 | sub recurse_to_slaves { |
2325 | my ( $self, $args, $level ) = @_; |
2326 | $level ||= 0; |
2327 | - my $dp = $args->{dsn_parser}; |
2328 | - my $dsn = $args->{dsn}; |
2329 | + my $dp = $self->{DSNParser}; |
2330 | + my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
2331 | + my $dsn = $args->{dsn}; |
2332 | |
2333 | - if ( lc($args->{method} || '') eq 'none' ) { |
2334 | + my $methods = $self->_resolve_recursion_methods($dsn); |
2335 | + PTDEBUG && _d('Recursion methods:', @$methods); |
2336 | + if ( lc($methods->[0]) eq 'none' ) { |
2337 | PTDEBUG && _d('Not recursing to slaves'); |
2338 | return; |
2339 | } |
2340 | @@ -6555,11 +6596,11 @@ |
2341 | |
2342 | $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
2343 | |
2344 | - if ( !defined $args->{recurse} || $level < $args->{recurse} ) { |
2345 | + if ( !defined $recurse || $level < $recurse ) { |
2346 | |
2347 | my @slaves = |
2348 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
2349 | - $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method}); |
2350 | + $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
2351 | |
2352 | foreach my $slave ( @slaves ) { |
2353 | PTDEBUG && _d('Recursing from', |
2354 | @@ -6571,25 +6612,14 @@ |
2355 | } |
2356 | |
2357 | sub find_slave_hosts { |
2358 | - my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_; |
2359 | + my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
2360 | |
2361 | - my @methods = qw(processlist hosts); |
2362 | - if ( $method ) { |
2363 | - @methods = grep { $_ ne $method } @methods; |
2364 | - unshift @methods, $method; |
2365 | - } |
2366 | - else { |
2367 | - if ( ($dsn->{P} || 3306) != 3306 ) { |
2368 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
2369 | - @methods = qw(hosts); |
2370 | - } |
2371 | - } |
2372 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
2373 | - 'using methods', @methods); |
2374 | + 'using methods', @$methods); |
2375 | |
2376 | my @slaves; |
2377 | METHOD: |
2378 | - foreach my $method ( @methods ) { |
2379 | + foreach my $method ( @$methods ) { |
2380 | my $find_slaves = "_find_slaves_by_$method"; |
2381 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
2382 | @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
2383 | @@ -7092,13 +7122,16 @@ |
2384 | |
2385 | sub get_cxn_from_dsn_table { |
2386 | my ($self, %args) = @_; |
2387 | - my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter); |
2388 | + my @required_args = qw(dsn_table_dsn make_cxn); |
2389 | foreach my $arg ( @required_args ) { |
2390 | die "I need a $arg argument" unless $args{$arg}; |
2391 | } |
2392 | - my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args}; |
2393 | + my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
2394 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
2395 | |
2396 | + my $dp = $self->{DSNParser}; |
2397 | + my $q = $self->{Quoter}; |
2398 | + |
2399 | my $dsn = $dp->parse($dsn_table_dsn); |
2400 | my $dsn_table; |
2401 | if ( $dsn->{D} && $dsn->{t} ) { |
2402 | @@ -8320,6 +8353,13 @@ |
2403 | return 0; |
2404 | } |
2405 | |
2406 | + eval { |
2407 | + MasterSlave::check_recursion_method($o->get('recursion-method')); |
2408 | + }; |
2409 | + if ( $EVAL_ERROR ) { |
2410 | + $o->save_error("Invalid --recursion-method: $EVAL_ERROR") |
2411 | + } |
2412 | + |
2413 | $o->usage_or_errors(); |
2414 | |
2415 | # ######################################################################## |
2416 | @@ -8338,7 +8378,7 @@ |
2417 | # Do the work. |
2418 | # ######################################################################## |
2419 | my $tp = new TableParser( Quoter => $q ); |
2420 | - my $ms = new MasterSlave(); |
2421 | + my $ms = new MasterSlave(OptionParser=>$o,DSNParser=>$dp,Quoter=>$q); |
2422 | my $du = new MySQLDump( cache => 0 ); |
2423 | my $rt = new Retry(); |
2424 | my $chunker = new TableChunker( Quoter => $q, TableParser => $tp ); |
2425 | @@ -8736,11 +8776,10 @@ |
2426 | # then sync them. |
2427 | else { |
2428 | $ms->recurse_to_slaves( |
2429 | - { dbh => $src->{dbh}, |
2430 | - dsn => $src->{dsn}, |
2431 | - dsn_parser => $dp, |
2432 | - recurse => 1, |
2433 | - callback => sub { |
2434 | + { dbh => $src->{dbh}, |
2435 | + dsn => $src->{dsn}, |
2436 | + recurse => 1, |
2437 | + callback => sub { |
2438 | my ( $dsn, $dbh, $level, $parent ) = @_; |
2439 | my $all_diffs = $checksum->find_replication_differences( |
2440 | $dbh, $o->get('replicate')); |
2441 | @@ -8809,7 +8848,6 @@ |
2442 | |
2443 | return; |
2444 | }, # recurse_to_slaves() callback |
2445 | - method => $o->get('recursion-method'), |
2446 | }, |
2447 | ); |
2448 | } # DSN is master |
2449 | @@ -10767,7 +10805,7 @@ |
2450 | |
2451 | =item --recursion-method |
2452 | |
2453 | -type: string |
2454 | +type: array; default: processlist,hosts |
2455 | |
2456 | Preferred recursion method used to find slaves. |
2457 | |
2458 | |
2459 | === modified file 'lib/MasterSlave.pm' |
2460 | --- lib/MasterSlave.pm 2012-07-13 04:16:04 +0000 |
2461 | +++ lib/MasterSlave.pm 2012-08-02 17:46:19 +0000 |
2462 | @@ -1,4 +1,4 @@ |
2463 | -# This program is copyright 2007-2011 Baron Schwartz, 2011 Percona Inc. |
2464 | +# This program is copyright 2007-2011 Baron Schwartz, 2011-2012 Percona Inc. |
2465 | # Feedback and improvements are welcome. |
2466 | # |
2467 | # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED |
2468 | @@ -27,8 +27,33 @@ |
2469 | use English qw(-no_match_vars); |
2470 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
2471 | |
2472 | +# Sub: check_recursion_method |
2473 | +# Check that the arrayref of recursion methods passed in is valid |
2474 | +sub check_recursion_method { |
2475 | + my ($methods) = @_; |
2476 | + |
2477 | + if ( @$methods != 1 ) { |
2478 | + if ( grep({ !m/processlist|hosts/i } @$methods) |
2479 | + && $methods->[0] !~ /^dsn=/i ) |
2480 | + { |
2481 | + die "Invalid combination of recursion methods: " |
2482 | + . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
2483 | + . "Only hosts and processlist may be combined.\n" |
2484 | + } |
2485 | + } |
2486 | + else { |
2487 | + my ($method) = @$methods; |
2488 | + die "Invalid recursion method: " . ( $method || 'undef' ) |
2489 | + unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
2490 | + } |
2491 | +} |
2492 | + |
2493 | sub new { |
2494 | my ( $class, %args ) = @_; |
2495 | + my @required_args = qw(OptionParser DSNParser Quoter); |
2496 | + foreach my $arg ( @required_args ) { |
2497 | + die "I need a $arg argument" unless $args{$arg}; |
2498 | + } |
2499 | my $self = { |
2500 | %args, |
2501 | replication_thread => {}, |
2502 | @@ -38,28 +63,27 @@ |
2503 | |
2504 | sub get_slaves { |
2505 | my ($self, %args) = @_; |
2506 | - my @required_args = qw(make_cxn OptionParser DSNParser Quoter); |
2507 | + my @required_args = qw(make_cxn); |
2508 | foreach my $arg ( @required_args ) { |
2509 | die "I need a $arg argument" unless $args{$arg}; |
2510 | } |
2511 | - my ($make_cxn, $o, $dp) = @args{@required_args}; |
2512 | - |
2513 | - my $slaves = []; |
2514 | - my $method = $o->get('recursion-method'); |
2515 | - PTDEBUG && _d('Slave recursion method:', $method); |
2516 | - if ( !$method || $method =~ m/processlist|hosts/i ) { |
2517 | + my ($make_cxn) = @args{@required_args}; |
2518 | + |
2519 | + my $slaves = []; |
2520 | + my $dp = $self->{DSNParser}; |
2521 | + my $methods = $self->_resolve_recursion_methods($args{dsn}); |
2522 | + |
2523 | + if ( grep { m/processlist|hosts/i } @$methods ) { |
2524 | my @required_args = qw(dbh dsn); |
2525 | foreach my $arg ( @required_args ) { |
2526 | die "I need a $arg argument" unless $args{$arg}; |
2527 | } |
2528 | my ($dbh, $dsn) = @args{@required_args}; |
2529 | + |
2530 | $self->recurse_to_slaves( |
2531 | - { dbh => $dbh, |
2532 | - dsn => $dsn, |
2533 | - dsn_parser => $dp, |
2534 | - recurse => $o->get('recurse'), |
2535 | - method => $o->get('recursion-method'), |
2536 | - callback => sub { |
2537 | + { dbh => $dbh, |
2538 | + dsn => $dsn, |
2539 | + callback => sub { |
2540 | my ( $dsn, $dbh, $level, $parent ) = @_; |
2541 | return unless $level; |
2542 | PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
2543 | @@ -69,25 +93,41 @@ |
2544 | } |
2545 | ); |
2546 | } |
2547 | - elsif ( $method =~ m/^dsn=/i ) { |
2548 | - my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i; |
2549 | + elsif ( $methods->[0] =~ m/^dsn=/i ) { |
2550 | + (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
2551 | $slaves = $self->get_cxn_from_dsn_table( |
2552 | %args, |
2553 | dsn_table_dsn => $dsn_table_dsn, |
2554 | ); |
2555 | } |
2556 | - elsif ( $method =~ m/none/i ) { |
2557 | - # https://bugs.launchpad.net/percona-toolkit/+bug/987694 |
2558 | + elsif ( $methods->[0] =~ m/none/i ) { |
2559 | PTDEBUG && _d('Not getting to slaves'); |
2560 | } |
2561 | else { |
2562 | - die "Invalid --recursion-method: $method. Valid values are: " |
2563 | - . "dsn=DSN, hosts, or processlist.\n"; |
2564 | + die "Unexpected recursion methods: @$methods"; |
2565 | } |
2566 | - |
2567 | + |
2568 | return $slaves; |
2569 | } |
2570 | |
2571 | +sub _resolve_recursion_methods { |
2572 | + my ($self, $dsn) = @_; |
2573 | + my $o = $self->{OptionParser}; |
2574 | + if ( $o->got('recursion-method') ) { |
2575 | + # Use whatever the user explicitly gave on the command line. |
2576 | + return $o->get('recursion-method'); |
2577 | + } |
2578 | + elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
2579 | + # Special case: hosts is best when port is non-standard. |
2580 | + PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
2581 | + return [qw(hosts)]; |
2582 | + } |
2583 | + else { |
2584 | + # Use the option's default. |
2585 | + return $o->get('recursion-method'); |
2586 | + } |
2587 | +} |
2588 | + |
2589 | # Sub: recurse_to_slaves |
2590 | # Descend to slaves by examining SHOW SLAVE HOSTS. |
2591 | # The callback gets the slave's DSN, dbh, parent, and the recursion level |
2592 | @@ -111,10 +151,16 @@ |
2593 | sub recurse_to_slaves { |
2594 | my ( $self, $args, $level ) = @_; |
2595 | $level ||= 0; |
2596 | - my $dp = $args->{dsn_parser}; |
2597 | - my $dsn = $args->{dsn}; |
2598 | + my $dp = $self->{DSNParser}; |
2599 | + my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
2600 | + my $dsn = $args->{dsn}; |
2601 | |
2602 | - if ( lc($args->{method} || '') eq 'none' ) { |
2603 | + # Re-resolve the recursion methods for each slave. In most cases |
2604 | + # it won't change, but it could if one slave uses standard port (3306) |
2605 | + # and another does not. |
2606 | + my $methods = $self->_resolve_recursion_methods($dsn); |
2607 | + PTDEBUG && _d('Recursion methods:', @$methods); |
2608 | + if ( lc($methods->[0]) eq 'none' ) { |
2609 | # https://bugs.launchpad.net/percona-toolkit/+bug/987694 |
2610 | PTDEBUG && _d('Not recursing to slaves'); |
2611 | return; |
2612 | @@ -154,13 +200,13 @@ |
2613 | # Call the callback! |
2614 | $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
2615 | |
2616 | - if ( !defined $args->{recurse} || $level < $args->{recurse} ) { |
2617 | + if ( !defined $recurse || $level < $recurse ) { |
2618 | |
2619 | # Find the slave hosts. Eliminate hosts that aren't slaves of me (as |
2620 | # revealed by server_id and master_id). |
2621 | my @slaves = |
2622 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
2623 | - $self->find_slave_hosts($dp, $dbh, $dsn, $args->{method}); |
2624 | + $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
2625 | |
2626 | foreach my $slave ( @slaves ) { |
2627 | PTDEBUG && _d('Recursing from', |
2628 | @@ -184,27 +230,14 @@ |
2629 | # If a method is given, it becomes the preferred (first tried) method. |
2630 | # Searching stops as soon as a method finds slaves. |
2631 | sub find_slave_hosts { |
2632 | - my ( $self, $dsn_parser, $dbh, $dsn, $method ) = @_; |
2633 | + my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
2634 | |
2635 | - my @methods = qw(processlist hosts); |
2636 | - if ( $method ) { |
2637 | - # Remove all but the given method. |
2638 | - @methods = grep { $_ ne $method } @methods; |
2639 | - # Add given method to the head of the list. |
2640 | - unshift @methods, $method; |
2641 | - } |
2642 | - else { |
2643 | - if ( ($dsn->{P} || 3306) != 3306 ) { |
2644 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
2645 | - @methods = qw(hosts); |
2646 | - } |
2647 | - } |
2648 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
2649 | - 'using methods', @methods); |
2650 | + 'using methods', @$methods); |
2651 | |
2652 | my @slaves; |
2653 | METHOD: |
2654 | - foreach my $method ( @methods ) { |
2655 | + foreach my $method ( @$methods ) { |
2656 | my $find_slaves = "_find_slaves_by_$method"; |
2657 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
2658 | @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
2659 | @@ -849,13 +882,16 @@ |
2660 | |
2661 | sub get_cxn_from_dsn_table { |
2662 | my ($self, %args) = @_; |
2663 | - my @required_args = qw(dsn_table_dsn make_cxn DSNParser Quoter); |
2664 | + my @required_args = qw(dsn_table_dsn make_cxn); |
2665 | foreach my $arg ( @required_args ) { |
2666 | die "I need a $arg argument" unless $args{$arg}; |
2667 | } |
2668 | - my ($dsn_table_dsn, $make_cxn, $dp, $q) = @args{@required_args}; |
2669 | + my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
2670 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
2671 | |
2672 | + my $dp = $self->{DSNParser}; |
2673 | + my $q = $self->{Quoter}; |
2674 | + |
2675 | my $dsn = $dp->parse($dsn_table_dsn); |
2676 | my $dsn_table; |
2677 | if ( $dsn->{D} && $dsn->{t} ) { |
2678 | |
2679 | === modified file 't/lib/MasterSlave.t' |
2680 | --- t/lib/MasterSlave.t 2012-07-23 15:28:25 +0000 |
2681 | +++ t/lib/MasterSlave.t 2012-08-02 17:46:19 +0000 |
2682 | @@ -9,7 +9,7 @@ |
2683 | use strict; |
2684 | use warnings FATAL => 'all'; |
2685 | use English qw(-no_match_vars); |
2686 | -use Test::More tests => 52; |
2687 | +use Test::More; |
2688 | |
2689 | use MasterSlave; |
2690 | use DSNParser; |
2691 | @@ -20,13 +20,13 @@ |
2692 | use Sandbox; |
2693 | use PerconaTest; |
2694 | |
2695 | -my $ms = new MasterSlave(); |
2696 | +use Data::Dumper; |
2697 | + |
2698 | my $dp = new DSNParser(opts=>$dsn_opts); |
2699 | my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); |
2700 | |
2701 | my $master_dbh = $sb->get_dbh_for('master'); |
2702 | my $slave_dbh = $sb->get_dbh_for('slave1'); |
2703 | - |
2704 | my $master_dsn = { |
2705 | h => '127.1', |
2706 | P => '12345', |
2707 | @@ -34,25 +34,29 @@ |
2708 | p => 'msandbox', |
2709 | }; |
2710 | |
2711 | -# ############################################################################ |
2712 | -# get_slaves() wrapper around recurse_to_slaves() |
2713 | -# ############################################################################ |
2714 | my $q = new Quoter; |
2715 | my $o = new OptionParser(description => 'MasterSlave'); |
2716 | $o->get_specs("$trunk/bin/pt-table-checksum"); |
2717 | |
2718 | +my $ms = new MasterSlave( |
2719 | + OptionParser => $o, |
2720 | + DSNParser => $dp, |
2721 | + Quoter => $q, |
2722 | +); |
2723 | + |
2724 | +# ############################################################################ |
2725 | +# get_slaves() wrapper around recurse_to_slaves() |
2726 | +# ############################################################################ |
2727 | + |
2728 | SKIP: { |
2729 | skip "Cannot connect to sandbox master", 2 unless $master_dbh; |
2730 | - @ARGV = (); |
2731 | + local @ARGV = (); |
2732 | $o->get_opts(); |
2733 | - |
2734 | + |
2735 | my $slaves = $ms->get_slaves( |
2736 | - dbh => $master_dbh, |
2737 | - dsn => $master_dsn, |
2738 | - OptionParser => $o, |
2739 | - DSNParser => $dp, |
2740 | - Quoter => $q, |
2741 | - make_cxn => sub { |
2742 | + dbh => $master_dbh, |
2743 | + dsn => $master_dsn, |
2744 | + make_cxn => sub { |
2745 | my $cxn = new Cxn( |
2746 | @_, |
2747 | DSNParser => $dp, |
2748 | @@ -78,7 +82,7 @@ |
2749 | master_id => 12345, |
2750 | source => 'hosts', |
2751 | }, |
2752 | - 'get_slaves() from recurse_to_slaves()' |
2753 | + 'get_slaves() from recurse_to_slaves() with a default --recursion-method' |
2754 | ); |
2755 | |
2756 | my ($id) = $slaves->[0]->dbh()->selectrow_array('SELECT @@SERVER_ID'); |
2757 | @@ -93,16 +97,13 @@ |
2758 | # and ignore it. This tests nonetheless that "processlist" isn't |
2759 | # misspelled, which would cause the sub to die. |
2760 | # https://bugs.launchpad.net/percona-toolkit/+bug/921802 |
2761 | - @ARGV = ('--recursion-method', 'processlist'); |
2762 | + local @ARGV = ('--recursion-method', 'processlist'); |
2763 | $o->get_opts(); |
2764 | |
2765 | $slaves = $ms->get_slaves( |
2766 | - OptionParser => $o, |
2767 | - DSNParser => $dp, |
2768 | - Quoter => $q, |
2769 | - dbh => $master_dbh, |
2770 | - dsn => $master_dsn, |
2771 | - make_cxn => sub { |
2772 | + dbh => $master_dbh, |
2773 | + dsn => $master_dsn, |
2774 | + make_cxn => sub { |
2775 | my $cxn = new Cxn( |
2776 | @_, |
2777 | DSNParser => $dp, |
2778 | @@ -146,12 +147,9 @@ |
2779 | throws_ok( |
2780 | sub { |
2781 | $slaves = $ms->get_slaves( |
2782 | - OptionParser => $o, |
2783 | - DSNParser => $dp, |
2784 | - Quoter => $q, |
2785 | - dbh => $ro_dbh, |
2786 | - dsn => $ro_dsn, |
2787 | - make_cxn => sub { |
2788 | + dbh => $ro_dbh, |
2789 | + dsn => $ro_dsn, |
2790 | + make_cxn => sub { |
2791 | my $cxn = new Cxn( |
2792 | @_, |
2793 | DSNParser => $dp, |
2794 | @@ -169,12 +167,9 @@ |
2795 | @ARGV = ('--recursion-method', 'none'); |
2796 | $o->get_opts(); |
2797 | $slaves = $ms->get_slaves( |
2798 | - OptionParser => $o, |
2799 | - DSNParser => $dp, |
2800 | - Quoter => $q, |
2801 | - dbh => $ro_dbh, |
2802 | - dsn => $ro_dsn, |
2803 | - make_cxn => sub { |
2804 | + dbh => $ro_dbh, |
2805 | + dsn => $ro_dsn, |
2806 | + make_cxn => sub { |
2807 | my $cxn = new Cxn( |
2808 | @_, |
2809 | DSNParser => $dp, |
2810 | @@ -190,14 +185,13 @@ |
2811 | "No privs needed for --recursion-method=none (bug 987694)" |
2812 | ); |
2813 | |
2814 | + @ARGV = ('--recursion-method', 'none', '--recurse', '2'); |
2815 | + $o->get_opts(); |
2816 | my $recursed = 0; |
2817 | $ms->recurse_to_slaves( |
2818 | - { dsn_parser => $dp, |
2819 | - dbh => $ro_dbh, |
2820 | - dsn => $ro_dsn, |
2821 | - recurse => 2, |
2822 | - callback => sub { $recursed++ }, |
2823 | - method => 'none', |
2824 | + { dbh => $ro_dbh, |
2825 | + dsn => $ro_dsn, |
2826 | + callback => sub { $recursed++ }, |
2827 | }); |
2828 | is( |
2829 | $recursed, |
2830 | @@ -231,10 +225,10 @@ |
2831 | diag(`$trunk/sandbox/stop-sandbox $port >/dev/null 2>&1`); |
2832 | } |
2833 | } |
2834 | -diag(`$trunk/sandbox/start-sandbox master 2900 >/dev/null 2>&1`); |
2835 | -diag(`$trunk/sandbox/start-sandbox slave 2903 2900 >/dev/null 2>&1`); |
2836 | -diag(`$trunk/sandbox/start-sandbox slave 2901 2900 >/dev/null 2>&1`); |
2837 | -diag(`$trunk/sandbox/start-sandbox slave 2902 2901 >/dev/null 2>&1`); |
2838 | +diag(`$trunk/sandbox/start-sandbox master 2900`); |
2839 | +diag(`$trunk/sandbox/start-sandbox slave 2903 2900`); |
2840 | +diag(`$trunk/sandbox/start-sandbox slave 2901 2900`); |
2841 | +diag(`$trunk/sandbox/start-sandbox slave 2902 2901`); |
2842 | |
2843 | # I discovered something weird while updating this test. Above, you see that |
2844 | # slave2 is started first, then the others. Before, slave2 was started last, |
2845 | @@ -279,11 +273,12 @@ |
2846 | . " from $dsn->{source}"); |
2847 | }; |
2848 | |
2849 | +@ARGV = ('--recurse', '2'); |
2850 | +$o->get_opts(); |
2851 | + |
2852 | $ms->recurse_to_slaves( |
2853 | - { dsn_parser => $dp, |
2854 | - dbh => $dbh, |
2855 | + { dbh => $dbh, |
2856 | dsn => $dsn, |
2857 | - recurse => 2, |
2858 | callback => $callback, |
2859 | skip_callback => $skip_callback, |
2860 | }); |
2861 | @@ -707,13 +702,40 @@ |
2862 | 'dbh created from DSN table works' |
2863 | ); |
2864 | |
2865 | +# ############################################################################ |
2866 | +# Invalid recursion methods are caught |
2867 | +# ############################################################################ |
2868 | +local $EVAL_ERROR; |
2869 | +eval { |
2870 | + MasterSlave::check_recursion_method([qw(stuff)]) |
2871 | +}; |
2872 | + |
2873 | +like( |
2874 | + $EVAL_ERROR, |
2875 | + qr/Invalid recursion method: stuff/, |
2876 | + "--recursion-method stuff causes error" |
2877 | +); |
2878 | + |
2879 | +local $EVAL_ERROR; |
2880 | +eval { |
2881 | + MasterSlave::check_recursion_method([qw(processlist stuff)]) |
2882 | +}; |
2883 | + |
2884 | +like( |
2885 | + $EVAL_ERROR, |
2886 | + qr/Invalid combination of recursion methods: processlist, stuff/, |
2887 | + "--recursion-method processlist,stuff causes error", |
2888 | +); |
2889 | + |
2890 | # ############################################################################# |
2891 | # Done. |
2892 | # ############################################################################# |
2893 | $sb->wipe_clean($master_dbh); |
2894 | -diag(`$trunk/sandbox/stop-sandbox 2903 2902 2901 2900 >/dev/null 2>&1`); |
2895 | +diag(`$trunk/sandbox/stop-sandbox 2903 2902 2901 2900`); |
2896 | diag(`/tmp/12346/use -e "set global read_only=1"`); |
2897 | diag(`/tmp/12347/use -e "set global read_only=1"`); |
2898 | -diag(`$trunk/sandbox/test-env reset >/dev/null 2>&1`); |
2899 | +$sb->wait_for_slaves(); |
2900 | +diag(`$trunk/sandbox/test-env reset`); |
2901 | ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
2902 | +done_testing; |
2903 | exit; |
2904 | |
2905 | === modified file 't/pt-table-checksum/option_sanity.t' |
2906 | --- t/pt-table-checksum/option_sanity.t 2012-05-25 21:34:58 +0000 |
2907 | +++ t/pt-table-checksum/option_sanity.t 2012-08-02 17:46:19 +0000 |
2908 | @@ -9,7 +9,7 @@ |
2909 | use strict; |
2910 | use warnings FATAL => 'all'; |
2911 | use English qw(-no_match_vars); |
2912 | -use Test::More tests => 19; |
2913 | +use Test::More; |
2914 | |
2915 | use PerconaTest; |
2916 | shift @INC; # our unshift (above) |
2917 | @@ -95,6 +95,18 @@ |
2918 | "Default --replicate-check=TRUE" |
2919 | ); |
2920 | |
2921 | +like( |
2922 | + $output, |
2923 | + qr/^\s+--recursion-method=a/m, |
2924 | + "--recursion-method is an array" |
2925 | +); |
2926 | + |
2927 | +like( |
2928 | + $output, |
2929 | + qr/^\s+--recursion-method\s+processlist,hosts/m, |
2930 | + "Default --recursion-method is processlist,hosts" |
2931 | +); |
2932 | + |
2933 | # ############################################################################ |
2934 | # Check opts that disable other opts. |
2935 | # ############################################################################ |
2936 | @@ -170,4 +182,4 @@ |
2937 | # ############################################################################# |
2938 | # Done. |
2939 | # ############################################################################# |
2940 | -exit; |
2941 | +done_testing; |
The --recursion-method values are checked in MasterSlave: :get_slaves( ). I think we should fail earlier by implementing a class method like MasterSlalve: :check_ recursion_ method( ) that we can call when checking other options (like Progress: validate_ spec()) . So something like:
eval { :check_ recursion_ method( $o->get( 'recursion- method' )); save_error( "Invalid --recursion method: $EVAL_ERROR");
MasterSlave:
};
if ( $EVAL_ERROR ) {
$o->
}
That method can also sanity check against things like --recursion-method none,hosts.