Merge lp:~percona-toolkit-dev/percona-toolkit/pxc-autodiscover-nodes into lp:percona-toolkit/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
Reviewer Review Type Date Requested Status
Daniel Nichter Approve
Review via email: mp+156489@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Daniel Nichter (daniel-nichter) wrote :

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,
410 + $self->autodetect_nodes(
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]autodetect-nodes

Let's no add more options. I think we can add "cluster" as a value to --recursion-method.

review: Needs Fixing
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.

Subscribers

People subscribed via source and target branches