Merge lp:~percona-toolkit-dev/percona-toolkit/fix-1062563-1063912-ptc-pxc-bugs into lp:percona-toolkit/2.1
- fix-1062563-1063912-ptc-pxc-bugs
- Merge into 2.1
Status: | Superseded |
---|---|
Proposed branch: | lp:~percona-toolkit-dev/percona-toolkit/fix-1062563-1063912-ptc-pxc-bugs |
Merge into: | lp:percona-toolkit/2.1 |
Diff against target: |
1196 lines (+834/-103) 10 files modified
bin/pt-config-diff (+1/-15) bin/pt-kill (+9/-19) bin/pt-online-schema-change (+9/-19) bin/pt-table-checksum (+144/-25) lib/Cxn.pm (+1/-15) lib/Percona/XtraDB/Cluster.pm (+123/-0) lib/Sandbox.pm (+85/-4) t/lib/Cxn.t (+4/-6) t/lib/Percona/XtraDB/Cluster.t (+216/-0) t/pt-table-checksum/pxc.t (+242/-0) |
To merge this branch: | bzr merge lp:~percona-toolkit-dev/percona-toolkit/fix-1062563-1063912-ptc-pxc-bugs |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel Nichter | Needs Fixing | ||
Review via email: mp+130191@code.launchpad.net |
This proposal has been superseded by a proposal from 2012-11-08.
Commit message
Description of the change
- 417. By Daniel Nichter
-
Merge fix-pt-
upgrade- select-bug-1060774 - 418. By Daniel Nichter
-
Merge fix-938068-
ptc-slave- binlog- formats - 419. By Daniel Nichter
-
Merge fix-978133-
remove- pqd-priv- checks - 420. By Daniel Nichter
-
Redirect sys cmd 2>/dev/null in pt-kill tests to avoid false-positive errors.
- 421. By Daniel Nichter
-
Fix RawLogParser.t. Use diag in PerconaTest.pm.
- 422. By Daniel Nichter
-
Remove all_privs tests. Update Percona:
:Toolkit: :VERSION. - 423. By Daniel Nichter
-
Make CompareResults.t stable--yet another case of not waiting for replication.
- 424. By Daniel Nichter
-
Merge fix-pt-osc-del-trg-bug-1062324.
- 425. By Daniel Nichter
-
Merge opt-parsing-
exit-status-bug-1039074. - 426. By Daniel Nichter
-
Merge quiet-progress-bug-1039541.
- 427. By Daniel Nichter
-
Merge pdl-partitions-bug-1043528.
- 428. By Daniel Nichter
-
Merge pt-stalk-iter-1-bug-1070434.
- 429. By Daniel Nichter
-
Merge find_my_cnf_file-bug-1070916.
- 430. By Daniel Nichter
-
Remove $Sandbox:
:Percona: :Toolkit: :VERSION and use $Percona: :Toolkit: :VERSION instead since it's the authoritative version. - 431. By Daniel Nichter
-
Add deprecation notice to pt-log-player for PT 2.2.
- 432. By Daniel Nichter
-
Fix t/pt-slave-
delay/auto_ restart. t and use direct call, no backticks. - 433. By Daniel Nichter
-
Don't use literal values for t/pt-heartbeat/
basics. t 'It is being updated' test. Use direct call rather than backticks. - 434. By Daniel Nichter
-
Make pt-archiver --sleep tests more precise and reliable.
- 435. By Daniel Nichter
-
Merge fix-ptc-
slave-binglog- formats- on-5.0. - 436. By Daniel Nichter
-
Merge pt-find-docu-bug-1013407
- 437. By Daniel Nichter
-
Merge fix-995896-
cat-in- daemon - 438. By Brian Fraser
-
Merged fix-821715-
enable- local-infile- in-dsn - 439. By Brian Fraser
-
Merged fix-1052722-
pt-fifo- split-n- minus-1- rows-initially - 440. By Brian Fraser
-
Updated modules in all tools
Daniel Nichter (daniel-nichter) wrote : | # |
Brian Fraser (fraserbn) wrote : | # |
->start_sandbox, ->stop_sandbox and ->port_for are pseudo-vestigial (they are wrappers that aren't needed, but I find ->start_
set_as_slave and start_cluster are there mostly for convenience and brevity.
But since they are all ultimately wrappers around the sandbox/ scripts, sure, it could be done.
- 441. By Daniel Nichter
-
Merge fix-938660-
ptc-chunk- size-limit- 0 - 442. By Daniel Nichter
-
Merge pt-show-
grant-col- privs-bug-866075 - 443. By Brian Fraser
-
Merged fix-1059732-
ptc-hash- functions - 444. By Brian Fraser
-
bin/pt-
table-checksum: Missing word in an error message - 445. By Daniel Nichter
-
Merge fix-1009510-
1039569- ptc-check- table-on- replicas - 446. By Daniel Nichter
-
Merged fix-i26211-
1058285- 821722- implicit- ansi_quotes
Daniel Nichter (daniel-nichter) wrote : | # |
The PXC code in Cxn.pm doesn't seem to belong there. Let's move it and its tests to separate files, i.e. Percona/
- 447. By Brian Fraser
-
Merged fix-1073532-
Mo-Scalar- Util-PP - 448. By Brian Fraser
-
Merged sandbox-
get_dbh_ for-fix - 449. By Brian Fraser
-
Removed the L option from the dsn_opts exported by PerconaTest, as it was useless and breaking tests, and added an L=1 to a leftover pt-archiver --bulk-insert call
- 450. By Daniel Nichter
-
Merge fix-pt-fel-bug-1075773
- 451. By Brian Fraser
-
Merged fix-1062563-
1063912- ptc-pxc- bugs and resolved conflicts, added the missing Percona: :XtraDB: :Cluster file - 452. By Brian Fraser
-
Reverted a few unnecessary changes
- 453. By Brian Fraser
-
Split the Percona:
:XtraDB: :Cluster tests in two files: One that requires a PXC sandbox, and a general tests file
Unmerged revisions
Preview Diff
1 | === modified file 'bin/pt-config-diff' |
2 | --- bin/pt-config-diff 2012-10-31 09:18:34 +0000 |
3 | +++ bin/pt-config-diff 2012-11-08 20:42:20 +0000 |
4 | @@ -1466,6 +1466,7 @@ |
5 | use warnings FATAL => 'all'; |
6 | use English qw(-no_match_vars); |
7 | use Scalar::Util qw(blessed); |
8 | + |
9 | use constant { |
10 | PTDEBUG => $ENV{PTDEBUG} || 0, |
11 | PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0, |
12 | @@ -1572,21 +1573,6 @@ |
13 | return $self->{hostname} || $self->{dsn_name} || 'unknown host'; |
14 | } |
15 | |
16 | -sub is_cluster_node { |
17 | - my ($self) = @_; |
18 | - return $self->{is_cluster_node} if defined $self->{is_cluster_node}; |
19 | - |
20 | - my $sql = "SHOW VARIABLES LIKE 'wsrep_on'"; |
21 | - PTDEBUG && _d($sql); |
22 | - my $row = $self->{dbh}->selectrow_arrayref($sql); |
23 | - PTDEBUG && _d(defined $row ? @$row : 'undef'); |
24 | - $self->{is_cluster_node} = $row && $row->[1] |
25 | - ? ($row->[1] eq 'ON' || $row->[1] eq '1') |
26 | - : 0; |
27 | - |
28 | - return $self->{is_cluster_node}; |
29 | -} |
30 | - |
31 | sub DESTROY { |
32 | my ($self) = @_; |
33 | if ( $self->{dbh} |
34 | |
35 | === modified file 'bin/pt-kill' |
36 | --- bin/pt-kill 2012-11-07 07:12:37 +0000 |
37 | +++ bin/pt-kill 2012-11-08 20:42:20 +0000 |
38 | @@ -2478,16 +2478,20 @@ |
39 | $Data::Dumper::Sortkeys = 1; |
40 | $Data::Dumper::Quotekeys = 0; |
41 | |
42 | +local $EVAL_ERROR; |
43 | +eval { |
44 | + require Quoter; |
45 | +}; |
46 | + |
47 | sub new { |
48 | my ( $class, %args ) = @_; |
49 | - my @required_args = qw(Quoter); |
50 | - foreach my $arg ( @required_args ) { |
51 | - die "I need a $arg argument" unless $args{$arg}; |
52 | - } |
53 | my $self = { %args }; |
54 | + $self->{Quoter} ||= Quoter->new(); |
55 | return bless $self, $class; |
56 | } |
57 | |
58 | +sub Quoter { shift->{Quoter} } |
59 | + |
60 | sub get_create_table { |
61 | my ( $self, $dbh, $db, $tbl ) = @_; |
62 | die "I need a dbh parameter" unless $dbh; |
63 | @@ -4711,6 +4715,7 @@ |
64 | use warnings FATAL => 'all'; |
65 | use English qw(-no_match_vars); |
66 | use Scalar::Util qw(blessed); |
67 | + |
68 | use constant { |
69 | PTDEBUG => $ENV{PTDEBUG} || 0, |
70 | PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0, |
71 | @@ -4817,21 +4822,6 @@ |
72 | return $self->{hostname} || $self->{dsn_name} || 'unknown host'; |
73 | } |
74 | |
75 | -sub is_cluster_node { |
76 | - my ($self) = @_; |
77 | - return $self->{is_cluster_node} if defined $self->{is_cluster_node}; |
78 | - |
79 | - my $sql = "SHOW VARIABLES LIKE 'wsrep_on'"; |
80 | - PTDEBUG && _d($sql); |
81 | - my $row = $self->{dbh}->selectrow_arrayref($sql); |
82 | - PTDEBUG && _d(defined $row ? @$row : 'undef'); |
83 | - $self->{is_cluster_node} = $row && $row->[1] |
84 | - ? ($row->[1] eq 'ON' || $row->[1] eq '1') |
85 | - : 0; |
86 | - |
87 | - return $self->{is_cluster_node}; |
88 | -} |
89 | - |
90 | sub DESTROY { |
91 | my ($self) = @_; |
92 | if ( $self->{dbh} |
93 | |
94 | === modified file 'bin/pt-online-schema-change' |
95 | --- bin/pt-online-schema-change 2012-11-07 07:12:37 +0000 |
96 | +++ bin/pt-online-schema-change 2012-11-08 20:42:20 +0000 |
97 | @@ -2712,16 +2712,20 @@ |
98 | $Data::Dumper::Sortkeys = 1; |
99 | $Data::Dumper::Quotekeys = 0; |
100 | |
101 | +local $EVAL_ERROR; |
102 | +eval { |
103 | + require Quoter; |
104 | +}; |
105 | + |
106 | sub new { |
107 | my ( $class, %args ) = @_; |
108 | - my @required_args = qw(Quoter); |
109 | - foreach my $arg ( @required_args ) { |
110 | - die "I need a $arg argument" unless $args{$arg}; |
111 | - } |
112 | my $self = { %args }; |
113 | + $self->{Quoter} ||= Quoter->new(); |
114 | return bless $self, $class; |
115 | } |
116 | |
117 | +sub Quoter { shift->{Quoter} } |
118 | + |
119 | sub get_create_table { |
120 | my ( $self, $dbh, $db, $tbl ) = @_; |
121 | die "I need a dbh parameter" unless $dbh; |
122 | @@ -3341,6 +3345,7 @@ |
123 | use warnings FATAL => 'all'; |
124 | use English qw(-no_match_vars); |
125 | use Scalar::Util qw(blessed); |
126 | + |
127 | use constant { |
128 | PTDEBUG => $ENV{PTDEBUG} || 0, |
129 | PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0, |
130 | @@ -3447,21 +3452,6 @@ |
131 | return $self->{hostname} || $self->{dsn_name} || 'unknown host'; |
132 | } |
133 | |
134 | -sub is_cluster_node { |
135 | - my ($self) = @_; |
136 | - return $self->{is_cluster_node} if defined $self->{is_cluster_node}; |
137 | - |
138 | - my $sql = "SHOW VARIABLES LIKE 'wsrep_on'"; |
139 | - PTDEBUG && _d($sql); |
140 | - my $row = $self->{dbh}->selectrow_arrayref($sql); |
141 | - PTDEBUG && _d(defined $row ? @$row : 'undef'); |
142 | - $self->{is_cluster_node} = $row && $row->[1] |
143 | - ? ($row->[1] eq 'ON' || $row->[1] eq '1') |
144 | - : 0; |
145 | - |
146 | - return $self->{is_cluster_node}; |
147 | -} |
148 | - |
149 | sub DESTROY { |
150 | my ($self) = @_; |
151 | if ( $self->{dbh} |
152 | |
153 | === modified file 'bin/pt-table-checksum' |
154 | --- bin/pt-table-checksum 2012-11-07 07:12:37 +0000 |
155 | +++ bin/pt-table-checksum 2012-11-08 20:42:20 +0000 |
156 | @@ -21,6 +21,7 @@ |
157 | OptionParser |
158 | Mo |
159 | Cxn |
160 | + Percona::XtraDB::Cluster |
161 | Quoter |
162 | VersionParser |
163 | TableParser |
164 | @@ -3211,6 +3212,7 @@ |
165 | use warnings FATAL => 'all'; |
166 | use English qw(-no_match_vars); |
167 | use Scalar::Util qw(blessed); |
168 | + |
169 | use constant { |
170 | PTDEBUG => $ENV{PTDEBUG} || 0, |
171 | PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0, |
172 | @@ -3317,21 +3319,6 @@ |
173 | return $self->{hostname} || $self->{dsn_name} || 'unknown host'; |
174 | } |
175 | |
176 | -sub is_cluster_node { |
177 | - my ($self) = @_; |
178 | - return $self->{is_cluster_node} if defined $self->{is_cluster_node}; |
179 | - |
180 | - my $sql = "SHOW VARIABLES LIKE 'wsrep_on'"; |
181 | - PTDEBUG && _d($sql); |
182 | - my $row = $self->{dbh}->selectrow_arrayref($sql); |
183 | - PTDEBUG && _d(defined $row ? @$row : 'undef'); |
184 | - $self->{is_cluster_node} = $row && $row->[1] |
185 | - ? ($row->[1] eq 'ON' || $row->[1] eq '1') |
186 | - : 0; |
187 | - |
188 | - return $self->{is_cluster_node}; |
189 | -} |
190 | - |
191 | sub DESTROY { |
192 | my ($self) = @_; |
193 | if ( $self->{dbh} |
194 | @@ -3358,6 +3345,104 @@ |
195 | # ########################################################################### |
196 | |
197 | # ########################################################################### |
198 | +# Percona::XtraDB::Cluster package |
199 | +# This package is a copy without comments from the original. The original |
200 | +# with comments and its test file can be found in the Bazaar repository at, |
201 | +# lib/Percona/XtraDB/Cluster.pm |
202 | +# t/lib/Percona/XtraDB/Cluster.t |
203 | +# See https://launchpad.net/percona-toolkit for more information. |
204 | +# ########################################################################### |
205 | +{ |
206 | + |
207 | +package Percona::XtraDB::Cluster; |
208 | +use Mo; |
209 | +use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
210 | + |
211 | +sub is_cluster_node { |
212 | + my ($self, $cxn) = @_; |
213 | + return $self->{is_cluster_node}->{$cxn} if defined $self->{is_cluster_node}->{$cxn}; |
214 | + |
215 | + my $sql = "SHOW VARIABLES LIKE 'wsrep_on'"; |
216 | + PTDEBUG && _d($sql); |
217 | + my $row = $cxn->dbh->selectrow_arrayref($sql); |
218 | + PTDEBUG && _d(defined $row ? @$row : 'undef'); |
219 | + $self->{is_cluster_node}->{$cxn} = $row && $row->[1] |
220 | + ? ($row->[1] eq 'ON' || $row->[1] eq '1') |
221 | + : 0; |
222 | + |
223 | + return $self->{is_cluster_node}->{$cxn}; |
224 | +} |
225 | + |
226 | +sub same_cluster { |
227 | + my ($self, $cxn1, $cxn2) = @_; |
228 | + return unless $self->is_cluster_node($cxn1) && $self->is_cluster_node($cxn2); |
229 | + return if $self->is_master_of($cxn1, $cxn2) || $self->is_master_of($cxn2, $cxn1); |
230 | + |
231 | + my $sql = q{SHOW VARIABLES LIKE 'wsrep_cluster_name'}; |
232 | + PTDEBUG && _d($sql); |
233 | + my (undef, $row) = $cxn1->dbh->selectrow_array($sql); |
234 | + my (undef, $cxn2_row) = $cxn2->dbh->selectrow_array($sql); |
235 | + |
236 | + return unless $row eq $cxn2_row; |
237 | + |
238 | + $sql = q{SHOW VARIABLES LIKE 'wsrep_cluster_address'}; |
239 | + PTDEBUG && _d($sql); |
240 | + my (undef, $addr) = $cxn1->dbh->selectrow_array($sql); |
241 | + my (undef, $cxn2_addr) = $cxn2->dbh->selectrow_array($sql); |
242 | + |
243 | + return if $addr eq 'gcomm://' && $cxn2_addr eq 'gcomm://'; |
244 | + |
245 | + if ( $addr eq 'gcomm://' ) { |
246 | + $addr = $self->_find_full_gcomm_addr($cxn1->dbh); |
247 | + } |
248 | + elsif ( $cxn2_addr eq 'gcomm://' ) { |
249 | + $cxn2_addr = $self->_find_full_gcomm_addr($cxn2->dbh); |
250 | + } |
251 | + |
252 | + return 1 if lc($addr) eq lc($cxn2_addr); |
253 | + |
254 | + return 1; |
255 | +} |
256 | + |
257 | +sub is_master_of { |
258 | + my ($self, $cxn1, $cxn2) = @_; |
259 | + |
260 | + my $cxn2_dbh = $cxn2->dbh; |
261 | + my $sql = q{SHOW SLAVE STATUS}; |
262 | + PTDEBUG && _d($sql); |
263 | + local $cxn2_dbh->{FetchHashKeyName} = 'NAME_lc'; |
264 | + my $slave_status = $cxn2_dbh->selectrow_hashref($sql); |
265 | + return unless ref($slave_status) eq 'HASH'; |
266 | + |
267 | + my $port = $cxn1->dsn->{P}; |
268 | + return unless $slave_status->{master_port} eq $port; |
269 | + return 1 if $cxn1->dsn->{h} eq $slave_status->{master_host}; |
270 | + |
271 | + my $host = scalar gethostbyname($cxn1->dsn->{h}); |
272 | + my $master_host = scalar gethostbyname($slave_status->{master_host}); |
273 | + return 1 if $master_host eq $host; |
274 | + return; |
275 | +} |
276 | + |
277 | +sub _find_full_gcomm_addr { |
278 | + my ($self, $dbh) = @_; |
279 | + |
280 | + my $sql = q{SHOW VARIABLES LIKE 'wsrep_provider_options'}; |
281 | + PTDEBUG && _d($sql); |
282 | + my (undef, $provider_opts) = $dbh->selectrow_array($sql); |
283 | + my ($prov_addr) = $provider_opts =~ m{\Qgmcast.listen_addr\E\s*=\s*tcp://([^:]+:[0-9]+)\s*;}i; |
284 | + my $full_gcomm = "gcomm://$prov_addr"; |
285 | + PTDEBUG && _d("gcomm address: ", $full_gcomm); |
286 | + return $full_gcomm; |
287 | +} |
288 | + |
289 | +1; |
290 | +} |
291 | +# ########################################################################### |
292 | +# End Percona::XtraDB::Cluster package |
293 | +# ########################################################################### |
294 | + |
295 | +# ########################################################################### |
296 | # Quoter package |
297 | # This package is a copy without comments from the original. The original |
298 | # with comments and its test file can be found in the Bazaar repository at, |
299 | @@ -5281,7 +5366,7 @@ |
300 | } |
301 | my ($dbh) = @args{@required_args}; |
302 | my $o = $self->{OptionParser}; |
303 | - my @funcs = qw(CRC32 FNV1A_64 FNV_64 MD5 SHA1); |
304 | + my @funcs = qw(CRC32 FNV1A_64 FNV_64 MURMUR_HASH MD5 SHA1); |
305 | |
306 | if ( my $func = $o->get('function') ) { |
307 | unshift @funcs, $func; |
308 | @@ -5302,7 +5387,7 @@ |
309 | PTDEBUG && _d('Chosen hash func:', $result); |
310 | return $func; |
311 | } |
312 | - die $error || 'No hash functions (CRC32, MD5, etc.) are available'; |
313 | + die($error || 'No hash functions (CRC32, MD5, etc.) are available'); |
314 | } |
315 | |
316 | sub _get_crc_width { |
317 | @@ -8535,6 +8620,9 @@ |
318 | $have_time = sub { return 1; }; |
319 | } |
320 | |
321 | + # PXC helper class |
322 | + my $cluster = Percona::XtraDB::Cluster->new(); |
323 | + |
324 | # ######################################################################## |
325 | # If this is not a dry run (--explain was not specified), then we're |
326 | # going to checksum the tables, so do the necessary preparations and |
327 | @@ -8604,12 +8692,42 @@ |
328 | die $err if $err; |
329 | } |
330 | |
331 | - if ( $master_cxn->is_cluster_node() && !@$slaves ) { |
332 | - die $master_cxn->name() . " is a cluster node but no other nodes " |
333 | - . "or regular replicas were found. Use --recursion-method=dsn " |
334 | - . "to specify the other nodes in the cluster.\n"; |
335 | - } |
336 | - |
337 | + if ( $cluster->is_cluster_node($master_cxn) ) { |
338 | + if ( !@$slaves ) { |
339 | + die $master_cxn->name() . " is a cluster node but no other nodes " |
340 | + . "or regular replicas were found. Use --recursion-method=dsn " |
341 | + . "to specify the other nodes in the cluster.\n"; |
342 | + } |
343 | + else { |
344 | + my $err = ''; |
345 | + for my $slave (@$slaves) { |
346 | + if ( $cluster->is_cluster_node($slave) |
347 | + && !$cluster->same_cluster($slave, $master_cxn) ) { |
348 | + $err .= $slave->name() . " is a cluster node, but doesn't " |
349 | + . "belong to the same cluster as " . $master_cxn->name() |
350 | + . ". This is not currently supported; You can try " |
351 | + . "using --recursion-method=dsn to specify all nodes " |
352 | + . "in the slave cluster.\n" |
353 | + } |
354 | + } |
355 | + warn $err if $err; |
356 | + } |
357 | + } |
358 | + elsif ( @$slaves ) { |
359 | + my $err = ''; |
360 | + for my $slave (@$slaves) { |
361 | + if ( $cluster->is_cluster_node($slave) ) { |
362 | + $err .= $slave->name() . " is a cluster node, but " |
363 | + . $master_cxn->name() . " is not. This is not currently " |
364 | + . "supported; You can try to specify " |
365 | + . "all nodes in the cluster with " |
366 | + . "--recursion-method=dsn if you want them checksummed.\n" |
367 | + } |
368 | + } |
369 | + warn $err if $err; |
370 | + } |
371 | + |
372 | + |
373 | if ( $o->get('check-slave-lag') ) { |
374 | PTDEBUG && _d('Will use --check-slave-lag to check for slave lag'); |
375 | my $cxn = $make_cxn->( |
376 | @@ -8629,7 +8747,8 @@ |
377 | # to appear should be sufficient. |
378 | @$slave_lag_cxns = grep { |
379 | my $slave_cxn = $_; |
380 | - if ( $slave_cxn->is_cluster_node() ) { |
381 | + if ( $cluster->is_cluster_node($slave_cxn) |
382 | + && $cluster->same_cluster($master_cxn, $slave_cxn) ) { |
383 | warn "Not checking replica lag on " . $slave_cxn->name() |
384 | . " because it is a cluster node.\n"; |
385 | 0; |
386 | @@ -8844,7 +8963,7 @@ |
387 | . "(db, tbl, chunk, chunk_index," |
388 | . " lower_boundary, upper_boundary, this_cnt, this_crc) " |
389 | . "SELECT" |
390 | - . ($master_cxn->is_cluster_node() ? ' /*!99997*/' : '') |
391 | + . ($cluster->is_cluster_node($master_cxn) ? ' /*!99997*/' : '') |
392 | . " ?, ?, ?, ?, ?, ?,"; |
393 | my $past_cols = " COUNT(*), '0'"; |
394 | |
395 | |
396 | === modified file 'lib/Cxn.pm' |
397 | --- lib/Cxn.pm 2012-10-08 18:53:54 +0000 |
398 | +++ lib/Cxn.pm 2012-11-08 20:42:20 +0000 |
399 | @@ -36,6 +36,7 @@ |
400 | use warnings FATAL => 'all'; |
401 | use English qw(-no_match_vars); |
402 | use Scalar::Util qw(blessed); |
403 | + |
404 | use constant { |
405 | PTDEBUG => $ENV{PTDEBUG} || 0, |
406 | # Hostnames make testing less accurate. Tests need to see |
407 | @@ -195,21 +196,6 @@ |
408 | return $self->{hostname} || $self->{dsn_name} || 'unknown host'; |
409 | } |
410 | |
411 | -sub is_cluster_node { |
412 | - my ($self) = @_; |
413 | - return $self->{is_cluster_node} if defined $self->{is_cluster_node}; |
414 | - |
415 | - my $sql = "SHOW VARIABLES LIKE 'wsrep_on'"; |
416 | - PTDEBUG && _d($sql); |
417 | - my $row = $self->{dbh}->selectrow_arrayref($sql); |
418 | - PTDEBUG && _d(defined $row ? @$row : 'undef'); |
419 | - $self->{is_cluster_node} = $row && $row->[1] |
420 | - ? ($row->[1] eq 'ON' || $row->[1] eq '1') |
421 | - : 0; |
422 | - |
423 | - return $self->{is_cluster_node}; |
424 | -} |
425 | - |
426 | sub DESTROY { |
427 | my ($self) = @_; |
428 | if ( $self->{dbh} |
429 | |
430 | === added directory 'lib/Percona/XtraDB' |
431 | === added file 'lib/Percona/XtraDB/Cluster.pm' |
432 | --- lib/Percona/XtraDB/Cluster.pm 1970-01-01 00:00:00 +0000 |
433 | +++ lib/Percona/XtraDB/Cluster.pm 2012-11-08 20:42:20 +0000 |
434 | @@ -0,0 +1,123 @@ |
435 | +# This program is copyright 2011 Percona Inc. |
436 | +# Feedback and improvements are welcome. |
437 | +# |
438 | +# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED |
439 | +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF |
440 | +# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
441 | +# |
442 | +# This program is free software; you can redistribute it and/or modify it under |
443 | +# the terms of the GNU General Public License as published by the Free Software |
444 | +# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar |
445 | +# systems, you can issue `man perlgpl' or `man perlartistic' to read these |
446 | +# licenses. |
447 | +# |
448 | +# You should have received a copy of the GNU General Public License along with |
449 | +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
450 | +# Place, Suite 330, Boston, MA 02111-1307 USA. |
451 | +# ########################################################################### |
452 | +# Percona::XtraDB::Cluster package |
453 | +# ########################################################################### |
454 | +{ |
455 | +# Package: Percona::XtraDB::Cluster |
456 | +# Percona::XtraDB::Cluster has helper methods to deal with Percona XtraDB Cluster |
457 | +# based servers |
458 | + |
459 | +package Percona::XtraDB::Cluster; |
460 | +use Mo; |
461 | +use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
462 | + |
463 | +sub is_cluster_node { |
464 | + my ($self, $cxn) = @_; |
465 | + return $self->{is_cluster_node}->{$cxn} if defined $self->{is_cluster_node}->{$cxn}; |
466 | + |
467 | + my $sql = "SHOW VARIABLES LIKE 'wsrep_on'"; |
468 | + PTDEBUG && _d($sql); |
469 | + my $row = $cxn->dbh->selectrow_arrayref($sql); |
470 | + PTDEBUG && _d(defined $row ? @$row : 'undef'); |
471 | + $self->{is_cluster_node}->{$cxn} = $row && $row->[1] |
472 | + ? ($row->[1] eq 'ON' || $row->[1] eq '1') |
473 | + : 0; |
474 | + |
475 | + return $self->{is_cluster_node}->{$cxn}; |
476 | +} |
477 | + |
478 | +sub same_cluster { |
479 | + my ($self, $cxn1, $cxn2) = @_; |
480 | + return unless $self->is_cluster_node($cxn1) && $self->is_cluster_node($cxn2); |
481 | + return if $self->is_master_of($cxn1, $cxn2) || $self->is_master_of($cxn2, $cxn1); |
482 | + |
483 | + my $sql = q{SHOW VARIABLES LIKE 'wsrep_cluster_name'}; |
484 | + PTDEBUG && _d($sql); |
485 | + my (undef, $row) = $cxn1->dbh->selectrow_array($sql); |
486 | + my (undef, $cxn2_row) = $cxn2->dbh->selectrow_array($sql); |
487 | + |
488 | + return unless $row eq $cxn2_row; |
489 | + |
490 | + # Now it becomes tricky. Ostensibly clusters shouldn't have the |
491 | + # same name, but tell that to the world. |
492 | + $sql = q{SHOW VARIABLES LIKE 'wsrep_cluster_address'}; |
493 | + PTDEBUG && _d($sql); |
494 | + my (undef, $addr) = $cxn1->dbh->selectrow_array($sql); |
495 | + my (undef, $cxn2_addr) = $cxn2->dbh->selectrow_array($sql); |
496 | + |
497 | + # If they both have gcomm://, then they are both the first |
498 | + # node of a cluster, so they can't be in the same one. |
499 | + return if $addr eq 'gcomm://' && $cxn2_addr eq 'gcomm://'; |
500 | + |
501 | + if ( $addr eq 'gcomm://' ) { |
502 | + $addr = $self->_find_full_gcomm_addr($cxn1->dbh); |
503 | + } |
504 | + elsif ( $cxn2_addr eq 'gcomm://' ) { |
505 | + $cxn2_addr = $self->_find_full_gcomm_addr($cxn2->dbh); |
506 | + } |
507 | + |
508 | + # Meanwhile, if they have the same address, then |
509 | + # they are definitely part of the same cluster |
510 | + return 1 if lc($addr) eq lc($cxn2_addr); |
511 | + |
512 | + # However, this still leaves us with the issue that |
513 | + # the cluster addresses could look like this: |
514 | + # node1 -> node2, node2 -> node1, |
515 | + # or |
516 | + # node1 -> node2 addr, |
517 | + # node2 -> node3 addr, |
518 | + # node3 -> node1 addr, |
519 | + # TODO No clue what to do here |
520 | + return 1; |
521 | +} |
522 | + |
523 | +sub is_master_of { |
524 | + my ($self, $cxn1, $cxn2) = @_; |
525 | + |
526 | + my $cxn2_dbh = $cxn2->dbh; |
527 | + my $sql = q{SHOW SLAVE STATUS}; |
528 | + PTDEBUG && _d($sql); |
529 | + local $cxn2_dbh->{FetchHashKeyName} = 'NAME_lc'; |
530 | + my $slave_status = $cxn2_dbh->selectrow_hashref($sql); |
531 | + return unless ref($slave_status) eq 'HASH'; |
532 | + |
533 | + my $port = $cxn1->dsn->{P}; |
534 | + return unless $slave_status->{master_port} eq $port; |
535 | + return 1 if $cxn1->dsn->{h} eq $slave_status->{master_host}; |
536 | + |
537 | + # They might be the same but in different format |
538 | + my $host = scalar gethostbyname($cxn1->dsn->{h}); |
539 | + my $master_host = scalar gethostbyname($slave_status->{master_host}); |
540 | + return 1 if $master_host eq $host; |
541 | + return; |
542 | +} |
543 | + |
544 | +sub _find_full_gcomm_addr { |
545 | + my ($self, $dbh) = @_; |
546 | + |
547 | + my $sql = q{SHOW VARIABLES LIKE 'wsrep_provider_options'}; |
548 | + PTDEBUG && _d($sql); |
549 | + my (undef, $provider_opts) = $dbh->selectrow_array($sql); |
550 | + my ($prov_addr) = $provider_opts =~ m{\Qgmcast.listen_addr\E\s*=\s*tcp://([^:]+:[0-9]+)\s*;}i; |
551 | + my $full_gcomm = "gcomm://$prov_addr"; |
552 | + PTDEBUG && _d("gcomm address: ", $full_gcomm); |
553 | + return $full_gcomm; |
554 | +} |
555 | + |
556 | +1; |
557 | +} |
558 | |
559 | === modified file 'lib/Sandbox.pm' |
560 | --- lib/Sandbox.pm 2012-11-07 07:13:50 +0000 |
561 | +++ lib/Sandbox.pm 2012-11-08 20:42:20 +0000 |
562 | @@ -39,6 +39,8 @@ |
563 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
564 | use constant PTDEVDEBUG => $ENV{PTDEVDEBUG} || 0; |
565 | |
566 | +use IO::Socket::INET; |
567 | + |
568 | my $trunk = $ENV{PERCONA_TOOLKIT_BRANCH}; |
569 | |
570 | my %port_for = ( |
571 | @@ -74,7 +76,7 @@ |
572 | return if !defined $cmd || !$cmd; |
573 | my $use = $self->_use_for($server) . " $cmd"; |
574 | PTDEBUG && _d('"Executing', $use, 'on', $server); |
575 | - my $out = `$use 2>&1`; |
576 | + my $out = `$use`; |
577 | if ( $? >> 8 ) { |
578 | die "Failed to execute $cmd on $server: $out"; |
579 | } |
580 | @@ -130,7 +132,7 @@ |
581 | } |
582 | |
583 | sub load_file { |
584 | - my ( $self, $server, $file, $use_db ) = @_; |
585 | + my ( $self, $server, $file, $use_db, %args ) = @_; |
586 | _check_server($server); |
587 | $file = "$trunk/$file"; |
588 | if ( !-f $file ) { |
589 | @@ -141,11 +143,11 @@ |
590 | |
591 | my $use = $self->_use_for($server) . " $d < $file"; |
592 | PTDEBUG && _d('Loading', $file, 'on', $server, ':', $use); |
593 | - my $out = `$use 2>&1`; |
594 | + my $out = `$use`; |
595 | if ( $? >> 8 ) { |
596 | die "Failed to execute $file on $server: $out"; |
597 | } |
598 | - $self->wait_for_slaves(); |
599 | + $self->wait_for_slaves() unless $args{no_wait}; |
600 | } |
601 | |
602 | sub _use_for { |
603 | @@ -420,6 +422,85 @@ |
604 | return ($output || '') =~ /1/; |
605 | } |
606 | |
607 | +sub set_as_slave { |
608 | + my ($self, $server, $master_server, @extras) = @_; |
609 | + PTDEBUG && _d("Setting $server as slave of $master_server"); |
610 | + my $master_port = $port_for{$master_server}; |
611 | + my $sql = join ", ", qq{change master to master_host='127.0.0.1'}, |
612 | + qq{master_user='msandbox'}, |
613 | + qq{master_password='msandbox'}, |
614 | + qq{master_port=$master_port}, |
615 | + @extras; |
616 | + for my $sql_to_run ($sql, "start slave") { |
617 | + my $out = $self->use($server, qq{-e "$sql_to_run"}); |
618 | + PTDEBUG && _d($out); |
619 | + } |
620 | +} |
621 | + |
622 | +sub start_sandbox { |
623 | + my ($self, $mode, $server, $master_server) = @_; |
624 | + my $port = $port_for{$server}; |
625 | + my $master_port = $master_server ? $port_for{$master_server} : ''; |
626 | + my $out = `$trunk/sandbox/start-sandbox $mode $port $master_port`; |
627 | + die $out if $CHILD_ERROR; |
628 | + return $out; |
629 | +} |
630 | + |
631 | +sub stop_sandbox { |
632 | + my ($self, @sandboxes) = @_; |
633 | + my @ports = @port_for{@sandboxes}; |
634 | + my $out = `$trunk/sandbox/stop-sandbox @ports`; |
635 | + die $out if $CHILD_ERROR; |
636 | + return $out; |
637 | +} |
638 | + |
639 | +sub start_cluster { |
640 | + my ($self, %args) = @_; |
641 | + my $cluster_size = $args{cluster_size} || 3; |
642 | + |
643 | + my $out = ''; |
644 | + |
645 | + my ($node1, @nodes) = map { |
646 | + my $node_name = "node$_"; |
647 | + $node_name = "_$node_name" while exists $port_for{$node_name}; |
648 | + $port_for{$node_name} = $self->_get_unused_port(); |
649 | + $node_name |
650 | + } 1..$cluster_size; |
651 | + |
652 | + local $ENV{CLUSTER_NAME} = $args{cluster_name} if $args{cluster_name}; |
653 | + $self->start_sandbox("cluster", $node1); |
654 | + for my $node ( @nodes ) { |
655 | + $self->start_sandbox("cluster", $node, $node1); |
656 | + } |
657 | + |
658 | + return ($node1, @nodes); |
659 | +} |
660 | + |
661 | +# Lifted from Nginx::Test on CPAN |
662 | +sub _get_unused_port { |
663 | + my $port = 50000 + int (rand() * 5000); |
664 | + |
665 | + while ($port++ < 64000) { |
666 | + my $sock = IO::Socket::INET->new ( |
667 | + Listen => 5, |
668 | + LocalAddr => '127.0.0.1', |
669 | + LocalPort => $port, |
670 | + Proto => 'tcp', |
671 | + ReuseAddr => 1 |
672 | + ) or next; |
673 | + |
674 | + $sock->close; |
675 | + return $port; |
676 | + } |
677 | + |
678 | + die "Cannot find an open port"; |
679 | +} |
680 | + |
681 | +sub port_for { |
682 | + my ($self, $server) = @_; |
683 | + return $port_for{$server}; |
684 | +} |
685 | + |
686 | 1; |
687 | } |
688 | # ########################################################################### |
689 | |
690 | === modified file 't/lib/Cxn.t' |
691 | --- t/lib/Cxn.t 2012-07-23 02:37:39 +0000 |
692 | +++ t/lib/Cxn.t 2012-11-08 20:42:20 +0000 |
693 | @@ -9,7 +9,8 @@ |
694 | use strict; |
695 | use warnings FATAL => 'all'; |
696 | use English qw(-no_match_vars); |
697 | -use Test::More tests => 19; |
698 | +use Test::More; |
699 | +use Data::Dumper; |
700 | |
701 | use Sandbox; |
702 | use OptionParser; |
703 | @@ -17,8 +18,7 @@ |
704 | use Quoter; |
705 | use PerconaTest; |
706 | use Cxn; |
707 | - |
708 | -use Data::Dumper; |
709 | +use VersionParser; |
710 | |
711 | my $q = new Quoter(); |
712 | my $dp = new DSNParser(opts=>$dsn_opts); |
713 | @@ -248,12 +248,10 @@ |
714 | "Default cxn inherits default connection options" |
715 | ); |
716 | |
717 | -@ARGV = (); |
718 | -$o->get_opts(); |
719 | - |
720 | # ############################################################################# |
721 | # Done. |
722 | # ############################################################################# |
723 | $master_dbh->disconnect() if $master_dbh; |
724 | ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
725 | +done_testing; |
726 | exit; |
727 | |
728 | === added directory 't/lib/Percona' |
729 | === added directory 't/lib/Percona/XtraDB' |
730 | === added file 't/lib/Percona/XtraDB/Cluster.t' |
731 | --- t/lib/Percona/XtraDB/Cluster.t 1970-01-01 00:00:00 +0000 |
732 | +++ t/lib/Percona/XtraDB/Cluster.t 2012-11-08 20:42:20 +0000 |
733 | @@ -0,0 +1,216 @@ |
734 | +#!/usr/bin/perl |
735 | + |
736 | +BEGIN { |
737 | + die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n" |
738 | + unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH}; |
739 | + unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib"; |
740 | +}; |
741 | + |
742 | +use strict; |
743 | +use warnings FATAL => 'all'; |
744 | +use English qw(-no_match_vars); |
745 | +use Test::More; |
746 | +use Data::Dumper; |
747 | + |
748 | +# Hostnames make testing less accurate. Tests need to see |
749 | +# that such-and-such happened on specific slave hosts, but |
750 | +# the sandbox servers are all on one host so all slaves have |
751 | +# the same hostname. |
752 | +$ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} = 1; |
753 | + |
754 | +use Sandbox; |
755 | +use OptionParser; |
756 | +use DSNParser; |
757 | +use Quoter; |
758 | +use PerconaTest; |
759 | +use Cxn; |
760 | +use VersionParser; |
761 | + |
762 | +use Percona::XtraDB::Cluster; |
763 | + |
764 | +my $q = new Quoter(); |
765 | +my $dp = new DSNParser(opts=>$dsn_opts); |
766 | +my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); |
767 | +my $master_dbh = $sb->get_dbh_for('master'); |
768 | + |
769 | +my $cluster = Percona::XtraDB::Cluster->new(); |
770 | + |
771 | +if ( !$master_dbh ) { |
772 | + plan skip_all => 'Cannot connect to sandbox master'; |
773 | +} |
774 | + |
775 | +my $o = new OptionParser(description => 'Cxn'); |
776 | +$o->get_specs("$trunk/bin/pt-table-checksum"); |
777 | +$o->get_opts(); |
778 | +$dp->prop('set-vars', $o->get('set-vars')); |
779 | + |
780 | +sub make_cxn { |
781 | + my (%args) = @_; |
782 | + $o->get_opts(); |
783 | + return new Cxn( |
784 | + OptionParser => $o, |
785 | + DSNParser => $dp, |
786 | + %args, |
787 | + ); |
788 | +} |
789 | + |
790 | +local @ARGV = (); |
791 | +$o->get_opts(); |
792 | + |
793 | +diag("Starting master1"); |
794 | +$sb->start_sandbox("master", "master1"); |
795 | + |
796 | +my ($master_cxn, $slave1_cxn, $master1_cxn) |
797 | + = map { |
798 | + my $cxn = make_cxn( dsn_string => $sb->dsn_for($_) ); |
799 | + $cxn->connect(); |
800 | + $cxn; |
801 | + } qw( master slave1 master1 ); |
802 | + |
803 | +for my $cxn ( $master_cxn, $slave1_cxn, $master1_cxn ) { |
804 | + ok( |
805 | + !$cluster->is_cluster_node($cxn), |
806 | + "is_cluster_node works correctly for non-nodes " . $cxn->name |
807 | + ); |
808 | +} |
809 | + |
810 | +ok($cluster->is_master_of($master_cxn, $slave1_cxn), "is_master_of(master, slave1) is true"); |
811 | +ok(!$cluster->is_master_of($slave1_cxn, $master_cxn), "is_master_of(slave1, master) is false"); |
812 | + |
813 | +my $db_flavor = VersionParser->new($master_dbh)->flavor(); |
814 | +SKIP: { |
815 | + skip "PXC-only test", 17 |
816 | + unless $db_flavor =~ /XtraDB Cluster/; |
817 | + |
818 | + diag("Starting a 1-node PXC"); |
819 | + my ($node) = $sb->start_cluster(cluster_size => 1); |
820 | + |
821 | + my $cxn1 = make_cxn( dsn_string => $sb->dsn_for($node) ); |
822 | + $cxn1->connect(); |
823 | + ok( |
824 | + $cluster->is_cluster_node($cxn1), |
825 | + "is_cluster_node works correctly for cluster nodes" |
826 | + ); |
827 | + |
828 | + ok( |
829 | + !$cluster->is_master_of($master1_cxn, $cxn1), |
830 | + "->is_master_of works correctly for a server unrelated to a cluster" |
831 | + ); |
832 | + |
833 | + diag("Setting node as a slave of master1"); |
834 | + $sb->set_as_slave($node, "master1"); |
835 | + ok( |
836 | + $cluster->is_master_of($master1_cxn, $cxn1), |
837 | + "->is_master_of works correctly for master -> cluster" |
838 | + ); |
839 | + ok( |
840 | + !$cluster->is_master_of($cxn1, $master1_cxn), |
841 | + "...and the inverse returns the expected result" |
842 | + ); |
843 | + ok( |
844 | + !$cluster->same_cluster($master1_cxn, $cxn1), |
845 | + "->same_cluster works for master -> cluster" |
846 | + ); |
847 | + diag("Restarting the cluster"); |
848 | + diag($sb->stop_sandbox($node)); |
849 | + ($node) = $sb->start_cluster(cluster_size => 1); |
850 | + $cxn1 = make_cxn( dsn_string => $sb->dsn_for($node) ); |
851 | + $cxn1->connect(); |
852 | + |
853 | + diag("Setting master1 as a slave of the node"); |
854 | + $sb->set_as_slave("master1", $node); |
855 | + ok( |
856 | + $cluster->is_master_of($cxn1, $master1_cxn), |
857 | + "->is_master_of works correctly for cluster -> master" |
858 | + ); |
859 | + ok( |
860 | + !$cluster->is_master_of($master1_cxn, $cxn1), |
861 | + "...and the inverse returns the expected result" |
862 | + ); |
863 | + |
864 | + ok( |
865 | + !$cluster->same_cluster($cxn1, $master1_cxn), |
866 | + "->same_cluster works for cluster -> master" |
867 | + ); |
868 | + |
869 | + diag("Starting a 2-node PXC"); |
870 | + my ($node2, $node3) = $sb->start_cluster(cluster_size => 2); |
871 | + |
872 | + my $cxn2 = make_cxn( dsn_string => $sb->dsn_for($node2) ); |
873 | + $cxn2->connect(); |
874 | + my $cxn3 = make_cxn( dsn_string => $sb->dsn_for($node3) ); |
875 | + $cxn3->connect(); |
876 | + ok( |
877 | + $cluster->is_cluster_node($cxn2), |
878 | + "is_cluster_node correctly finds that this node is part of a cluster" |
879 | + ); |
880 | + |
881 | + ok( |
882 | + !$cluster->same_cluster($cxn1, $cxn2), |
883 | + "and same_cluster correctly finds that they don't belong to the same cluster, even when they have the same cluster name" |
884 | + ); |
885 | + |
886 | + ok( |
887 | + $cluster->same_cluster($cxn2, $cxn3), |
888 | + "...but does find that they are in the same cluster, even if one is node1" |
889 | + ); |
890 | + |
891 | + TODO: { |
892 | + local $::TODO = "Should detected that (cluster1.node1) (cluster2.node2) come from different clusters, but doesn't"; |
893 | + ok( |
894 | + !$cluster->same_cluster($cxn1, $cxn3), |
895 | + "...same_cluster works correctly when they have the same cluster names" |
896 | + ); |
897 | + } |
898 | + |
899 | + diag("Making the second cluster a slave of the first"); |
900 | + $sb->set_as_slave($node2, $node); |
901 | + ok($cluster->is_master_of($cxn1, $cxn2), "is_master_of(cluster1, cluster2) works"); |
902 | + |
903 | + ok( |
904 | + !$cluster->same_cluster($cxn1, $cxn2), |
905 | + "...same_cluster works correctly when they are cluster1.node1.master -> cluster2.node1.slave" |
906 | + ); |
907 | + |
908 | + diag($sb->stop_sandbox($node2, $node3)); |
909 | + diag("Starting a 3-node cluster"); |
910 | + my $node4; |
911 | + ($node2, $node3, $node4) |
912 | + = $sb->start_cluster( |
913 | + cluster_size => 3, |
914 | + cluster_name => "pt_cxn_test", |
915 | + ); |
916 | + $cxn2 = make_cxn( dsn_string => $sb->dsn_for($node2) ); |
917 | + $cxn2->connect(); |
918 | + $cxn3 = make_cxn( dsn_string => $sb->dsn_for($node3) ); |
919 | + $cxn3->connect(); |
920 | + my $cxn4 = make_cxn( dsn_string => $sb->dsn_for($node4) ); |
921 | + $cxn4->connect(); |
922 | + |
923 | + ok( |
924 | + !$cluster->same_cluster($cxn1, $cxn2), |
925 | + "...same_cluster works correctly when they have different cluster names & the are both gcomm" |
926 | + ); |
927 | + |
928 | + ok( |
929 | + !$cluster->same_cluster($cxn1, $cxn3), |
930 | + "same_cluster detects that (cluster1.node1) (cluster2.node2) come from different clusters if they have different cluster_names" |
931 | + ); |
932 | + |
933 | + ok( |
934 | + $cluster->same_cluster($cxn2, $cxn3), |
935 | + "sanity check: but still finds that nodes in the same cluster belong together" |
936 | + ); |
937 | + |
938 | + diag($sb->stop_sandbox($node, $node2, $node3, $node4)); |
939 | +} |
940 | + |
941 | +diag($sb->stop_sandbox("master1")); |
942 | + |
943 | +# ############################################################################# |
944 | +# Done. |
945 | +# ############################################################################# |
946 | +$master_dbh->disconnect() if $master_dbh; |
947 | +ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
948 | +done_testing; |
949 | +exit; |
950 | |
951 | === added file 't/pt-table-checksum/pxc.t' |
952 | --- t/pt-table-checksum/pxc.t 1970-01-01 00:00:00 +0000 |
953 | +++ t/pt-table-checksum/pxc.t 2012-11-08 20:42:20 +0000 |
954 | @@ -0,0 +1,242 @@ |
955 | +#!/usr/bin/env perl |
956 | + |
957 | +BEGIN { |
958 | + die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n" |
959 | + unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH}; |
960 | + unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib"; |
961 | +}; |
962 | + |
963 | +use strict; |
964 | +use warnings FATAL => 'all'; |
965 | +use English qw(-no_match_vars); |
966 | +use Test::More; |
967 | +use Data::Dumper; |
968 | +use File::Spec::Functions; |
969 | + |
970 | +# Hostnames make testing less accurate. Tests need to see |
971 | +# that such-and-such happened on specific slave hosts, but |
972 | +# the sandbox servers are all on one host so all slaves have |
973 | +# the same hostname. |
974 | +$ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} = 1; |
975 | + |
976 | +use PerconaTest; |
977 | +use Sandbox; |
978 | + |
979 | +require "$trunk/bin/pt-table-checksum"; |
980 | +# Do this after requiring ptc, since it uses Mo |
981 | +require VersionParser; |
982 | + |
983 | +my $dp = new DSNParser(opts=>$dsn_opts); |
984 | +my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); |
985 | +my $master_dbh = $sb->get_dbh_for('master'); |
986 | + |
987 | +my $db_flavor = VersionParser->new($master_dbh)->flavor(); |
988 | + |
989 | +if ( !$master_dbh ) { |
990 | + plan skip_all => 'Cannot connect to sandbox master'; |
991 | +} |
992 | +elsif ( $db_flavor !~ /XtraDB Cluster/ ) { |
993 | + plan skip_all => "PXC tests"; |
994 | +} |
995 | + |
996 | +# The sandbox servers run with lock_wait_timeout=3 and it's not dynamic |
997 | +# so we need to specify --lock-wait-timeout=3 else the tool will die. |
998 | +my $master_dsn = 'h=127.1,P=12345,u=msandbox,p=msandbox'; |
999 | +my @args = ($master_dsn, qw(--lock-wait-timeout 3)); |
1000 | +my $output; |
1001 | +my $exit_status; |
1002 | +my $sample = "t/pt-table-checksum/samples/"; |
1003 | + |
1004 | +# ############################################################################# |
1005 | +# pt-table-checksum v2.1.4 doesn't detect diffs on Percona XtraDB Cluster nodes |
1006 | +# https://bugs.launchpad.net/percona-toolkit/+bug/1062563 |
1007 | +# ############################################################################# |
1008 | + |
1009 | +sub make_dbh_differ { |
1010 | + my ($dbh, @vals) = @_; |
1011 | + @vals = (@vals ? @vals : 1); |
1012 | + # Make them differ... |
1013 | + $dbh->do("DROP DATABASE IF EXISTS bug_1062563"); |
1014 | + $dbh->do("CREATE DATABASE bug_1062563"); |
1015 | + $dbh->do("CREATE TABLE bug_1062563.ptc_pxc (i int)"); |
1016 | + |
1017 | + # Now make this node different from the rest |
1018 | + $dbh->do("set sql_log_bin=0"); |
1019 | + $dbh->do("INSERT INTO bug_1062563.ptc_pxc (i) VALUES ($_)") for @vals; |
1020 | + $dbh->do("set sql_log_bin=1"); |
1021 | +} |
1022 | + |
1023 | +diag("Creating a 5-node PXC cluster..."); |
1024 | +my @nodes = $sb->start_cluster(cluster_size => 5); |
1025 | +diag("Nodes: ", Dumper( { map { $_ => $sb->port_for($_) } @nodes } )); |
1026 | + |
1027 | +my $node2 = $nodes[1]; |
1028 | +my $node2_dbh = $sb->get_dbh_for($node2); |
1029 | + |
1030 | +my $node2_slave = "master3"; |
1031 | + |
1032 | +diag("Creating a slave for $node2..."); |
1033 | +{ |
1034 | + local $ENV{BINLOG_FORMAT} = 'ROW'; |
1035 | + diag($sb->start_sandbox("slave", $node2_slave, $node2)); |
1036 | +} |
1037 | +my $node_slave_dbh = $sb->get_dbh_for($node2_slave); |
1038 | + |
1039 | +make_dbh_differ($node2_dbh); |
1040 | + |
1041 | +# And make its slave differ as well |
1042 | +PerconaTest::wait_for_table($sb->get_dbh_for($nodes[-1]), "bug_1062563.ptc_pxc"); |
1043 | +PerconaTest::wait_for_table($node_slave_dbh, "bug_1062563.ptc_pxc"); |
1044 | +$node_slave_dbh->do("INSERT INTO bug_1062563.ptc_pxc (i) VALUES ($_)") for 3, 4; |
1045 | + |
1046 | +my $dsns_table_sql = catfile(qw(t lib samples MasterSlave dsn_table.sql)); |
1047 | +$sb->load_file($node2, $dsns_table_sql, undef, no_wait => 1); |
1048 | +$node2_dbh->do("DELETE FROM dsn_t.dsns"); # Delete 12346 |
1049 | +my $sth = $node2_dbh->prepare("INSERT INTO dsn_t.dsns VALUES (null, null, ?)"); |
1050 | +for my $dsn ( map { $sb->dsn_for($_) } @nodes[0,2..$#nodes], $node2_slave ) { |
1051 | + $sth->execute($dsn); |
1052 | +} |
1053 | + |
1054 | +my $node2_dsn = $sb->dsn_for($node2); |
1055 | +$output = output( |
1056 | + sub { pt_table_checksum::main( |
1057 | + $node2_dsn, qw(--lock-wait-timeout 3), |
1058 | + qw(-d bug_1062563), |
1059 | + '--recursion-method', "dsn=D=dsn_t,t=dsns" |
1060 | + ) }, |
1061 | + stderr => 1, |
1062 | +); |
1063 | + |
1064 | +is( |
1065 | + PerconaTest::count_checksum_results($output, 'diffs'), |
1066 | + 1, |
1067 | + "Bug 1062563: Detects diffs between PXC nodes" |
1068 | +) or diag($output); |
1069 | + |
1070 | +my @cluster_nodes = $output =~ /(because it is a cluster node)/g; |
1071 | +is( |
1072 | + scalar(@cluster_nodes), |
1073 | + 4, |
1074 | + "Skips all the cluster nodes in the dsns table" |
1075 | +) or diag($output); |
1076 | + |
1077 | +# Now try with just the slave |
1078 | + |
1079 | +$node2_dbh->do("DELETE FROM dsn_t.dsns"); |
1080 | +$sth->execute($sb->dsn_for($node2_slave)); |
1081 | + |
1082 | +$output = output( |
1083 | + sub { pt_table_checksum::main( |
1084 | + $node2_dsn, qw(--lock-wait-timeout 3), |
1085 | + qw(--chunk-size 1), |
1086 | + qw(-d bug_1062563), |
1087 | + '--recursion-method', "dsn=D=dsn_t,t=dsns" |
1088 | + ) }, |
1089 | + stderr => 1, |
1090 | +); |
1091 | + |
1092 | +is( |
1093 | + PerconaTest::count_checksum_results($output, 'diffs'), |
1094 | + 1, |
1095 | + "Bug 1062563: Detects diffs on slaves where the master is a PXC node" |
1096 | +) or diag($output); |
1097 | + |
1098 | +$sth->finish(); |
1099 | +diag("Stopping the PXC cluster and the slave..."); |
1100 | +$sb->stop_sandbox($node2_slave, @nodes); |
1101 | + |
1102 | +# Now checking that cluster -> cluster works |
1103 | + |
1104 | +diag("Creating two 3-node clusters..."); |
1105 | +my @cluster1 = $sb->start_cluster(cluster_size => 3, cluster_name => "pt_test_cluster_1"); |
1106 | +my @cluster2 = $sb->start_cluster(cluster_size => 3, cluster_name => "pt_test_cluster_2"); |
1107 | +diag("Cluster 1: ", Dumper( { map { $_ => $sb->port_for($_) } @cluster1 } )); |
1108 | +diag("Cluster 2: ", Dumper( { map { $_ => $sb->port_for($_) } @cluster2 } )); |
1109 | + |
1110 | +$sb->set_as_slave($cluster2[0], $cluster1[0]); |
1111 | + |
1112 | +my $cluster1_dbh = $sb->get_dbh_for($cluster1[0]); |
1113 | +my $cluster2_dbh = $sb->get_dbh_for($cluster2[0]); |
1114 | +make_dbh_differ($cluster1_dbh); |
1115 | + |
1116 | +# And make its slave differ as well |
1117 | +PerconaTest::wait_for_table($sb->get_dbh_for($cluster2[-1]), "bug_1062563.ptc_pxc"); |
1118 | +PerconaTest::wait_for_table($sb->get_dbh_for($cluster1[-1]), "bug_1062563.ptc_pxc"); |
1119 | +PerconaTest::wait_for_table($cluster2_dbh, "bug_1062563.ptc_pxc"); |
1120 | +$cluster2_dbh->do("INSERT INTO bug_1062563.ptc_pxc (i) VALUES ($_)") for 3, 4; |
1121 | + |
1122 | +$dsns_table_sql = catfile(qw(t lib samples MasterSlave dsn_table.sql)); |
1123 | +$sb->load_file($cluster1[0], $dsns_table_sql, undef, no_wait => 1); |
1124 | +$cluster1_dbh->do("DELETE FROM dsn_t.dsns"); # Delete 12346 |
1125 | +$sth = $cluster1_dbh->prepare("INSERT INTO dsn_t.dsns VALUES (null, null, ?)"); |
1126 | +for my $dsn ( map { $sb->dsn_for($_) } @cluster1[1..$#cluster1], $cluster2[0] ) { |
1127 | + $sth->execute($dsn); |
1128 | +} |
1129 | +$sth->finish(); |
1130 | + |
1131 | +my $cluster1_dsn = $sb->dsn_for($cluster1[0]); |
1132 | +$output = output( |
1133 | + sub { pt_table_checksum::main( |
1134 | + $cluster1_dsn, qw(--lock-wait-timeout 3), |
1135 | + qw(-d bug_1062563), |
1136 | + '--recursion-method', "dsn=D=dsn_t,t=dsns" |
1137 | + ) }, |
1138 | + stderr => 1, |
1139 | +); |
1140 | + |
1141 | +is( |
1142 | + PerconaTest::count_checksum_results($output, 'diffs'), |
1143 | + 1, |
1144 | + "Bug 1062563: Detects diffs between PXC nodes when cluster -> cluster" |
1145 | +) or diag($output); |
1146 | + |
1147 | +like( |
1148 | + $output, |
1149 | + qr/is a cluster node, but doesn't belong to the same cluster as/, #' |
1150 | + "Shows a warning when cluster -> cluster" |
1151 | +) or diag($output); |
1152 | + |
1153 | +diag("Starting master1..."); |
1154 | +$sb->start_sandbox("master", "master1"); |
1155 | +diag("Setting it as master of a node in the first cluster"); |
1156 | +$sb->set_as_slave($cluster1[0], "master1"); |
1157 | + |
1158 | +my $master1_dbh = $sb->get_dbh_for("master1"); |
1159 | +make_dbh_differ($master1_dbh, 10..50); |
1160 | + |
1161 | +my $master1_dsn = $sb->dsn_for("master1"); |
1162 | +$output = output( |
1163 | + sub { pt_table_checksum::main( |
1164 | + $master1_dsn, qw(--lock-wait-timeout 3), |
1165 | + qw(-d bug_1062563), |
1166 | + ) }, |
1167 | + stderr => 1, |
1168 | +); |
1169 | + |
1170 | +is( |
1171 | + PerconaTest::count_checksum_results($output, 'diffs'), |
1172 | + 1, |
1173 | + "Bug 1062563: Detects diffs when master -> cluster" |
1174 | +) or diag($output); |
1175 | + |
1176 | +is( |
1177 | + PerconaTest::count_checksum_results($output, 'rows'), |
1178 | + 41, |
1179 | + "Bug 1062563: Correct number of rows for master -> cluster" |
1180 | +) or diag($output); |
1181 | + |
1182 | +like( |
1183 | + $output, |
1184 | + qr/is a cluster node, but .*? is not. This is not currently supported/, |
1185 | + "Shows a warning when master -> cluster" |
1186 | +) or diag($output); |
1187 | + |
1188 | +diag("Stopping both clusters and master1..."); |
1189 | +$sb->stop_sandbox(@cluster1, @cluster2, "master1"); |
1190 | + |
1191 | +# ############################################################################# |
1192 | +# Done. |
1193 | +# ############################################################################# |
1194 | +$sb->wipe_clean($master_dbh); |
1195 | +ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); |
1196 | +done_testing; |
Are the new subs in Sandbox.pm and how Cxn.t works with a cluster vestiges of portable- test-suite, or can we do this stuff now with the sandbox/ scripts?