Merge lp:~percona-toolkit-dev/percona-toolkit/pxc-autodiscover-nodes into lp:percona-toolkit/2.2
- pxc-autodiscover-nodes
- Merge into 2.2
Proposed by
Brian Fraser
Status: | Merged |
---|---|
Merged at revision: | 573 |
Proposed branch: | lp:~percona-toolkit-dev/percona-toolkit/pxc-autodiscover-nodes |
Merge into: | lp:percona-toolkit/2.2 |
Diff against target: |
1037 lines (+598/-219) 6 files modified
bin/pt-table-checksum (+188/-24) lib/Cxn.pm (+36/-0) lib/MasterSlave.pm (+2/-1) lib/Percona/XtraDB/Cluster.pm (+121/-0) sandbox/servers/pxc/5.5/my.sandbox.cnf (+1/-1) t/pt-table-checksum/pxc.t (+250/-193) |
To merge this branch: | bzr merge lp:~percona-toolkit-dev/percona-toolkit/pxc-autodiscover-nodes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel Nichter | Approve | ||
Review via email: mp+156489@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 569. By Daniel Nichter
-
Merge version-
in-all- bash-tools-bug-821502. - 570. By Daniel Nichter
-
Merge hostname-bug-947893-2.2.
- 571. By Daniel Nichter
-
Merge pt-heartbeat-utc-bug-1163372-2.2.
- 572. By Brian Fraser
-
Rebased the branch to trunk
Revision history for this message
Daniel Nichter (daniel-nichter) wrote : | # |
Still need to
334 + my ($self, %args) = @_;
335 + my @cxns = @{$args{cxns}};
Use/check @required_args.
and use "foreach" instead of "for", but the major stuff is done/fixed.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bin/pt-table-checksum' |
2 | --- bin/pt-table-checksum 2013-04-04 21:45:50 +0000 |
3 | +++ bin/pt-table-checksum 2013-04-12 16:10:32 +0000 |
4 | @@ -3581,6 +3581,32 @@ |
5 | return $self->{hostname} || $self->{dsn_name} || 'unknown host'; |
6 | } |
7 | |
8 | +sub remove_duplicate_cxns { |
9 | + my ($self, %args) = @_; |
10 | + my @cxns = @{$args{cxns}}; |
11 | + my $seen_ids = $args{seen_ids} || {}; |
12 | + PTDEBUG && _d("Removing duplicates from ", join(" ", map { $_->name } @cxns)); |
13 | + my @trimmed_cxns; |
14 | + |
15 | + for my $cxn ( @cxns ) { |
16 | + my $dbh = $cxn->dbh(); |
17 | + my $sql = q{SELECT @@server_id}; |
18 | + PTDEBUG && _d($sql); |
19 | + my ($id) = $dbh->selectrow_array($sql); |
20 | + PTDEBUG && _d('Server ID for ', $cxn->name, ': ', $id); |
21 | + |
22 | + if ( ! $seen_ids->{$id}++ ) { |
23 | + push @trimmed_cxns, $cxn |
24 | + } |
25 | + else { |
26 | + PTDEBUG && _d("Removing ", $cxn->name, |
27 | + ", ID ", $id, ", because we've already seen it"); |
28 | + } |
29 | + } |
30 | + |
31 | + return \@trimmed_cxns; |
32 | +} |
33 | + |
34 | sub DESTROY { |
35 | my ($self) = @_; |
36 | |
37 | @@ -3634,6 +3660,8 @@ |
38 | use Lmo; |
39 | use Data::Dumper; |
40 | |
41 | +{ local $EVAL_ERROR; eval { require Cxn } }; |
42 | + |
43 | sub get_cluster_name { |
44 | my ($self, $cxn) = @_; |
45 | my $sql = "SHOW VARIABLES LIKE 'wsrep\_cluster\_name'"; |
46 | @@ -3667,6 +3695,57 @@ |
47 | return ($val1 || '') eq ($val2 || ''); |
48 | } |
49 | |
50 | +sub find_cluster_nodes { |
51 | + my ($self, %args) = @_; |
52 | + |
53 | + my $dbh = $args{dbh}; |
54 | + my $dsn = $args{dsn}; |
55 | + my $dp = $args{DSNParser}; |
56 | + my $make_cxn = $args{make_cxn}; |
57 | + |
58 | + |
59 | + my $sql = q{SHOW STATUS LIKE 'wsrep\_incoming\_addresses'}; |
60 | + PTDEBUG && _d($sql); |
61 | + my (undef, $addresses) = $dbh->selectrow_array($sql); |
62 | + PTDEBUG && _d("Cluster nodes found: ", $addresses); |
63 | + return unless $addresses; |
64 | + |
65 | + my @addresses = grep { !/\Aunspecified\z/i } |
66 | + split /,\s*/, $addresses; |
67 | + |
68 | + my @nodes; |
69 | + foreach my $address ( @addresses ) { |
70 | + my ($host, $port) = split /:/, $address; |
71 | + my $spec = "h=$host" |
72 | + . ($port ? ",P=$port" : ""); |
73 | + my $node_dsn = $dp->parse($spec, $dsn); |
74 | + my $node_dbh = eval { $dp->get_dbh( |
75 | + $dp->get_cxn_params($node_dsn), { AutoCommit => 1 }) }; |
76 | + if ( $EVAL_ERROR ) { |
77 | + print STDERR "Cannot connect to ", $dp->as_string($node_dsn), |
78 | + ", discovered through $sql: $EVAL_ERROR\n"; |
79 | + if ( !$port && $dsn->{P} != 3306 ) { |
80 | + $address .= ":3306"; |
81 | + redo; |
82 | + } |
83 | + next; |
84 | + } |
85 | + PTDEBUG && _d('Connected to', $dp->as_string($node_dsn)); |
86 | + $node_dbh->disconnect(); |
87 | + |
88 | + push @nodes, $make_cxn->(dsn => $node_dsn); |
89 | + } |
90 | + |
91 | + return \@nodes; |
92 | +} |
93 | + |
94 | +sub remove_duplicate_cxns { |
95 | + my ($self, %args) = @_; |
96 | + my @cxns = @{$args{cxns}}; |
97 | + my $seen_ids = $args{seen_ids}; |
98 | + return Cxn->remove_duplicate_cxns(%args); |
99 | +} |
100 | + |
101 | sub same_cluster { |
102 | my ($self, $cxn1, $cxn2) = @_; |
103 | |
104 | @@ -3678,6 +3757,59 @@ |
105 | return ($cluster1 || '') eq ($cluster2 || ''); |
106 | } |
107 | |
108 | +sub autodetect_nodes { |
109 | + my ($self, %args) = @_; |
110 | + my $ms = $args{MasterSlave}; |
111 | + my $dp = $args{DSNParser}; |
112 | + my $make_cxn = $args{make_cxn}; |
113 | + my $nodes = $args{nodes}; |
114 | + my $seen_ids = $args{seen_ids}; |
115 | + |
116 | + my $new_nodes = []; |
117 | + |
118 | + return $new_nodes unless @$nodes; |
119 | + |
120 | + for my $node ( @$nodes ) { |
121 | + my $nodes_found = $self->find_cluster_nodes( |
122 | + dbh => $node->dbh(), |
123 | + dsn => $node->dsn(), |
124 | + make_cxn => $make_cxn, |
125 | + DSNParser => $dp, |
126 | + ); |
127 | + push @$new_nodes, @$nodes_found; |
128 | + } |
129 | + |
130 | + $new_nodes = $self->remove_duplicate_cxns( |
131 | + cxns => $new_nodes, |
132 | + seen_ids => $seen_ids |
133 | + ); |
134 | + |
135 | + my $new_slaves = []; |
136 | + foreach my $node (@$new_nodes) { |
137 | + my $node_slaves = $ms->get_slaves( |
138 | + dbh => $node->dbh(), |
139 | + dsn => $node->dsn(), |
140 | + make_cxn => $make_cxn, |
141 | + ); |
142 | + push @$new_slaves, @$node_slaves; |
143 | + } |
144 | + |
145 | + $new_slaves = $self->remove_duplicate_cxns( |
146 | + cxns => $new_slaves, |
147 | + seen_ids => $seen_ids |
148 | + ); |
149 | + |
150 | + my @new_slave_nodes = grep { $self->is_cluster_node($_) } @$new_slaves; |
151 | + |
152 | + my $slaves_of_slaves = $self->autodetect_nodes( |
153 | + %args, |
154 | + nodes => \@new_slave_nodes, |
155 | + ); |
156 | + |
157 | + my @autodetected_nodes = ( @$new_nodes, @$new_slaves, @$slaves_of_slaves ); |
158 | + return \@autodetected_nodes; |
159 | +} |
160 | + |
161 | sub _d { |
162 | my ($package, undef, $line) = caller 0; |
163 | @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
164 | @@ -4770,6 +4902,8 @@ |
165 | my $dp = $self->{DSNParser}; |
166 | my $methods = $self->_resolve_recursion_methods($args{dsn}); |
167 | |
168 | + return $slaves unless @$methods; |
169 | + |
170 | if ( grep { m/processlist|hosts/i } @$methods ) { |
171 | my @required_args = qw(dbh dsn); |
172 | foreach my $arg ( @required_args ) { |
173 | @@ -8776,11 +8910,22 @@ |
174 | } |
175 | } |
176 | |
177 | + my $autodiscover_cluster; |
178 | + my $recursion_method = []; |
179 | + foreach my $method ( @{$o->get('recursion-method')} ) { |
180 | + if ( $method eq 'cluster' ) { |
181 | + $autodiscover_cluster = 1; |
182 | + } |
183 | + else { |
184 | + push @$recursion_method, $method |
185 | + } |
186 | + } |
187 | + $o->set('recursion-method', $recursion_method); |
188 | eval { |
189 | MasterSlave::check_recursion_method($o->get('recursion-method')); |
190 | }; |
191 | if ( $EVAL_ERROR ) { |
192 | - $o->save_error("Invalid --recursion-method: $EVAL_ERROR") |
193 | + $o->save_error($EVAL_ERROR) |
194 | } |
195 | |
196 | $o->usage_or_errors(); |
197 | @@ -8959,33 +9104,44 @@ |
198 | # ##################################################################### |
199 | # Find and connect to slaves. |
200 | # ##################################################################### |
201 | + my $make_cxn_cluster = sub { |
202 | + my $cxn = $make_cxn->(@_, prev_dsn => $master_cxn->dsn()); |
203 | + $cluster_name_for{$cxn} = $cluster->is_cluster_node($cxn); |
204 | + return $cxn; |
205 | + }; |
206 | + |
207 | $slaves = $ms->get_slaves( |
208 | dbh => $master_dbh, |
209 | dsn => $master_dsn, |
210 | - make_cxn => sub { |
211 | - my $cxn = $make_cxn->(@_, prev_dsn => $master_cxn->dsn()); |
212 | - $cluster_name_for{$cxn} = $cluster->is_cluster_node($cxn); |
213 | - return $cxn; |
214 | - }, |
215 | + make_cxn => $make_cxn_cluster, |
216 | ); |
217 | |
218 | - # If the "master" is a cluster node, then a DSN table should have been |
219 | - # used, and it may have all nodes' DSNs so the user can run the tool |
220 | - # on any node, in which case it has the "master" node, the DSN given |
221 | - # on the command line. So detect and remove this dupe. |
222 | - if ( $cluster_name_for{$master_cxn} ) { |
223 | - @$slaves = grep { |
224 | - my $slave_cxn = $_; |
225 | - if ( $cluster->same_node($master_cxn, $slave_cxn) ) { |
226 | - PTDEBUG && _d('Removing ', $slave_cxn->name, 'from slaves', |
227 | - 'because it is the master'); |
228 | - 0; |
229 | - } |
230 | - else { |
231 | - $slave_cxn; |
232 | - } |
233 | - } @$slaves; |
234 | - } |
235 | + my %seen_ids; |
236 | + for my $cxn ($master_cxn, @$slaves) { |
237 | + my $dbh = $cxn->dbh(); |
238 | + my $sql = q{SELECT @@server_id}; |
239 | + PTDEBUG && _d($cxn, $dbh, $sql); |
240 | + my ($id) = $dbh->selectrow_array($sql); |
241 | + $seen_ids{$id}++; |
242 | + } |
243 | + |
244 | + if ( $autodiscover_cluster ) { |
245 | + my @known_nodes = grep { $cluster_name_for{$_} } $master_cxn, @$slaves; |
246 | + my $new_cxns = $cluster->autodetect_nodes( |
247 | + nodes => \@known_nodes, |
248 | + MasterSlave => $ms, |
249 | + DSNParser => $dp, |
250 | + make_cxn => $make_cxn_cluster, |
251 | + seen_ids => \%seen_ids, |
252 | + ); |
253 | + push @$slaves, @$new_cxns; |
254 | + } |
255 | + |
256 | + my $trimmed_nodes = Cxn->remove_duplicate_cxns( |
257 | + cxns => [ $master_cxn, @$slaves ], |
258 | + ); |
259 | + ($master_cxn, @$slaves) = @$trimmed_nodes; |
260 | + |
261 | PTDEBUG && _d(scalar @$slaves, 'slaves found'); |
262 | if ( !@$slaves && $o->get('recursion-method')->[0] ne 'none' ) { |
263 | $exit_status |= 1; |
264 | @@ -9090,7 +9246,7 @@ |
265 | warn "Diffs will only be detected if the cluster is " |
266 | . "consistent with " . $direct_slave->name . " because " |
267 | . $master_cxn->name . " is a traditional replication master " |
268 | - . " but these replicas are cluster nodes:\n" |
269 | + . "but these replicas are cluster nodes:\n" |
270 | . join("\n", map { ' ' . $_->name } @nodes) . "\n" |
271 | . "For more information, please read the Percona XtraDB " |
272 | . "Cluster section of the tool's documentation.\n"; |
273 | @@ -11765,6 +11921,7 @@ |
274 | =========== ================== |
275 | processlist SHOW PROCESSLIST |
276 | hosts SHOW SLAVE HOSTS |
277 | + cluster SHOW STATUS LIKE 'wsrep\_incoming\_addresses' |
278 | dsn=DSN DSNs from a table |
279 | none Do not find slaves |
280 | |
281 | @@ -11775,6 +11932,13 @@ |
282 | The C<hosts> method requires replicas to be configured with C<report_host>, |
283 | C<report_port>, etc. |
284 | |
285 | +The C<cluster> method requires a cluster based on Galera 23.7.3 or newer, |
286 | +such as Percona XtraDB Cluster versions 5.5.29 and above. This will |
287 | +autodiscover nodes in a cluster using C<SHOW STATUS LIKE 'wsrep\_incoming\_addresses'>. |
288 | +Note that you can combine C<cluster> with C<processlist> and C<hosts> to |
289 | +have the tool autodiscover all cluster nodes and traditional slaves, |
290 | +but this is considered highly experimental. |
291 | + |
292 | The C<dsn> method is special: rather than automatically discovering replicas, |
293 | this method specifies a table with replica DSNs. The tool will only connect |
294 | to these replicas. This method works best when replicas do not use the same |
295 | |
296 | === modified file 'lib/Cxn.pm' |
297 | --- lib/Cxn.pm 2013-04-04 21:31:21 +0000 |
298 | +++ lib/Cxn.pm 2013-04-12 16:10:32 +0000 |
299 | @@ -220,6 +220,42 @@ |
300 | return $self->{hostname} || $self->{dsn_name} || 'unknown host'; |
301 | } |
302 | |
303 | +# There's two reasons why there might be dupes: |
304 | +# If the "master" is a cluster node, then a DSN table might have been |
305 | +# used, and it may have all nodes' DSNs so the user can run the tool |
306 | +# on any node, in which case it has the "master" node, the DSN given |
307 | +# on the command line. |
308 | +# On the other hand, maybe find_cluster_nodes worked, in which case |
309 | +# we definitely have a dupe for the master cxn, but we may also have a |
310 | +# dupe for every other node if this was unsed in conjunction with a |
311 | +# DSN table. |
312 | +# So try to detect and remove those. |
313 | +sub remove_duplicate_cxns { |
314 | + my ($self, %args) = @_; |
315 | + my @cxns = @{$args{cxns}}; |
316 | + my $seen_ids = $args{seen_ids} || {}; |
317 | + PTDEBUG && _d("Removing duplicates from ", join(" ", map { $_->name } @cxns)); |
318 | + my @trimmed_cxns; |
319 | + |
320 | + for my $cxn ( @cxns ) { |
321 | + my $dbh = $cxn->dbh(); |
322 | + my $sql = q{SELECT @@server_id}; |
323 | + PTDEBUG && _d($sql); |
324 | + my ($id) = $dbh->selectrow_array($sql); |
325 | + PTDEBUG && _d('Server ID for ', $cxn->name, ': ', $id); |
326 | + |
327 | + if ( ! $seen_ids->{$id}++ ) { |
328 | + push @trimmed_cxns, $cxn |
329 | + } |
330 | + else { |
331 | + PTDEBUG && _d("Removing ", $cxn->name, |
332 | + ", ID ", $id, ", because we've already seen it"); |
333 | + } |
334 | + } |
335 | + |
336 | + return \@trimmed_cxns; |
337 | +} |
338 | + |
339 | sub DESTROY { |
340 | my ($self) = @_; |
341 | |
342 | |
343 | === modified file 'lib/MasterSlave.pm' |
344 | --- lib/MasterSlave.pm 2013-01-03 00:19:16 +0000 |
345 | +++ lib/MasterSlave.pm 2013-04-12 16:10:32 +0000 |
346 | @@ -73,6 +73,8 @@ |
347 | my $dp = $self->{DSNParser}; |
348 | my $methods = $self->_resolve_recursion_methods($args{dsn}); |
349 | |
350 | + return $slaves unless @$methods; |
351 | + |
352 | if ( grep { m/processlist|hosts/i } @$methods ) { |
353 | my @required_args = qw(dbh dsn); |
354 | foreach my $arg ( @required_args ) { |
355 | @@ -114,7 +116,6 @@ |
356 | my ($self, $dsn) = @_; |
357 | my $o = $self->{OptionParser}; |
358 | if ( $o->got('recursion-method') ) { |
359 | - # Use whatever the user explicitly gave on the command line. |
360 | return $o->get('recursion-method'); |
361 | } |
362 | elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
363 | |
364 | === modified file 'lib/Percona/XtraDB/Cluster.pm' |
365 | --- lib/Percona/XtraDB/Cluster.pm 2013-02-01 17:06:13 +0000 |
366 | +++ lib/Percona/XtraDB/Cluster.pm 2013-04-12 16:10:32 +0000 |
367 | @@ -30,6 +30,8 @@ |
368 | use Lmo; |
369 | use Data::Dumper; |
370 | |
371 | +{ local $EVAL_ERROR; eval { require Cxn } }; |
372 | + |
373 | sub get_cluster_name { |
374 | my ($self, $cxn) = @_; |
375 | my $sql = "SHOW VARIABLES LIKE 'wsrep\_cluster\_name'"; |
376 | @@ -63,6 +65,70 @@ |
377 | return ($val1 || '') eq ($val2 || ''); |
378 | } |
379 | |
380 | +# TODO: Check that the PXC version supports wsrep_incoming_addresses |
381 | +# Not really necessary, actually. But in case it's needed, |
382 | +# wsrep_provider_version =~ /[0-9]+\.[0-9]+\(r([0-9]+)\)/ && $1 >= 137 |
383 | +sub find_cluster_nodes { |
384 | + my ($self, %args) = @_; |
385 | + |
386 | + my $dbh = $args{dbh}; |
387 | + my $dsn = $args{dsn}; |
388 | + my $dp = $args{DSNParser}; |
389 | + my $make_cxn = $args{make_cxn}; |
390 | + |
391 | + # Ostensibly the caller should've done this already, but |
392 | + # useful for safety. |
393 | + # TODO this fails with a strange error. |
394 | + #$dp->fill_in_dsn($dbh, $dsn); |
395 | + |
396 | + my $sql = q{SHOW STATUS LIKE 'wsrep\_incoming\_addresses'}; |
397 | + PTDEBUG && _d($sql); |
398 | + my (undef, $addresses) = $dbh->selectrow_array($sql); |
399 | + PTDEBUG && _d("Cluster nodes found: ", $addresses); |
400 | + return unless $addresses; |
401 | + |
402 | + my @addresses = grep { !/\Aunspecified\z/i } |
403 | + split /,\s*/, $addresses; |
404 | + |
405 | + my @nodes; |
406 | + foreach my $address ( @addresses ) { |
407 | + my ($host, $port) = split /:/, $address; |
408 | + my $spec = "h=$host" |
409 | + . ($port ? ",P=$port" : ""); |
410 | + my $node_dsn = $dp->parse($spec, $dsn); |
411 | + my $node_dbh = eval { $dp->get_dbh( |
412 | + $dp->get_cxn_params($node_dsn), { AutoCommit => 1 }) }; |
413 | + if ( $EVAL_ERROR ) { |
414 | + print STDERR "Cannot connect to ", $dp->as_string($node_dsn), |
415 | + ", discovered through $sql: $EVAL_ERROR\n"; |
416 | + # This is a bit strange, so an explanation is called for. |
417 | + # If there wasn't a port, that means that this bug |
418 | + # https://bugs.launchpad.net/percona-toolkit/+bug/1082406 |
419 | + # isn't fixed on this version of PXC. We tried using the |
420 | + # master's port, but that didn't work. So try again, using |
421 | + # the default port. |
422 | + if ( !$port && $dsn->{P} != 3306 ) { |
423 | + $address .= ":3306"; |
424 | + redo; |
425 | + } |
426 | + next; |
427 | + } |
428 | + PTDEBUG && _d('Connected to', $dp->as_string($node_dsn)); |
429 | + $node_dbh->disconnect(); |
430 | + |
431 | + push @nodes, $make_cxn->(dsn => $node_dsn); |
432 | + } |
433 | + |
434 | + return \@nodes; |
435 | +} |
436 | + |
437 | +sub remove_duplicate_cxns { |
438 | + my ($self, %args) = @_; |
439 | + my @cxns = @{$args{cxns}}; |
440 | + my $seen_ids = $args{seen_ids}; |
441 | + return Cxn->remove_duplicate_cxns(%args); |
442 | +} |
443 | + |
444 | sub same_cluster { |
445 | my ($self, $cxn1, $cxn2) = @_; |
446 | |
447 | @@ -75,6 +141,61 @@ |
448 | return ($cluster1 || '') eq ($cluster2 || ''); |
449 | } |
450 | |
451 | +sub autodetect_nodes { |
452 | + my ($self, %args) = @_; |
453 | + my $ms = $args{MasterSlave}; |
454 | + my $dp = $args{DSNParser}; |
455 | + my $make_cxn = $args{make_cxn}; |
456 | + my $nodes = $args{nodes}; |
457 | + my $seen_ids = $args{seen_ids}; |
458 | + |
459 | + my $new_nodes = []; |
460 | + |
461 | + return $new_nodes unless @$nodes; |
462 | + |
463 | + for my $node ( @$nodes ) { |
464 | + my $nodes_found = $self->find_cluster_nodes( |
465 | + dbh => $node->dbh(), |
466 | + dsn => $node->dsn(), |
467 | + make_cxn => $make_cxn, |
468 | + DSNParser => $dp, |
469 | + ); |
470 | + push @$new_nodes, @$nodes_found; |
471 | + } |
472 | + |
473 | + $new_nodes = $self->remove_duplicate_cxns( |
474 | + cxns => $new_nodes, |
475 | + seen_ids => $seen_ids |
476 | + ); |
477 | + |
478 | + my $new_slaves = []; |
479 | + foreach my $node (@$new_nodes) { |
480 | + my $node_slaves = $ms->get_slaves( |
481 | + dbh => $node->dbh(), |
482 | + dsn => $node->dsn(), |
483 | + make_cxn => $make_cxn, |
484 | + ); |
485 | + push @$new_slaves, @$node_slaves; |
486 | + } |
487 | + |
488 | + $new_slaves = $self->remove_duplicate_cxns( |
489 | + cxns => $new_slaves, |
490 | + seen_ids => $seen_ids |
491 | + ); |
492 | + |
493 | + # If some of the new slaves is a cluster node, autodetect new nodes |
494 | + # from there too. |
495 | + my @new_slave_nodes = grep { $self->is_cluster_node($_) } @$new_slaves; |
496 | + |
497 | + my $slaves_of_slaves = $self->autodetect_nodes( |
498 | + %args, |
499 | + nodes => \@new_slave_nodes, |
500 | + ); |
501 | + |
502 | + my @autodetected_nodes = ( @$new_nodes, @$new_slaves, @$slaves_of_slaves ); |
503 | + return \@autodetected_nodes; |
504 | +} |
505 | + |
506 | sub _d { |
507 | my ($package, undef, $line) = caller 0; |
508 | @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
509 | |
510 | === modified file 'sandbox/servers/pxc/5.5/my.sandbox.cnf' |
511 | --- sandbox/servers/pxc/5.5/my.sandbox.cnf 2012-10-15 16:53:37 +0000 |
512 | +++ sandbox/servers/pxc/5.5/my.sandbox.cnf 2013-04-12 16:10:32 +0000 |
513 | @@ -31,7 +31,7 @@ |
514 | wsrep_provider = LIBGALERA |
515 | wsrep_cluster_address = CLUSTER_AD |
516 | wsrep_sst_receive_address = ADDR:RECEIVE_PRT |
517 | -wsrep_node_incoming_address= ADDR |
518 | +wsrep_node_incoming_address= ADDR:PORT |
519 | wsrep_slave_threads = 2 |
520 | wsrep_cluster_name = CLUSTER_NAME |
521 | wsrep_provider_options = "gmcast.listen_addr=tcp://ADDR:LISTEN_PRT;" |
522 | |
523 | === modified file 't/pt-table-checksum/pxc.t' |
524 | --- t/pt-table-checksum/pxc.t 2013-03-08 18:07:05 +0000 |
525 | +++ t/pt-table-checksum/pxc.t 2013-04-12 16:10:32 +0000 |
526 | @@ -22,6 +22,8 @@ |
527 | use Sandbox; |
528 | require "$trunk/bin/pt-table-checksum"; |
529 | |
530 | +my $ip = qr/\Q127.1\E|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/; |
531 | + |
532 | my $dp = new DSNParser(opts=>$dsn_opts); |
533 | my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); |
534 | my $node1 = $sb->get_dbh_for('node1'); |
535 | @@ -75,34 +77,41 @@ |
536 | |
537 | like( |
538 | $output, |
539 | - qr/h=127.1,P=12345 is a cluster node but no other nodes/, |
540 | + qr/h=127(?:\Q.0.0\E)?.1,P=12345 is a cluster node but no other nodes/, |
541 | "Dies if no other nodes are found" |
542 | ); |
543 | |
544 | -$output = output( |
545 | - sub { pt_table_checksum::main(@args, |
546 | - '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns") |
547 | - }, |
548 | - stderr => 1, |
549 | -); |
550 | - |
551 | -is( |
552 | - PerconaTest::count_checksum_results($output, 'errors'), |
553 | - 0, |
554 | - "No diffs: no errors" |
555 | -); |
556 | - |
557 | -is( |
558 | - PerconaTest::count_checksum_results($output, 'skipped'), |
559 | - 0, |
560 | - "No diffs: no skips" |
561 | -); |
562 | - |
563 | -is( |
564 | - PerconaTest::count_checksum_results($output, 'diffs'), |
565 | - 0, |
566 | - "No diffs: no diffs" |
567 | -); |
568 | +for my $args ( |
569 | + ["using recusion-method=dsn", '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns"], |
570 | + ["using recursion-method=cluster", '--recursion-method', 'cluster'] |
571 | + ) |
572 | +{ |
573 | + my $test = shift @$args; |
574 | + $output = output( |
575 | + sub { pt_table_checksum::main(@args, |
576 | + @$args) |
577 | + }, |
578 | + stderr => 1, |
579 | + ); |
580 | + |
581 | + is( |
582 | + PerconaTest::count_checksum_results($output, 'errors'), |
583 | + 0, |
584 | + "No diffs: no errors ($test)" |
585 | + ); |
586 | + |
587 | + is( |
588 | + PerconaTest::count_checksum_results($output, 'skipped'), |
589 | + 0, |
590 | + "No diffs: no skips ($test)" |
591 | + ); |
592 | + |
593 | + is( |
594 | + PerconaTest::count_checksum_results($output, 'diffs'), |
595 | + 0, |
596 | + "No diffs: no diffs ($test)" |
597 | + ); |
598 | +} |
599 | |
600 | # Now really test checksumming a cluster. To create a diff we have to disable |
601 | # the binlog. Although PXC doesn't need or use the binlog to communicate |
602 | @@ -135,45 +144,53 @@ |
603 | "Node3 not changed" |
604 | ); |
605 | |
606 | -$output = output( |
607 | - sub { pt_table_checksum::main(@args, |
608 | - '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns") |
609 | - }, |
610 | - stderr => 1, |
611 | -); |
612 | - |
613 | -is( |
614 | - PerconaTest::count_checksum_results($output, 'errors'), |
615 | - 0, |
616 | - "1 diff: no errors" |
617 | -); |
618 | - |
619 | -is( |
620 | - PerconaTest::count_checksum_results($output, 'skipped'), |
621 | - 0, |
622 | - "1 diff: no skips" |
623 | -); |
624 | - |
625 | -is( |
626 | - PerconaTest::count_checksum_results($output, 'diffs'), |
627 | - 1, |
628 | - "1 diff: 1 diff" |
629 | -) or diag($output); |
630 | - |
631 | -# 11-17T13:02:54 0 1 26 1 0 0.021 test.t |
632 | -like( |
633 | - $output, |
634 | - qr/^\S+\s+ # ts |
635 | - 0\s+ # errors |
636 | - 1\s+ # diffs |
637 | - 26\s+ # rows |
638 | - \d+\s+ # chunks |
639 | - 0\s+ # skipped |
640 | - \S+\s+ # time |
641 | - test.t$ # table |
642 | - /xm, |
643 | - "1 diff: it's in test.t" |
644 | -); |
645 | +for my $args ( |
646 | + ["using recusion-method=dsn", '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns"], |
647 | + ["using recursion-method=cluster", '--recursion-method', 'cluster'] |
648 | + ) |
649 | +{ |
650 | + my $test = shift @$args; |
651 | + |
652 | + $output = output( |
653 | + sub { pt_table_checksum::main(@args, |
654 | + @$args) |
655 | + }, |
656 | + stderr => 1, |
657 | + ); |
658 | + |
659 | + is( |
660 | + PerconaTest::count_checksum_results($output, 'errors'), |
661 | + 0, |
662 | + "1 diff: no errors ($test)" |
663 | + ); |
664 | + |
665 | + is( |
666 | + PerconaTest::count_checksum_results($output, 'skipped'), |
667 | + 0, |
668 | + "1 diff: no skips ($test)" |
669 | + ); |
670 | + |
671 | + is( |
672 | + PerconaTest::count_checksum_results($output, 'diffs'), |
673 | + 1, |
674 | + "1 diff: 1 diff ($test)" |
675 | + ) or diag($output); |
676 | + |
677 | + # 11-17T13:02:54 0 1 26 1 0 0.021 test.t |
678 | + like( |
679 | + $output, |
680 | + qr/^\S+\s+ # ts |
681 | + 0\s+ # errors |
682 | + 1\s+ # diffs |
683 | + 26\s+ # rows |
684 | + \d+\s+ # chunks |
685 | + 0\s+ # skipped |
686 | + \S+\s+ # time |
687 | + test.t$ # table |
688 | + /xm, |
689 | + "1 diff: it's in test.t ($test)" |
690 | + ); |
691 | +} |
692 | |
693 | # ############################################################################# |
694 | # cluster, node1 -> slave, run on node1 |
695 | @@ -205,33 +222,48 @@ |
696 | # https://bugs.launchpad.net/percona-toolkit/+bug/1080385 |
697 | # Cluster nodes default to ROW format because that's what Galeara |
698 | # works best with, even though it doesn't really use binlogs. |
699 | -$output = output( |
700 | - sub { pt_table_checksum::main(@args, |
701 | - '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns") |
702 | - }, |
703 | - stderr => 1, |
704 | -); |
705 | - |
706 | -like( |
707 | - $output, |
708 | - qr/replica h=127.1,P=12348 has binlog_format ROW/i, |
709 | - "--check-binlog-format warns about slave's binlog format" |
710 | -); |
711 | - |
712 | -# Now really test that diffs on the slave are detected. |
713 | -$output = output( |
714 | - sub { pt_table_checksum::main(@args, |
715 | - '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns", |
716 | - qw(--no-check-binlog-format)), |
717 | - }, |
718 | - stderr => 1, |
719 | -); |
720 | - |
721 | -is( |
722 | - PerconaTest::count_checksum_results($output, 'diffs'), |
723 | - 1, |
724 | - "Detects diffs on slave of cluster node1" |
725 | -) or diag($output); |
726 | +for my $args ( |
727 | + ["using recusion-method=dsn", '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns"], |
728 | + ["using recursion-method=cluster,hosts", '--recursion-method', 'cluster,hosts'] |
729 | + ) |
730 | +{ |
731 | + my $test = shift @$args; |
732 | + |
733 | + # Wait for the slave to apply the binlogs from node1 (its master). |
734 | + # Then change it so it's not consistent. |
735 | + PerconaTest::wait_for_table($slave_dbh, 'test.t'); |
736 | + $sb->wait_for_slaves('cslave1'); |
737 | + $slave_dbh->do("update test.t set c='zebra' where c='z'"); |
738 | + |
739 | + $output = output( |
740 | + sub { pt_table_checksum::main(@args, |
741 | + @$args) |
742 | + }, |
743 | + stderr => 1, |
744 | + ); |
745 | + |
746 | + like( |
747 | + $output, |
748 | + qr/replica h=127(?:\Q.0.0\E)?\.1,P=12348 has binlog_format ROW/i, |
749 | + "--check-binlog-format warns about slave's binlog format ($test)" |
750 | + ); |
751 | + |
752 | + # Now really test that diffs on the slave are detected. |
753 | + $output = output( |
754 | + sub { pt_table_checksum::main(@args, |
755 | + @$args, |
756 | + qw(--no-check-binlog-format)), |
757 | + }, |
758 | + stderr => 1, |
759 | + ); |
760 | + |
761 | + is( |
762 | + PerconaTest::count_checksum_results($output, 'diffs'), |
763 | + 1, |
764 | + "Detects diffs on slave of cluster node1 ($test)" |
765 | + ) or diag($output); |
766 | + |
767 | +} |
768 | |
769 | $slave_dbh->disconnect; |
770 | $sb->stop_sandbox('cslave1'); |
771 | @@ -267,20 +299,28 @@ |
772 | "Slave is changed" |
773 | ); |
774 | |
775 | -$output = output( |
776 | - sub { pt_table_checksum::main(@args, |
777 | - '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns", |
778 | - qw(--no-check-binlog-format -d test)), |
779 | - }, |
780 | - stderr => 1, |
781 | -); |
782 | - |
783 | -is( |
784 | - PerconaTest::count_checksum_results($output, 'diffs'), |
785 | - 0, |
786 | - "Limitation: does not detect diffs on slave of cluster node2" |
787 | -) or diag($output); |
788 | - |
789 | +for my $args ( |
790 | + ["using recusion-method=dsn", '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns"], |
791 | + ["using recursion-method=cluster,hosts", '--recursion-method', 'cluster,hosts'] |
792 | + ) |
793 | +{ |
794 | + my $test = shift @$args; |
795 | + |
796 | + $output = output( |
797 | + sub { pt_table_checksum::main(@args, |
798 | + @$args, |
799 | + qw(--no-check-binlog-format -d test)), |
800 | + }, |
801 | + stderr => 1, |
802 | + ); |
803 | + |
804 | + is( |
805 | + PerconaTest::count_checksum_results($output, 'diffs'), |
806 | + 0, |
807 | + "Limitation: does not detect diffs on slave of cluster node2 ($test)" |
808 | + ) or diag($output); |
809 | +} |
810 | + |
811 | $slave_dbh->disconnect; |
812 | $sb->stop_sandbox('cslave1'); |
813 | |
814 | @@ -454,96 +494,113 @@ |
815 | |
816 | like( |
817 | $output, |
818 | - qr/the direct replica of h=127.1,P=12349 was not found or specified/, |
819 | + qr/the direct replica of h=$ip,P=12349 was not found or specified/, |
820 | "Warns that direct replica of the master isn't found or specified", |
821 | ); |
822 | |
823 | # Use the other DSN table with all three nodes. Now the tool should |
824 | # give a more specific warning than that ^. |
825 | -$output = output( |
826 | - sub { pt_table_checksum::main($master_dsn, |
827 | - '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns", |
828 | - qw(-d test)) |
829 | - }, |
830 | - stderr => 1, |
831 | -); |
832 | - |
833 | -is( |
834 | - PerconaTest::count_checksum_results($output, 'diffs'), |
835 | - 1, |
836 | - "...check all nodes: 1 diff" |
837 | -) or diag($output); |
838 | - |
839 | -# 11-17T13:02:54 0 1 26 1 0 0.021 test.t |
840 | -like( |
841 | - $output, |
842 | - qr/^\S+\s+ # ts |
843 | - 0\s+ # errors |
844 | - 1\s+ # diffs |
845 | - 26\s+ # rows |
846 | - \d+\s+ # chunks |
847 | - 0\s+ # skipped |
848 | - \S+\s+ # time |
849 | - test.t$ # table |
850 | - /xm, |
851 | - "...check all nodes: it's in test.t" |
852 | -); |
853 | - |
854 | -like( |
855 | - $output, |
856 | - qr/Diffs will only be detected if the cluster is consistent with h=127.1,P=12345 because h=127.1,P=12349/, |
857 | - "Warns that diffs only detected if cluster consistent with direct replica", |
858 | -); |
859 | - |
860 | -# Restore node1 so the cluster is consistent, but then make node2 differ. |
861 | -# ptc should NOT detect this diff because the checksum query will replicate |
862 | -# to node1, node1 isn't different, so it broadcasts the result in ROW format |
863 | -# that all is ok, which node2 gets and thus false reports. This is why |
864 | -# those ^ warnings exist. |
865 | -$node1->do("set sql_log_bin=0"); |
866 | -$node1->do("update test.t set c='z' where c='zebra'"); |
867 | -$node1->do("set sql_log_bin=1"); |
868 | - |
869 | -$node2->do("set sql_log_bin=0"); |
870 | -$node2->do("update test.t set c='zebra' where c='z'"); |
871 | -$node2->do("set sql_log_bin=1"); |
872 | - |
873 | -($row) = $node2->selectrow_array("select c from test.t order by c desc limit 1"); |
874 | -is( |
875 | - $row, |
876 | - "zebra", |
877 | - "Node2 is changed again" |
878 | -); |
879 | - |
880 | -($row) = $node1->selectrow_array("select c from test.t order by c desc limit 1"); |
881 | -is( |
882 | - $row, |
883 | - "z", |
884 | - "Node1 not changed again" |
885 | -); |
886 | - |
887 | -($row) = $node3->selectrow_array("select c from test.t order by c desc limit 1"); |
888 | -is( |
889 | - $row, |
890 | - "z", |
891 | - "Node3 not changed again" |
892 | -); |
893 | - |
894 | -# the other DSN table with all three nodes, but it won't matter because |
895 | -# node1 is going to broadcast the false-positive that there are no diffs. |
896 | -$output = output( |
897 | - sub { pt_table_checksum::main($master_dsn, |
898 | - '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns", |
899 | - qw(-d test)) |
900 | - }, |
901 | - stderr => 1, |
902 | -); |
903 | - |
904 | -is( |
905 | - PerconaTest::count_checksum_results($output, 'diffs'), |
906 | - 0, |
907 | - "Limitation: diff not on direct replica not detected" |
908 | -) or diag($output); |
909 | +# Originally, these tested a dsn table with all nodes; now we hijack |
910 | +# those tests to also try the autodetection |
911 | +for my $args ( |
912 | + ["using recusion-method=dsn", '--recursion-method', "dsn=$node1_dsn,D=dsns,t=dsns"], |
913 | + ["using recursion-method=cluster,hosts", '--recursion-method', 'cluster,hosts'] |
914 | + ) |
915 | +{ |
916 | + my $test = shift @$args; |
917 | + |
918 | + # Make a diff on node1. If ptc is really auto-detecting node1, then it |
919 | + # should report this diff. |
920 | + $node1->do("set sql_log_bin=0"); |
921 | + $node1->do("update test.t set c='zebra' where c='z'"); |
922 | + $node1->do("set sql_log_bin=1"); |
923 | + |
924 | + $output = output( |
925 | + sub { pt_table_checksum::main($master_dsn, |
926 | + @$args, |
927 | + qw(-d test)) |
928 | + }, |
929 | + stderr => 1, |
930 | + ); |
931 | + |
932 | + is( |
933 | + PerconaTest::count_checksum_results($output, 'diffs'), |
934 | + 1, |
935 | + "...check all nodes: 1 diff ($test)" |
936 | + ) or diag($output); |
937 | + |
938 | + # 11-17T13:02:54 0 1 26 1 0 0.021 test.t |
939 | + like( |
940 | + $output, |
941 | + qr/^\S+\s+ # ts |
942 | + 0\s+ # errors |
943 | + 1\s+ # diffs |
944 | + 26\s+ # rows |
945 | + \d+\s+ # chunks |
946 | + 0\s+ # skipped |
947 | + \S+\s+ # time |
948 | + test.t$ # table |
949 | + /xm, |
950 | + "...check all nodes: it's in test.t ($test)" |
951 | + ); |
952 | + |
953 | + like( |
954 | + $output, |
955 | + qr/Diffs will only be detected if the cluster is consistent with h=$ip,P=12345 because h=$ip,P=12349/, |
956 | + "Warns that diffs only detected if cluster consistent with direct replica ($test)", |
957 | + ); |
958 | + |
959 | + # Restore node1 so the cluster is consistent, but then make node2 differ. |
960 | + # ptc should NOT detect this diff because the checksum query will replicate |
961 | + # to node1, node1 isn't different, so it broadcasts the result in ROW format |
962 | + # that all is ok, which node2 gets and thus false reports. This is why |
963 | + # those ^ warnings exist. |
964 | + $node1->do("set sql_log_bin=0"); |
965 | + $node1->do("update test.t set c='z' where c='zebra'"); |
966 | + $node1->do("set sql_log_bin=1"); |
967 | + |
968 | + $node2->do("set sql_log_bin=0"); |
969 | + $node2->do("update test.t set c='zebra' where c='z'"); |
970 | + $node2->do("set sql_log_bin=1"); |
971 | + |
972 | + ($row) = $node2->selectrow_array("select c from test.t order by c desc limit 1"); |
973 | + is( |
974 | + $row, |
975 | + "zebra", |
976 | + "Node2 is changed again ($test)" |
977 | + ); |
978 | + |
979 | + ($row) = $node1->selectrow_array("select c from test.t order by c desc limit 1"); |
980 | + is( |
981 | + $row, |
982 | + "z", |
983 | + "Node1 not changed again ($test)" |
984 | + ); |
985 | + |
986 | + ($row) = $node3->selectrow_array("select c from test.t order by c desc limit 1"); |
987 | + is( |
988 | + $row, |
989 | + "z", |
990 | + "Node3 not changed again ($test)" |
991 | + ); |
992 | + |
993 | + # the other DSN table with all three nodes, but it won't matter because |
994 | + # node1 is going to broadcast the false-positive that there are no diffs. |
995 | + $output = output( |
996 | + sub { pt_table_checksum::main($master_dsn, |
997 | + @$args, |
998 | + qw(-d test)) |
999 | + }, |
1000 | + stderr => 1, |
1001 | + ); |
1002 | + |
1003 | + is( |
1004 | + PerconaTest::count_checksum_results($output, 'diffs'), |
1005 | + 0, |
1006 | + "Limitation: diff not on direct replica not detected ($test)" |
1007 | + ) or diag($output); |
1008 | + |
1009 | +} |
1010 | |
1011 | # ########################################################################### |
1012 | # Be sure to stop the slave on node1, else further test will die with: |
1013 | @@ -587,13 +644,13 @@ |
1014 | |
1015 | like( |
1016 | $output, |
1017 | - qr/h=127.1,P=12345 is in cluster pt_sandbox_cluster/, |
1018 | + qr/h=127(?:\Q.0.0\E)?.1,P=12345 is in cluster pt_sandbox_cluster/, |
1019 | "Detects that node1 is in pt_sandbox_cluster" |
1020 | ); |
1021 | |
1022 | like( |
1023 | $output, |
1024 | - qr/h=127.1,P=2900 is in cluster cluster2/, |
1025 | + qr/h=127(?:\Q.0.0\E)?.1,P=2900 is in cluster cluster2/, |
1026 | "Detects that node4 is in cluster2" |
1027 | ); |
1028 | |
1029 | @@ -602,7 +659,7 @@ |
1030 | qr/test/, |
1031 | "Different clusters, no results" |
1032 | ); |
1033 | - |
1034 | + |
1035 | $sb->stop_sandbox(qw(node4 node5 node6)); |
1036 | |
1037 | # Restore the DSN table in case there are more tests. |
I'll focus on changed to lib/Percona/ XtraDB/ Cluster. pm:
268 +sub find_cluster_nodes {
269 + my ($self, %args) = @_;
270 +
271 + my $dbh = $args{dbh};
Let's use and check @required_args.
###
287 + my @addresses = grep !/\Aunspecified \z/i,
288 + split /,\s*/, $addresses;
Let's use the prevailing style: grep { ... } split ..., e.g like later:
407 + my @new_slave_nodes = grep { $self-> is_cluster_ node($_ ) } @new_slaves;
###
314 + $node_dbh- >disconnect( );
315 +
316 + push @nodes, $make_cxn->(dsn => $node_dsn);
Can we just keep the already connected node dbh instead of disconnect and re-connecting later? I think Cxn::new() can be passed a live dbh?
###
319 + return @nodes;
Return \@nodes. This makes testing easier:
my $got = find_cluster_ nodes(. ..);
is_deeply(
$got,
$expected,
"..."
) or diag(Dumper($got));
I think returning lists is best for small subs that are always used in list contexts, but there's no specific "rules" about this.
###
334 + my ($self, %args) = @_;
335 + my @cxns = @{$args{cxns}};
Use/check @required_args.
###
337 + PTDEBUG && _d("Removing duplicates from ", join " ", map { $_->name } @cxns);
I would make the join() clearer in this case by using parentheses.
###
340 + for my $cxn ( @cxns ) {
Use foreach like you did earlier in other place (and because it's the established style).
###
342 + my $sql = q{SELECT @@SERVER_ID};
Use @@server_id. That's the informal standard, i.e. like how FUNCTIONS() are informally always uppercase.
###
351 + PTDEBUG && _d("Removing ", $cxn->name,
352 + ", ID $id, because we've already seen it");
$id will probably always be defined, but if not, that PTDEBUG will throw an error.
###
356 + return @trimmed_cxns;
Return arrayref here too.
###
367 + my ($self, %args) = @_;
368 + my $ms = $args{MasterSlave};
Use @required_args.
###
377 + for my $node ( @$nodes ) {
Use foreach.
###
09 + return @new_nodes, @new_slaves, autodetect_ nodes(
410 + $self->
411 + %args,
412 + nodes => \@new_slave_nodes,
413 + );
That's too complex, simplify. Return something like { new_nodes => $new_nodes, ... }, no function call in the return call.
###
248 +=item --[no]autodetec t-nodes
Let's no add more options. I think we can add "cluster" as a value to --recursion-method.