Merge lp:~percona-toolkit-dev/percona-toolkit/masterslave-redesign-prototype into lp:percona-toolkit/2.1
- masterslave-redesign-prototype
- Merge into 2.1
Status: | Superseded |
---|---|
Proposed branch: | lp:~percona-toolkit-dev/percona-toolkit/masterslave-redesign-prototype |
Merge into: | lp:percona-toolkit/2.1 |
Diff against target: |
8531 lines (+2670/-3965) 17 files modified
bin/pt-archiver (+199/-245) bin/pt-heartbeat (+644/-240) bin/pt-kill (+79/-1197) bin/pt-online-schema-change (+184/-240) bin/pt-query-digest (+87/-750) bin/pt-slave-find (+376/-256) bin/pt-slave-restart (+191/-241) bin/pt-table-checksum (+191/-242) bin/pt-table-sync (+46/-0) bin/pt-upgrade (+15/-5) lib/Cxn.pm (+19/-1) lib/MasterSlave.pm (+103/-296) lib/Mo.pm (+43/-5) lib/Percona/Object.pm (+84/-0) lib/Processlist.pm (+103/-6) t/lib/MasterSlave.t (+79/-234) t/lib/Processlist.t (+227/-7) |
To merge this branch: | bzr merge lp:~percona-toolkit-dev/percona-toolkit/masterslave-redesign-prototype |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel Nichter | Needs Fixing | ||
Review via email: mp+130197@code.launchpad.net |
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
- 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 - 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 Daniel Nichter
-
Merge pt-osc-data-loss-bug-1068562
- 452. By Daniel Nichter
-
Merge fix-1045317-
pt-osc- statistics - 453. By Daniel Nichter
-
Remove duplicate code block I created by merge bits and pieces of branches, thereby confusing bzr.
- 454. By Brian Fraser
-
Merged OptionParser-
remove- optional_ value & updated modules - 455. By Brian Fraser
-
t/pt-online-
schema- change/ basics. t: --statistics test outputs an extra line in 5.5 - 456. By Brian Fraser
-
Merged fix-1047335-
crashed- tables - 457. By Brian Fraser
-
Merged fix-1062563-
1063912- ptc-pxc- bugs - 458. By Brian Fraser
-
Missing /g in a Quoter regex
- 459. By Brian Fraser
-
Missing sample file for a pt-osc test
- 460. By Brian Fraser
-
Merged masterslave-
redesign- prototype and resolved conflicts - 461. By Brian Fraser
-
lib/MasterSlave.pm: Make get_slave_status work under PTDEBUG
- 462. By Brian Fraser
-
Cxn: Incorporate the short_host logic from pt-slave-find into ->name(), enabled by the short_host attribute
- 463. By Brian Fraser
-
bin/pt-slave-find: Use Cxn, remove short_host
- 464. By Brian Fraser
-
MasterSlave: stop using one-off prepared statements
- 465. By Brian Fraser
-
Fixed tests broken during the redesign
- 466. By Brian Fraser
-
Updated modules for all tools
Unmerged revisions
- 466. By Brian Fraser
-
Updated modules for all tools
- 465. By Brian Fraser
-
Fixed tests broken during the redesign
- 464. By Brian Fraser
-
MasterSlave: stop using one-off prepared statements
- 463. By Brian Fraser
-
bin/pt-slave-find: Use Cxn, remove short_host
- 462. By Brian Fraser
-
Cxn: Incorporate the short_host logic from pt-slave-find into ->name(), enabled by the short_host attribute
- 461. By Brian Fraser
-
lib/MasterSlave.pm: Make get_slave_status work under PTDEBUG
- 460. By Brian Fraser
-
Merged masterslave-
redesign- prototype and resolved conflicts
Preview Diff
1 | === modified file 'bin/pt-archiver' |
2 | --- bin/pt-archiver 2012-11-09 16:48:17 +0000 |
3 | +++ bin/pt-archiver 2012-11-14 08:53:21 +0000 |
4 | @@ -16,6 +16,7 @@ |
5 | Percona::Toolkit |
6 | OptionParser |
7 | Mo |
8 | + Percona::Object |
9 | TableParser |
10 | DSNParser |
11 | VersionParser |
12 | @@ -1138,7 +1139,6 @@ |
13 | push @args_to_delete, $attr; |
14 | } |
15 | } |
16 | - |
17 | delete $args->{$_} for @args_to_delete; |
18 | |
19 | for my $attribute ( keys %$args ) { |
20 | @@ -1183,6 +1183,43 @@ |
21 | } |
22 | return $ref; |
23 | } |
24 | + |
25 | + sub meta { |
26 | + my $class = shift; |
27 | + return Mo::Meta::Class->new(class => $class); |
28 | + } |
29 | +} |
30 | + |
31 | +{ |
32 | + package Mo::Meta::Class; |
33 | + |
34 | + sub new { |
35 | + my $class = shift; |
36 | + return bless { @_ }, $class |
37 | + } |
38 | + |
39 | + sub class { shift->{class} } |
40 | + |
41 | + sub attributes { |
42 | + my $self = shift; |
43 | + return keys %{$metadata_for{$self->class}} |
44 | + } |
45 | + |
46 | + sub attributes_for_new { |
47 | + my $self = shift; |
48 | + my @attributes; |
49 | + |
50 | + while ( my ($attr, $meta) = each %{$metadata_for{$self->class}} ) { |
51 | + if ( exists $meta->{init_arg} ) { |
52 | + push @attributes, $meta->{init_arg} |
53 | + if defined $meta->{init_arg}; |
54 | + } |
55 | + else { |
56 | + push @attributes, $attr; |
57 | + } |
58 | + } |
59 | + return @attributes; |
60 | + } |
61 | } |
62 | |
63 | my %export_for; |
64 | @@ -1342,16 +1379,17 @@ |
65 | |
66 | sub _check_type_constaints { |
67 | my ($attribute, $I, $I_name, $val) = @_; |
68 | + local $@; |
69 | ( ref($I) eq 'CODE' |
70 | - ? $I->($val) |
71 | + ? eval { $I->($val) } |
72 | : (ref $val eq $I |
73 | || ($val && $val eq $I) |
74 | || (exists $TYPES{$I} && $TYPES{$I}->($val))) |
75 | ) |
76 | || Carp::confess( |
77 | - qq<Attribute ($attribute) does not pass the type constraint because: > |
78 | - . qq<Validation failed for '$I_name' with value > |
79 | - . (defined $val ? Mo::Dumper($val) : 'undef') ) |
80 | + qq<Attribute ($attribute) does not pass the type constraint because: > |
81 | + . ( $@ || ( qq<Validation failed for '$I_name' with value > |
82 | + . defined $val ? Mo::Dumper($val) : 'undef') ) ) |
83 | } |
84 | |
85 | sub _has_handles { |
86 | @@ -1524,6 +1562,52 @@ |
87 | # ########################################################################### |
88 | |
89 | # ########################################################################### |
90 | +# Percona::Object package |
91 | +# This package is a copy without comments from the original. The original |
92 | +# with comments and its test file can be found in the Bazaar repository at, |
93 | +# lib/Percona/Object.pm |
94 | +# t/lib/Percona/Object.t |
95 | +# See https://launchpad.net/percona-toolkit for more information. |
96 | +# ########################################################################### |
97 | +{ |
98 | +package Percona::Object; |
99 | + |
100 | +use strict; |
101 | +use warnings FATAL => 'all'; |
102 | +use English qw(-no_match_vars); |
103 | +use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
104 | + |
105 | +use Mo; |
106 | + |
107 | +sub BUILDARGS { |
108 | + my $self = shift; |
109 | + my $args = $self->SUPER::BUILDARGS(@_); |
110 | + |
111 | + return $args unless $args->{OptionParser}; |
112 | + |
113 | + my $o = $args->{OptionParser}; |
114 | + my @attributes = $self->meta->attributes_for_new(); |
115 | + |
116 | + foreach my $attr ( @attributes ) { |
117 | + next if exists $args->{$attr}; |
118 | + (my $attr_for_o = $attr) =~ tr/_/-/; |
119 | + if ( $o->has($attr_for_o) ) { |
120 | + $args->{$attr} = $o->get($attr_for_o) |
121 | + } elsif ( $attr eq 'DSNParser' ) { |
122 | + $args->{DSNParser} = $o->DSNParser; |
123 | + } |
124 | + } |
125 | + |
126 | + return $args; |
127 | +} |
128 | + |
129 | +1; |
130 | +} |
131 | +# ########################################################################### |
132 | +# End Percona::Object package |
133 | +# ########################################################################### |
134 | + |
135 | +# ########################################################################### |
136 | # TableParser package |
137 | # This package is a copy without comments from the original. The original |
138 | # with comments and its test file can be found in the Bazaar repository at, |
139 | @@ -1544,16 +1628,20 @@ |
140 | $Data::Dumper::Sortkeys = 1; |
141 | $Data::Dumper::Quotekeys = 0; |
142 | |
143 | +local $EVAL_ERROR; |
144 | +eval { |
145 | + require Quoter; |
146 | +}; |
147 | + |
148 | sub new { |
149 | my ( $class, %args ) = @_; |
150 | - my @required_args = qw(Quoter); |
151 | - foreach my $arg ( @required_args ) { |
152 | - die "I need a $arg argument" unless $args{$arg}; |
153 | - } |
154 | my $self = { %args }; |
155 | + $self->{Quoter} ||= Quoter->new(); |
156 | return bless $self, $class; |
157 | } |
158 | |
159 | +sub Quoter { shift->{Quoter} } |
160 | + |
161 | sub get_create_table { |
162 | my ( $self, $dbh, $db, $tbl ) = @_; |
163 | die "I need a dbh parameter" unless $dbh; |
164 | @@ -2544,12 +2632,18 @@ |
165 | |
166 | sub split_unquote { |
167 | my ( $self, $db_tbl, $default_db ) = @_; |
168 | - $db_tbl =~ s/`//g; |
169 | my ( $db, $tbl ) = split(/[.]/, $db_tbl); |
170 | if ( !$tbl ) { |
171 | $tbl = $db; |
172 | $db = $default_db; |
173 | } |
174 | + for ($db, $tbl) { |
175 | + next unless $_; |
176 | + s/\A`//; |
177 | + s/`\z//; |
178 | + s/``/`/g; |
179 | + } |
180 | + |
181 | return ($db, $tbl); |
182 | } |
183 | |
184 | @@ -3101,6 +3195,61 @@ |
185 | use English qw(-no_match_vars); |
186 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
187 | |
188 | +use Mo; |
189 | + |
190 | +extends qw( Percona::Object ); |
191 | + |
192 | +local $EVAL_ERROR; |
193 | +eval { |
194 | + require Quoter; |
195 | +}; |
196 | + |
197 | +has Quoter => ( |
198 | + is => 'ro', |
199 | + isa => 'Quoter', |
200 | + default => sub { Quoter->new() }, |
201 | +); |
202 | + |
203 | +has DSNParser => ( |
204 | + is => 'ro', |
205 | + isa => 'DSNParser', |
206 | + required => 1, |
207 | +); |
208 | + |
209 | +has recursion_method => ( |
210 | + is => 'ro', |
211 | + isa => sub { |
212 | + die "recursion_method should be an arrayref, not " . ($_[0] || 'undef') |
213 | + unless ref($_[0]) eq 'ARRAY'; |
214 | + check_recursion_method($_[0]); |
215 | + return 1; |
216 | + }, |
217 | + required => 1, |
218 | +); |
219 | + |
220 | +has _explicit_recursion_method => ( |
221 | + is => 'ro', |
222 | + isa => 'Bool', |
223 | + default => sub { 1 }, |
224 | +); |
225 | + |
226 | +has recurse => ( |
227 | + is => 'ro', |
228 | + isa => 'Maybe[Int]', |
229 | + required => 1, |
230 | +); |
231 | + |
232 | +sub BUILDARGS { |
233 | + my $self = shift; |
234 | + my $args = $self->SUPER::BUILDARGS(@_); |
235 | + my $o = delete $args->{OptionParser}; |
236 | + |
237 | + $args->{_explicit_recursion_method} = $o->got('recursion-method') |
238 | + if $o; |
239 | + |
240 | + return $args; |
241 | +} |
242 | + |
243 | sub check_recursion_method { |
244 | my ($methods) = @_; |
245 | |
246 | @@ -3120,19 +3269,6 @@ |
247 | } |
248 | } |
249 | |
250 | -sub new { |
251 | - my ( $class, %args ) = @_; |
252 | - my @required_args = qw(OptionParser DSNParser Quoter); |
253 | - foreach my $arg ( @required_args ) { |
254 | - die "I need a $arg argument" unless $args{$arg}; |
255 | - } |
256 | - my $self = { |
257 | - %args, |
258 | - replication_thread => {}, |
259 | - }; |
260 | - return bless $self, $class; |
261 | -} |
262 | - |
263 | sub get_slaves { |
264 | my ($self, %args) = @_; |
265 | my @required_args = qw(make_cxn); |
266 | @@ -3142,10 +3278,10 @@ |
267 | my ($make_cxn) = @args{@required_args}; |
268 | |
269 | my $slaves = []; |
270 | - my $dp = $self->{DSNParser}; |
271 | + my $dp = $self->DSNParser; |
272 | my $methods = $self->_resolve_recursion_methods($args{dsn}); |
273 | |
274 | - if ( grep { m/processlist|hosts/i } @$methods ) { |
275 | + if ( grep { m/^(?:processlist|hosts)$/i } @$methods ) { |
276 | my @required_args = qw(dbh dsn); |
277 | foreach my $arg ( @required_args ) { |
278 | die "I need a $arg argument" unless $args{$arg}; |
279 | @@ -3184,24 +3320,20 @@ |
280 | |
281 | sub _resolve_recursion_methods { |
282 | my ($self, $dsn) = @_; |
283 | - my $o = $self->{OptionParser}; |
284 | - if ( $o->got('recursion-method') ) { |
285 | - return $o->get('recursion-method'); |
286 | - } |
287 | - elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
288 | + if ( !$self->_explicit_recursion_method() && $dsn && ($dsn->{P} || 3306) != 3306 ) { |
289 | PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
290 | return [qw(hosts)]; |
291 | } |
292 | else { |
293 | - return $o->get('recursion-method'); |
294 | + return $self->recursion_method(); |
295 | } |
296 | } |
297 | |
298 | sub recurse_to_slaves { |
299 | my ( $self, $args, $level ) = @_; |
300 | $level ||= 0; |
301 | - my $dp = $self->{DSNParser}; |
302 | - my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
303 | + my $dp = $self->DSNParser; |
304 | + my $recurse = $args->{recurse} || $self->recurse(); |
305 | my $dsn = $args->{dsn}; |
306 | |
307 | my $methods = $self->_resolve_recursion_methods($dsn); |
308 | @@ -3245,7 +3377,7 @@ |
309 | |
310 | my @slaves = |
311 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
312 | - $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
313 | + $self->find_slave_hosts($dbh, $dsn, $methods); |
314 | |
315 | foreach my $slave ( @slaves ) { |
316 | PTDEBUG && _d('Recursing from', |
317 | @@ -3257,7 +3389,8 @@ |
318 | } |
319 | |
320 | sub find_slave_hosts { |
321 | - my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
322 | + my ( $self, $dbh, $dsn, $methods ) = @_; |
323 | + my $dsn_parser = $self->DSNParser; |
324 | |
325 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
326 | 'using methods', @$methods); |
327 | @@ -3267,7 +3400,7 @@ |
328 | foreach my $method ( @$methods ) { |
329 | my $find_slaves = "_find_slaves_by_$method"; |
330 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
331 | - @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
332 | + @slaves = $self->$find_slaves($dbh, $dsn); |
333 | last METHOD if @slaves; |
334 | } |
335 | |
336 | @@ -3276,7 +3409,8 @@ |
337 | } |
338 | |
339 | sub _find_slaves_by_processlist { |
340 | - my ( $self, $dsn_parser, $dbh, $dsn ) = @_; |
341 | + my ( $self, $dbh, $dsn ) = @_; |
342 | + my $dsn_parser = $self->DSNParser; |
343 | |
344 | my @slaves = map { |
345 | my $slave = $dsn_parser->parse("h=$_", $dsn); |
346 | @@ -3296,7 +3430,8 @@ |
347 | } |
348 | |
349 | sub _find_slaves_by_hosts { |
350 | - my ( $self, $dsn_parser, $dbh, $dsn ) = @_; |
351 | + my ( $self, $dbh, $dsn ) = @_; |
352 | + my $dsn_parser = $self->DSNParser; |
353 | |
354 | my @slaves; |
355 | my $sql = 'SHOW SLAVE HOSTS'; |
356 | @@ -3407,41 +3542,32 @@ |
357 | } |
358 | |
359 | sub get_master_dsn { |
360 | - my ( $self, $dbh, $dsn, $dsn_parser ) = @_; |
361 | - my $master = $self->get_slave_status($dbh) or return undef; |
362 | - my $spec = "h=$master->{master_host},P=$master->{master_port}"; |
363 | - return $dsn_parser->parse($spec, $dsn); |
364 | + my ( $self, $dbh, $dsn ) = @_; |
365 | + my $dsn_parser = $self->DSNParser; |
366 | + my $master = $self->get_slave_status($dbh) or return undef; |
367 | + my $spec = "h=$master->{master_host},P=$master->{master_port}"; |
368 | + return $dsn_parser->parse($spec, $dsn); |
369 | } |
370 | |
371 | sub get_slave_status { |
372 | my ( $self, $dbh ) = @_; |
373 | - if ( !$self->{not_a_slave}->{$dbh} ) { |
374 | - my $sth = $self->{sths}->{$dbh}->{SLAVE_STATUS} |
375 | - ||= $dbh->prepare('SHOW SLAVE STATUS'); |
376 | - PTDEBUG && _d($dbh, 'SHOW SLAVE STATUS'); |
377 | - $sth->execute(); |
378 | - my ($ss) = @{$sth->fetchall_arrayref({})}; |
379 | - |
380 | - if ( $ss && %$ss ) { |
381 | - $ss = { map { lc($_) => $ss->{$_} } keys %$ss }; # lowercase the keys |
382 | - return $ss; |
383 | - } |
384 | - |
385 | - PTDEBUG && _d('This server returns nothing for SHOW SLAVE STATUS'); |
386 | - $self->{not_a_slave}->{$dbh}++; |
387 | + my $sth = $dbh->prepare('SHOW SLAVE STATUS'); |
388 | + PTDEBUG && _d($dbh, 'SHOW SLAVE STATUS'); |
389 | + $sth->execute(); |
390 | + my ($ss) = @{$sth->fetchall_arrayref({})}; |
391 | + |
392 | + if ( $ss && %$ss ) { |
393 | + $ss = { map { lc($_) => $ss->{$_} } keys %$ss }; # lowercase the keys |
394 | + return $ss; |
395 | } |
396 | + |
397 | + PTDEBUG && _d('This server returns nothing for SHOW SLAVE STATUS'); |
398 | } |
399 | |
400 | sub get_master_status { |
401 | my ( $self, $dbh ) = @_; |
402 | |
403 | - if ( $self->{not_a_master}->{$dbh} ) { |
404 | - PTDEBUG && _d('Server on dbh', $dbh, 'is not a master'); |
405 | - return; |
406 | - } |
407 | - |
408 | - my $sth = $self->{sths}->{$dbh}->{MASTER_STATUS} |
409 | - ||= $dbh->prepare('SHOW MASTER STATUS'); |
410 | + my $sth = $dbh->prepare('SHOW MASTER STATUS'); |
411 | PTDEBUG && _d($dbh, 'SHOW MASTER STATUS'); |
412 | $sth->execute(); |
413 | my ($ms) = @{$sth->fetchall_arrayref({})}; |
414 | @@ -3451,7 +3577,6 @@ |
415 | |
416 | if ( !$ms || scalar keys %$ms < 2 ) { |
417 | PTDEBUG && _d('Server on dbh', $dbh, 'does not seem to be a master'); |
418 | - $self->{not_a_master}->{$dbh}++; |
419 | } |
420 | |
421 | return { map { lc($_) => $ms->{$_} } keys %$ms }; # lowercase the keys |
422 | @@ -3492,8 +3617,7 @@ |
423 | |
424 | sub stop_slave { |
425 | my ( $self, $dbh ) = @_; |
426 | - my $sth = $self->{sths}->{$dbh}->{STOP_SLAVE} |
427 | - ||= $dbh->prepare('STOP SLAVE'); |
428 | + my $sth = $dbh->prepare('STOP SLAVE'); |
429 | PTDEBUG && _d($dbh, $sth->{Statement}); |
430 | $sth->execute(); |
431 | } |
432 | @@ -3507,103 +3631,18 @@ |
433 | $dbh->do($sql); |
434 | } |
435 | else { |
436 | - my $sth = $self->{sths}->{$dbh}->{START_SLAVE} |
437 | - ||= $dbh->prepare('START SLAVE'); |
438 | + my $sth = $dbh->prepare('START SLAVE'); |
439 | PTDEBUG && _d($dbh, $sth->{Statement}); |
440 | $sth->execute(); |
441 | } |
442 | } |
443 | |
444 | -sub catchup_to_master { |
445 | - my ( $self, $slave, $master, $timeout ) = @_; |
446 | - $self->stop_slave($master); |
447 | - $self->stop_slave($slave); |
448 | - my $slave_status = $self->get_slave_status($slave); |
449 | - my $slave_pos = $self->repl_posn($slave_status); |
450 | - my $master_status = $self->get_master_status($master); |
451 | - my $master_pos = $self->repl_posn($master_status); |
452 | - PTDEBUG && _d('Master position:', $self->pos_to_string($master_pos), |
453 | - 'Slave position:', $self->pos_to_string($slave_pos)); |
454 | - |
455 | - my $result; |
456 | - if ( $self->pos_cmp($slave_pos, $master_pos) < 0 ) { |
457 | - PTDEBUG && _d('Waiting for slave to catch up to master'); |
458 | - $self->start_slave($slave, $master_pos); |
459 | - |
460 | - $result = $self->wait_for_master( |
461 | - master_status => $master_status, |
462 | - slave_dbh => $slave, |
463 | - timeout => $timeout, |
464 | - master_status => $master_status |
465 | - ); |
466 | - if ( !defined $result->{result} ) { |
467 | - $slave_status = $self->get_slave_status($slave); |
468 | - if ( !$self->slave_is_running($slave_status) ) { |
469 | - PTDEBUG && _d('Master position:', |
470 | - $self->pos_to_string($master_pos), |
471 | - 'Slave position:', $self->pos_to_string($slave_pos)); |
472 | - $slave_pos = $self->repl_posn($slave_status); |
473 | - if ( $self->pos_cmp($slave_pos, $master_pos) != 0 ) { |
474 | - die "MASTER_POS_WAIT() returned NULL but slave has not " |
475 | - . "caught up to master"; |
476 | - } |
477 | - PTDEBUG && _d('Slave is caught up to master and stopped'); |
478 | - } |
479 | - else { |
480 | - die "Slave has not caught up to master and it is still running"; |
481 | - } |
482 | - } |
483 | - } |
484 | - else { |
485 | - PTDEBUG && _d("Slave is already caught up to master"); |
486 | - } |
487 | - |
488 | - return $result; |
489 | -} |
490 | - |
491 | -sub catchup_to_same_pos { |
492 | - my ( $self, $s1_dbh, $s2_dbh ) = @_; |
493 | - $self->stop_slave($s1_dbh); |
494 | - $self->stop_slave($s2_dbh); |
495 | - my $s1_status = $self->get_slave_status($s1_dbh); |
496 | - my $s2_status = $self->get_slave_status($s2_dbh); |
497 | - my $s1_pos = $self->repl_posn($s1_status); |
498 | - my $s2_pos = $self->repl_posn($s2_status); |
499 | - if ( $self->pos_cmp($s1_pos, $s2_pos) < 0 ) { |
500 | - $self->start_slave($s1_dbh, $s2_pos); |
501 | - } |
502 | - elsif ( $self->pos_cmp($s2_pos, $s1_pos) < 0 ) { |
503 | - $self->start_slave($s2_dbh, $s1_pos); |
504 | - } |
505 | - |
506 | - $s1_status = $self->get_slave_status($s1_dbh); |
507 | - $s2_status = $self->get_slave_status($s2_dbh); |
508 | - $s1_pos = $self->repl_posn($s1_status); |
509 | - $s2_pos = $self->repl_posn($s2_status); |
510 | - |
511 | - if ( $self->slave_is_running($s1_status) |
512 | - || $self->slave_is_running($s2_status) |
513 | - || $self->pos_cmp($s1_pos, $s2_pos) != 0) |
514 | - { |
515 | - die "The servers aren't both stopped at the same position"; |
516 | - } |
517 | - |
518 | -} |
519 | - |
520 | -sub slave_is_running { |
521 | +sub _slave_is_running { |
522 | my ( $self, $slave_status ) = @_; |
523 | return ($slave_status->{slave_sql_running} || 'No') eq 'Yes'; |
524 | } |
525 | |
526 | -sub has_slave_updates { |
527 | - my ( $self, $dbh ) = @_; |
528 | - my $sql = q{SHOW VARIABLES LIKE 'log_slave_updates'}; |
529 | - PTDEBUG && _d($dbh, $sql); |
530 | - my ($name, $value) = $dbh->selectrow_array($sql); |
531 | - return $value && $value =~ m/^(1|ON)$/; |
532 | -} |
533 | - |
534 | -sub repl_posn { |
535 | +sub _repl_posn { |
536 | my ( $self, $status ) = @_; |
537 | if ( exists $status->{file} && exists $status->{position} ) { |
538 | return { |
539 | @@ -3626,89 +3665,10 @@ |
540 | return $stat->{seconds_behind_master}; |
541 | } |
542 | |
543 | -sub pos_cmp { |
544 | +sub _pos_cmp { |
545 | my ( $self, $a, $b ) = @_; |
546 | - return $self->pos_to_string($a) cmp $self->pos_to_string($b); |
547 | -} |
548 | - |
549 | -sub short_host { |
550 | - my ( $self, $dsn ) = @_; |
551 | - my ($host, $port); |
552 | - if ( $dsn->{master_host} ) { |
553 | - $host = $dsn->{master_host}; |
554 | - $port = $dsn->{master_port}; |
555 | - } |
556 | - else { |
557 | - $host = $dsn->{h}; |
558 | - $port = $dsn->{P}; |
559 | - } |
560 | - return ($host || '[default]') . ( ($port || 3306) == 3306 ? '' : ":$port" ); |
561 | -} |
562 | - |
563 | -sub is_replication_thread { |
564 | - my ( $self, $query, %args ) = @_; |
565 | - return unless $query; |
566 | - |
567 | - my $type = lc($args{type} || 'all'); |
568 | - die "Invalid type: $type" |
569 | - unless $type =~ m/^binlog_dump|slave_io|slave_sql|all$/i; |
570 | - |
571 | - my $match = 0; |
572 | - if ( $type =~ m/binlog_dump|all/i ) { |
573 | - $match = 1 |
574 | - if ($query->{Command} || $query->{command} || '') eq "Binlog Dump"; |
575 | - } |
576 | - if ( !$match ) { |
577 | - if ( ($query->{User} || $query->{user} || '') eq "system user" ) { |
578 | - PTDEBUG && _d("Slave replication thread"); |
579 | - if ( $type ne 'all' ) { |
580 | - my $state = $query->{State} || $query->{state} || ''; |
581 | - |
582 | - if ( $state =~ m/^init|end$/ ) { |
583 | - PTDEBUG && _d("Special state:", $state); |
584 | - $match = 1; |
585 | - } |
586 | - else { |
587 | - my ($slave_sql) = $state =~ m/ |
588 | - ^(Waiting\sfor\sthe\snext\sevent |
589 | - |Reading\sevent\sfrom\sthe\srelay\slog |
590 | - |Has\sread\sall\srelay\slog;\swaiting |
591 | - |Making\stemp\sfile |
592 | - |Waiting\sfor\sslave\smutex\son\sexit)/xi; |
593 | - |
594 | - $match = $type eq 'slave_sql' && $slave_sql ? 1 |
595 | - : $type eq 'slave_io' && !$slave_sql ? 1 |
596 | - : 0; |
597 | - } |
598 | - } |
599 | - else { |
600 | - $match = 1; |
601 | - } |
602 | - } |
603 | - else { |
604 | - PTDEBUG && _d('Not system user'); |
605 | - } |
606 | - |
607 | - if ( !defined $args{check_known_ids} || $args{check_known_ids} ) { |
608 | - my $id = $query->{Id} || $query->{id}; |
609 | - if ( $match ) { |
610 | - $self->{replication_thread}->{$id} = 1; |
611 | - } |
612 | - else { |
613 | - if ( $self->{replication_thread}->{$id} ) { |
614 | - PTDEBUG && _d("Thread ID is a known replication thread ID"); |
615 | - $match = 1; |
616 | - } |
617 | - } |
618 | - } |
619 | - } |
620 | - |
621 | - PTDEBUG && _d('Matches', $type, 'replication thread:', |
622 | - ($match ? 'yes' : 'no'), '; match:', $match); |
623 | - |
624 | - return $match; |
625 | -} |
626 | - |
627 | + return $self->_pos_to_string($a) cmp $self->_pos_to_string($b); |
628 | +} |
629 | |
630 | sub get_replication_filters { |
631 | my ( $self, %args ) = @_; |
632 | @@ -3753,18 +3713,12 @@ |
633 | } |
634 | |
635 | |
636 | -sub pos_to_string { |
637 | +sub _pos_to_string { |
638 | my ( $self, $pos ) = @_; |
639 | my $fmt = '%s/%020d'; |
640 | return sprintf($fmt, @{$pos}{qw(file position)}); |
641 | } |
642 | |
643 | -sub reset_known_replication_threads { |
644 | - my ( $self ) = @_; |
645 | - $self->{replication_thread} = {}; |
646 | - return; |
647 | -} |
648 | - |
649 | sub get_cxn_from_dsn_table { |
650 | my ($self, %args) = @_; |
651 | my @required_args = qw(dsn_table_dsn make_cxn); |
652 | @@ -3774,8 +3728,8 @@ |
653 | my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
654 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
655 | |
656 | - my $dp = $self->{DSNParser}; |
657 | - my $q = $self->{Quoter}; |
658 | + my $dp = $self->DSNParser; |
659 | + my $q = $self->Quoter; |
660 | |
661 | my $dsn = $dp->parse($dsn_table_dsn); |
662 | my $dsn_table; |
663 | |
664 | === modified file 'bin/pt-heartbeat' |
665 | --- bin/pt-heartbeat 2012-11-09 16:48:17 +0000 |
666 | +++ bin/pt-heartbeat 2012-11-14 08:53:21 +0000 |
667 | @@ -46,6 +46,540 @@ |
668 | # ########################################################################### |
669 | |
670 | # ########################################################################### |
671 | +# Mo package |
672 | +# This package is a copy without comments from the original. The original |
673 | +# with comments and its test file can be found in the Bazaar repository at, |
674 | +# lib/Mo.pm |
675 | +# t/lib/Mo.t |
676 | +# See https://launchpad.net/percona-toolkit for more information. |
677 | +# ########################################################################### |
678 | +{ |
679 | +BEGIN { |
680 | +$INC{"Mo.pm"} = __FILE__; |
681 | +package Mo; |
682 | +our $VERSION = '0.30_Percona'; # Forked from 0.30 of Mo. |
683 | + |
684 | +{ |
685 | + no strict 'refs'; |
686 | + sub _glob_for { |
687 | + return \*{shift()} |
688 | + } |
689 | + |
690 | + sub _stash_for { |
691 | + return \%{ shift() . "::" }; |
692 | + } |
693 | +} |
694 | + |
695 | +use strict; |
696 | +use warnings qw( FATAL all ); |
697 | + |
698 | +use Carp (); |
699 | +use Scalar::Util qw(looks_like_number blessed); |
700 | + |
701 | + |
702 | +our %TYPES = ( |
703 | + Bool => sub { !$_[0] || (defined $_[0] && looks_like_number($_[0]) && $_[0] == 1) }, |
704 | + Num => sub { defined $_[0] && looks_like_number($_[0]) }, |
705 | + Int => sub { defined $_[0] && looks_like_number($_[0]) && $_[0] == int($_[0]) }, |
706 | + Str => sub { defined $_[0] }, |
707 | + Object => sub { defined $_[0] && blessed($_[0]) }, |
708 | + FileHandle => sub { local $@; require IO::Handle; fileno($_[0]) && $_[0]->opened }, |
709 | + |
710 | + map { |
711 | + my $type = /R/ ? $_ : uc $_; |
712 | + $_ . "Ref" => sub { ref $_[0] eq $type } |
713 | + } qw(Array Code Hash Regexp Glob Scalar) |
714 | +); |
715 | + |
716 | +our %metadata_for; |
717 | +{ |
718 | + package Mo::Object; |
719 | + |
720 | + sub new { |
721 | + my $class = shift; |
722 | + my $args = $class->BUILDARGS(@_); |
723 | + |
724 | + my @args_to_delete; |
725 | + while ( my ($attr, $meta) = each %{$metadata_for{$class}} ) { |
726 | + next unless exists $meta->{init_arg}; |
727 | + my $init_arg = $meta->{init_arg}; |
728 | + |
729 | + if ( defined $init_arg ) { |
730 | + $args->{$attr} = delete $args->{$init_arg}; |
731 | + } |
732 | + else { |
733 | + push @args_to_delete, $attr; |
734 | + } |
735 | + } |
736 | + delete $args->{$_} for @args_to_delete; |
737 | + |
738 | + for my $attribute ( keys %$args ) { |
739 | + if ( my $coerce = $metadata_for{$class}{$attribute}{coerce} ) { |
740 | + $args->{$attribute} = $coerce->($args->{$attribute}); |
741 | + } |
742 | + if ( my $I = $metadata_for{$class}{$attribute}{isa} ) { |
743 | + ( (my $I_name), $I ) = @{$I}; |
744 | + Mo::_check_type_constaints($attribute, $I, $I_name, $args->{$attribute}); |
745 | + } |
746 | + } |
747 | + |
748 | + while ( my ($attribute, $meta) = each %{$metadata_for{$class}} ) { |
749 | + next unless $meta->{required}; |
750 | + Carp::confess("Attribute ($attribute) is required for $class") |
751 | + if ! exists $args->{$attribute} |
752 | + } |
753 | + |
754 | + @_ = %$args; |
755 | + my $self = bless $args, $class; |
756 | + |
757 | + my @build_subs; |
758 | + my $linearized_isa = mro::get_linear_isa($class); |
759 | + |
760 | + for my $isa_class ( @$linearized_isa ) { |
761 | + unshift @build_subs, *{ Mo::_glob_for "${isa_class}::BUILD" }{CODE}; |
762 | + } |
763 | + exists &$_ && $_->( $self, @_ ) for grep { defined } @build_subs; |
764 | + return $self; |
765 | + } |
766 | + |
767 | + sub BUILDARGS { |
768 | + shift; |
769 | + my $ref; |
770 | + if ( @_ == 1 && ref($_[0]) ) { |
771 | + Carp::confess("Single parameters to new() must be a HASH ref") |
772 | + unless ref($_[0]) eq ref({}); |
773 | + $ref = {%{$_[0]}} # We want a new reference, always |
774 | + } |
775 | + else { |
776 | + $ref = { @_ }; |
777 | + } |
778 | + return $ref; |
779 | + } |
780 | + |
781 | + sub meta { |
782 | + my $class = shift; |
783 | + return Mo::Meta::Class->new(class => $class); |
784 | + } |
785 | +} |
786 | + |
787 | +{ |
788 | + package Mo::Meta::Class; |
789 | + |
790 | + sub new { |
791 | + my $class = shift; |
792 | + return bless { @_ }, $class |
793 | + } |
794 | + |
795 | + sub class { shift->{class} } |
796 | + |
797 | + sub attributes { |
798 | + my $self = shift; |
799 | + return keys %{$metadata_for{$self->class}} |
800 | + } |
801 | + |
802 | + sub attributes_for_new { |
803 | + my $self = shift; |
804 | + my @attributes; |
805 | + |
806 | + while ( my ($attr, $meta) = each %{$metadata_for{$self->class}} ) { |
807 | + if ( exists $meta->{init_arg} ) { |
808 | + push @attributes, $meta->{init_arg} |
809 | + if defined $meta->{init_arg}; |
810 | + } |
811 | + else { |
812 | + push @attributes, $attr; |
813 | + } |
814 | + } |
815 | + return @attributes; |
816 | + } |
817 | +} |
818 | + |
819 | +my %export_for; |
820 | +sub Mo::import { |
821 | + warnings->import(qw(FATAL all)); |
822 | + strict->import(); |
823 | + |
824 | + my $caller = scalar caller(); # Caller's package |
825 | + my $caller_pkg = $caller . "::"; # Caller's package with :: at the end |
826 | + my (%exports, %options); |
827 | + |
828 | + my (undef, @features) = @_; |
829 | + my %ignore = ( map { $_ => 1 } qw( is isa init_arg builder buildargs clearer predicate build handles default required ) ); |
830 | + for my $feature (grep { !$ignore{$_} } @features) { |
831 | + { local $@; require "Mo/$feature.pm"; } |
832 | + { |
833 | + no strict 'refs'; |
834 | + &{"Mo::${feature}::e"}( |
835 | + $caller_pkg, |
836 | + \%exports, |
837 | + \%options, |
838 | + \@_ |
839 | + ); |
840 | + } |
841 | + } |
842 | + |
843 | + return if $exports{M}; |
844 | + |
845 | + %exports = ( |
846 | + extends => sub { |
847 | + for my $class ( map { "$_" } @_ ) { |
848 | + $class =~ s{::|'}{/}g; |
849 | + { local $@; eval { require "$class.pm" } } # or warn $@; |
850 | + } |
851 | + _set_package_isa($caller, @_); |
852 | + _set_inherited_metadata($caller); |
853 | + }, |
854 | + has => sub { |
855 | + my $names = shift; |
856 | + for my $attribute ( ref $names ? @$names : $names ) { |
857 | + my %args = @_; |
858 | + my $method = ($args{is} || '') eq 'ro' |
859 | + ? sub { |
860 | + Carp::confess("Cannot assign a value to a read-only accessor at reader ${caller_pkg}${attribute}") |
861 | + if $#_; |
862 | + return $_[0]{$attribute}; |
863 | + } |
864 | + : sub { |
865 | + return $#_ |
866 | + ? $_[0]{$attribute} = $_[1] |
867 | + : $_[0]{$attribute}; |
868 | + }; |
869 | + |
870 | + $metadata_for{$caller}{$attribute} = (); |
871 | + |
872 | + if ( my $I = $args{isa} ) { |
873 | + my $orig_I = $I; |
874 | + my $type; |
875 | + if ( $I =~ /\A(ArrayRef|Maybe)\[(.*)\]\z/ ) { |
876 | + $I = _nested_constraints($attribute, $1, $2); |
877 | + } |
878 | + $metadata_for{$caller}{$attribute}{isa} = [$orig_I, $I]; |
879 | + my $orig_method = $method; |
880 | + $method = sub { |
881 | + if ( $#_ ) { |
882 | + Mo::_check_type_constaints($attribute, $I, $orig_I, $_[1]); |
883 | + } |
884 | + goto &$orig_method; |
885 | + }; |
886 | + } |
887 | + |
888 | + if ( my $builder = $args{builder} ) { |
889 | + my $original_method = $method; |
890 | + $method = sub { |
891 | + $#_ |
892 | + ? goto &$original_method |
893 | + : ! exists $_[0]{$attribute} |
894 | + ? $_[0]{$attribute} = $_[0]->$builder |
895 | + : goto &$original_method |
896 | + }; |
897 | + } |
898 | + |
899 | + if ( my $code = $args{default} ) { |
900 | + Carp::confess("${caller}::${attribute}'s default is $code, but should be a coderef") |
901 | + unless ref($code) eq 'CODE'; |
902 | + my $original_method = $method; |
903 | + $method = sub { |
904 | + $#_ |
905 | + ? goto &$original_method |
906 | + : ! exists $_[0]{$attribute} |
907 | + ? $_[0]{$attribute} = $_[0]->$code |
908 | + : goto &$original_method |
909 | + }; |
910 | + } |
911 | + |
912 | + if ( my $role = $args{does} ) { |
913 | + my $original_method = $method; |
914 | + $method = sub { |
915 | + if ( $#_ ) { |
916 | + Carp::confess(qq<Attribute ($attribute) doesn't consume a '$role' role">) |
917 | + unless Scalar::Util::blessed($_[1]) && eval { $_[1]->does($role) } |
918 | + } |
919 | + goto &$original_method |
920 | + }; |
921 | + } |
922 | + |
923 | + if ( my $coercion = $args{coerce} ) { |
924 | + $metadata_for{$caller}{$attribute}{coerce} = $coercion; |
925 | + my $original_method = $method; |
926 | + $method = sub { |
927 | + if ( $#_ ) { |
928 | + return $original_method->($_[0], $coercion->($_[1])) |
929 | + } |
930 | + goto &$original_method; |
931 | + } |
932 | + } |
933 | + |
934 | + $method = $options{$_}->($method, $attribute, @_) |
935 | + for sort keys %options; |
936 | + |
937 | + *{ _glob_for "${caller}::$attribute" } = $method; |
938 | + |
939 | + if ( $args{required} ) { |
940 | + $metadata_for{$caller}{$attribute}{required} = 1; |
941 | + } |
942 | + |
943 | + if ($args{clearer}) { |
944 | + *{ _glob_for "${caller}::$args{clearer}" } |
945 | + = sub { delete shift->{$attribute} } |
946 | + } |
947 | + |
948 | + if ($args{predicate}) { |
949 | + *{ _glob_for "${caller}::$args{predicate}" } |
950 | + = sub { exists shift->{$attribute} } |
951 | + } |
952 | + |
953 | + if ($args{handles}) { |
954 | + _has_handles($caller, $attribute, \%args); |
955 | + } |
956 | + |
957 | + if (exists $args{init_arg}) { |
958 | + $metadata_for{$caller}{$attribute}{init_arg} = $args{init_arg}; |
959 | + } |
960 | + } |
961 | + }, |
962 | + %exports, |
963 | + ); |
964 | + |
965 | + $export_for{$caller} = [ keys %exports ]; |
966 | + |
967 | + for my $keyword ( keys %exports ) { |
968 | + *{ _glob_for "${caller}::$keyword" } = $exports{$keyword} |
969 | + } |
970 | + *{ _glob_for "${caller}::extends" }{CODE}->( "Mo::Object" ) |
971 | + unless @{ *{ _glob_for "${caller}::ISA" }{ARRAY} || [] }; |
972 | +}; |
973 | + |
974 | +sub _check_type_constaints { |
975 | + my ($attribute, $I, $I_name, $val) = @_; |
976 | + local $@; |
977 | + ( ref($I) eq 'CODE' |
978 | + ? eval { $I->($val) } |
979 | + : (ref $val eq $I |
980 | + || ($val && $val eq $I) |
981 | + || (exists $TYPES{$I} && $TYPES{$I}->($val))) |
982 | + ) |
983 | + || Carp::confess( |
984 | + qq<Attribute ($attribute) does not pass the type constraint because: > |
985 | + . ( $@ || ( qq<Validation failed for '$I_name' with value > |
986 | + . defined $val ? Mo::Dumper($val) : 'undef') ) ) |
987 | +} |
988 | + |
989 | +sub _has_handles { |
990 | + my ($caller, $attribute, $args) = @_; |
991 | + my $handles = $args->{handles}; |
992 | + |
993 | + my $ref = ref $handles; |
994 | + my $kv; |
995 | + if ( $ref eq ref [] ) { |
996 | + $kv = { map { $_,$_ } @{$handles} }; |
997 | + } |
998 | + elsif ( $ref eq ref {} ) { |
999 | + $kv = $handles; |
1000 | + } |
1001 | + elsif ( $ref eq ref qr// ) { |
1002 | + Carp::confess("Cannot delegate methods based on a Regexp without a type constraint (isa)") |
1003 | + unless $args->{isa}; |
1004 | + my $target_class = $args->{isa}; |
1005 | + $kv = { |
1006 | + map { $_, $_ } |
1007 | + grep { $_ =~ $handles } |
1008 | + grep { !exists $Mo::Object::{$_} && $target_class->can($_) } |
1009 | + grep { $_ ne 'has' && $_ ne 'extends' } |
1010 | + keys %{ _stash_for $target_class } |
1011 | + }; |
1012 | + } |
1013 | + else { |
1014 | + Carp::confess("handles for $ref not yet implemented"); |
1015 | + } |
1016 | + |
1017 | + while ( my ($method, $target) = each %{$kv} ) { |
1018 | + my $name = _glob_for "${caller}::$method"; |
1019 | + Carp::confess("You cannot overwrite a locally defined method ($method) with a delegation") |
1020 | + if defined &$name; |
1021 | + |
1022 | + my ($target, @curried_args) = ref($target) ? @$target : $target; |
1023 | + *$name = sub { |
1024 | + my $self = shift; |
1025 | + my $delegate_to = $self->$attribute(); |
1026 | + my $error = "Cannot delegate $method to $target because the value of $attribute"; |
1027 | + Carp::confess("$error is not defined") unless $delegate_to; |
1028 | + Carp::confess("$error is not an object (got '$delegate_to')") |
1029 | + unless Scalar::Util::blessed($delegate_to) || (!ref($delegate_to) && $delegate_to->can($target)); |
1030 | + return $delegate_to->$target(@curried_args, @_); |
1031 | + } |
1032 | + } |
1033 | +} |
1034 | + |
1035 | +sub _nested_constraints { |
1036 | + my ($attribute, $aggregate_type, $type) = @_; |
1037 | + |
1038 | + my $inner_types; |
1039 | + if ( $type =~ /\A(ArrayRef|Maybe)\[(.*)\]\z/ ) { |
1040 | + $inner_types = _nested_constraints($1, $2); |
1041 | + } |
1042 | + else { |
1043 | + $inner_types = $TYPES{$type}; |
1044 | + } |
1045 | + |
1046 | + if ( $aggregate_type eq 'ArrayRef' ) { |
1047 | + return sub { |
1048 | + my ($val) = @_; |
1049 | + return unless ref($val) eq ref([]); |
1050 | + |
1051 | + if ($inner_types) { |
1052 | + for my $value ( @{$val} ) { |
1053 | + return unless $inner_types->($value) |
1054 | + } |
1055 | + } |
1056 | + else { |
1057 | + for my $value ( @{$val} ) { |
1058 | + return unless $value && ($value eq $type |
1059 | + || (Scalar::Util::blessed($value) && $value->isa($type))); |
1060 | + } |
1061 | + } |
1062 | + return 1; |
1063 | + }; |
1064 | + } |
1065 | + elsif ( $aggregate_type eq 'Maybe' ) { |
1066 | + return sub { |
1067 | + my ($value) = @_; |
1068 | + return 1 if ! defined($value); |
1069 | + if ($inner_types) { |
1070 | + return unless $inner_types->($value) |
1071 | + } |
1072 | + else { |
1073 | + return unless $value eq $type |
1074 | + || (Scalar::Util::blessed($value) && $value->isa($type)); |
1075 | + } |
1076 | + return 1; |
1077 | + } |
1078 | + } |
1079 | + else { |
1080 | + Carp::confess("Nested aggregate types are only implemented for ArrayRefs and Maybe"); |
1081 | + } |
1082 | +} |
1083 | + |
1084 | +sub _set_package_isa { |
1085 | + my ($package, @new_isa) = @_; |
1086 | + |
1087 | + *{ _glob_for "${package}::ISA" } = [@new_isa]; |
1088 | +} |
1089 | + |
1090 | +sub _set_inherited_metadata { |
1091 | + my $class = shift; |
1092 | + my $linearized_isa = mro::get_linear_isa($class); |
1093 | + my %new_metadata; |
1094 | + |
1095 | + for my $isa_class (reverse @$linearized_isa) { |
1096 | + %new_metadata = ( |
1097 | + %new_metadata, |
1098 | + %{ $metadata_for{$isa_class} || {} }, |
1099 | + ); |
1100 | + } |
1101 | + $metadata_for{$class} = \%new_metadata; |
1102 | +} |
1103 | + |
1104 | +sub unimport { |
1105 | + my $caller = scalar caller(); |
1106 | + my $stash = _stash_for( $caller ); |
1107 | + |
1108 | + delete $stash->{$_} for @{$export_for{$caller}}; |
1109 | +} |
1110 | + |
1111 | +sub Dumper { |
1112 | + require Data::Dumper; |
1113 | + local $Data::Dumper::Indent = 0; |
1114 | + local $Data::Dumper::Sortkeys = 0; |
1115 | + local $Data::Dumper::Quotekeys = 0; |
1116 | + local $Data::Dumper::Terse = 1; |
1117 | + |
1118 | + Data::Dumper::Dumper(@_) |
1119 | +} |
1120 | + |
1121 | +BEGIN { |
1122 | + if ($] >= 5.010) { |
1123 | + { local $@; require mro; } |
1124 | + } |
1125 | + else { |
1126 | + local $@; |
1127 | + eval { |
1128 | + require MRO::Compat; |
1129 | + } or do { |
1130 | + *mro::get_linear_isa = *mro::get_linear_isa_dfs = sub { |
1131 | + no strict 'refs'; |
1132 | + |
1133 | + my $classname = shift; |
1134 | + |
1135 | + my @lin = ($classname); |
1136 | + my %stored; |
1137 | + foreach my $parent (@{"$classname\::ISA"}) { |
1138 | + my $plin = mro::get_linear_isa_dfs($parent); |
1139 | + foreach (@$plin) { |
1140 | + next if exists $stored{$_}; |
1141 | + push(@lin, $_); |
1142 | + $stored{$_} = 1; |
1143 | + } |
1144 | + } |
1145 | + return \@lin; |
1146 | + }; |
1147 | + } |
1148 | + } |
1149 | +} |
1150 | + |
1151 | +} |
1152 | +1; |
1153 | +} |
1154 | +# ########################################################################### |
1155 | +# End Mo package |
1156 | +# ########################################################################### |
1157 | + |
1158 | +# ########################################################################### |
1159 | +# Percona::Object package |
1160 | +# This package is a copy without comments from the original. The original |
1161 | +# with comments and its test file can be found in the Bazaar repository at, |
1162 | +# lib/Percona/Object.pm |
1163 | +# t/lib/Percona/Object.t |
1164 | +# See https://launchpad.net/percona-toolkit for more information. |
1165 | +# ########################################################################### |
1166 | +{ |
1167 | +package Percona::Object; |
1168 | + |
1169 | +use strict; |
1170 | +use warnings FATAL => 'all'; |
1171 | +use English qw(-no_match_vars); |
1172 | +use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
1173 | + |
1174 | +use Mo; |
1175 | + |
1176 | +sub BUILDARGS { |
1177 | + my $self = shift; |
1178 | + my $args = $self->SUPER::BUILDARGS(@_); |
1179 | + |
1180 | + return $args unless $args->{OptionParser}; |
1181 | + |
1182 | + my $o = $args->{OptionParser}; |
1183 | + my @attributes = $self->meta->attributes_for_new(); |
1184 | + |
1185 | + foreach my $attr ( @attributes ) { |
1186 | + next if exists $args->{$attr}; |
1187 | + (my $attr_for_o = $attr) =~ tr/_/-/; |
1188 | + if ( $o->has($attr_for_o) ) { |
1189 | + $args->{$attr} = $o->get($attr_for_o) |
1190 | + } elsif ( $attr eq 'DSNParser' ) { |
1191 | + $args->{DSNParser} = $o->DSNParser; |
1192 | + } |
1193 | + } |
1194 | + |
1195 | + return $args; |
1196 | +} |
1197 | + |
1198 | +1; |
1199 | +} |
1200 | +# ########################################################################### |
1201 | +# End Percona::Object package |
1202 | +# ########################################################################### |
1203 | + |
1204 | +# ########################################################################### |
1205 | # MasterSlave package |
1206 | # This package is a copy without comments from the original. The original |
1207 | # with comments and its test file can be found in the Bazaar repository at, |
1208 | @@ -61,6 +595,61 @@ |
1209 | use English qw(-no_match_vars); |
1210 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
1211 | |
1212 | +use Mo; |
1213 | + |
1214 | +extends qw( Percona::Object ); |
1215 | + |
1216 | +local $EVAL_ERROR; |
1217 | +eval { |
1218 | + require Quoter; |
1219 | +}; |
1220 | + |
1221 | +has Quoter => ( |
1222 | + is => 'ro', |
1223 | + isa => 'Quoter', |
1224 | + default => sub { Quoter->new() }, |
1225 | +); |
1226 | + |
1227 | +has DSNParser => ( |
1228 | + is => 'ro', |
1229 | + isa => 'DSNParser', |
1230 | + required => 1, |
1231 | +); |
1232 | + |
1233 | +has recursion_method => ( |
1234 | + is => 'ro', |
1235 | + isa => sub { |
1236 | + die "recursion_method should be an arrayref, not " . ($_[0] || 'undef') |
1237 | + unless ref($_[0]) eq 'ARRAY'; |
1238 | + check_recursion_method($_[0]); |
1239 | + return 1; |
1240 | + }, |
1241 | + required => 1, |
1242 | +); |
1243 | + |
1244 | +has _explicit_recursion_method => ( |
1245 | + is => 'ro', |
1246 | + isa => 'Bool', |
1247 | + default => sub { 1 }, |
1248 | +); |
1249 | + |
1250 | +has recurse => ( |
1251 | + is => 'ro', |
1252 | + isa => 'Maybe[Int]', |
1253 | + required => 1, |
1254 | +); |
1255 | + |
1256 | +sub BUILDARGS { |
1257 | + my $self = shift; |
1258 | + my $args = $self->SUPER::BUILDARGS(@_); |
1259 | + my $o = delete $args->{OptionParser}; |
1260 | + |
1261 | + $args->{_explicit_recursion_method} = $o->got('recursion-method') |
1262 | + if $o; |
1263 | + |
1264 | + return $args; |
1265 | +} |
1266 | + |
1267 | sub check_recursion_method { |
1268 | my ($methods) = @_; |
1269 | |
1270 | @@ -80,19 +669,6 @@ |
1271 | } |
1272 | } |
1273 | |
1274 | -sub new { |
1275 | - my ( $class, %args ) = @_; |
1276 | - my @required_args = qw(OptionParser DSNParser Quoter); |
1277 | - foreach my $arg ( @required_args ) { |
1278 | - die "I need a $arg argument" unless $args{$arg}; |
1279 | - } |
1280 | - my $self = { |
1281 | - %args, |
1282 | - replication_thread => {}, |
1283 | - }; |
1284 | - return bless $self, $class; |
1285 | -} |
1286 | - |
1287 | sub get_slaves { |
1288 | my ($self, %args) = @_; |
1289 | my @required_args = qw(make_cxn); |
1290 | @@ -102,10 +678,10 @@ |
1291 | my ($make_cxn) = @args{@required_args}; |
1292 | |
1293 | my $slaves = []; |
1294 | - my $dp = $self->{DSNParser}; |
1295 | + my $dp = $self->DSNParser; |
1296 | my $methods = $self->_resolve_recursion_methods($args{dsn}); |
1297 | |
1298 | - if ( grep { m/processlist|hosts/i } @$methods ) { |
1299 | + if ( grep { m/^(?:processlist|hosts)$/i } @$methods ) { |
1300 | my @required_args = qw(dbh dsn); |
1301 | foreach my $arg ( @required_args ) { |
1302 | die "I need a $arg argument" unless $args{$arg}; |
1303 | @@ -144,24 +720,20 @@ |
1304 | |
1305 | sub _resolve_recursion_methods { |
1306 | my ($self, $dsn) = @_; |
1307 | - my $o = $self->{OptionParser}; |
1308 | - if ( $o->got('recursion-method') ) { |
1309 | - return $o->get('recursion-method'); |
1310 | - } |
1311 | - elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
1312 | + if ( !$self->_explicit_recursion_method() && $dsn && ($dsn->{P} || 3306) != 3306 ) { |
1313 | PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
1314 | return [qw(hosts)]; |
1315 | } |
1316 | else { |
1317 | - return $o->get('recursion-method'); |
1318 | + return $self->recursion_method(); |
1319 | } |
1320 | } |
1321 | |
1322 | sub recurse_to_slaves { |
1323 | my ( $self, $args, $level ) = @_; |
1324 | $level ||= 0; |
1325 | - my $dp = $self->{DSNParser}; |
1326 | - my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
1327 | + my $dp = $self->DSNParser; |
1328 | + my $recurse = $args->{recurse} || $self->recurse(); |
1329 | my $dsn = $args->{dsn}; |
1330 | |
1331 | my $methods = $self->_resolve_recursion_methods($dsn); |
1332 | @@ -205,7 +777,7 @@ |
1333 | |
1334 | my @slaves = |
1335 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
1336 | - $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
1337 | + $self->find_slave_hosts($dbh, $dsn, $methods); |
1338 | |
1339 | foreach my $slave ( @slaves ) { |
1340 | PTDEBUG && _d('Recursing from', |
1341 | @@ -217,7 +789,8 @@ |
1342 | } |
1343 | |
1344 | sub find_slave_hosts { |
1345 | - my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
1346 | + my ( $self, $dbh, $dsn, $methods ) = @_; |
1347 | + my $dsn_parser = $self->DSNParser; |
1348 | |
1349 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
1350 | 'using methods', @$methods); |
1351 | @@ -227,7 +800,7 @@ |
1352 | foreach my $method ( @$methods ) { |
1353 | my $find_slaves = "_find_slaves_by_$method"; |
1354 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
1355 | - @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
1356 | + @slaves = $self->$find_slaves($dbh, $dsn); |
1357 | last METHOD if @slaves; |
1358 | } |
1359 | |
1360 | @@ -236,7 +809,8 @@ |
1361 | } |
1362 | |
1363 | sub _find_slaves_by_processlist { |
1364 | - my ( $self, $dsn_parser, $dbh, $dsn ) = @_; |
1365 | + my ( $self, $dbh, $dsn ) = @_; |
1366 | + my $dsn_parser = $self->DSNParser; |
1367 | |
1368 | my @slaves = map { |
1369 | my $slave = $dsn_parser->parse("h=$_", $dsn); |
1370 | @@ -256,7 +830,8 @@ |
1371 | } |
1372 | |
1373 | sub _find_slaves_by_hosts { |
1374 | - my ( $self, $dsn_parser, $dbh, $dsn ) = @_; |
1375 | + my ( $self, $dbh, $dsn ) = @_; |
1376 | + my $dsn_parser = $self->DSNParser; |
1377 | |
1378 | my @slaves; |
1379 | my $sql = 'SHOW SLAVE HOSTS'; |
1380 | @@ -367,41 +942,32 @@ |
1381 | } |
1382 | |
1383 | sub get_master_dsn { |
1384 | - my ( $self, $dbh, $dsn, $dsn_parser ) = @_; |
1385 | - my $master = $self->get_slave_status($dbh) or return undef; |
1386 | - my $spec = "h=$master->{master_host},P=$master->{master_port}"; |
1387 | - return $dsn_parser->parse($spec, $dsn); |
1388 | + my ( $self, $dbh, $dsn ) = @_; |
1389 | + my $dsn_parser = $self->DSNParser; |
1390 | + my $master = $self->get_slave_status($dbh) or return undef; |
1391 | + my $spec = "h=$master->{master_host},P=$master->{master_port}"; |
1392 | + return $dsn_parser->parse($spec, $dsn); |
1393 | } |
1394 | |
1395 | sub get_slave_status { |
1396 | my ( $self, $dbh ) = @_; |
1397 | - if ( !$self->{not_a_slave}->{$dbh} ) { |
1398 | - my $sth = $self->{sths}->{$dbh}->{SLAVE_STATUS} |
1399 | - ||= $dbh->prepare('SHOW SLAVE STATUS'); |
1400 | - PTDEBUG && _d($dbh, 'SHOW SLAVE STATUS'); |
1401 | - $sth->execute(); |
1402 | - my ($ss) = @{$sth->fetchall_arrayref({})}; |
1403 | - |
1404 | - if ( $ss && %$ss ) { |
1405 | - $ss = { map { lc($_) => $ss->{$_} } keys %$ss }; # lowercase the keys |
1406 | - return $ss; |
1407 | - } |
1408 | - |
1409 | - PTDEBUG && _d('This server returns nothing for SHOW SLAVE STATUS'); |
1410 | - $self->{not_a_slave}->{$dbh}++; |
1411 | + my $sth = $dbh->prepare('SHOW SLAVE STATUS'); |
1412 | + PTDEBUG && _d($dbh, 'SHOW SLAVE STATUS'); |
1413 | + $sth->execute(); |
1414 | + my ($ss) = @{$sth->fetchall_arrayref({})}; |
1415 | + |
1416 | + if ( $ss && %$ss ) { |
1417 | + $ss = { map { lc($_) => $ss->{$_} } keys %$ss }; # lowercase the keys |
1418 | + return $ss; |
1419 | } |
1420 | + |
1421 | + PTDEBUG && _d('This server returns nothing for SHOW SLAVE STATUS'); |
1422 | } |
1423 | |
1424 | sub get_master_status { |
1425 | my ( $self, $dbh ) = @_; |
1426 | |
1427 | - if ( $self->{not_a_master}->{$dbh} ) { |
1428 | - PTDEBUG && _d('Server on dbh', $dbh, 'is not a master'); |
1429 | - return; |
1430 | - } |
1431 | - |
1432 | - my $sth = $self->{sths}->{$dbh}->{MASTER_STATUS} |
1433 | - ||= $dbh->prepare('SHOW MASTER STATUS'); |
1434 | + my $sth = $dbh->prepare('SHOW MASTER STATUS'); |
1435 | PTDEBUG && _d($dbh, 'SHOW MASTER STATUS'); |
1436 | $sth->execute(); |
1437 | my ($ms) = @{$sth->fetchall_arrayref({})}; |
1438 | @@ -411,7 +977,6 @@ |
1439 | |
1440 | if ( !$ms || scalar keys %$ms < 2 ) { |
1441 | PTDEBUG && _d('Server on dbh', $dbh, 'does not seem to be a master'); |
1442 | - $self->{not_a_master}->{$dbh}++; |
1443 | } |
1444 | |
1445 | return { map { lc($_) => $ms->{$_} } keys %$ms }; # lowercase the keys |
1446 | @@ -452,8 +1017,7 @@ |
1447 | |
1448 | sub stop_slave { |
1449 | my ( $self, $dbh ) = @_; |
1450 | - my $sth = $self->{sths}->{$dbh}->{STOP_SLAVE} |
1451 | - ||= $dbh->prepare('STOP SLAVE'); |
1452 | + my $sth = $dbh->prepare('STOP SLAVE'); |
1453 | PTDEBUG && _d($dbh, $sth->{Statement}); |
1454 | $sth->execute(); |
1455 | } |
1456 | @@ -467,103 +1031,18 @@ |
1457 | $dbh->do($sql); |
1458 | } |
1459 | else { |
1460 | - my $sth = $self->{sths}->{$dbh}->{START_SLAVE} |
1461 | - ||= $dbh->prepare('START SLAVE'); |
1462 | + my $sth = $dbh->prepare('START SLAVE'); |
1463 | PTDEBUG && _d($dbh, $sth->{Statement}); |
1464 | $sth->execute(); |
1465 | } |
1466 | } |
1467 | |
1468 | -sub catchup_to_master { |
1469 | - my ( $self, $slave, $master, $timeout ) = @_; |
1470 | - $self->stop_slave($master); |
1471 | - $self->stop_slave($slave); |
1472 | - my $slave_status = $self->get_slave_status($slave); |
1473 | - my $slave_pos = $self->repl_posn($slave_status); |
1474 | - my $master_status = $self->get_master_status($master); |
1475 | - my $master_pos = $self->repl_posn($master_status); |
1476 | - PTDEBUG && _d('Master position:', $self->pos_to_string($master_pos), |
1477 | - 'Slave position:', $self->pos_to_string($slave_pos)); |
1478 | - |
1479 | - my $result; |
1480 | - if ( $self->pos_cmp($slave_pos, $master_pos) < 0 ) { |
1481 | - PTDEBUG && _d('Waiting for slave to catch up to master'); |
1482 | - $self->start_slave($slave, $master_pos); |
1483 | - |
1484 | - $result = $self->wait_for_master( |
1485 | - master_status => $master_status, |
1486 | - slave_dbh => $slave, |
1487 | - timeout => $timeout, |
1488 | - master_status => $master_status |
1489 | - ); |
1490 | - if ( !defined $result->{result} ) { |
1491 | - $slave_status = $self->get_slave_status($slave); |
1492 | - if ( !$self->slave_is_running($slave_status) ) { |
1493 | - PTDEBUG && _d('Master position:', |
1494 | - $self->pos_to_string($master_pos), |
1495 | - 'Slave position:', $self->pos_to_string($slave_pos)); |
1496 | - $slave_pos = $self->repl_posn($slave_status); |
1497 | - if ( $self->pos_cmp($slave_pos, $master_pos) != 0 ) { |
1498 | - die "MASTER_POS_WAIT() returned NULL but slave has not " |
1499 | - . "caught up to master"; |
1500 | - } |
1501 | - PTDEBUG && _d('Slave is caught up to master and stopped'); |
1502 | - } |
1503 | - else { |
1504 | - die "Slave has not caught up to master and it is still running"; |
1505 | - } |
1506 | - } |
1507 | - } |
1508 | - else { |
1509 | - PTDEBUG && _d("Slave is already caught up to master"); |
1510 | - } |
1511 | - |
1512 | - return $result; |
1513 | -} |
1514 | - |
1515 | -sub catchup_to_same_pos { |
1516 | - my ( $self, $s1_dbh, $s2_dbh ) = @_; |
1517 | - $self->stop_slave($s1_dbh); |
1518 | - $self->stop_slave($s2_dbh); |
1519 | - my $s1_status = $self->get_slave_status($s1_dbh); |
1520 | - my $s2_status = $self->get_slave_status($s2_dbh); |
1521 | - my $s1_pos = $self->repl_posn($s1_status); |
1522 | - my $s2_pos = $self->repl_posn($s2_status); |
1523 | - if ( $self->pos_cmp($s1_pos, $s2_pos) < 0 ) { |
1524 | - $self->start_slave($s1_dbh, $s2_pos); |
1525 | - } |
1526 | - elsif ( $self->pos_cmp($s2_pos, $s1_pos) < 0 ) { |
1527 | - $self->start_slave($s2_dbh, $s1_pos); |
1528 | - } |
1529 | - |
1530 | - $s1_status = $self->get_slave_status($s1_dbh); |
1531 | - $s2_status = $self->get_slave_status($s2_dbh); |
1532 | - $s1_pos = $self->repl_posn($s1_status); |
1533 | - $s2_pos = $self->repl_posn($s2_status); |
1534 | - |
1535 | - if ( $self->slave_is_running($s1_status) |
1536 | - || $self->slave_is_running($s2_status) |
1537 | - || $self->pos_cmp($s1_pos, $s2_pos) != 0) |
1538 | - { |
1539 | - die "The servers aren't both stopped at the same position"; |
1540 | - } |
1541 | - |
1542 | -} |
1543 | - |
1544 | -sub slave_is_running { |
1545 | +sub _slave_is_running { |
1546 | my ( $self, $slave_status ) = @_; |
1547 | return ($slave_status->{slave_sql_running} || 'No') eq 'Yes'; |
1548 | } |
1549 | |
1550 | -sub has_slave_updates { |
1551 | - my ( $self, $dbh ) = @_; |
1552 | - my $sql = q{SHOW VARIABLES LIKE 'log_slave_updates'}; |
1553 | - PTDEBUG && _d($dbh, $sql); |
1554 | - my ($name, $value) = $dbh->selectrow_array($sql); |
1555 | - return $value && $value =~ m/^(1|ON)$/; |
1556 | -} |
1557 | - |
1558 | -sub repl_posn { |
1559 | +sub _repl_posn { |
1560 | my ( $self, $status ) = @_; |
1561 | if ( exists $status->{file} && exists $status->{position} ) { |
1562 | return { |
1563 | @@ -586,89 +1065,10 @@ |
1564 | return $stat->{seconds_behind_master}; |
1565 | } |
1566 | |
1567 | -sub pos_cmp { |
1568 | +sub _pos_cmp { |
1569 | my ( $self, $a, $b ) = @_; |
1570 | - return $self->pos_to_string($a) cmp $self->pos_to_string($b); |
1571 | -} |
1572 | - |
1573 | -sub short_host { |
1574 | - my ( $self, $dsn ) = @_; |
1575 | - my ($host, $port); |
1576 | - if ( $dsn->{master_host} ) { |
1577 | - $host = $dsn->{master_host}; |
1578 | - $port = $dsn->{master_port}; |
1579 | - } |
1580 | - else { |
1581 | - $host = $dsn->{h}; |
1582 | - $port = $dsn->{P}; |
1583 | - } |
1584 | - return ($host || '[default]') . ( ($port || 3306) == 3306 ? '' : ":$port" ); |
1585 | -} |
1586 | - |
1587 | -sub is_replication_thread { |
1588 | - my ( $self, $query, %args ) = @_; |
1589 | - return unless $query; |
1590 | - |
1591 | - my $type = lc($args{type} || 'all'); |
1592 | - die "Invalid type: $type" |
1593 | - unless $type =~ m/^binlog_dump|slave_io|slave_sql|all$/i; |
1594 | - |
1595 | - my $match = 0; |
1596 | - if ( $type =~ m/binlog_dump|all/i ) { |
1597 | - $match = 1 |
1598 | - if ($query->{Command} || $query->{command} || '') eq "Binlog Dump"; |
1599 | - } |
1600 | - if ( !$match ) { |
1601 | - if ( ($query->{User} || $query->{user} || '') eq "system user" ) { |
1602 | - PTDEBUG && _d("Slave replication thread"); |
1603 | - if ( $type ne 'all' ) { |
1604 | - my $state = $query->{State} || $query->{state} || ''; |
1605 | - |
1606 | - if ( $state =~ m/^init|end$/ ) { |
1607 | - PTDEBUG && _d("Special state:", $state); |
1608 | - $match = 1; |
1609 | - } |
1610 | - else { |
1611 | - my ($slave_sql) = $state =~ m/ |
1612 | - ^(Waiting\sfor\sthe\snext\sevent |
1613 | - |Reading\sevent\sfrom\sthe\srelay\slog |
1614 | - |Has\sread\sall\srelay\slog;\swaiting |
1615 | - |Making\stemp\sfile |
1616 | - |Waiting\sfor\sslave\smutex\son\sexit)/xi; |
1617 | - |
1618 | - $match = $type eq 'slave_sql' && $slave_sql ? 1 |
1619 | - : $type eq 'slave_io' && !$slave_sql ? 1 |
1620 | - : 0; |
1621 | - } |
1622 | - } |
1623 | - else { |
1624 | - $match = 1; |
1625 | - } |
1626 | - } |
1627 | - else { |
1628 | - PTDEBUG && _d('Not system user'); |
1629 | - } |
1630 | - |
1631 | - if ( !defined $args{check_known_ids} || $args{check_known_ids} ) { |
1632 | - my $id = $query->{Id} || $query->{id}; |
1633 | - if ( $match ) { |
1634 | - $self->{replication_thread}->{$id} = 1; |
1635 | - } |
1636 | - else { |
1637 | - if ( $self->{replication_thread}->{$id} ) { |
1638 | - PTDEBUG && _d("Thread ID is a known replication thread ID"); |
1639 | - $match = 1; |
1640 | - } |
1641 | - } |
1642 | - } |
1643 | - } |
1644 | - |
1645 | - PTDEBUG && _d('Matches', $type, 'replication thread:', |
1646 | - ($match ? 'yes' : 'no'), '; match:', $match); |
1647 | - |
1648 | - return $match; |
1649 | -} |
1650 | - |
1651 | + return $self->_pos_to_string($a) cmp $self->_pos_to_string($b); |
1652 | +} |
1653 | |
1654 | sub get_replication_filters { |
1655 | my ( $self, %args ) = @_; |
1656 | @@ -713,18 +1113,12 @@ |
1657 | } |
1658 | |
1659 | |
1660 | -sub pos_to_string { |
1661 | +sub _pos_to_string { |
1662 | my ( $self, $pos ) = @_; |
1663 | my $fmt = '%s/%020d'; |
1664 | return sprintf($fmt, @{$pos}{qw(file position)}); |
1665 | } |
1666 | |
1667 | -sub reset_known_replication_threads { |
1668 | - my ( $self ) = @_; |
1669 | - $self->{replication_thread} = {}; |
1670 | - return; |
1671 | -} |
1672 | - |
1673 | sub get_cxn_from_dsn_table { |
1674 | my ($self, %args) = @_; |
1675 | my @required_args = qw(dsn_table_dsn make_cxn); |
1676 | @@ -734,8 +1128,8 @@ |
1677 | my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
1678 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
1679 | |
1680 | - my $dp = $self->{DSNParser}; |
1681 | - my $q = $self->{Quoter}; |
1682 | + my $dp = $self->DSNParser; |
1683 | + my $q = $self->Quoter; |
1684 | |
1685 | my $dsn = $dp->parse($dsn_table_dsn); |
1686 | my $dsn_table; |
1687 | @@ -2426,12 +2820,18 @@ |
1688 | |
1689 | sub split_unquote { |
1690 | my ( $self, $db_tbl, $default_db ) = @_; |
1691 | - $db_tbl =~ s/`//g; |
1692 | my ( $db, $tbl ) = split(/[.]/, $db_tbl); |
1693 | if ( !$tbl ) { |
1694 | $tbl = $db; |
1695 | $db = $default_db; |
1696 | } |
1697 | + for ($db, $tbl) { |
1698 | + next unless $_; |
1699 | + s/\A`//; |
1700 | + s/`\z//; |
1701 | + s/``/`/g; |
1702 | + } |
1703 | + |
1704 | return ($db, $tbl); |
1705 | } |
1706 | |
1707 | @@ -2524,16 +2924,20 @@ |
1708 | $Data::Dumper::Sortkeys = 1; |
1709 | $Data::Dumper::Quotekeys = 0; |
1710 | |
1711 | +local $EVAL_ERROR; |
1712 | +eval { |
1713 | + require Quoter; |
1714 | +}; |
1715 | + |
1716 | sub new { |
1717 | my ( $class, %args ) = @_; |
1718 | - my @required_args = qw(Quoter); |
1719 | - foreach my $arg ( @required_args ) { |
1720 | - die "I need a $arg argument" unless $args{$arg}; |
1721 | - } |
1722 | my $self = { %args }; |
1723 | + $self->{Quoter} ||= Quoter->new(); |
1724 | return bless $self, $class; |
1725 | } |
1726 | |
1727 | +sub Quoter { shift->{Quoter} } |
1728 | + |
1729 | sub get_create_table { |
1730 | my ( $self, $dbh, $db, $tbl ) = @_; |
1731 | die "I need a dbh parameter" unless $dbh; |
1732 | |
1733 | === modified file 'bin/pt-kill' |
1734 | --- bin/pt-kill 2012-11-09 21:41:32 +0000 |
1735 | +++ bin/pt-kill 2012-11-14 08:53:21 +0000 |
1736 | @@ -15,14 +15,12 @@ |
1737 | $INC{$_} = __FILE__ for map { (my $pkg = "$_.pm") =~ s!::!/!g; $pkg } (qw( |
1738 | Percona::Toolkit |
1739 | OptionParser |
1740 | - Mo |
1741 | DSNParser |
1742 | Daemon |
1743 | Transformers |
1744 | TableParser |
1745 | Processlist |
1746 | TextResultSetParser |
1747 | - MasterSlave |
1748 | Quoter |
1749 | QueryRewriter |
1750 | Retry |
1751 | @@ -1077,457 +1075,6 @@ |
1752 | # ########################################################################### |
1753 | |
1754 | # ########################################################################### |
1755 | -# Mo package |
1756 | -# This package is a copy without comments from the original. The original |
1757 | -# with comments and its test file can be found in the Bazaar repository at, |
1758 | -# lib/Mo.pm |
1759 | -# t/lib/Mo.t |
1760 | -# See https://launchpad.net/percona-toolkit for more information. |
1761 | -# ########################################################################### |
1762 | -{ |
1763 | -BEGIN { |
1764 | -$INC{"Mo.pm"} = __FILE__; |
1765 | -package Mo; |
1766 | -our $VERSION = '0.30_Percona'; # Forked from 0.30 of Mo. |
1767 | - |
1768 | -{ |
1769 | - no strict 'refs'; |
1770 | - sub _glob_for { |
1771 | - return \*{shift()} |
1772 | - } |
1773 | - |
1774 | - sub _stash_for { |
1775 | - return \%{ shift() . "::" }; |
1776 | - } |
1777 | -} |
1778 | - |
1779 | -use strict; |
1780 | -use warnings qw( FATAL all ); |
1781 | - |
1782 | -use Carp (); |
1783 | -use Scalar::Util qw(looks_like_number blessed); |
1784 | - |
1785 | - |
1786 | -our %TYPES = ( |
1787 | - Bool => sub { !$_[0] || (defined $_[0] && looks_like_number($_[0]) && $_[0] == 1) }, |
1788 | - Num => sub { defined $_[0] && looks_like_number($_[0]) }, |
1789 | - Int => sub { defined $_[0] && looks_like_number($_[0]) && $_[0] == int($_[0]) }, |
1790 | - Str => sub { defined $_[0] }, |
1791 | - Object => sub { defined $_[0] && blessed($_[0]) }, |
1792 | - FileHandle => sub { local $@; require IO::Handle; fileno($_[0]) && $_[0]->opened }, |
1793 | - |
1794 | - map { |
1795 | - my $type = /R/ ? $_ : uc $_; |
1796 | - $_ . "Ref" => sub { ref $_[0] eq $type } |
1797 | - } qw(Array Code Hash Regexp Glob Scalar) |
1798 | -); |
1799 | - |
1800 | -our %metadata_for; |
1801 | -{ |
1802 | - package Mo::Object; |
1803 | - |
1804 | - sub new { |
1805 | - my $class = shift; |
1806 | - my $args = $class->BUILDARGS(@_); |
1807 | - |
1808 | - my @args_to_delete; |
1809 | - while ( my ($attr, $meta) = each %{$metadata_for{$class}} ) { |
1810 | - next unless exists $meta->{init_arg}; |
1811 | - my $init_arg = $meta->{init_arg}; |
1812 | - |
1813 | - if ( defined $init_arg ) { |
1814 | - $args->{$attr} = delete $args->{$init_arg}; |
1815 | - } |
1816 | - else { |
1817 | - push @args_to_delete, $attr; |
1818 | - } |
1819 | - } |
1820 | - |
1821 | - delete $args->{$_} for @args_to_delete; |
1822 | - |
1823 | - for my $attribute ( keys %$args ) { |
1824 | - if ( my $coerce = $metadata_for{$class}{$attribute}{coerce} ) { |
1825 | - $args->{$attribute} = $coerce->($args->{$attribute}); |
1826 | - } |
1827 | - if ( my $I = $metadata_for{$class}{$attribute}{isa} ) { |
1828 | - ( (my $I_name), $I ) = @{$I}; |
1829 | - Mo::_check_type_constaints($attribute, $I, $I_name, $args->{$attribute}); |
1830 | - } |
1831 | - } |
1832 | - |
1833 | - while ( my ($attribute, $meta) = each %{$metadata_for{$class}} ) { |
1834 | - next unless $meta->{required}; |
1835 | - Carp::confess("Attribute ($attribute) is required for $class") |
1836 | - if ! exists $args->{$attribute} |
1837 | - } |
1838 | - |
1839 | - @_ = %$args; |
1840 | - my $self = bless $args, $class; |
1841 | - |
1842 | - my @build_subs; |
1843 | - my $linearized_isa = mro::get_linear_isa($class); |
1844 | - |
1845 | - for my $isa_class ( @$linearized_isa ) { |
1846 | - unshift @build_subs, *{ Mo::_glob_for "${isa_class}::BUILD" }{CODE}; |
1847 | - } |
1848 | - exists &$_ && $_->( $self, @_ ) for grep { defined } @build_subs; |
1849 | - return $self; |
1850 | - } |
1851 | - |
1852 | - sub BUILDARGS { |
1853 | - shift; |
1854 | - my $ref; |
1855 | - if ( @_ == 1 && ref($_[0]) ) { |
1856 | - Carp::confess("Single parameters to new() must be a HASH ref") |
1857 | - unless ref($_[0]) eq ref({}); |
1858 | - $ref = {%{$_[0]}} # We want a new reference, always |
1859 | - } |
1860 | - else { |
1861 | - $ref = { @_ }; |
1862 | - } |
1863 | - return $ref; |
1864 | - } |
1865 | -} |
1866 | - |
1867 | -my %export_for; |
1868 | -sub Mo::import { |
1869 | - warnings->import(qw(FATAL all)); |
1870 | - strict->import(); |
1871 | - |
1872 | - my $caller = scalar caller(); # Caller's package |
1873 | - my $caller_pkg = $caller . "::"; # Caller's package with :: at the end |
1874 | - my (%exports, %options); |
1875 | - |
1876 | - my (undef, @features) = @_; |
1877 | - my %ignore = ( map { $_ => 1 } qw( is isa init_arg builder buildargs clearer predicate build handles default required ) ); |
1878 | - for my $feature (grep { !$ignore{$_} } @features) { |
1879 | - { local $@; require "Mo/$feature.pm"; } |
1880 | - { |
1881 | - no strict 'refs'; |
1882 | - &{"Mo::${feature}::e"}( |
1883 | - $caller_pkg, |
1884 | - \%exports, |
1885 | - \%options, |
1886 | - \@_ |
1887 | - ); |
1888 | - } |
1889 | - } |
1890 | - |
1891 | - return if $exports{M}; |
1892 | - |
1893 | - %exports = ( |
1894 | - extends => sub { |
1895 | - for my $class ( map { "$_" } @_ ) { |
1896 | - $class =~ s{::|'}{/}g; |
1897 | - { local $@; eval { require "$class.pm" } } # or warn $@; |
1898 | - } |
1899 | - _set_package_isa($caller, @_); |
1900 | - _set_inherited_metadata($caller); |
1901 | - }, |
1902 | - has => sub { |
1903 | - my $names = shift; |
1904 | - for my $attribute ( ref $names ? @$names : $names ) { |
1905 | - my %args = @_; |
1906 | - my $method = ($args{is} || '') eq 'ro' |
1907 | - ? sub { |
1908 | - Carp::confess("Cannot assign a value to a read-only accessor at reader ${caller_pkg}${attribute}") |
1909 | - if $#_; |
1910 | - return $_[0]{$attribute}; |
1911 | - } |
1912 | - : sub { |
1913 | - return $#_ |
1914 | - ? $_[0]{$attribute} = $_[1] |
1915 | - : $_[0]{$attribute}; |
1916 | - }; |
1917 | - |
1918 | - $metadata_for{$caller}{$attribute} = (); |
1919 | - |
1920 | - if ( my $I = $args{isa} ) { |
1921 | - my $orig_I = $I; |
1922 | - my $type; |
1923 | - if ( $I =~ /\A(ArrayRef|Maybe)\[(.*)\]\z/ ) { |
1924 | - $I = _nested_constraints($attribute, $1, $2); |
1925 | - } |
1926 | - $metadata_for{$caller}{$attribute}{isa} = [$orig_I, $I]; |
1927 | - my $orig_method = $method; |
1928 | - $method = sub { |
1929 | - if ( $#_ ) { |
1930 | - Mo::_check_type_constaints($attribute, $I, $orig_I, $_[1]); |
1931 | - } |
1932 | - goto &$orig_method; |
1933 | - }; |
1934 | - } |
1935 | - |
1936 | - if ( my $builder = $args{builder} ) { |
1937 | - my $original_method = $method; |
1938 | - $method = sub { |
1939 | - $#_ |
1940 | - ? goto &$original_method |
1941 | - : ! exists $_[0]{$attribute} |
1942 | - ? $_[0]{$attribute} = $_[0]->$builder |
1943 | - : goto &$original_method |
1944 | - }; |
1945 | - } |
1946 | - |
1947 | - if ( my $code = $args{default} ) { |
1948 | - Carp::confess("${caller}::${attribute}'s default is $code, but should be a coderef") |
1949 | - unless ref($code) eq 'CODE'; |
1950 | - my $original_method = $method; |
1951 | - $method = sub { |
1952 | - $#_ |
1953 | - ? goto &$original_method |
1954 | - : ! exists $_[0]{$attribute} |
1955 | - ? $_[0]{$attribute} = $_[0]->$code |
1956 | - : goto &$original_method |
1957 | - }; |
1958 | - } |
1959 | - |
1960 | - if ( my $role = $args{does} ) { |
1961 | - my $original_method = $method; |
1962 | - $method = sub { |
1963 | - if ( $#_ ) { |
1964 | - Carp::confess(qq<Attribute ($attribute) doesn't consume a '$role' role">) |
1965 | - unless Scalar::Util::blessed($_[1]) && eval { $_[1]->does($role) } |
1966 | - } |
1967 | - goto &$original_method |
1968 | - }; |
1969 | - } |
1970 | - |
1971 | - if ( my $coercion = $args{coerce} ) { |
1972 | - $metadata_for{$caller}{$attribute}{coerce} = $coercion; |
1973 | - my $original_method = $method; |
1974 | - $method = sub { |
1975 | - if ( $#_ ) { |
1976 | - return $original_method->($_[0], $coercion->($_[1])) |
1977 | - } |
1978 | - goto &$original_method; |
1979 | - } |
1980 | - } |
1981 | - |
1982 | - $method = $options{$_}->($method, $attribute, @_) |
1983 | - for sort keys %options; |
1984 | - |
1985 | - *{ _glob_for "${caller}::$attribute" } = $method; |
1986 | - |
1987 | - if ( $args{required} ) { |
1988 | - $metadata_for{$caller}{$attribute}{required} = 1; |
1989 | - } |
1990 | - |
1991 | - if ($args{clearer}) { |
1992 | - *{ _glob_for "${caller}::$args{clearer}" } |
1993 | - = sub { delete shift->{$attribute} } |
1994 | - } |
1995 | - |
1996 | - if ($args{predicate}) { |
1997 | - *{ _glob_for "${caller}::$args{predicate}" } |
1998 | - = sub { exists shift->{$attribute} } |
1999 | - } |
2000 | - |
2001 | - if ($args{handles}) { |
2002 | - _has_handles($caller, $attribute, \%args); |
2003 | - } |
2004 | - |
2005 | - if (exists $args{init_arg}) { |
2006 | - $metadata_for{$caller}{$attribute}{init_arg} = $args{init_arg}; |
2007 | - } |
2008 | - } |
2009 | - }, |
2010 | - %exports, |
2011 | - ); |
2012 | - |
2013 | - $export_for{$caller} = [ keys %exports ]; |
2014 | - |
2015 | - for my $keyword ( keys %exports ) { |
2016 | - *{ _glob_for "${caller}::$keyword" } = $exports{$keyword} |
2017 | - } |
2018 | - *{ _glob_for "${caller}::extends" }{CODE}->( "Mo::Object" ) |
2019 | - unless @{ *{ _glob_for "${caller}::ISA" }{ARRAY} || [] }; |
2020 | -}; |
2021 | - |
2022 | -sub _check_type_constaints { |
2023 | - my ($attribute, $I, $I_name, $val) = @_; |
2024 | - ( ref($I) eq 'CODE' |
2025 | - ? $I->($val) |
2026 | - : (ref $val eq $I |
2027 | - || ($val && $val eq $I) |
2028 | - || (exists $TYPES{$I} && $TYPES{$I}->($val))) |
2029 | - ) |
2030 | - || Carp::confess( |
2031 | - qq<Attribute ($attribute) does not pass the type constraint because: > |
2032 | - . qq<Validation failed for '$I_name' with value > |
2033 | - . (defined $val ? Mo::Dumper($val) : 'undef') ) |
2034 | -} |
2035 | - |
2036 | -sub _has_handles { |
2037 | - my ($caller, $attribute, $args) = @_; |
2038 | - my $handles = $args->{handles}; |
2039 | - |
2040 | - my $ref = ref $handles; |
2041 | - my $kv; |
2042 | - if ( $ref eq ref [] ) { |
2043 | - $kv = { map { $_,$_ } @{$handles} }; |
2044 | - } |
2045 | - elsif ( $ref eq ref {} ) { |
2046 | - $kv = $handles; |
2047 | - } |
2048 | - elsif ( $ref eq ref qr// ) { |
2049 | - Carp::confess("Cannot delegate methods based on a Regexp without a type constraint (isa)") |
2050 | - unless $args->{isa}; |
2051 | - my $target_class = $args->{isa}; |
2052 | - $kv = { |
2053 | - map { $_, $_ } |
2054 | - grep { $_ =~ $handles } |
2055 | - grep { !exists $Mo::Object::{$_} && $target_class->can($_) } |
2056 | - grep { $_ ne 'has' && $_ ne 'extends' } |
2057 | - keys %{ _stash_for $target_class } |
2058 | - }; |
2059 | - } |
2060 | - else { |
2061 | - Carp::confess("handles for $ref not yet implemented"); |
2062 | - } |
2063 | - |
2064 | - while ( my ($method, $target) = each %{$kv} ) { |
2065 | - my $name = _glob_for "${caller}::$method"; |
2066 | - Carp::confess("You cannot overwrite a locally defined method ($method) with a delegation") |
2067 | - if defined &$name; |
2068 | - |
2069 | - my ($target, @curried_args) = ref($target) ? @$target : $target; |
2070 | - *$name = sub { |
2071 | - my $self = shift; |
2072 | - my $delegate_to = $self->$attribute(); |
2073 | - my $error = "Cannot delegate $method to $target because the value of $attribute"; |
2074 | - Carp::confess("$error is not defined") unless $delegate_to; |
2075 | - Carp::confess("$error is not an object (got '$delegate_to')") |
2076 | - unless Scalar::Util::blessed($delegate_to) || (!ref($delegate_to) && $delegate_to->can($target)); |
2077 | - return $delegate_to->$target(@curried_args, @_); |
2078 | - } |
2079 | - } |
2080 | -} |
2081 | - |
2082 | -sub _nested_constraints { |
2083 | - my ($attribute, $aggregate_type, $type) = @_; |
2084 | - |
2085 | - my $inner_types; |
2086 | - if ( $type =~ /\A(ArrayRef|Maybe)\[(.*)\]\z/ ) { |
2087 | - $inner_types = _nested_constraints($1, $2); |
2088 | - } |
2089 | - else { |
2090 | - $inner_types = $TYPES{$type}; |
2091 | - } |
2092 | - |
2093 | - if ( $aggregate_type eq 'ArrayRef' ) { |
2094 | - return sub { |
2095 | - my ($val) = @_; |
2096 | - return unless ref($val) eq ref([]); |
2097 | - |
2098 | - if ($inner_types) { |
2099 | - for my $value ( @{$val} ) { |
2100 | - return unless $inner_types->($value) |
2101 | - } |
2102 | - } |
2103 | - else { |
2104 | - for my $value ( @{$val} ) { |
2105 | - return unless $value && ($value eq $type |
2106 | - || (Scalar::Util::blessed($value) && $value->isa($type))); |
2107 | - } |
2108 | - } |
2109 | - return 1; |
2110 | - }; |
2111 | - } |
2112 | - elsif ( $aggregate_type eq 'Maybe' ) { |
2113 | - return sub { |
2114 | - my ($value) = @_; |
2115 | - return 1 if ! defined($value); |
2116 | - if ($inner_types) { |
2117 | - return unless $inner_types->($value) |
2118 | - } |
2119 | - else { |
2120 | - return unless $value eq $type |
2121 | - || (Scalar::Util::blessed($value) && $value->isa($type)); |
2122 | - } |
2123 | - return 1; |
2124 | - } |
2125 | - } |
2126 | - else { |
2127 | - Carp::confess("Nested aggregate types are only implemented for ArrayRefs and Maybe"); |
2128 | - } |
2129 | -} |
2130 | - |
2131 | -sub _set_package_isa { |
2132 | - my ($package, @new_isa) = @_; |
2133 | - |
2134 | - *{ _glob_for "${package}::ISA" } = [@new_isa]; |
2135 | -} |
2136 | - |
2137 | -sub _set_inherited_metadata { |
2138 | - my $class = shift; |
2139 | - my $linearized_isa = mro::get_linear_isa($class); |
2140 | - my %new_metadata; |
2141 | - |
2142 | - for my $isa_class (reverse @$linearized_isa) { |
2143 | - %new_metadata = ( |
2144 | - %new_metadata, |
2145 | - %{ $metadata_for{$isa_class} || {} }, |
2146 | - ); |
2147 | - } |
2148 | - $metadata_for{$class} = \%new_metadata; |
2149 | -} |
2150 | - |
2151 | -sub unimport { |
2152 | - my $caller = scalar caller(); |
2153 | - my $stash = _stash_for( $caller ); |
2154 | - |
2155 | - delete $stash->{$_} for @{$export_for{$caller}}; |
2156 | -} |
2157 | - |
2158 | -sub Dumper { |
2159 | - require Data::Dumper; |
2160 | - local $Data::Dumper::Indent = 0; |
2161 | - local $Data::Dumper::Sortkeys = 0; |
2162 | - local $Data::Dumper::Quotekeys = 0; |
2163 | - local $Data::Dumper::Terse = 1; |
2164 | - |
2165 | - Data::Dumper::Dumper(@_) |
2166 | -} |
2167 | - |
2168 | -BEGIN { |
2169 | - if ($] >= 5.010) { |
2170 | - { local $@; require mro; } |
2171 | - } |
2172 | - else { |
2173 | - local $@; |
2174 | - eval { |
2175 | - require MRO::Compat; |
2176 | - } or do { |
2177 | - *mro::get_linear_isa = *mro::get_linear_isa_dfs = sub { |
2178 | - no strict 'refs'; |
2179 | - |
2180 | - my $classname = shift; |
2181 | - |
2182 | - my @lin = ($classname); |
2183 | - my %stored; |
2184 | - foreach my $parent (@{"$classname\::ISA"}) { |
2185 | - my $plin = mro::get_linear_isa_dfs($parent); |
2186 | - foreach (@$plin) { |
2187 | - next if exists $stored{$_}; |
2188 | - push(@lin, $_); |
2189 | - $stored{$_} = 1; |
2190 | - } |
2191 | - } |
2192 | - return \@lin; |
2193 | - }; |
2194 | - } |
2195 | - } |
2196 | -} |
2197 | - |
2198 | -} |
2199 | -1; |
2200 | -} |
2201 | -# ########################################################################### |
2202 | -# End Mo package |
2203 | -# ########################################################################### |
2204 | - |
2205 | -# ########################################################################### |
2206 | # DSNParser package |
2207 | # This package is a copy without comments from the original. The original |
2208 | # with comments and its test file can be found in the Bazaar repository at, |
2209 | @@ -2905,7 +2452,7 @@ |
2210 | |
2211 | sub new { |
2212 | my ( $class, %args ) = @_; |
2213 | - foreach my $arg ( qw(MasterSlave) ) { |
2214 | + foreach my $arg ( qw() ) { |
2215 | die "I need a $arg argument" unless $args{$arg}; |
2216 | } |
2217 | my $self = { |
2218 | @@ -3105,7 +2652,6 @@ |
2219 | sub find { |
2220 | my ( $self, $proclist, %find_spec ) = @_; |
2221 | PTDEBUG && _d('find specs:', Dumper(\%find_spec)); |
2222 | - my $ms = $self->{MasterSlave}; |
2223 | |
2224 | my @matches; |
2225 | QUERY: |
2226 | @@ -3114,7 +2660,7 @@ |
2227 | my $matched = 0; |
2228 | |
2229 | if ( !$find_spec{replication_threads} |
2230 | - && $ms->is_replication_thread($query) ) { |
2231 | + && $self->is_replication_thread($query) ) { |
2232 | PTDEBUG && _d('Skipping replication thread'); |
2233 | next QUERY; |
2234 | } |
2235 | @@ -3214,6 +2760,75 @@ |
2236 | && $query->{Info} =~ m/$property/; |
2237 | } |
2238 | |
2239 | +sub is_replication_thread { |
2240 | + my ( $self, $query, %args ) = @_; |
2241 | + return unless $query; |
2242 | + |
2243 | + my $type = lc($args{type} || 'all'); |
2244 | + die "Invalid type: $type" |
2245 | + unless $type =~ m/^(?:binlog_dump|slave_io|slave_sql|all)$/i; |
2246 | + |
2247 | + my $match = 0; |
2248 | + if ( $type =~ m/binlog_dump|all/i ) { |
2249 | + $match = 1 |
2250 | + if ($query->{Command} || $query->{command} || '') eq "Binlog Dump"; |
2251 | + } |
2252 | + if ( !$match ) { |
2253 | + if ( lc($query->{User} || $query->{user} || '') eq "system user" ) { |
2254 | + PTDEBUG && _d("Slave replication thread"); |
2255 | + if ( $type ne 'all' ) { |
2256 | + my $state = $query->{State} || $query->{state} || ''; |
2257 | + |
2258 | + if ( $state =~ m/^(?:init|end)$/ ) { |
2259 | + PTDEBUG && _d("Special state:", $state); |
2260 | + $match = 1; |
2261 | + } |
2262 | + else { |
2263 | + my ($slave_sql) = $state =~ m/ |
2264 | + ^(Waiting\sfor\sthe\snext\sevent |
2265 | + |Reading\sevent\sfrom\sthe\srelay\slog |
2266 | + |Has\sread\sall\srelay\slog;\swaiting |
2267 | + |Making\stemp\sfile |
2268 | + |Waiting\sfor\sslave\smutex\son\sexit)/xi; |
2269 | + |
2270 | + $match = $type eq 'slave_sql' && $slave_sql ? 1 |
2271 | + : $type eq 'slave_io' && !$slave_sql ? 1 |
2272 | + : 0; |
2273 | + } |
2274 | + } |
2275 | + else { |
2276 | + $match = 1; |
2277 | + } |
2278 | + } |
2279 | + else { |
2280 | + PTDEBUG && _d('Not system user'); |
2281 | + } |
2282 | + |
2283 | + if ( !defined $args{check_known_ids} || $args{check_known_ids} ) { |
2284 | + my $id = $query->{Id} || $query->{id}; |
2285 | + if ( $match ) { |
2286 | + $self->{_replication_thread}->{$id} = 1; |
2287 | + } |
2288 | + else { |
2289 | + if ( $self->{_replication_thread}->{$id} ) { |
2290 | + PTDEBUG && _d("Thread ID is a known replication thread ID"); |
2291 | + $match = 1; |
2292 | + } |
2293 | + } |
2294 | + } |
2295 | + } |
2296 | + |
2297 | + PTDEBUG && _d('Matches', $type, 'replication thread:', |
2298 | + ($match ? 'yes' : 'no'), '; match:', $match); |
2299 | + |
2300 | + return $match; |
2301 | +} |
2302 | + |
2303 | +sub reset_known_replication_threads { |
2304 | + my ($self) = @_; |
2305 | + $self->{_replication_thread} = {}; |
2306 | +} |
2307 | + |
2308 | sub _d { |
2309 | my ($package, undef, $line) = caller 0; |
2310 | @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
2311 | @@ -3373,740 +2988,6 @@ |
2312 | # ########################################################################### |
2313 | |
2314 | # ########################################################################### |
2315 | -# MasterSlave package |
2316 | -# This package is a copy without comments from the original. The original |
2317 | -# with comments and its test file can be found in the Bazaar repository at, |
2318 | -# lib/MasterSlave.pm |
2319 | -# t/lib/MasterSlave.t |
2320 | -# See https://launchpad.net/percona-toolkit for more information. |
2321 | -# ########################################################################### |
2322 | -{ |
2323 | -package MasterSlave; |
2324 | - |
2325 | -use strict; |
2326 | -use warnings FATAL => 'all'; |
2327 | -use English qw(-no_match_vars); |
2328 | -use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
2329 | - |
2330 | -sub check_recursion_method { |
2331 | - my ($methods) = @_; |
2332 | - |
2333 | - if ( @$methods != 1 ) { |
2334 | - if ( grep({ !m/processlist|hosts/i } @$methods) |
2335 | - && $methods->[0] !~ /^dsn=/i ) |
2336 | - { |
2337 | - die "Invalid combination of recursion methods: " |
2338 | - . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
2339 | - . "Only hosts and processlist may be combined.\n" |
2340 | - } |
2341 | - } |
2342 | - else { |
2343 | - my ($method) = @$methods; |
2344 | - die "Invalid recursion method: " . ( $method || 'undef' ) |
2345 | - unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
2346 | - } |
2347 | -} |
2348 | - |
2349 | -sub new { |
2350 | - my ( $class, %args ) = @_; |
2351 | - my @required_args = qw(OptionParser DSNParser Quoter); |
2352 | - foreach my $arg ( @required_args ) { |
2353 | - die "I need a $arg argument" unless $args{$arg}; |
2354 | - } |
2355 | - my $self = { |
2356 | - %args, |
2357 | - replication_thread => {}, |
2358 | - }; |
2359 | - return bless $self, $class; |
2360 | -} |
2361 | - |
2362 | -sub get_slaves { |
2363 | - my ($self, %args) = @_; |
2364 | - my @required_args = qw(make_cxn); |
2365 | - foreach my $arg ( @required_args ) { |
2366 | - die "I need a $arg argument" unless $args{$arg}; |
2367 | - } |
2368 | - my ($make_cxn) = @args{@required_args}; |
2369 | - |
2370 | - my $slaves = []; |
2371 | - my $dp = $self->{DSNParser}; |
2372 | - my $methods = $self->_resolve_recursion_methods($args{dsn}); |
2373 | - |
2374 | - if ( grep { m/processlist|hosts/i } @$methods ) { |
2375 | - my @required_args = qw(dbh dsn); |
2376 | - foreach my $arg ( @required_args ) { |
2377 | - die "I need a $arg argument" unless $args{$arg}; |
2378 | - } |
2379 | - my ($dbh, $dsn) = @args{@required_args}; |
2380 | - |
2381 | - $self->recurse_to_slaves( |
2382 | - { dbh => $dbh, |
2383 | - dsn => $dsn, |
2384 | - callback => sub { |
2385 | - my ( $dsn, $dbh, $level, $parent ) = @_; |
2386 | - return unless $level; |
2387 | - PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
2388 | - push @$slaves, $make_cxn->(dsn => $dsn, dbh => $dbh); |
2389 | - return; |
2390 | - }, |
2391 | - } |
2392 | - ); |
2393 | - } |
2394 | - elsif ( $methods->[0] =~ m/^dsn=/i ) { |
2395 | - (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
2396 | - $slaves = $self->get_cxn_from_dsn_table( |
2397 | - %args, |
2398 | - dsn_table_dsn => $dsn_table_dsn, |
2399 | - ); |
2400 | - } |
2401 | - elsif ( $methods->[0] =~ m/none/i ) { |
2402 | - PTDEBUG && _d('Not getting to slaves'); |
2403 | - } |
2404 | - else { |
2405 | - die "Unexpected recursion methods: @$methods"; |
2406 | - } |
2407 | - |
2408 | - return $slaves; |
2409 | -} |
2410 | - |
2411 | -sub _resolve_recursion_methods { |
2412 | - my ($self, $dsn) = @_; |
2413 | - my $o = $self->{OptionParser}; |
2414 | - if ( $o->got('recursion-method') ) { |
2415 | - return $o->get('recursion-method'); |
2416 | - } |
2417 | - elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
2418 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
2419 | - return [qw(hosts)]; |
2420 | - } |
2421 | - else { |
2422 | - return $o->get('recursion-method'); |
2423 | - } |
2424 | -} |
2425 | - |
2426 | -sub recurse_to_slaves { |
2427 | - my ( $self, $args, $level ) = @_; |
2428 | - $level ||= 0; |
2429 | - my $dp = $self->{DSNParser}; |
2430 | - my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
2431 | - my $dsn = $args->{dsn}; |
2432 | - |
2433 | - my $methods = $self->_resolve_recursion_methods($dsn); |
2434 | - PTDEBUG && _d('Recursion methods:', @$methods); |
2435 | - if ( lc($methods->[0]) eq 'none' ) { |
2436 | - PTDEBUG && _d('Not recursing to slaves'); |
2437 | - return; |
2438 | - } |
2439 | - |
2440 | - my $dbh; |
2441 | - eval { |
2442 | - $dbh = $args->{dbh} || $dp->get_dbh( |
2443 | - $dp->get_cxn_params($dsn), { AutoCommit => 1 }); |
2444 | - PTDEBUG && _d('Connected to', $dp->as_string($dsn)); |
2445 | - }; |
2446 | - if ( $EVAL_ERROR ) { |
2447 | - print STDERR "Cannot connect to ", $dp->as_string($dsn), "\n" |
2448 | - or die "Cannot print: $OS_ERROR"; |
2449 | - return; |
2450 | - } |
2451 | - |
2452 | - my $sql = 'SELECT @@SERVER_ID'; |
2453 | - PTDEBUG && _d($sql); |
2454 | - my ($id) = $dbh->selectrow_array($sql); |
2455 | - PTDEBUG && _d('Working on server ID', $id); |
2456 | - my $master_thinks_i_am = $dsn->{server_id}; |
2457 | - if ( !defined $id |
2458 | - || ( defined $master_thinks_i_am && $master_thinks_i_am != $id ) |
2459 | - || $args->{server_ids_seen}->{$id}++ |
2460 | - ) { |
2461 | - PTDEBUG && _d('Server ID seen, or not what master said'); |
2462 | - if ( $args->{skip_callback} ) { |
2463 | - $args->{skip_callback}->($dsn, $dbh, $level, $args->{parent}); |
2464 | - } |
2465 | - return; |
2466 | - } |
2467 | - |
2468 | - $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
2469 | - |
2470 | - if ( !defined $recurse || $level < $recurse ) { |
2471 | - |
2472 | - my @slaves = |
2473 | - grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
2474 | - $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
2475 | - |
2476 | - foreach my $slave ( @slaves ) { |
2477 | - PTDEBUG && _d('Recursing from', |
2478 | - $dp->as_string($dsn), 'to', $dp->as_string($slave)); |
2479 | - $self->recurse_to_slaves( |
2480 | - { %$args, dsn => $slave, dbh => undef, parent => $dsn }, $level + 1 ); |
2481 | - } |
2482 | - } |
2483 | -} |
2484 | - |
2485 | -sub find_slave_hosts { |
2486 | - my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
2487 | - |
2488 | - PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
2489 | - 'using methods', @$methods); |
2490 | - |
2491 | - my @slaves; |
2492 | - METHOD: |
2493 | - foreach my $method ( @$methods ) { |
2494 | - my $find_slaves = "_find_slaves_by_$method"; |
2495 | - PTDEBUG && _d('Finding slaves with', $find_slaves); |
2496 | - @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
2497 | - last METHOD if @slaves; |
2498 | - } |
2499 | - |
2500 | - PTDEBUG && _d('Found', scalar(@slaves), 'slaves'); |
2501 | - return @slaves; |
2502 | -} |
2503 | - |
2504 | -sub _find_slaves_by_processlist { |
2505 | - my ( $self, $dsn_parser, $dbh, $dsn ) = @_; |
2506 | - |
2507 | - my @slaves = map { |
2508 | - my $slave = $dsn_parser->parse("h=$_", $dsn); |
2509 | - $slave->{source} = 'processlist'; |
2510 | - $slave; |
2511 | - } |
2512 | - grep { $_ } |
2513 | - map { |
2514 | - my ( $host ) = $_->{host} =~ m/^([^:]+):/; |
2515 | - if ( $host eq 'localhost' ) { |
2516 | - $host = '127.0.0.1'; # Replication never uses sockets. |
2517 | - } |
2518 | - $host; |
2519 | - } $self->get_connected_slaves($dbh); |
2520 | - |
2521 | - return @slaves; |
2522 | -} |
2523 | - |
2524 | -sub _find_slaves_by_hosts { |
2525 | - my ( $self, $dsn_parser, $dbh, $dsn ) = @_; |
2526 | - |
2527 | - my @slaves; |
2528 | - my $sql = 'SHOW SLAVE HOSTS'; |
2529 | - PTDEBUG && _d($dbh, $sql); |
2530 | - @slaves = @{$dbh->selectall_arrayref($sql, { Slice => {} })}; |
2531 | - |
2532 | - if ( @slaves ) { |
2533 | - PTDEBUG && _d('Found some SHOW SLAVE HOSTS info'); |
2534 | - @slaves = map { |
2535 | - my %hash; |
2536 | - @hash{ map { lc $_ } keys %$_ } = values %$_; |
2537 | - my $spec = "h=$hash{host},P=$hash{port}" |
2538 | - . ( $hash{user} ? ",u=$hash{user}" : '') |
2539 | - . ( $hash{password} ? ",p=$hash{password}" : ''); |
2540 | - my $dsn = $dsn_parser->parse($spec, $dsn); |
2541 | - $dsn->{server_id} = $hash{server_id}; |
2542 | - $dsn->{master_id} = $hash{master_id}; |
2543 | - $dsn->{source} = 'hosts'; |
2544 | - $dsn; |
2545 | - } @slaves; |
2546 | - } |
2547 | - |
2548 | - return @slaves; |
2549 | -} |
2550 | - |
2551 | -sub get_connected_slaves { |
2552 | - my ( $self, $dbh ) = @_; |
2553 | - |
2554 | - my $show = "SHOW GRANTS FOR "; |
2555 | - my $user = 'CURRENT_USER()'; |
2556 | - my $sql = $show . $user; |
2557 | - PTDEBUG && _d($dbh, $sql); |
2558 | - |
2559 | - my $proc; |
2560 | - eval { |
2561 | - $proc = grep { |
2562 | - m/ALL PRIVILEGES.*?\*\.\*|PROCESS/ |
2563 | - } @{$dbh->selectcol_arrayref($sql)}; |
2564 | - }; |
2565 | - if ( $EVAL_ERROR ) { |
2566 | - |
2567 | - if ( $EVAL_ERROR =~ m/no such grant defined for user/ ) { |
2568 | - PTDEBUG && _d('Retrying SHOW GRANTS without host; error:', |
2569 | - $EVAL_ERROR); |
2570 | - ($user) = split('@', $user); |
2571 | - $sql = $show . $user; |
2572 | - PTDEBUG && _d($sql); |
2573 | - eval { |
2574 | - $proc = grep { |
2575 | - m/ALL PRIVILEGES.*?\*\.\*|PROCESS/ |
2576 | - } @{$dbh->selectcol_arrayref($sql)}; |
2577 | - }; |
2578 | - } |
2579 | - |
2580 | - die "Failed to $sql: $EVAL_ERROR" if $EVAL_ERROR; |
2581 | - } |
2582 | - if ( !$proc ) { |
2583 | - die "You do not have the PROCESS privilege"; |
2584 | - } |
2585 | - |
2586 | - $sql = 'SHOW PROCESSLIST'; |
2587 | - PTDEBUG && _d($dbh, $sql); |
2588 | - grep { $_->{command} =~ m/Binlog Dump/i } |
2589 | - map { # Lowercase the column names |
2590 | - my %hash; |
2591 | - @hash{ map { lc $_ } keys %$_ } = values %$_; |
2592 | - \%hash; |
2593 | - } |
2594 | - @{$dbh->selectall_arrayref($sql, { Slice => {} })}; |
2595 | -} |
2596 | - |
2597 | -sub is_master_of { |
2598 | - my ( $self, $master, $slave ) = @_; |
2599 | - my $master_status = $self->get_master_status($master) |
2600 | - or die "The server specified as a master is not a master"; |
2601 | - my $slave_status = $self->get_slave_status($slave) |
2602 | - or die "The server specified as a slave is not a slave"; |
2603 | - my @connected = $self->get_connected_slaves($master) |
2604 | - or die "The server specified as a master has no connected slaves"; |
2605 | - my (undef, $port) = $master->selectrow_array("SHOW VARIABLES LIKE 'port'"); |
2606 | - |
2607 | - if ( $port != $slave_status->{master_port} ) { |
2608 | - die "The slave is connected to $slave_status->{master_port} " |
2609 | - . "but the master's port is $port"; |
2610 | - } |
2611 | - |
2612 | - if ( !grep { $slave_status->{master_user} eq $_->{user} } @connected ) { |
2613 | - die "I don't see any slave I/O thread connected with user " |
2614 | - . $slave_status->{master_user}; |
2615 | - } |
2616 | - |
2617 | - if ( ($slave_status->{slave_io_state} || '') |
2618 | - eq 'Waiting for master to send event' ) |
2619 | - { |
2620 | - my ( $master_log_name, $master_log_num ) |
2621 | - = $master_status->{file} =~ m/^(.*?)\.0*([1-9][0-9]*)$/; |
2622 | - my ( $slave_log_name, $slave_log_num ) |
2623 | - = $slave_status->{master_log_file} =~ m/^(.*?)\.0*([1-9][0-9]*)$/; |
2624 | - if ( $master_log_name ne $slave_log_name |
2625 | - || abs($master_log_num - $slave_log_num) > 1 ) |
2626 | - { |
2627 | - die "The slave thinks it is reading from " |
2628 | - . "$slave_status->{master_log_file}, but the " |
2629 | - . "master is writing to $master_status->{file}"; |
2630 | - } |
2631 | - } |
2632 | - return 1; |
2633 | -} |
2634 | - |
2635 | -sub get_master_dsn { |
2636 | - my ( $self, $dbh, $dsn, $dsn_parser ) = @_; |
2637 | - my $master = $self->get_slave_status($dbh) or return undef; |
2638 | - my $spec = "h=$master->{master_host},P=$master->{master_port}"; |
2639 | - return $dsn_parser->parse($spec, $dsn); |
2640 | -} |
2641 | - |
2642 | -sub get_slave_status { |
2643 | - my ( $self, $dbh ) = @_; |
2644 | - if ( !$self->{not_a_slave}->{$dbh} ) { |
2645 | - my $sth = $self->{sths}->{$dbh}->{SLAVE_STATUS} |
2646 | - ||= $dbh->prepare('SHOW SLAVE STATUS'); |
2647 | - PTDEBUG && _d($dbh, 'SHOW SLAVE STATUS'); |
2648 | - $sth->execute(); |
2649 | - my ($ss) = @{$sth->fetchall_arrayref({})}; |
2650 | - |
2651 | - if ( $ss && %$ss ) { |
2652 | - $ss = { map { lc($_) => $ss->{$_} } keys %$ss }; # lowercase the keys |
2653 | - return $ss; |
2654 | - } |
2655 | - |
2656 | - PTDEBUG && _d('This server returns nothing for SHOW SLAVE STATUS'); |
2657 | - $self->{not_a_slave}->{$dbh}++; |
2658 | - } |
2659 | -} |
2660 | - |
2661 | -sub get_master_status { |
2662 | - my ( $self, $dbh ) = @_; |
2663 | - |
2664 | - if ( $self->{not_a_master}->{$dbh} ) { |
2665 | - PTDEBUG && _d('Server on dbh', $dbh, 'is not a master'); |
2666 | - return; |
2667 | - } |
2668 | - |
2669 | - my $sth = $self->{sths}->{$dbh}->{MASTER_STATUS} |
2670 | - ||= $dbh->prepare('SHOW MASTER STATUS'); |
2671 | - PTDEBUG && _d($dbh, 'SHOW MASTER STATUS'); |
2672 | - $sth->execute(); |
2673 | - my ($ms) = @{$sth->fetchall_arrayref({})}; |
2674 | - PTDEBUG && _d( |
2675 | - $ms ? map { "$_=" . (defined $ms->{$_} ? $ms->{$_} : '') } keys %$ms |
2676 | - : ''); |
2677 | - |
2678 | - if ( !$ms || scalar keys %$ms < 2 ) { |
2679 | - PTDEBUG && _d('Server on dbh', $dbh, 'does not seem to be a master'); |
2680 | - $self->{not_a_master}->{$dbh}++; |
2681 | - } |
2682 | - |
2683 | - return { map { lc($_) => $ms->{$_} } keys %$ms }; # lowercase the keys |
2684 | -} |
2685 | - |
2686 | -sub wait_for_master { |
2687 | - my ( $self, %args ) = @_; |
2688 | - my @required_args = qw(master_status slave_dbh); |
2689 | - foreach my $arg ( @required_args ) { |
2690 | - die "I need a $arg argument" unless $args{$arg}; |
2691 | - } |
2692 | - my ($master_status, $slave_dbh) = @args{@required_args}; |
2693 | - my $timeout = $args{timeout} || 60; |
2694 | - |
2695 | - my $result; |
2696 | - my $waited; |
2697 | - if ( $master_status ) { |
2698 | - my $sql = "SELECT MASTER_POS_WAIT('$master_status->{file}', " |
2699 | - . "$master_status->{position}, $timeout)"; |
2700 | - PTDEBUG && _d($slave_dbh, $sql); |
2701 | - my $start = time; |
2702 | - ($result) = $slave_dbh->selectrow_array($sql); |
2703 | - |
2704 | - $waited = time - $start; |
2705 | - |
2706 | - PTDEBUG && _d('Result of waiting:', $result); |
2707 | - PTDEBUG && _d("Waited", $waited, "seconds"); |
2708 | - } |
2709 | - else { |
2710 | - PTDEBUG && _d('Not waiting: this server is not a master'); |
2711 | - } |
2712 | - |
2713 | - return { |
2714 | - result => $result, |
2715 | - waited => $waited, |
2716 | - }; |
2717 | -} |
2718 | - |
2719 | -sub stop_slave { |
2720 | - my ( $self, $dbh ) = @_; |
2721 | - my $sth = $self->{sths}->{$dbh}->{STOP_SLAVE} |
2722 | - ||= $dbh->prepare('STOP SLAVE'); |
2723 | - PTDEBUG && _d($dbh, $sth->{Statement}); |
2724 | - $sth->execute(); |
2725 | -} |
2726 | - |
2727 | -sub start_slave { |
2728 | - my ( $self, $dbh, $pos ) = @_; |
2729 | - if ( $pos ) { |
2730 | - my $sql = "START SLAVE UNTIL MASTER_LOG_FILE='$pos->{file}', " |
2731 | - . "MASTER_LOG_POS=$pos->{position}"; |
2732 | - PTDEBUG && _d($dbh, $sql); |
2733 | - $dbh->do($sql); |
2734 | - } |
2735 | - else { |
2736 | - my $sth = $self->{sths}->{$dbh}->{START_SLAVE} |
2737 | - ||= $dbh->prepare('START SLAVE'); |
2738 | - PTDEBUG && _d($dbh, $sth->{Statement}); |
2739 | - $sth->execute(); |
2740 | - } |
2741 | -} |
2742 | - |
2743 | -sub catchup_to_master { |
2744 | - my ( $self, $slave, $master, $timeout ) = @_; |
2745 | - $self->stop_slave($master); |
2746 | - $self->stop_slave($slave); |
2747 | - my $slave_status = $self->get_slave_status($slave); |
2748 | - my $slave_pos = $self->repl_posn($slave_status); |
2749 | - my $master_status = $self->get_master_status($master); |
2750 | - my $master_pos = $self->repl_posn($master_status); |
2751 | - PTDEBUG && _d('Master position:', $self->pos_to_string($master_pos), |
2752 | - 'Slave position:', $self->pos_to_string($slave_pos)); |
2753 | - |
2754 | - my $result; |
2755 | - if ( $self->pos_cmp($slave_pos, $master_pos) < 0 ) { |
2756 | - PTDEBUG && _d('Waiting for slave to catch up to master'); |
2757 | - $self->start_slave($slave, $master_pos); |
2758 | - |
2759 | - $result = $self->wait_for_master( |
2760 | - master_status => $master_status, |
2761 | - slave_dbh => $slave, |
2762 | - timeout => $timeout, |
2763 | - master_status => $master_status |
2764 | - ); |
2765 | - if ( !defined $result->{result} ) { |
2766 | - $slave_status = $self->get_slave_status($slave); |
2767 | - if ( !$self->slave_is_running($slave_status) ) { |
2768 | - PTDEBUG && _d('Master position:', |
2769 | - $self->pos_to_string($master_pos), |
2770 | - 'Slave position:', $self->pos_to_string($slave_pos)); |
2771 | - $slave_pos = $self->repl_posn($slave_status); |
2772 | - if ( $self->pos_cmp($slave_pos, $master_pos) != 0 ) { |
2773 | - die "MASTER_POS_WAIT() returned NULL but slave has not " |
2774 | - . "caught up to master"; |
2775 | - } |
2776 | - PTDEBUG && _d('Slave is caught up to master and stopped'); |
2777 | - } |
2778 | - else { |
2779 | - die "Slave has not caught up to master and it is still running"; |
2780 | - } |
2781 | - } |
2782 | - } |
2783 | - else { |
2784 | - PTDEBUG && _d("Slave is already caught up to master"); |
2785 | - } |
2786 | - |
2787 | - return $result; |
2788 | -} |
2789 | - |
2790 | -sub catchup_to_same_pos { |
2791 | - my ( $self, $s1_dbh, $s2_dbh ) = @_; |
2792 | - $self->stop_slave($s1_dbh); |
2793 | - $self->stop_slave($s2_dbh); |
2794 | - my $s1_status = $self->get_slave_status($s1_dbh); |
2795 | - my $s2_status = $self->get_slave_status($s2_dbh); |
2796 | - my $s1_pos = $self->repl_posn($s1_status); |
2797 | - my $s2_pos = $self->repl_posn($s2_status); |
2798 | - if ( $self->pos_cmp($s1_pos, $s2_pos) < 0 ) { |
2799 | - $self->start_slave($s1_dbh, $s2_pos); |
2800 | - } |
2801 | - elsif ( $self->pos_cmp($s2_pos, $s1_pos) < 0 ) { |
2802 | - $self->start_slave($s2_dbh, $s1_pos); |
2803 | - } |
2804 | - |
2805 | - $s1_status = $self->get_slave_status($s1_dbh); |
2806 | - $s2_status = $self->get_slave_status($s2_dbh); |
2807 | - $s1_pos = $self->repl_posn($s1_status); |
2808 | - $s2_pos = $self->repl_posn($s2_status); |
2809 | - |
2810 | - if ( $self->slave_is_running($s1_status) |
2811 | - || $self->slave_is_running($s2_status) |
2812 | - || $self->pos_cmp($s1_pos, $s2_pos) != 0) |
2813 | - { |
2814 | - die "The servers aren't both stopped at the same position"; |
2815 | - } |
2816 | - |
2817 | -} |
2818 | - |
2819 | -sub slave_is_running { |
2820 | - my ( $self, $slave_status ) = @_; |
2821 | - return ($slave_status->{slave_sql_running} || 'No') eq 'Yes'; |
2822 | -} |
2823 | - |
2824 | -sub has_slave_updates { |
2825 | - my ( $self, $dbh ) = @_; |
2826 | - my $sql = q{SHOW VARIABLES LIKE 'log_slave_updates'}; |
2827 | - PTDEBUG && _d($dbh, $sql); |
2828 | - my ($name, $value) = $dbh->selectrow_array($sql); |
2829 | - return $value && $value =~ m/^(1|ON)$/; |
2830 | -} |
2831 | - |
2832 | -sub repl_posn { |
2833 | - my ( $self, $status ) = @_; |
2834 | - if ( exists $status->{file} && exists $status->{position} ) { |
2835 | - return { |
2836 | - file => $status->{file}, |
2837 | - position => $status->{position}, |
2838 | - }; |
2839 | - } |
2840 | - else { |
2841 | - return { |
2842 | - file => $status->{relay_master_log_file}, |
2843 | - position => $status->{exec_master_log_pos}, |
2844 | - }; |
2845 | - } |
2846 | -} |
2847 | - |
2848 | -sub get_slave_lag { |
2849 | - my ( $self, $dbh ) = @_; |
2850 | - my $stat = $self->get_slave_status($dbh); |
2851 | - return unless $stat; # server is not a slave |
2852 | - return $stat->{seconds_behind_master}; |
2853 | -} |
2854 | - |
2855 | -sub pos_cmp { |
2856 | - my ( $self, $a, $b ) = @_; |
2857 | - return $self->pos_to_string($a) cmp $self->pos_to_string($b); |
2858 | -} |
2859 | - |
2860 | -sub short_host { |
2861 | - my ( $self, $dsn ) = @_; |
2862 | - my ($host, $port); |
2863 | - if ( $dsn->{master_host} ) { |
2864 | - $host = $dsn->{master_host}; |
2865 | - $port = $dsn->{master_port}; |
2866 | - } |
2867 | - else { |
2868 | - $host = $dsn->{h}; |
2869 | - $port = $dsn->{P}; |
2870 | - } |
2871 | - return ($host || '[default]') . ( ($port || 3306) == 3306 ? '' : ":$port" ); |
2872 | -} |
2873 | - |
2874 | -sub is_replication_thread { |
2875 | - my ( $self, $query, %args ) = @_; |
2876 | - return unless $query; |
2877 | - |
2878 | - my $type = lc($args{type} || 'all'); |
2879 | - die "Invalid type: $type" |
2880 | - unless $type =~ m/^binlog_dump|slave_io|slave_sql|all$/i; |
2881 | - |
2882 | - my $match = 0; |
2883 | - if ( $type =~ m/binlog_dump|all/i ) { |
2884 | - $match = 1 |
2885 | - if ($query->{Command} || $query->{command} || '') eq "Binlog Dump"; |
2886 | - } |
2887 | - if ( !$match ) { |
2888 | - if ( ($query->{User} || $query->{user} || '') eq "system user" ) { |
2889 | - PTDEBUG && _d("Slave replication thread"); |
2890 | - if ( $type ne 'all' ) { |
2891 | - my $state = $query->{State} || $query->{state} || ''; |
2892 | - |
2893 | - if ( $state =~ m/^init|end$/ ) { |
2894 | - PTDEBUG && _d("Special state:", $state); |
2895 | - $match = 1; |
2896 | - } |
2897 | - else { |
2898 | - my ($slave_sql) = $state =~ m/ |
2899 | - ^(Waiting\sfor\sthe\snext\sevent |
2900 | - |Reading\sevent\sfrom\sthe\srelay\slog |
2901 | - |Has\sread\sall\srelay\slog;\swaiting |
2902 | - |Making\stemp\sfile |
2903 | - |Waiting\sfor\sslave\smutex\son\sexit)/xi; |
2904 | - |
2905 | - $match = $type eq 'slave_sql' && $slave_sql ? 1 |
2906 | - : $type eq 'slave_io' && !$slave_sql ? 1 |
2907 | - : 0; |
2908 | - } |
2909 | - } |
2910 | - else { |
2911 | - $match = 1; |
2912 | - } |
2913 | - } |
2914 | - else { |
2915 | - PTDEBUG && _d('Not system user'); |
2916 | - } |
2917 | - |
2918 | - if ( !defined $args{check_known_ids} || $args{check_known_ids} ) { |
2919 | - my $id = $query->{Id} || $query->{id}; |
2920 | - if ( $match ) { |
2921 | - $self->{replication_thread}->{$id} = 1; |
2922 | - } |
2923 | - else { |
2924 | - if ( $self->{replication_thread}->{$id} ) { |
2925 | - PTDEBUG && _d("Thread ID is a known replication thread ID"); |
2926 | - $match = 1; |
2927 | - } |
2928 | - } |
2929 | - } |
2930 | - } |
2931 | - |
2932 | - PTDEBUG && _d('Matches', $type, 'replication thread:', |
2933 | - ($match ? 'yes' : 'no'), '; match:', $match); |
2934 | - |
2935 | - return $match; |
2936 | -} |
2937 | - |
2938 | - |
2939 | -sub get_replication_filters { |
2940 | - my ( $self, %args ) = @_; |
2941 | - my @required_args = qw(dbh); |
2942 | - foreach my $arg ( @required_args ) { |
2943 | - die "I need a $arg argument" unless $args{$arg}; |
2944 | - } |
2945 | - my ($dbh) = @args{@required_args}; |
2946 | - |
2947 | - my %filters = (); |
2948 | - |
2949 | - my $status = $self->get_master_status($dbh); |
2950 | - if ( $status ) { |
2951 | - map { $filters{$_} = $status->{$_} } |
2952 | - grep { defined $status->{$_} && $status->{$_} ne '' } |
2953 | - qw( |
2954 | - binlog_do_db |
2955 | - binlog_ignore_db |
2956 | - ); |
2957 | - } |
2958 | - |
2959 | - $status = $self->get_slave_status($dbh); |
2960 | - if ( $status ) { |
2961 | - map { $filters{$_} = $status->{$_} } |
2962 | - grep { defined $status->{$_} && $status->{$_} ne '' } |
2963 | - qw( |
2964 | - replicate_do_db |
2965 | - replicate_ignore_db |
2966 | - replicate_do_table |
2967 | - replicate_ignore_table |
2968 | - replicate_wild_do_table |
2969 | - replicate_wild_ignore_table |
2970 | - ); |
2971 | - |
2972 | - my $sql = "SHOW VARIABLES LIKE 'slave_skip_errors'"; |
2973 | - PTDEBUG && _d($dbh, $sql); |
2974 | - my $row = $dbh->selectrow_arrayref($sql); |
2975 | - $filters{slave_skip_errors} = $row->[1] if $row->[1] && $row->[1] ne 'OFF'; |
2976 | - } |
2977 | - |
2978 | - return \%filters; |
2979 | -} |
2980 | - |
2981 | - |
2982 | -sub pos_to_string { |
2983 | - my ( $self, $pos ) = @_; |
2984 | - my $fmt = '%s/%020d'; |
2985 | - return sprintf($fmt, @{$pos}{qw(file position)}); |
2986 | -} |
2987 | - |
2988 | -sub reset_known_replication_threads { |
2989 | - my ( $self ) = @_; |
2990 | - $self->{replication_thread} = {}; |
2991 | - return; |
2992 | -} |
2993 | - |
2994 | -sub get_cxn_from_dsn_table { |
2995 | - my ($self, %args) = @_; |
2996 | - my @required_args = qw(dsn_table_dsn make_cxn); |
2997 | - foreach my $arg ( @required_args ) { |
2998 | - die "I need a $arg argument" unless $args{$arg}; |
2999 | - } |
3000 | - my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
3001 | - PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
3002 | - |
3003 | - my $dp = $self->{DSNParser}; |
3004 | - my $q = $self->{Quoter}; |
3005 | - |
3006 | - my $dsn = $dp->parse($dsn_table_dsn); |
3007 | - my $dsn_table; |
3008 | - if ( $dsn->{D} && $dsn->{t} ) { |
3009 | - $dsn_table = $q->quote($dsn->{D}, $dsn->{t}); |
3010 | - } |
3011 | - elsif ( $dsn->{t} && $dsn->{t} =~ m/\./ ) { |
3012 | - $dsn_table = $q->quote($q->split_unquote($dsn->{t})); |
3013 | - } |
3014 | - else { |
3015 | - die "DSN table DSN does not specify a database (D) " |
3016 | - . "or a database-qualified table (t)"; |
3017 | - } |
3018 | - |
3019 | - my $dsn_tbl_cxn = $make_cxn->(dsn => $dsn); |
3020 | - my $dbh = $dsn_tbl_cxn->connect(); |
3021 | - my $sql = "SELECT dsn FROM $dsn_table ORDER BY id"; |
3022 | - PTDEBUG && _d($sql); |
3023 | - my $dsn_strings = $dbh->selectcol_arrayref($sql); |
3024 | - my @cxn; |
3025 | - if ( $dsn_strings ) { |
3026 | - foreach my $dsn_string ( @$dsn_strings ) { |
3027 | - PTDEBUG && _d('DSN from DSN table:', $dsn_string); |
3028 | - push @cxn, $make_cxn->(dsn_string => $dsn_string); |
3029 | - } |
3030 | - } |
3031 | - return \@cxn; |
3032 | -} |
3033 | - |
3034 | -sub _d { |
3035 | - my ($package, undef, $line) = caller 0; |
3036 | - @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
3037 | - map { defined $_ ? $_ : 'undef' } |
3038 | - @_; |
3039 | - print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; |
3040 | -} |
3041 | - |
3042 | -1; |
3043 | -} |
3044 | -# ########################################################################### |
3045 | -# End MasterSlave package |
3046 | -# ########################################################################### |
3047 | - |
3048 | -# ########################################################################### |
3049 | # Quoter package |
3050 | # This package is a copy without comments from the original. The original |
3051 | # with comments and its test file can be found in the Bazaar repository at, |
3052 | @@ -4149,12 +3030,18 @@ |
3053 | |
3054 | sub split_unquote { |
3055 | my ( $self, $db_tbl, $default_db ) = @_; |
3056 | - $db_tbl =~ s/`//g; |
3057 | my ( $db, $tbl ) = split(/[.]/, $db_tbl); |
3058 | if ( !$tbl ) { |
3059 | $tbl = $db; |
3060 | $db = $default_db; |
3061 | } |
3062 | + for ($db, $tbl) { |
3063 | + next unless $_; |
3064 | + s/\A`//; |
3065 | + s/`\z//; |
3066 | + s/``/`/g; |
3067 | + } |
3068 | + |
3069 | return ($db, $tbl); |
3070 | } |
3071 | |
3072 | @@ -6260,12 +5147,7 @@ |
3073 | # ######################################################################## |
3074 | # Make input sub that will either get processlist from MySQL or a file. |
3075 | # ######################################################################## |
3076 | - my $ms = new MasterSlave( |
3077 | - OptionParser => $o, |
3078 | - DSNParser => $dp, |
3079 | - Quoter => "Quoter", |
3080 | - ); |
3081 | - my $pl = new Processlist(MasterSlave => $ms); |
3082 | + my $pl = new Processlist(); |
3083 | my $qr = new QueryRewriter(); |
3084 | |
3085 | my $cxn; |
3086 | |
3087 | === modified file 'bin/pt-online-schema-change' |
3088 | --- bin/pt-online-schema-change 2012-11-10 12:54:39 +0000 |
3089 | +++ bin/pt-online-schema-change 2012-11-14 08:53:21 +0000 |
3090 | @@ -16,6 +16,7 @@ |
3091 | Percona::Toolkit |
3092 | OptionParser |
3093 | Mo |
3094 | + Percona::Object |
3095 | VersionParser |
3096 | DSNParser |
3097 | Daemon |
3098 | @@ -1149,7 +1150,6 @@ |
3099 | push @args_to_delete, $attr; |
3100 | } |
3101 | } |
3102 | - |
3103 | delete $args->{$_} for @args_to_delete; |
3104 | |
3105 | for my $attribute ( keys %$args ) { |
3106 | @@ -1194,6 +1194,43 @@ |
3107 | } |
3108 | return $ref; |
3109 | } |
3110 | + |
3111 | + sub meta { |
3112 | + my $class = shift; |
3113 | + return Mo::Meta::Class->new(class => $class); |
3114 | + } |
3115 | +} |
3116 | + |
3117 | +{ |
3118 | + package Mo::Meta::Class; |
3119 | + |
3120 | + sub new { |
3121 | + my $class = shift; |
3122 | + return bless { @_ }, $class |
3123 | + } |
3124 | + |
3125 | + sub class { shift->{class} } |
3126 | + |
3127 | + sub attributes { |
3128 | + my $self = shift; |
3129 | + return keys %{$metadata_for{$self->class}} |
3130 | + } |
3131 | + |
3132 | + sub attributes_for_new { |
3133 | + my $self = shift; |
3134 | + my @attributes; |
3135 | + |
3136 | + while ( my ($attr, $meta) = each %{$metadata_for{$self->class}} ) { |
3137 | + if ( exists $meta->{init_arg} ) { |
3138 | + push @attributes, $meta->{init_arg} |
3139 | + if defined $meta->{init_arg}; |
3140 | + } |
3141 | + else { |
3142 | + push @attributes, $attr; |
3143 | + } |
3144 | + } |
3145 | + return @attributes; |
3146 | + } |
3147 | } |
3148 | |
3149 | my %export_for; |
3150 | @@ -1353,16 +1390,17 @@ |
3151 | |
3152 | sub _check_type_constaints { |
3153 | my ($attribute, $I, $I_name, $val) = @_; |
3154 | + local $@; |
3155 | ( ref($I) eq 'CODE' |
3156 | - ? $I->($val) |
3157 | + ? eval { $I->($val) } |
3158 | : (ref $val eq $I |
3159 | || ($val && $val eq $I) |
3160 | || (exists $TYPES{$I} && $TYPES{$I}->($val))) |
3161 | ) |
3162 | || Carp::confess( |
3163 | - qq<Attribute ($attribute) does not pass the type constraint because: > |
3164 | - . qq<Validation failed for '$I_name' with value > |
3165 | - . (defined $val ? Mo::Dumper($val) : 'undef') ) |
3166 | + qq<Attribute ($attribute) does not pass the type constraint because: > |
3167 | + . ( $@ || ( qq<Validation failed for '$I_name' with value > |
3168 | + . defined $val ? Mo::Dumper($val) : 'undef') ) ) |
3169 | } |
3170 | |
3171 | sub _has_handles { |
3172 | @@ -1535,6 +1573,52 @@ |
3173 | # ########################################################################### |
3174 | |
3175 | # ########################################################################### |
3176 | +# Percona::Object package |
3177 | +# This package is a copy without comments from the original. The original |
3178 | +# with comments and its test file can be found in the Bazaar repository at, |
3179 | +# lib/Percona/Object.pm |
3180 | +# t/lib/Percona/Object.t |
3181 | +# See https://launchpad.net/percona-toolkit for more information. |
3182 | +# ########################################################################### |
3183 | +{ |
3184 | +package Percona::Object; |
3185 | + |
3186 | +use strict; |
3187 | +use warnings FATAL => 'all'; |
3188 | +use English qw(-no_match_vars); |
3189 | +use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
3190 | + |
3191 | +use Mo; |
3192 | + |
3193 | +sub BUILDARGS { |
3194 | + my $self = shift; |
3195 | + my $args = $self->SUPER::BUILDARGS(@_); |
3196 | + |
3197 | + return $args unless $args->{OptionParser}; |
3198 | + |
3199 | + my $o = $args->{OptionParser}; |
3200 | + my @attributes = $self->meta->attributes_for_new(); |
3201 | + |
3202 | + foreach my $attr ( @attributes ) { |
3203 | + next if exists $args->{$attr}; |
3204 | + (my $attr_for_o = $attr) =~ tr/_/-/; |
3205 | + if ( $o->has($attr_for_o) ) { |
3206 | + $args->{$attr} = $o->get($attr_for_o) |
3207 | + } elsif ( $attr eq 'DSNParser' ) { |
3208 | + $args->{DSNParser} = $o->DSNParser; |
3209 | + } |
3210 | + } |
3211 | + |
3212 | + return $args; |
3213 | +} |
3214 | + |
3215 | +1; |
3216 | +} |
3217 | +# ########################################################################### |
3218 | +# End Percona::Object package |
3219 | +# ########################################################################### |
3220 | + |
3221 | +# ########################################################################### |
3222 | # VersionParser package |
3223 | # This package is a copy without comments from the original. The original |
3224 | # with comments and its test file can be found in the Bazaar repository at, |
3225 | @@ -3847,6 +3931,61 @@ |
3226 | use English qw(-no_match_vars); |
3227 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
3228 | |
3229 | +use Mo; |
3230 | + |
3231 | +extends qw( Percona::Object ); |
3232 | + |
3233 | +local $EVAL_ERROR; |
3234 | +eval { |
3235 | + require Quoter; |
3236 | +}; |
3237 | + |
3238 | +has Quoter => ( |
3239 | + is => 'ro', |
3240 | + isa => 'Quoter', |
3241 | + default => sub { Quoter->new() }, |
3242 | +); |
3243 | + |
3244 | +has DSNParser => ( |
3245 | + is => 'ro', |
3246 | + isa => 'DSNParser', |
3247 | + required => 1, |
3248 | +); |
3249 | + |
3250 | +has recursion_method => ( |
3251 | + is => 'ro', |
3252 | + isa => sub { |
3253 | + die "recursion_method should be an arrayref, not " . ($_[0] || 'undef') |
3254 | + unless ref($_[0]) eq 'ARRAY'; |
3255 | + check_recursion_method($_[0]); |
3256 | + return 1; |
3257 | + }, |
3258 | + required => 1, |
3259 | +); |
3260 | + |
3261 | +has _explicit_recursion_method => ( |
3262 | + is => 'ro', |
3263 | + isa => 'Bool', |
3264 | + default => sub { 1 }, |
3265 | +); |
3266 | + |
3267 | +has recurse => ( |
3268 | + is => 'ro', |
3269 | + isa => 'Maybe[Int]', |
3270 | + required => 1, |
3271 | +); |
3272 | + |
3273 | +sub BUILDARGS { |
3274 | + my $self = shift; |
3275 | + my $args = $self->SUPER::BUILDARGS(@_); |
3276 | + my $o = delete $args->{OptionParser}; |
3277 | + |
3278 | + $args->{_explicit_recursion_method} = $o->got('recursion-method') |
3279 | + if $o; |
3280 | + |
3281 | + return $args; |
3282 | +} |
3283 | + |
3284 | sub check_recursion_method { |
3285 | my ($methods) = @_; |
3286 | |
3287 | @@ -3866,19 +4005,6 @@ |
3288 | } |
3289 | } |
3290 | |
3291 | -sub new { |
3292 | - my ( $class, %args ) = @_; |
3293 | - my @required_args = qw(OptionParser DSNParser Quoter); |
3294 | - foreach my $arg ( @required_args ) { |
3295 | - die "I need a $arg argument" unless $args{$arg}; |
3296 | - } |
3297 | - my $self = { |
3298 | - %args, |
3299 | - replication_thread => {}, |
3300 | - }; |
3301 | - return bless $self, $class; |
3302 | -} |
3303 | - |
3304 | sub get_slaves { |
3305 | my ($self, %args) = @_; |
3306 | my @required_args = qw(make_cxn); |
3307 | @@ -3888,10 +4014,10 @@ |
3308 | my ($make_cxn) = @args{@required_args}; |
3309 | |
3310 | my $slaves = []; |
3311 | - my $dp = $self->{DSNParser}; |
3312 | + my $dp = $self->DSNParser; |
3313 | my $methods = $self->_resolve_recursion_methods($args{dsn}); |
3314 | |
3315 | - if ( grep { m/processlist|hosts/i } @$methods ) { |
3316 | + if ( grep { m/^(?:processlist|hosts)$/i } @$methods ) { |
3317 | my @required_args = qw(dbh dsn); |
3318 | foreach my $arg ( @required_args ) { |
3319 | die "I need a $arg argument" unless $args{$arg}; |
3320 | @@ -3930,24 +4056,20 @@ |
3321 | |
3322 | sub _resolve_recursion_methods { |
3323 | my ($self, $dsn) = @_; |
3324 | - my $o = $self->{OptionParser}; |
3325 | - if ( $o->got('recursion-method') ) { |
3326 | - return $o->get('recursion-method'); |
3327 | - } |
3328 | - elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
3329 | + if ( !$self->_explicit_recursion_method() && $dsn && ($dsn->{P} || 3306) != 3306 ) { |
3330 | PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
3331 | return [qw(hosts)]; |
3332 | } |
3333 | else { |
3334 | - return $o->get('recursion-method'); |
3335 | + return $self->recursion_method(); |
3336 | } |
3337 | } |
3338 | |
3339 | sub recurse_to_slaves { |
3340 | my ( $self, $args, $level ) = @_; |
3341 | $level ||= 0; |
3342 | - my $dp = $self->{DSNParser}; |
3343 | - my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
3344 | + my $dp = $self->DSNParser; |
3345 | + my $recurse = $args->{recurse} || $self->recurse(); |
3346 | my $dsn = $args->{dsn}; |
3347 | |
3348 | my $methods = $self->_resolve_recursion_methods($dsn); |
3349 | @@ -3991,7 +4113,7 @@ |
3350 | |
3351 | my @slaves = |
3352 | grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
3353 | - $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
3354 | + $self->find_slave_hosts($dbh, $dsn, $methods); |
3355 | |
3356 | foreach my $slave ( @slaves ) { |
3357 | PTDEBUG && _d('Recursing from', |
3358 | @@ -4003,7 +4125,8 @@ |
3359 | } |
3360 | |
3361 | sub find_slave_hosts { |
3362 | - my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
3363 | + my ( $self, $dbh, $dsn, $methods ) = @_; |
3364 | + my $dsn_parser = $self->DSNParser; |
3365 | |
3366 | PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
3367 | 'using methods', @$methods); |
3368 | @@ -4013,7 +4136,7 @@ |
3369 | foreach my $method ( @$methods ) { |
3370 | my $find_slaves = "_find_slaves_by_$method"; |
3371 | PTDEBUG && _d('Finding slaves with', $find_slaves); |
3372 | - @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
3373 | + @slaves = $self->$find_slaves($dbh, $dsn); |
3374 | last METHOD if @slaves; |
3375 | } |
3376 | |
3377 | @@ -4022,7 +4145,8 @@ |
3378 | } |
3379 | |
3380 | sub _find_slaves_by_processlist { |
3381 | - my ( $self, $dsn_parser, $dbh, $dsn ) = @_; |
3382 | + my ( $self, $dbh, $dsn ) = @_; |
3383 | + my $dsn_parser = $self->DSNParser; |
3384 | |
3385 | my @slaves = map { |
3386 | my $slave = $dsn_parser->parse("h=$_", $dsn); |
3387 | @@ -4042,7 +4166,8 @@ |
3388 | } |
3389 | |
3390 | sub _find_slaves_by_hosts { |
3391 | - my ( $self, $dsn_parser, $dbh, $dsn ) = @_; |
3392 | + my ( $self, $dbh, $dsn ) = @_; |
3393 | + my $dsn_parser = $self->DSNParser; |
3394 | |
3395 | my @slaves; |
3396 | my $sql = 'SHOW SLAVE HOSTS'; |
3397 | @@ -4153,41 +4278,32 @@ |
3398 | } |
3399 | |
3400 | sub get_master_dsn { |
3401 | - my ( $self, $dbh, $dsn, $dsn_parser ) = @_; |
3402 | - my $master = $self->get_slave_status($dbh) or return undef; |
3403 | - my $spec = "h=$master->{master_host},P=$master->{master_port}"; |
3404 | - return $dsn_parser->parse($spec, $dsn); |
3405 | + my ( $self, $dbh, $dsn ) = @_; |
3406 | + my $dsn_parser = $self->DSNParser; |
3407 | + my $master = $self->get_slave_status($dbh) or return undef; |
3408 | + my $spec = "h=$master->{master_host},P=$master->{master_port}"; |
3409 | + return $dsn_parser->parse($spec, $dsn); |
3410 | } |
3411 | |
3412 | sub get_slave_status { |
3413 | my ( $self, $dbh ) = @_; |
3414 | - if ( !$self->{not_a_slave}->{$dbh} ) { |
3415 | - my $sth = $self->{sths}->{$dbh}->{SLAVE_STATUS} |
3416 | - ||= $dbh->prepare('SHOW SLAVE STATUS'); |
3417 | - PTDEBUG && _d($dbh, 'SHOW SLAVE STATUS'); |
3418 | - $sth->execute(); |
3419 | - my ($ss) = @{$sth->fetchall_arrayref({})}; |
3420 | - |
3421 | - if ( $ss && %$ss ) { |
3422 | - $ss = { map { lc($_) => $ss->{$_} } keys %$ss }; # lowercase the keys |
3423 | - return $ss; |
3424 | - } |
3425 | - |
3426 | - PTDEBUG && _d('This server returns nothing for SHOW SLAVE STATUS'); |
3427 | - $self->{not_a_slave}->{$dbh}++; |
3428 | + my $sth = $dbh->prepare('SHOW SLAVE STATUS'); |
3429 | + PTDEBUG && _d($dbh, 'SHOW SLAVE STATUS'); |
3430 | + $sth->execute(); |
3431 | + my ($ss) = @{$sth->fetchall_arrayref({})}; |
3432 | + |
3433 | + if ( $ss && %$ss ) { |
3434 | + $ss = { map { lc($_) => $ss->{$_} } keys %$ss }; # lowercase the keys |
3435 | + return $ss; |
3436 | } |
3437 | + |
3438 | + PTDEBUG && _d('This server returns nothing for SHOW SLAVE STATUS'); |
3439 | } |
3440 | |
3441 | sub get_master_status { |
3442 | my ( $self, $dbh ) = @_; |
3443 | |
3444 | - if ( $self->{not_a_master}->{$dbh} ) { |
3445 | - PTDEBUG && _d('Server on dbh', $dbh, 'is not a master'); |
3446 | - return; |
3447 | - } |
3448 | - |
3449 | - my $sth = $self->{sths}->{$dbh}->{MASTER_STATUS} |
3450 | - ||= $dbh->prepare('SHOW MASTER STATUS'); |
3451 | + my $sth = $dbh->prepare('SHOW MASTER STATUS'); |
3452 | PTDEBUG && _d($dbh, 'SHOW MASTER STATUS'); |
3453 | $sth->execute(); |
3454 | my ($ms) = @{$sth->fetchall_arrayref({})}; |
3455 | @@ -4197,7 +4313,6 @@ |
3456 | |
3457 | if ( !$ms || scalar keys %$ms < 2 ) { |
3458 | PTDEBUG && _d('Server on dbh', $dbh, 'does not seem to be a master'); |
3459 | - $self->{not_a_master}->{$dbh}++; |
3460 | } |
3461 | |
3462 | return { map { lc($_) => $ms->{$_} } keys %$ms }; # lowercase the keys |
3463 | @@ -4238,8 +4353,7 @@ |
3464 | |
3465 | sub stop_slave { |
3466 | my ( $self, $dbh ) = @_; |
3467 | - my $sth = $self->{sths}->{$dbh}->{STOP_SLAVE} |
3468 | - ||= $dbh->prepare('STOP SLAVE'); |
3469 | + my $sth = $dbh->prepare('STOP SLAVE'); |
3470 | PTDEBUG && _d($dbh, $sth->{Statement}); |
3471 | $sth->execute(); |
3472 | } |
3473 | @@ -4253,103 +4367,18 @@ |
3474 | $dbh->do($sql); |
3475 | } |
3476 | else { |
3477 | - my $sth = $self->{sths}->{$dbh}->{START_SLAVE} |
3478 | - ||= $dbh->prepare('START SLAVE'); |
3479 | + my $sth = $dbh->prepare('START SLAVE'); |
3480 | PTDEBUG && _d($dbh, $sth->{Statement}); |
3481 | $sth->execute(); |
3482 | } |
3483 | } |
3484 | |
3485 | -sub catchup_to_master { |
3486 | - my ( $self, $slave, $master, $timeout ) = @_; |
3487 | - $self->stop_slave($master); |
3488 | - $self->stop_slave($slave); |
3489 | - my $slave_status = $self->get_slave_status($slave); |
3490 | - my $slave_pos = $self->repl_posn($slave_status); |
3491 | - my $master_status = $self->get_master_status($master); |
3492 | - my $master_pos = $self->repl_posn($master_status); |
3493 | - PTDEBUG && _d('Master position:', $self->pos_to_string($master_pos), |
3494 | - 'Slave position:', $self->pos_to_string($slave_pos)); |
3495 | - |
3496 | - my $result; |
3497 | - if ( $self->pos_cmp($slave_pos, $master_pos) < 0 ) { |
3498 | - PTDEBUG && _d('Waiting for slave to catch up to master'); |
3499 | - $self->start_slave($slave, $master_pos); |
3500 | - |
3501 | - $result = $self->wait_for_master( |
3502 | - master_status => $master_status, |
3503 | - slave_dbh => $slave, |
3504 | - timeout => $timeout, |
3505 | - master_status => $master_status |
3506 | - ); |
3507 | - if ( !defined $result->{result} ) { |
3508 | - $slave_status = $self->get_slave_status($slave); |
3509 | - if ( !$self->slave_is_running($slave_status) ) { |
3510 | - PTDEBUG && _d('Master position:', |
3511 | - $self->pos_to_string($master_pos), |
3512 | - 'Slave position:', $self->pos_to_string($slave_pos)); |
3513 | - $slave_pos = $self->repl_posn($slave_status); |
3514 | - if ( $self->pos_cmp($slave_pos, $master_pos) != 0 ) { |
3515 | - die "MASTER_POS_WAIT() returned NULL but slave has not " |
3516 | - . "caught up to master"; |
3517 | - } |
3518 | - PTDEBUG && _d('Slave is caught up to master and stopped'); |
3519 | - } |
3520 | - else { |
3521 | - die "Slave has not caught up to master and it is still running"; |
3522 | - } |
3523 | - } |
3524 | - } |
3525 | - else { |
3526 | - PTDEBUG && _d("Slave is already caught up to master"); |
3527 | - } |
3528 | - |
3529 | - return $result; |
3530 | -} |
3531 | - |
3532 | -sub catchup_to_same_pos { |
3533 | - my ( $self, $s1_dbh, $s2_dbh ) = @_; |
3534 | - $self->stop_slave($s1_dbh); |
3535 | - $self->stop_slave($s2_dbh); |
3536 | - my $s1_status = $self->get_slave_status($s1_dbh); |
3537 | - my $s2_status = $self->get_slave_status($s2_dbh); |
3538 | - my $s1_pos = $self->repl_posn($s1_status); |
3539 | - my $s2_pos = $self->repl_posn($s2_status); |
3540 | - if ( $self->pos_cmp($s1_pos, $s2_pos) < 0 ) { |
3541 | - $self->start_slave($s1_dbh, $s2_pos); |
3542 | - } |
3543 | - elsif ( $self->pos_cmp($s2_pos, $s1_pos) < 0 ) { |
3544 | - $self->start_slave($s2_dbh, $s1_pos); |
3545 | - } |
3546 | - |
3547 | - $s1_status = $self->get_slave_status($s1_dbh); |
3548 | - $s2_status = $self->get_slave_status($s2_dbh); |
3549 | - $s1_pos = $self->repl_posn($s1_status); |
3550 | - $s2_pos = $self->repl_posn($s2_status); |
3551 | - |
3552 | - if ( $self->slave_is_running($s1_status) |
3553 | - || $self->slave_is_running($s2_status) |
3554 | - || $self->pos_cmp($s1_pos, $s2_pos) != 0) |
3555 | - { |
3556 | - die "The servers aren't both stopped at the same position"; |
3557 | - } |
3558 | - |
3559 | -} |
3560 | - |
3561 | -sub slave_is_running { |
3562 | +sub _slave_is_running { |
3563 | my ( $self, $slave_status ) = @_; |
3564 | return ($slave_status->{slave_sql_running} || 'No') eq 'Yes'; |
3565 | } |
3566 | |
3567 | -sub has_slave_updates { |
3568 | - my ( $self, $dbh ) = @_; |
3569 | - my $sql = q{SHOW VARIABLES LIKE 'log_slave_updates'}; |
3570 | - PTDEBUG && _d($dbh, $sql); |
3571 | - my ($name, $value) = $dbh->selectrow_array($sql); |
3572 | - return $value && $value =~ m/^(1|ON)$/; |
3573 | -} |
3574 | - |
3575 | -sub repl_posn { |
3576 | +sub _repl_posn { |
3577 | my ( $self, $status ) = @_; |
3578 | if ( exists $status->{file} && exists $status->{position} ) { |
3579 | return { |
3580 | @@ -4372,89 +4401,10 @@ |
3581 | return $stat->{seconds_behind_master}; |
3582 | } |
3583 | |
3584 | -sub pos_cmp { |
3585 | +sub _pos_cmp { |
3586 | my ( $self, $a, $b ) = @_; |
3587 | - return $self->pos_to_string($a) cmp $self->pos_to_string($b); |
3588 | -} |
3589 | - |
3590 | -sub short_host { |
3591 | - my ( $self, $dsn ) = @_; |
3592 | - my ($host, $port); |
3593 | - if ( $dsn->{master_host} ) { |
3594 | - $host = $dsn->{master_host}; |
3595 | - $port = $dsn->{master_port}; |
3596 | - } |
3597 | - else { |
3598 | - $host = $dsn->{h}; |
3599 | - $port = $dsn->{P}; |
3600 | - } |
3601 | - return ($host || '[default]') . ( ($port || 3306) == 3306 ? '' : ":$port" ); |
3602 | -} |
3603 | - |
3604 | -sub is_replication_thread { |
3605 | - my ( $self, $query, %args ) = @_; |
3606 | - return unless $query; |
3607 | - |
3608 | - my $type = lc($args{type} || 'all'); |
3609 | - die "Invalid type: $type" |
3610 | - unless $type =~ m/^binlog_dump|slave_io|slave_sql|all$/i; |
3611 | - |
3612 | - my $match = 0; |
3613 | - if ( $type =~ m/binlog_dump|all/i ) { |
3614 | - $match = 1 |
3615 | - if ($query->{Command} || $query->{command} || '') eq "Binlog Dump"; |
3616 | - } |
3617 | - if ( !$match ) { |
3618 | - if ( ($query->{User} || $query->{user} || '') eq "system user" ) { |
3619 | - PTDEBUG && _d("Slave replication thread"); |
3620 | - if ( $type ne 'all' ) { |
3621 | - my $state = $query->{State} || $query->{state} || ''; |
3622 | - |
3623 | - if ( $state =~ m/^init|end$/ ) { |
3624 | - PTDEBUG && _d("Special state:", $state); |
3625 | - $match = 1; |
3626 | - } |
3627 | - else { |
3628 | - my ($slave_sql) = $state =~ m/ |
3629 | - ^(Waiting\sfor\sthe\snext\sevent |
3630 | - |Reading\sevent\sfrom\sthe\srelay\slog |
3631 | - |Has\sread\sall\srelay\slog;\swaiting |
3632 | - |Making\stemp\sfile |
3633 | - |Waiting\sfor\sslave\smutex\son\sexit)/xi; |
3634 | - |
3635 | - $match = $type eq 'slave_sql' && $slave_sql ? 1 |
3636 | - : $type eq 'slave_io' && !$slave_sql ? 1 |
3637 | - : 0; |
3638 | - } |
3639 | - } |
3640 | - else { |
3641 | - $match = 1; |
3642 | - } |
3643 | - } |
3644 | - else { |
3645 | - PTDEBUG && _d('Not system user'); |
3646 | - } |
3647 | - |
3648 | - if ( !defined $args{check_known_ids} || $args{check_known_ids} ) { |
3649 | - my $id = $query->{Id} || $query->{id}; |
3650 | - if ( $match ) { |
3651 | - $self->{replication_thread}->{$id} = 1; |
3652 | - } |
3653 | - else { |
3654 | - if ( $self->{replication_thread}->{$id} ) { |
3655 | - PTDEBUG && _d("Thread ID is a known replication thread ID"); |
3656 | - $match = 1; |
3657 | - } |
3658 | - } |
3659 | - } |
3660 | - } |
3661 | - |
3662 | - PTDEBUG && _d('Matches', $type, 'replication thread:', |
3663 | - ($match ? 'yes' : 'no'), '; match:', $match); |
3664 | - |
3665 | - return $match; |
3666 | -} |
3667 | - |
3668 | + return $self->_pos_to_string($a) cmp $self->_pos_to_string($b); |
3669 | +} |
3670 | |
3671 | sub get_replication_filters { |
3672 | my ( $self, %args ) = @_; |
3673 | @@ -4499,18 +4449,12 @@ |
3674 | } |
3675 | |
3676 | |
3677 | -sub pos_to_string { |
3678 | +sub _pos_to_string { |
3679 | my ( $self, $pos ) = @_; |
3680 | my $fmt = '%s/%020d'; |
3681 | return sprintf($fmt, @{$pos}{qw(file position)}); |
3682 | } |
3683 | |
3684 | -sub reset_known_replication_threads { |
3685 | - my ( $self ) = @_; |
3686 | - $self->{replication_thread} = {}; |
3687 | - return; |
3688 | -} |
3689 | - |
3690 | sub get_cxn_from_dsn_table { |
3691 | my ($self, %args) = @_; |
3692 | my @required_args = qw(dsn_table_dsn make_cxn); |
3693 | @@ -4520,8 +4464,8 @@ |
3694 | my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
3695 | PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
3696 | |
3697 | - my $dp = $self->{DSNParser}; |
3698 | - my $q = $self->{Quoter}; |
3699 | + my $dp = $self->DSNParser; |
3700 | + my $q = $self->Quoter; |
3701 | |
3702 | my $dsn = $dp->parse($dsn_table_dsn); |
3703 | my $dsn_table; |
3704 | |
3705 | === modified file 'bin/pt-query-digest' |
3706 | --- bin/pt-query-digest 2012-11-09 16:48:17 +0000 |
3707 | +++ bin/pt-query-digest 2012-11-14 08:53:21 +0000 |
3708 | @@ -42,7 +42,6 @@ |
3709 | ProtocolParser |
3710 | HTTPProtocolParser |
3711 | ExecutionThrottler |
3712 | - MasterSlave |
3713 | Progress |
3714 | FileIterator |
3715 | ExplainAnalyzer |
3716 | @@ -492,12 +491,18 @@ |
3717 | |
3718 | sub split_unquote { |
3719 | my ( $self, $db_tbl, $default_db ) = @_; |
3720 | - $db_tbl =~ s/`//g; |
3721 | my ( $db, $tbl ) = split(/[.]/, $db_tbl); |
3722 | if ( !$tbl ) { |
3723 | $tbl = $db; |
3724 | $db = $default_db; |
3725 | } |
3726 | + for ($db, $tbl) { |
3727 | + next unless $_; |
3728 | + s/\A`//; |
3729 | + s/`\z//; |
3730 | + s/``/`/g; |
3731 | + } |
3732 | + |
3733 | return ($db, $tbl); |
3734 | } |
3735 | |
3736 | @@ -2373,7 +2378,7 @@ |
3737 | |
3738 | sub new { |
3739 | my ( $class, %args ) = @_; |
3740 | - foreach my $arg ( qw(MasterSlave) ) { |
3741 | + foreach my $arg ( qw() ) { |
3742 | die "I need a $arg argument" unless $args{$arg}; |
3743 | } |
3744 | my $self = { |
3745 | @@ -2573,7 +2578,6 @@ |
3746 | sub find { |
3747 | my ( $self, $proclist, %find_spec ) = @_; |
3748 | PTDEBUG && _d('find specs:', Dumper(\%find_spec)); |
3749 | - my $ms = $self->{MasterSlave}; |
3750 | |
3751 | my @matches; |
3752 | QUERY: |
3753 | @@ -2582,7 +2586,7 @@ |
3754 | my $matched = 0; |
3755 | |
3756 | if ( !$find_spec{replication_threads} |
3757 | - && $ms->is_replication_thread($query) ) { |
3758 | + && $self->is_replication_thread($query) ) { |
3759 | PTDEBUG && _d('Skipping replication thread'); |
3760 | next QUERY; |
3761 | } |
3762 | @@ -2682,6 +2686,75 @@ |
3763 | && $query->{Info} =~ m/$property/; |
3764 | } |
3765 | |
3766 | +sub is_replication_thread { |
3767 | + my ( $self, $query, %args ) = @_; |
3768 | + return unless $query; |
3769 | + |
3770 | + my $type = lc($args{type} || 'all'); |
3771 | + die "Invalid type: $type" |
3772 | + unless $type =~ m/^(?:binlog_dump|slave_io|slave_sql|all)$/i; |
3773 | + |
3774 | + my $match = 0; |
3775 | + if ( $type =~ m/binlog_dump|all/i ) { |
3776 | + $match = 1 |
3777 | + if ($query->{Command} || $query->{command} || '') eq "Binlog Dump"; |
3778 | + } |
3779 | + if ( !$match ) { |
3780 | + if ( lc($query->{User} || $query->{user} || '') eq "system user" ) { |
3781 | + PTDEBUG && _d("Slave replication thread"); |
3782 | + if ( $type ne 'all' ) { |
3783 | + my $state = $query->{State} || $query->{state} || ''; |
3784 | + |
3785 | + if ( $state =~ m/^(?:init|end)$/ ) { |
3786 | + PTDEBUG && _d("Special state:", $state); |
3787 | + $match = 1; |
3788 | + } |
3789 | + else { |
3790 | + my ($slave_sql) = $state =~ m/ |
3791 | + ^(Waiting\sfor\sthe\snext\sevent |
3792 | + |Reading\sevent\sfrom\sthe\srelay\slog |
3793 | + |Has\sread\sall\srelay\slog;\swaiting |
3794 | + |Making\stemp\sfile |
3795 | + |Waiting\sfor\sslave\smutex\son\sexit)/xi; |
3796 | + |
3797 | + $match = $type eq 'slave_sql' && $slave_sql ? 1 |
3798 | + : $type eq 'slave_io' && !$slave_sql ? 1 |
3799 | + : 0; |
3800 | + } |
3801 | + } |
3802 | + else { |
3803 | + $match = 1; |
3804 | + } |
3805 | + } |
3806 | + else { |
3807 | + PTDEBUG && _d('Not system user'); |
3808 | + } |
3809 | + |
3810 | + if ( !defined $args{check_known_ids} || $args{check_known_ids} ) { |
3811 | + my $id = $query->{Id} || $query->{id}; |
3812 | + if ( $match ) { |
3813 | + $self->{_replication_thread}->{$id} = 1; |
3814 | + } |
3815 | + else { |
3816 | + if ( $self->{_replication_thread}->{$id} ) { |
3817 | + PTDEBUG && _d("Thread ID is a known replication thread ID"); |
3818 | + $match = 1; |
3819 | + } |
3820 | + } |
3821 | + } |
3822 | + } |
3823 | + |
3824 | + PTDEBUG && _d('Matches', $type, 'replication thread:', |
3825 | + ($match ? 'yes' : 'no'), '; match:', $match); |
3826 | + |
3827 | + return $match; |
3828 | +} |
3829 | + |
3830 | +sub reset_known_replication_threads { |
3831 | + my ($self) = @_; |
3832 | + $self->{_replication_thread} = {}; |
3833 | +} |
3834 | + |
3835 | sub _d { |
3836 | my ($package, undef, $line) = caller 0; |
3837 | @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
3838 | @@ -8103,16 +8176,20 @@ |
3839 | $Data::Dumper::Sortkeys = 1; |
3840 | $Data::Dumper::Quotekeys = 0; |
3841 | |
3842 | +local $EVAL_ERROR; |
3843 | +eval { |
3844 | + require Quoter; |
3845 | +}; |
3846 | + |
3847 | sub new { |
3848 | my ( $class, %args ) = @_; |
3849 | - my @required_args = qw(Quoter); |
3850 | - foreach my $arg ( @required_args ) { |
3851 | - die "I need a $arg argument" unless $args{$arg}; |
3852 | - } |
3853 | my $self = { %args }; |
3854 | + $self->{Quoter} ||= Quoter->new(); |
3855 | return bless $self, $class; |
3856 | } |
3857 | |
3858 | +sub Quoter { shift->{Quoter} } |
3859 | + |
3860 | sub get_create_table { |
3861 | my ( $self, $dbh, $db, $tbl ) = @_; |
3862 | die "I need a dbh parameter" unless $dbh; |
3863 | @@ -10462,740 +10539,6 @@ |
3864 | # ########################################################################### |
3865 | |
3866 | # ########################################################################### |
3867 | -# MasterSlave package |
3868 | -# This package is a copy without comments from the original. The original |
3869 | -# with comments and its test file can be found in the Bazaar repository at, |
3870 | -# lib/MasterSlave.pm |
3871 | -# t/lib/MasterSlave.t |
3872 | -# See https://launchpad.net/percona-toolkit for more information. |
3873 | -# ########################################################################### |
3874 | -{ |
3875 | -package MasterSlave; |
3876 | - |
3877 | -use strict; |
3878 | -use warnings FATAL => 'all'; |
3879 | -use English qw(-no_match_vars); |
3880 | -use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
3881 | - |
3882 | -sub check_recursion_method { |
3883 | - my ($methods) = @_; |
3884 | - |
3885 | - if ( @$methods != 1 ) { |
3886 | - if ( grep({ !m/processlist|hosts/i } @$methods) |
3887 | - && $methods->[0] !~ /^dsn=/i ) |
3888 | - { |
3889 | - die "Invalid combination of recursion methods: " |
3890 | - . join(", ", map { defined($_) ? $_ : 'undef' } @$methods) . ". " |
3891 | - . "Only hosts and processlist may be combined.\n" |
3892 | - } |
3893 | - } |
3894 | - else { |
3895 | - my ($method) = @$methods; |
3896 | - die "Invalid recursion method: " . ( $method || 'undef' ) |
3897 | - unless $method && $method =~ m/^(?:processlist$|hosts$|none$|dsn=)/i; |
3898 | - } |
3899 | -} |
3900 | - |
3901 | -sub new { |
3902 | - my ( $class, %args ) = @_; |
3903 | - my @required_args = qw(OptionParser DSNParser Quoter); |
3904 | - foreach my $arg ( @required_args ) { |
3905 | - die "I need a $arg argument" unless $args{$arg}; |
3906 | - } |
3907 | - my $self = { |
3908 | - %args, |
3909 | - replication_thread => {}, |
3910 | - }; |
3911 | - return bless $self, $class; |
3912 | -} |
3913 | - |
3914 | -sub get_slaves { |
3915 | - my ($self, %args) = @_; |
3916 | - my @required_args = qw(make_cxn); |
3917 | - foreach my $arg ( @required_args ) { |
3918 | - die "I need a $arg argument" unless $args{$arg}; |
3919 | - } |
3920 | - my ($make_cxn) = @args{@required_args}; |
3921 | - |
3922 | - my $slaves = []; |
3923 | - my $dp = $self->{DSNParser}; |
3924 | - my $methods = $self->_resolve_recursion_methods($args{dsn}); |
3925 | - |
3926 | - if ( grep { m/processlist|hosts/i } @$methods ) { |
3927 | - my @required_args = qw(dbh dsn); |
3928 | - foreach my $arg ( @required_args ) { |
3929 | - die "I need a $arg argument" unless $args{$arg}; |
3930 | - } |
3931 | - my ($dbh, $dsn) = @args{@required_args}; |
3932 | - |
3933 | - $self->recurse_to_slaves( |
3934 | - { dbh => $dbh, |
3935 | - dsn => $dsn, |
3936 | - callback => sub { |
3937 | - my ( $dsn, $dbh, $level, $parent ) = @_; |
3938 | - return unless $level; |
3939 | - PTDEBUG && _d('Found slave:', $dp->as_string($dsn)); |
3940 | - push @$slaves, $make_cxn->(dsn => $dsn, dbh => $dbh); |
3941 | - return; |
3942 | - }, |
3943 | - } |
3944 | - ); |
3945 | - } |
3946 | - elsif ( $methods->[0] =~ m/^dsn=/i ) { |
3947 | - (my $dsn_table_dsn = join ",", @$methods) =~ s/^dsn=//i; |
3948 | - $slaves = $self->get_cxn_from_dsn_table( |
3949 | - %args, |
3950 | - dsn_table_dsn => $dsn_table_dsn, |
3951 | - ); |
3952 | - } |
3953 | - elsif ( $methods->[0] =~ m/none/i ) { |
3954 | - PTDEBUG && _d('Not getting to slaves'); |
3955 | - } |
3956 | - else { |
3957 | - die "Unexpected recursion methods: @$methods"; |
3958 | - } |
3959 | - |
3960 | - return $slaves; |
3961 | -} |
3962 | - |
3963 | -sub _resolve_recursion_methods { |
3964 | - my ($self, $dsn) = @_; |
3965 | - my $o = $self->{OptionParser}; |
3966 | - if ( $o->got('recursion-method') ) { |
3967 | - return $o->get('recursion-method'); |
3968 | - } |
3969 | - elsif ( $dsn && ($dsn->{P} || 3306) != 3306 ) { |
3970 | - PTDEBUG && _d('Port number is non-standard; using only hosts method'); |
3971 | - return [qw(hosts)]; |
3972 | - } |
3973 | - else { |
3974 | - return $o->get('recursion-method'); |
3975 | - } |
3976 | -} |
3977 | - |
3978 | -sub recurse_to_slaves { |
3979 | - my ( $self, $args, $level ) = @_; |
3980 | - $level ||= 0; |
3981 | - my $dp = $self->{DSNParser}; |
3982 | - my $recurse = $args->{recurse} || $self->{OptionParser}->get('recurse'); |
3983 | - my $dsn = $args->{dsn}; |
3984 | - |
3985 | - my $methods = $self->_resolve_recursion_methods($dsn); |
3986 | - PTDEBUG && _d('Recursion methods:', @$methods); |
3987 | - if ( lc($methods->[0]) eq 'none' ) { |
3988 | - PTDEBUG && _d('Not recursing to slaves'); |
3989 | - return; |
3990 | - } |
3991 | - |
3992 | - my $dbh; |
3993 | - eval { |
3994 | - $dbh = $args->{dbh} || $dp->get_dbh( |
3995 | - $dp->get_cxn_params($dsn), { AutoCommit => 1 }); |
3996 | - PTDEBUG && _d('Connected to', $dp->as_string($dsn)); |
3997 | - }; |
3998 | - if ( $EVAL_ERROR ) { |
3999 | - print STDERR "Cannot connect to ", $dp->as_string($dsn), "\n" |
4000 | - or die "Cannot print: $OS_ERROR"; |
4001 | - return; |
4002 | - } |
4003 | - |
4004 | - my $sql = 'SELECT @@SERVER_ID'; |
4005 | - PTDEBUG && _d($sql); |
4006 | - my ($id) = $dbh->selectrow_array($sql); |
4007 | - PTDEBUG && _d('Working on server ID', $id); |
4008 | - my $master_thinks_i_am = $dsn->{server_id}; |
4009 | - if ( !defined $id |
4010 | - || ( defined $master_thinks_i_am && $master_thinks_i_am != $id ) |
4011 | - || $args->{server_ids_seen}->{$id}++ |
4012 | - ) { |
4013 | - PTDEBUG && _d('Server ID seen, or not what master said'); |
4014 | - if ( $args->{skip_callback} ) { |
4015 | - $args->{skip_callback}->($dsn, $dbh, $level, $args->{parent}); |
4016 | - } |
4017 | - return; |
4018 | - } |
4019 | - |
4020 | - $args->{callback}->($dsn, $dbh, $level, $args->{parent}); |
4021 | - |
4022 | - if ( !defined $recurse || $level < $recurse ) { |
4023 | - |
4024 | - my @slaves = |
4025 | - grep { !$_->{master_id} || $_->{master_id} == $id } # Only my slaves. |
4026 | - $self->find_slave_hosts($dp, $dbh, $dsn, $methods); |
4027 | - |
4028 | - foreach my $slave ( @slaves ) { |
4029 | - PTDEBUG && _d('Recursing from', |
4030 | - $dp->as_string($dsn), 'to', $dp->as_string($slave)); |
4031 | - $self->recurse_to_slaves( |
4032 | - { %$args, dsn => $slave, dbh => undef, parent => $dsn }, $level + 1 ); |
4033 | - } |
4034 | - } |
4035 | -} |
4036 | - |
4037 | -sub find_slave_hosts { |
4038 | - my ( $self, $dsn_parser, $dbh, $dsn, $methods ) = @_; |
4039 | - |
4040 | - PTDEBUG && _d('Looking for slaves on', $dsn_parser->as_string($dsn), |
4041 | - 'using methods', @$methods); |
4042 | - |
4043 | - my @slaves; |
4044 | - METHOD: |
4045 | - foreach my $method ( @$methods ) { |
4046 | - my $find_slaves = "_find_slaves_by_$method"; |
4047 | - PTDEBUG && _d('Finding slaves with', $find_slaves); |
4048 | - @slaves = $self->$find_slaves($dsn_parser, $dbh, $dsn); |
4049 | - last METHOD if @slaves; |
4050 | - } |
4051 | - |
4052 | - PTDEBUG && _d('Found', scalar(@slaves), 'slaves'); |
4053 | - return @slaves; |
4054 | -} |
4055 | - |
4056 | -sub _find_slaves_by_processlist { |
4057 | - my ( $self, $dsn_parser, $dbh, $dsn ) = @_; |
4058 | - |
4059 | - my @slaves = map { |
4060 | - my $slave = $dsn_parser->parse("h=$_", $dsn); |
4061 | - $slave->{source} = 'processlist'; |
4062 | - $slave; |
4063 | - } |
4064 | - grep { $_ } |
4065 | - map { |
4066 | - my ( $host ) = $_->{host} =~ m/^([^:]+):/; |
4067 | - if ( $host eq 'localhost' ) { |
4068 | - $host = '127.0.0.1'; # Replication never uses sockets. |
4069 | - } |
4070 | - $host; |
4071 | - } $self->get_connected_slaves($dbh); |
4072 | - |
4073 | - return @slaves; |
4074 | -} |
4075 | - |
4076 | -sub _find_slaves_by_hosts { |
4077 | - my ( $self, $dsn_parser, $dbh, $dsn ) = @_; |
4078 | - |
4079 | - my @slaves; |
4080 | - my $sql = 'SHOW SLAVE HOSTS'; |
4081 | - PTDEBUG && _d($dbh, $sql); |
4082 | - @slaves = @{$dbh->selectall_arrayref($sql, { Slice => {} })}; |
4083 | - |
4084 | - if ( @slaves ) { |
4085 | - PTDEBUG && _d('Found some SHOW SLAVE HOSTS info'); |
4086 | - @slaves = map { |
4087 | - my %hash; |
4088 | - @hash{ map { lc $_ } keys %$_ } = values %$_; |
4089 | - my $spec = "h=$hash{host},P=$hash{port}" |
4090 | - . ( $hash{user} ? ",u=$hash{user}" : '') |
4091 | - . ( $hash{password} ? ",p=$hash{password}" : ''); |
4092 | - my $dsn = $dsn_parser->parse($spec, $dsn); |
4093 | - $dsn->{server_id} = $hash{server_id}; |
4094 | - $dsn->{master_id} = $hash{master_id}; |
4095 | - $dsn->{source} = 'hosts'; |
4096 | - $dsn; |
4097 | - } @slaves; |
4098 | - } |
4099 | - |
4100 | - return @slaves; |
4101 | -} |
4102 | - |
4103 | -sub get_connected_slaves { |
4104 | - my ( $self, $dbh ) = @_; |
4105 | - |
4106 | - my $show = "SHOW GRANTS FOR "; |
4107 | - my $user = 'CURRENT_USER()'; |
4108 | - my $sql = $show . $user; |
4109 | - PTDEBUG && _d($dbh, $sql); |
4110 | - |
4111 | - my $proc; |
4112 | - eval { |
4113 | - $proc = grep { |
4114 | - m/ALL PRIVILEGES.*?\*\.\*|PROCESS/ |
4115 | - } @{$dbh->selectcol_arrayref($sql)}; |
4116 | - }; |
4117 | - if ( $EVAL_ERROR ) { |
4118 | - |
4119 | - if ( $EVAL_ERROR =~ m/no such grant defined for user/ ) { |
4120 | - PTDEBUG && _d('Retrying SHOW GRANTS without host; error:', |
4121 | - $EVAL_ERROR); |
4122 | - ($user) = split('@', $user); |
4123 | - $sql = $show . $user; |
4124 | - PTDEBUG && _d($sql); |
4125 | - eval { |
4126 | - $proc = grep { |
4127 | - m/ALL PRIVILEGES.*?\*\.\*|PROCESS/ |
4128 | - } @{$dbh->selectcol_arrayref($sql)}; |
4129 | - }; |
4130 | - } |
4131 | - |
4132 | - die "Failed to $sql: $EVAL_ERROR" if $EVAL_ERROR; |
4133 | - } |
4134 | - if ( !$proc ) { |
4135 | - die "You do not have the PROCESS privilege"; |
4136 | - } |
4137 | - |
4138 | - $sql = 'SHOW PROCESSLIST'; |
4139 | - PTDEBUG && _d($dbh, $sql); |
4140 | - grep { $_->{command} =~ m/Binlog Dump/i } |
4141 | - map { # Lowercase the column names |
4142 | - my %hash; |
4143 | - @hash{ map { lc $_ } keys %$_ } = values %$_; |
4144 | - \%hash; |
4145 | - } |
4146 | - @{$dbh->selectall_arrayref($sql, { Slice => {} })}; |
4147 | -} |
4148 | - |
4149 | -sub is_master_of { |
4150 | - my ( $self, $master, $slave ) = @_; |
4151 | - my $master_status = $self->get_master_status($master) |
4152 | - or die "The server specified as a master is not a master"; |
4153 | - my $slave_status = $self->get_slave_status($slave) |
4154 | - or die "The server specified as a slave is not a slave"; |
4155 | - my @connected = $self->get_connected_slaves($master) |
4156 | - or die "The server specified as a master has no connected slaves"; |
4157 | - my (undef, $port) = $master->selectrow_array("SHOW VARIABLES LIKE 'port'"); |
4158 | - |
4159 | - if ( $port != $slave_status->{master_port} ) { |
4160 | - die "The slave is connected to $slave_status->{master_port} " |
4161 | - . "but the master's port is $port"; |
4162 | - } |
4163 | - |
4164 | - if ( !grep { $slave_status->{master_user} eq $_->{user} } @connected ) { |
4165 | - die "I don't see any slave I/O thread connected with user " |
4166 | - . $slave_status->{master_user}; |
4167 | - } |
4168 | - |
4169 | - if ( ($slave_status->{slave_io_state} || '') |
4170 | - eq 'Waiting for master to send event' ) |
4171 | - { |
4172 | - my ( $master_log_name, $master_log_num ) |
4173 | - = $master_status->{file} =~ m/^(.*?)\.0*([1-9][0-9]*)$/; |
4174 | - my ( $slave_log_name, $slave_log_num ) |
4175 | - = $slave_status->{master_log_file} =~ m/^(.*?)\.0*([1-9][0-9]*)$/; |
4176 | - if ( $master_log_name ne $slave_log_name |
4177 | - || abs($master_log_num - $slave_log_num) > 1 ) |
4178 | - { |
4179 | - die "The slave thinks it is reading from " |
4180 | - . "$slave_status->{master_log_file}, but the " |
4181 | - . "master is writing to $master_status->{file}"; |
4182 | - } |
4183 | - } |
4184 | - return 1; |
4185 | -} |
4186 | - |
4187 | -sub get_master_dsn { |
4188 | - my ( $self, $dbh, $dsn, $dsn_parser ) = @_; |
4189 | - my $master = $self->get_slave_status($dbh) or return undef; |
4190 | - my $spec = "h=$master->{master_host},P=$master->{master_port}"; |
4191 | - return $dsn_parser->parse($spec, $dsn); |
4192 | -} |
4193 | - |
4194 | -sub get_slave_status { |
4195 | - my ( $self, $dbh ) = @_; |
4196 | - if ( !$self->{not_a_slave}->{$dbh} ) { |
4197 | - my $sth = $self->{sths}->{$dbh}->{SLAVE_STATUS} |
4198 | - ||= $dbh->prepare('SHOW SLAVE STATUS'); |
4199 | - PTDEBUG && _d($dbh, 'SHOW SLAVE STATUS'); |
4200 | - $sth->execute(); |
4201 | - my ($ss) = @{$sth->fetchall_arrayref({})}; |
4202 | - |
4203 | - if ( $ss && %$ss ) { |
4204 | - $ss = { map { lc($_) => $ss->{$_} } keys %$ss }; # lowercase the keys |
4205 | - return $ss; |
4206 | - } |
4207 | - |
4208 | - PTDEBUG && _d('This server returns nothing for SHOW SLAVE STATUS'); |
4209 | - $self->{not_a_slave}->{$dbh}++; |
4210 | - } |
4211 | -} |
4212 | - |
4213 | -sub get_master_status { |
4214 | - my ( $self, $dbh ) = @_; |
4215 | - |
4216 | - if ( $self->{not_a_master}->{$dbh} ) { |
4217 | - PTDEBUG && _d('Server on dbh', $dbh, 'is not a master'); |
4218 | - return; |
4219 | - } |
4220 | - |
4221 | - my $sth = $self->{sths}->{$dbh}->{MASTER_STATUS} |
4222 | - ||= $dbh->prepare('SHOW MASTER STATUS'); |
4223 | - PTDEBUG && _d($dbh, 'SHOW MASTER STATUS'); |
4224 | - $sth->execute(); |
4225 | - my ($ms) = @{$sth->fetchall_arrayref({})}; |
4226 | - PTDEBUG && _d( |
4227 | - $ms ? map { "$_=" . (defined $ms->{$_} ? $ms->{$_} : '') } keys %$ms |
4228 | - : ''); |
4229 | - |
4230 | - if ( !$ms || scalar keys %$ms < 2 ) { |
4231 | - PTDEBUG && _d('Server on dbh', $dbh, 'does not seem to be a master'); |
4232 | - $self->{not_a_master}->{$dbh}++; |
4233 | - } |
4234 | - |
4235 | - return { map { lc($_) => $ms->{$_} } keys %$ms }; # lowercase the keys |
4236 | -} |
4237 | - |
4238 | -sub wait_for_master { |
4239 | - my ( $self, %args ) = @_; |
4240 | - my @required_args = qw(master_status slave_dbh); |
4241 | - foreach my $arg ( @required_args ) { |
4242 | - die "I need a $arg argument" unless $args{$arg}; |
4243 | - } |
4244 | - my ($master_status, $slave_dbh) = @args{@required_args}; |
4245 | - my $timeout = $args{timeout} || 60; |
4246 | - |
4247 | - my $result; |
4248 | - my $waited; |
4249 | - if ( $master_status ) { |
4250 | - my $sql = "SELECT MASTER_POS_WAIT('$master_status->{file}', " |
4251 | - . "$master_status->{position}, $timeout)"; |
4252 | - PTDEBUG && _d($slave_dbh, $sql); |
4253 | - my $start = time; |
4254 | - ($result) = $slave_dbh->selectrow_array($sql); |
4255 | - |
4256 | - $waited = time - $start; |
4257 | - |
4258 | - PTDEBUG && _d('Result of waiting:', $result); |
4259 | - PTDEBUG && _d("Waited", $waited, "seconds"); |
4260 | - } |
4261 | - else { |
4262 | - PTDEBUG && _d('Not waiting: this server is not a master'); |
4263 | - } |
4264 | - |
4265 | - return { |
4266 | - result => $result, |
4267 | - waited => $waited, |
4268 | - }; |
4269 | -} |
4270 | - |
4271 | -sub stop_slave { |
4272 | - my ( $self, $dbh ) = @_; |
4273 | - my $sth = $self->{sths}->{$dbh}->{STOP_SLAVE} |
4274 | - ||= $dbh->prepare('STOP SLAVE'); |
4275 | - PTDEBUG && _d($dbh, $sth->{Statement}); |
4276 | - $sth->execute(); |
4277 | -} |
4278 | - |
4279 | -sub start_slave { |
4280 | - my ( $self, $dbh, $pos ) = @_; |
4281 | - if ( $pos ) { |
4282 | - my $sql = "START SLAVE UNTIL MASTER_LOG_FILE='$pos->{file}', " |
4283 | - . "MASTER_LOG_POS=$pos->{position}"; |
4284 | - PTDEBUG && _d($dbh, $sql); |
4285 | - $dbh->do($sql); |
4286 | - } |
4287 | - else { |
4288 | - my $sth = $self->{sths}->{$dbh}->{START_SLAVE} |
4289 | - ||= $dbh->prepare('START SLAVE'); |
4290 | - PTDEBUG && _d($dbh, $sth->{Statement}); |
4291 | - $sth->execute(); |
4292 | - } |
4293 | -} |
4294 | - |
4295 | -sub catchup_to_master { |
4296 | - my ( $self, $slave, $master, $timeout ) = @_; |
4297 | - $self->stop_slave($master); |
4298 | - $self->stop_slave($slave); |
4299 | - my $slave_status = $self->get_slave_status($slave); |
4300 | - my $slave_pos = $self->repl_posn($slave_status); |
4301 | - my $master_status = $self->get_master_status($master); |
4302 | - my $master_pos = $self->repl_posn($master_status); |
4303 | - PTDEBUG && _d('Master position:', $self->pos_to_string($master_pos), |
4304 | - 'Slave position:', $self->pos_to_string($slave_pos)); |
4305 | - |
4306 | - my $result; |
4307 | - if ( $self->pos_cmp($slave_pos, $master_pos) < 0 ) { |
4308 | - PTDEBUG && _d('Waiting for slave to catch up to master'); |
4309 | - $self->start_slave($slave, $master_pos); |
4310 | - |
4311 | - $result = $self->wait_for_master( |
4312 | - master_status => $master_status, |
4313 | - slave_dbh => $slave, |
4314 | - timeout => $timeout, |
4315 | - master_status => $master_status |
4316 | - ); |
4317 | - if ( !defined $result->{result} ) { |
4318 | - $slave_status = $self->get_slave_status($slave); |
4319 | - if ( !$self->slave_is_running($slave_status) ) { |
4320 | - PTDEBUG && _d('Master position:', |
4321 | - $self->pos_to_string($master_pos), |
4322 | - 'Slave position:', $self->pos_to_string($slave_pos)); |
4323 | - $slave_pos = $self->repl_posn($slave_status); |
4324 | - if ( $self->pos_cmp($slave_pos, $master_pos) != 0 ) { |
4325 | - die "MASTER_POS_WAIT() returned NULL but slave has not " |
4326 | - . "caught up to master"; |
4327 | - } |
4328 | - PTDEBUG && _d('Slave is caught up to master and stopped'); |
4329 | - } |
4330 | - else { |
4331 | - die "Slave has not caught up to master and it is still running"; |
4332 | - } |
4333 | - } |
4334 | - } |
4335 | - else { |
4336 | - PTDEBUG && _d("Slave is already caught up to master"); |
4337 | - } |
4338 | - |
4339 | - return $result; |
4340 | -} |
4341 | - |
4342 | -sub catchup_to_same_pos { |
4343 | - my ( $self, $s1_dbh, $s2_dbh ) = @_; |
4344 | - $self->stop_slave($s1_dbh); |
4345 | - $self->stop_slave($s2_dbh); |
4346 | - my $s1_status = $self->get_slave_status($s1_dbh); |
4347 | - my $s2_status = $self->get_slave_status($s2_dbh); |
4348 | - my $s1_pos = $self->repl_posn($s1_status); |
4349 | - my $s2_pos = $self->repl_posn($s2_status); |
4350 | - if ( $self->pos_cmp($s1_pos, $s2_pos) < 0 ) { |
4351 | - $self->start_slave($s1_dbh, $s2_pos); |
4352 | - } |
4353 | - elsif ( $self->pos_cmp($s2_pos, $s1_pos) < 0 ) { |
4354 | - $self->start_slave($s2_dbh, $s1_pos); |
4355 | - } |
4356 | - |
4357 | - $s1_status = $self->get_slave_status($s1_dbh); |
4358 | - $s2_status = $self->get_slave_status($s2_dbh); |
4359 | - $s1_pos = $self->repl_posn($s1_status); |
4360 | - $s2_pos = $self->repl_posn($s2_status); |
4361 | - |
4362 | - if ( $self->slave_is_running($s1_status) |
4363 | - || $self->slave_is_running($s2_status) |
4364 | - || $self->pos_cmp($s1_pos, $s2_pos) != 0) |
4365 | - { |
4366 | - die "The servers aren't both stopped at the same position"; |
4367 | - } |
4368 | - |
4369 | -} |
4370 | - |
4371 | -sub slave_is_running { |
4372 | - my ( $self, $slave_status ) = @_; |
4373 | - return ($slave_status->{slave_sql_running} || 'No') eq 'Yes'; |
4374 | -} |
4375 | - |
4376 | -sub has_slave_updates { |
4377 | - my ( $self, $dbh ) = @_; |
4378 | - my $sql = q{SHOW VARIABLES LIKE 'log_slave_updates'}; |
4379 | - PTDEBUG && _d($dbh, $sql); |
4380 | - my ($name, $value) = $dbh->selectrow_array($sql); |
4381 | - return $value && $value =~ m/^(1|ON)$/; |
4382 | -} |
4383 | - |
4384 | -sub repl_posn { |
4385 | - my ( $self, $status ) = @_; |
4386 | - if ( exists $status->{file} && exists $status->{position} ) { |
4387 | - return { |
4388 | - file => $status->{file}, |
4389 | - position => $status->{position}, |
4390 | - }; |
4391 | - } |
4392 | - else { |
4393 | - return { |
4394 | - file => $status->{relay_master_log_file}, |
4395 | - position => $status->{exec_master_log_pos}, |
4396 | - }; |
4397 | - } |
4398 | -} |
4399 | - |
4400 | -sub get_slave_lag { |
4401 | - my ( $self, $dbh ) = @_; |
4402 | - my $stat = $self->get_slave_status($dbh); |
4403 | - return unless $stat; # server is not a slave |
4404 | - return $stat->{seconds_behind_master}; |
4405 | -} |
4406 | - |
4407 | -sub pos_cmp { |
4408 | - my ( $self, $a, $b ) = @_; |
4409 | - return $self->pos_to_string($a) cmp $self->pos_to_string($b); |
4410 | -} |
4411 | - |
4412 | -sub short_host { |
4413 | - my ( $self, $dsn ) = @_; |
4414 | - my ($host, $port); |
4415 | - if ( $dsn->{master_host} ) { |
4416 | - $host = $dsn->{master_host}; |
4417 | - $port = $dsn->{master_port}; |
4418 | - } |
4419 | - else { |
4420 | - $host = $dsn->{h}; |
4421 | - $port = $dsn->{P}; |
4422 | - } |
4423 | - return ($host || '[default]') . ( ($port || 3306) == 3306 ? '' : ":$port" ); |
4424 | -} |
4425 | - |
4426 | -sub is_replication_thread { |
4427 | - my ( $self, $query, %args ) = @_; |
4428 | - return unless $query; |
4429 | - |
4430 | - my $type = lc($args{type} || 'all'); |
4431 | - die "Invalid type: $type" |
4432 | - unless $type =~ m/^binlog_dump|slave_io|slave_sql|all$/i; |
4433 | - |
4434 | - my $match = 0; |
4435 | - if ( $type =~ m/binlog_dump|all/i ) { |
4436 | - $match = 1 |
4437 | - if ($query->{Command} || $query->{command} || '') eq "Binlog Dump"; |
4438 | - } |
4439 | - if ( !$match ) { |
4440 | - if ( ($query->{User} || $query->{user} || '') eq "system user" ) { |
4441 | - PTDEBUG && _d("Slave replication thread"); |
4442 | - if ( $type ne 'all' ) { |
4443 | - my $state = $query->{State} || $query->{state} || ''; |
4444 | - |
4445 | - if ( $state =~ m/^init|end$/ ) { |
4446 | - PTDEBUG && _d("Special state:", $state); |
4447 | - $match = 1; |
4448 | - } |
4449 | - else { |
4450 | - my ($slave_sql) = $state =~ m/ |
4451 | - ^(Waiting\sfor\sthe\snext\sevent |
4452 | - |Reading\sevent\sfrom\sthe\srelay\slog |
4453 | - |Has\sread\sall\srelay\slog;\swaiting |
4454 | - |Making\stemp\sfile |
4455 | - |Waiting\sfor\sslave\smutex\son\sexit)/xi; |
4456 | - |
4457 | - $match = $type eq 'slave_sql' && $slave_sql ? 1 |
4458 | - : $type eq 'slave_io' && !$slave_sql ? 1 |
4459 | - : 0; |
4460 | - } |
4461 | - } |
4462 | - else { |
4463 | - $match = 1; |
4464 | - } |
4465 | - } |
4466 | - else { |
4467 | - PTDEBUG && _d('Not system user'); |
4468 | - } |
4469 | - |
4470 | - if ( !defined $args{check_known_ids} || $args{check_known_ids} ) { |
4471 | - my $id = $query->{Id} || $query->{id}; |
4472 | - if ( $match ) { |
4473 | - $self->{replication_thread}->{$id} = 1; |
4474 | - } |
4475 | - else { |
4476 | - if ( $self->{replication_thread}->{$id} ) { |
4477 | - PTDEBUG && _d("Thread ID is a known replication thread ID"); |
4478 | - $match = 1; |
4479 | - } |
4480 | - } |
4481 | - } |
4482 | - } |
4483 | - |
4484 | - PTDEBUG && _d('Matches', $type, 'replication thread:', |
4485 | - ($match ? 'yes' : 'no'), '; match:', $match); |
4486 | - |
4487 | - return $match; |
4488 | -} |
4489 | - |
4490 | - |
4491 | -sub get_replication_filters { |
4492 | - my ( $self, %args ) = @_; |
4493 | - my @required_args = qw(dbh); |
4494 | - foreach my $arg ( @required_args ) { |
4495 | - die "I need a $arg argument" unless $args{$arg}; |
4496 | - } |
4497 | - my ($dbh) = @args{@required_args}; |
4498 | - |
4499 | - my %filters = (); |
4500 | - |
4501 | - my $status = $self->get_master_status($dbh); |
4502 | - if ( $status ) { |
4503 | - map { $filters{$_} = $status->{$_} } |
4504 | - grep { defined $status->{$_} && $status->{$_} ne '' } |
4505 | - qw( |
4506 | - binlog_do_db |
4507 | - binlog_ignore_db |
4508 | - ); |
4509 | - } |
4510 | - |
4511 | - $status = $self->get_slave_status($dbh); |
4512 | - if ( $status ) { |
4513 | - map { $filters{$_} = $status->{$_} } |
4514 | - grep { defined $status->{$_} && $status->{$_} ne '' } |
4515 | - qw( |
4516 | - replicate_do_db |
4517 | - replicate_ignore_db |
4518 | - replicate_do_table |
4519 | - replicate_ignore_table |
4520 | - replicate_wild_do_table |
4521 | - replicate_wild_ignore_table |
4522 | - ); |
4523 | - |
4524 | - my $sql = "SHOW VARIABLES LIKE 'slave_skip_errors'"; |
4525 | - PTDEBUG && _d($dbh, $sql); |
4526 | - my $row = $dbh->selectrow_arrayref($sql); |
4527 | - $filters{slave_skip_errors} = $row->[1] if $row->[1] && $row->[1] ne 'OFF'; |
4528 | - } |
4529 | - |
4530 | - return \%filters; |
4531 | -} |
4532 | - |
4533 | - |
4534 | -sub pos_to_string { |
4535 | - my ( $self, $pos ) = @_; |
4536 | - my $fmt = '%s/%020d'; |
4537 | - return sprintf($fmt, @{$pos}{qw(file position)}); |
4538 | -} |
4539 | - |
4540 | -sub reset_known_replication_threads { |
4541 | - my ( $self ) = @_; |
4542 | - $self->{replication_thread} = {}; |
4543 | - return; |
4544 | -} |
4545 | - |
4546 | -sub get_cxn_from_dsn_table { |
4547 | - my ($self, %args) = @_; |
4548 | - my @required_args = qw(dsn_table_dsn make_cxn); |
4549 | - foreach my $arg ( @required_args ) { |
4550 | - die "I need a $arg argument" unless $args{$arg}; |
4551 | - } |
4552 | - my ($dsn_table_dsn, $make_cxn) = @args{@required_args}; |
4553 | - PTDEBUG && _d('DSN table DSN:', $dsn_table_dsn); |
4554 | - |
4555 | - my $dp = $self->{DSNParser}; |
4556 | - my $q = $self->{Quoter}; |
4557 | - |
4558 | - my $dsn = $dp->parse($dsn_table_dsn); |
4559 | - my $dsn_table; |
4560 | - if ( $dsn->{D} && $dsn->{t} ) { |
4561 | - $dsn_table = $q->quote($dsn->{D}, $dsn->{t}); |
4562 | - } |
4563 | - elsif ( $dsn->{t} && $dsn->{t} =~ m/\./ ) { |
4564 | - $dsn_table = $q->quote($q->split_unquote($dsn->{t})); |
4565 | - } |
4566 | - else { |
4567 | - die "DSN table DSN does not specify a database (D) " |
4568 | - . "or a database-qualified table (t)"; |
4569 | - } |
4570 | - |
4571 | - my $dsn_tbl_cxn = $make_cxn->(dsn => $dsn); |
4572 | - my $dbh = $dsn_tbl_cxn->connect(); |
4573 | - my $sql = "SELECT dsn FROM $dsn_table ORDER BY id"; |
4574 | - PTDEBUG && _d($sql); |
4575 | - my $dsn_strings = $dbh->selectcol_arrayref($sql); |
4576 | - my @cxn; |
4577 | - if ( $dsn_strings ) { |
4578 | - foreach my $dsn_string ( @$dsn_strings ) { |
4579 | - PTDEBUG && _d('DSN from DSN table:', $dsn_string); |
4580 | - push @cxn, $make_cxn->(dsn_string => $dsn_string); |
4581 | - } |
4582 | - } |
4583 | - return \@cxn; |
4584 | -} |
4585 | - |
4586 | -sub _d { |
4587 | - my ($package, undef, $line) = caller 0; |
4588 | - @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
4589 | - map { defined $_ ? $_ : 'undef' } |
4590 | - @_; |
4591 | - print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; |
4592 | -} |
4593 | - |
4594 | -1; |
4595 | -} |
4596 | -# ########################################################################### |
4597 | -# End MasterSlave package |
4598 | -# ########################################################################### |
4599 | - |
4600 | -# ########################################################################### |
4601 | # Progress package |
4602 | # This package is a copy without comments from the original. The original |
4603 | # with comments and its test file can be found in the Bazaar repository at, |
4604 | @@ -13674,14 +13017,8 @@ |
4605 | { # event |
4606 | my $misc; |
4607 | if ( $ps_dsn = $o->get('processlist') ) { |
4608 | - my $ms = new MasterSlave( |
4609 | - OptionParser => $o, |
4610 | - DSNParser => $dp, |
4611 | - Quoter => $q, |
4612 | - ); |
4613 | my $pl = new Processlist( |
4614 | - interval => $o->get('interval') * 1_000_000, |
4615 | - MasterSlave => $ms |
4616 | + interval => $o->get('interval') * 1_000_000 |
4617 | ); |
4618 | my ( $sth, $cxn ); |
4619 | my $cur_server = 'processlist'; |
4620 | |
4621 | === modified file 'bin/pt-slave-find' |
4622 | --- bin/pt-slave-find 2012-11-09 16:31:13 +0000 |
4623 | +++ bin/pt-slave-find 2012-11-14 08:53:21 +0000 |
4624 | @@ -15,7 +15,9 @@ |
4625 | $INC{$_} = __FILE__ for map { (my $pkg = "$_.pm") =~ s!::!/!g; $pkg } (qw( |
4626 | OptionParser |
4627 | Mo |
4628 | + Percona::Object |
4629 | DSNParser |
4630 | + Cxn |
4631 | MasterSlave |
4632 | Daemon |
4633 | VersionParser |
4634 | @@ -1114,7 +1116,6 @@ |
4635 | push @args_to_delete, $attr; |
4636 | } |
4637 | } |
4638 | - |
4639 | delete $args->{$_} for @args_to_delete; |
4640 | |
4641 | for my $attribute ( keys %$args ) { |
4642 | @@ -1159,6 +1160,43 @@ |
4643 | } |
4644 | return $ref; |
4645 | } |
4646 | + |
4647 | + sub meta { |
4648 | + my $class = shift; |
4649 | + return Mo::Meta::Class->new(class => $class); |
4650 | + } |
4651 | +} |
4652 | + |
4653 | +{ |
4654 | + package Mo::Meta::Class; |
4655 | + |
4656 | + sub new { |
4657 | + my $class = shift; |
4658 | + return bless { @_ }, $class |
4659 | + } |
4660 | + |
4661 | + sub class { shift->{class} } |
4662 | + |
4663 | + sub attributes { |
4664 | + my $self = shift; |
4665 | + return keys %{$metadata_for{$self->class}} |
4666 | + } |
4667 | + |
4668 | + sub attributes_for_new { |
4669 | + my $self = shift; |
4670 | + my @attributes; |
4671 | + |
4672 | + while ( my ($attr, $meta) = each %{$metadata_for{$self->class}} ) { |
4673 | + if ( exists $meta->{init_arg} ) { |
4674 | + push @attributes, $meta->{init_arg} |
4675 | + if defined $meta->{init_arg}; |
4676 | + } |
4677 | + else { |
4678 | + push @attributes, $attr; |
4679 | + } |
4680 | + } |
4681 | + return @attributes; |
4682 | + } |
4683 | } |
4684 | |
4685 | my %export_for; |
4686 | @@ -1318,16 +1356,17 @@ |
4687 | |
4688 | sub _check_type_constaints { |
4689 | my ($attribute, $I, $I_name, $val) = @_; |
4690 | + local $@; |
4691 | ( ref($I) eq 'CODE' |
4692 | - ? $I->($val) |
4693 | + ? eval { $I->($val) } |
4694 | : (ref $val eq $I |
4695 | || ($val && $val eq $I) |
4696 | || (exists $TYPES{$I} && $TYPES{$I}->($val))) |
4697 | ) |
4698 | || Carp::confess( |
4699 | - qq<Attribute ($attribute) does not pass the type constraint because: > |
4700 | - . qq<Validation failed for '$I_name' with value > |
4701 | - . (defined $val ? Mo::Dumper($val) : 'undef') ) |
4702 | + qq<Attribute ($attribute) does not pass the type constraint because: > |
4703 | + . ( $@ || ( qq<Validation failed for '$I_name' with value > |
4704 | + . defined $val ? Mo::Dumper($val) : 'undef') ) ) |
4705 | } |
4706 | |
4707 | sub _has_handles { |
4708 | @@ -1500,6 +1539,52 @@ |
4709 | # ########################################################################### |
4710 | |
4711 | # ########################################################################### |
4712 | +# Percona::Object package |
4713 | +# This package is a copy without comments from the original. The original |
4714 | +# with comments and its test file can be found in the Bazaar repository at, |
4715 | +# lib/Percona/Object.pm |
4716 | +# t/lib/Percona/Object.t |
4717 | +# See https://launchpad.net/percona-toolkit for more information. |
4718 | +# ########################################################################### |
4719 | +{ |
4720 | +package Percona::Object; |
4721 | + |
4722 | +use strict; |
4723 | +use warnings FATAL => 'all'; |
4724 | +use English qw(-no_match_vars); |
4725 | +use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
4726 | + |
4727 | +use Mo; |
4728 | + |
4729 | +sub BUILDARGS { |
4730 | + my $self = shift; |
4731 | + my $args = $self->SUPER::BUILDARGS(@_); |
4732 | + |
4733 | + return $args unless $args->{OptionParser}; |
4734 | + |
4735 | + my $o = $args->{OptionParser}; |
4736 | + my @attributes = $self->meta->attributes_for_new(); |
4737 | + |
4738 | + foreach my $attr ( @attributes ) { |
4739 | + next if exists $args->{$attr}; |
4740 | + (my $attr_for_o = $attr) =~ tr/_/-/; |
4741 | + if ( $o->has($attr_for_o) ) { |
4742 | + $args->{$attr} = $o->get($attr_for_o) |
4743 | + } elsif ( $attr eq 'DSNParser' ) { |
4744 | + $args->{DSNParser} = $o->DSNParser; |
4745 | + } |
4746 | + } |
4747 | + |
4748 | + return $args; |
4749 | +} |
4750 | + |
4751 | +1; |
4752 | +} |
4753 | +# ########################################################################### |
4754 | +# End Percona::Object package |
4755 | +# ########################################################################### |
4756 | + |
4757 | +# ########################################################################### |
4758 | # DSNParser package |
4759 | # This package is a copy without comments from the original. The original |
4760 | # with comments and its test file can be found in the Bazaar repository at, |
4761 | @@ -1877,6 +1962,165 @@ |
4762 | # ########################################################################### |
4763 | |
4764 | # ########################################################################### |
4765 | +# Cxn package |
4766 | +# This package is a copy without comments from the original. The original |
4767 | +# with comments and its test file can be found in the Bazaar repository at, |
4768 | +# lib/Cxn.pm |
4769 | +# t/lib/Cxn.t |
4770 | +# See https://launchpad.net/percona-toolkit for more information. |
4771 | +# ########################################################################### |
4772 | +{ |
4773 | +package Cxn; |
4774 | + |
4775 | +use strict; |
4776 | +use warnings FATAL => 'all'; |
4777 | +use English qw(-no_match_vars); |
4778 | +use Scalar::Util qw(blessed); |
4779 | +use constant { |
4780 | + PTDEBUG => $ENV{PTDEBUG} || 0, |
4781 | + PERCONA_TOOLKIT_TEST_USE_DSN_NAMES => $ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} || 0, |
4782 | +}; |
4783 | + |
4784 | +sub new { |
4785 | + my ( $class, %args ) = @_; |
4786 | + my @required_args = qw(DSNParser OptionParser); |
4787 | + foreach my $arg ( @required_args ) { |
4788 | + die "I need a $arg argument" unless $args{$arg}; |
4789 | + }; |
4790 | + my ($dp, $o) = @args{@required_args}; |
4791 | + |
4792 | + my $dsn_defaults = $dp->parse_options($o); |
4793 | + my $prev_dsn = $args{prev_dsn}; |
4794 | + my $dsn = $args{dsn}; |
4795 | + if ( !$dsn ) { |
4796 | + $args{dsn_string} ||= 'h=' . ($dsn_defaults->{h} || 'localhost'); |
4797 | + |
4798 | + $dsn = $dp->parse( |
4799 | + $args{dsn_string}, $prev_dsn, $dsn_defaults); |
4800 | + } |
4801 | + elsif ( $prev_dsn ) { |
4802 | + $dsn = $dp->copy($prev_dsn, $dsn); |
4803 | + } |
4804 | + |
4805 | + my $self = { |
4806 | + dsn => $dsn, |
4807 | + dbh => $args{dbh}, |
4808 | + dsn_name => $dp->as_string($dsn, [qw(h P S)]), |
4809 | + hostname => '', |
4810 | + set => $args{set}, |
4811 | + NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, |
4812 | + dbh_set => 0, |
4813 | + OptionParser => $o, |
4814 | + DSNParser => $dp, |
4815 | + short_host => $args{short_host}, |
4816 | + }; |
4817 | + |
4818 | + return bless $self, $class; |
4819 | +} |
4820 | + |
4821 | +sub connect { |
4822 | + my ( $self ) = @_; |
4823 | + my $dsn = $self->{dsn}; |
4824 | + my $dp = $self->{DSNParser}; |
4825 | + my $o = $self->{OptionParser}; |
4826 | + |
4827 | + my $dbh = $self->{dbh}; |
4828 | + if ( !$dbh || !$dbh->ping() ) { |
4829 | + if ( $o->get('ask-pass') && !$self->{asked_for_pass} ) { |
4830 | + $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); |
4831 | + $self->{asked_for_pass} = 1; |
4832 | + } |
4833 | + $dbh = $dp->get_dbh($dp->get_cxn_params($dsn), { AutoCommit => 1 }); |
4834 | + } |
4835 | + PTDEBUG && _d($dbh, 'Connected dbh to', $self->{name}); |
4836 | + |
4837 | + return $self->set_dbh($dbh); |
4838 | +} |
4839 | + |
4840 | +sub set_dbh { |
4841 | + my ($self, $dbh) = @_; |
4842 | + |
4843 | + if ( $self->{dbh} && $self->{dbh} == $dbh && $self->{dbh_set} ) { |
4844 | + PTDEBUG && _d($dbh, 'Already set dbh'); |
4845 | + return $dbh; |
4846 | + } |
4847 | + |
4848 | + PTDEBUG && _d($dbh, 'Setting dbh'); |
4849 | + |
4850 | + $dbh->{FetchHashKeyName} = 'NAME_lc' if $self->{NAME_lc}; |
4851 | + |
4852 | + my $sql = 'SELECT @@hostname, @@server_id'; |
4853 | + PTDEBUG && _d($dbh, $sql); |
4854 | + my ($hostname, $server_id) = $dbh->selectrow_array($sql); |
4855 | + PTDEBUG && _d($dbh, 'hostname:', $hostname, $server_id); |
4856 | + if ( $hostname ) { |
4857 | + $self->{hostname} = $hostname; |
4858 | + } |
4859 | + |
4860 | + if ( my $set = $self->{set}) { |
4861 | + $set->($dbh); |
4862 | + } |
4863 | + |
4864 | + $self->{dbh} = $dbh; |
4865 | + $self->{dbh_set} = 1; |
4866 | + return $dbh; |
4867 | +} |
4868 | + |
4869 | +sub dbh { |
4870 | + my ($self) = @_; |
4871 | + return $self->{dbh}; |
4872 | +} |
4873 | + |
4874 | +sub dsn { |
4875 | + my ($self) = @_; |
4876 | + return $self->{dsn}; |
4877 | +} |
4878 | + |
4879 | +sub name { |
4880 | + my ($self) = @_; |
4881 | + if ( $self->{short_host} ) { |
4882 | + my $dsn = $self->{dsn}; |
4883 | + my ($host, $port); |
4884 | + if ( my $ss = $_[1] ) { |
4885 | + $host = $ss->{master_host}; |
4886 | + $port = $ss->{master_port}; |
4887 | + } |
4888 | + else { |
4889 | + $host = $dsn->{h}; |
4890 | + $port = $dsn->{P}; |
4891 | + } |
4892 | + return ($host || '[default]') . ( ($port || 3306) == 3306 ? '' : ":$port" ); |
4893 | + } |
4894 | + return $self->{dsn_name} if PERCONA_TOOLKIT_TEST_USE_DSN_NAMES; |
4895 | + return $self->{hostname} || $self->{dsn_name} || 'unknown host'; |
4896 | +} |
4897 | + |
4898 | +sub DESTROY { |
4899 | + my ($self) = @_; |
4900 | + if ( $self->{dbh} |
4901 | + && blessed($self->{dbh}) |
4902 | + && $self->{dbh}->can("disconnect") ) { |
4903 | + PTDEBUG && _d('Disconnecting dbh', $self->{dbh}, $self->{name}); |
4904 | + $self->{dbh}->disconnect(); |
4905 | + } |
4906 | + return; |
4907 | +} |
4908 | + |
4909 | +sub _d { |
4910 | + my ($package, undef, $line) = caller 0; |
4911 | + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
4912 | + map { defined $_ ? $_ : 'undef' } |
4913 | + @_; |
4914 | + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; |
4915 | +} |
4916 | + |
4917 | +1; |
4918 | +} |
4919 | +# ########################################################################### |
4920 | +# End Cxn package |
4921 | +# ########################################################################### |
4922 | + |
4923 | +# ########################################################################### |
4924 | # MasterSlave package |
4925 | # This package is a copy without comments from the original. The original |
4926 | # with comments and its test file can be found in the Bazaar repository at, |
4927 | @@ -1892,6 +2136,61 @@ |
4928 | use English qw(-no_match_vars); |
4929 | use constant PTDEBUG => $ENV{PTDEBUG} || 0; |
4930 | |
4931 | +use Mo; |
4932 | + |
4933 | +extends qw( Percona::Object ); |
4934 | + |
4935 | +local $EVAL_ERROR; |
4936 | +eval { |
4937 | + require Quoter; |
4938 | +}; |
4939 | + |
4940 | +has Quoter => ( |
4941 | + is => 'ro', |
4942 | + isa => 'Quoter', |
4943 | + default => sub { Quoter->new() }, |
4944 | +); |
4945 | + |
4946 | +has DSNParser => ( |
4947 | + is => 'ro', |
4948 | + isa => 'DSNParser', |
4949 | + required => 1, |
4950 | +); |
4951 | + |
4952 | +has recursion_method => ( |
4953 | + is => 'ro', |
4954 | + isa => sub { |
4955 | + die "recursion_method should be an arrayref, not " . ($_[0] || 'undef') |
4956 | + unless ref($_[0]) eq 'ARRAY'; |
4957 | + check_recursion_method($_[0]); |
4958 | + return 1; |
4959 | + }, |
4960 | + required => 1, |
4961 | +); |
4962 | + |
4963 | +has _explicit_recursion_method => ( |
4964 | + is => 'ro', |
4965 | + isa => 'Bool', |
4966 | + default => sub { 1 }, |
4967 | +); |
4968 | + |
4969 | +has recurse => ( |
4970 | + is => 'ro', |
4971 | + isa => 'Maybe[Int]', |
4972 | + required => 1, |
4973 | +); |
4974 | + |
4975 | +sub BUILDARGS { |
4976 | + my $self = shift; |
4977 | + my $args = $self->SUPER::BUILDARGS(@_); |
4978 | + my $o = delete $args->{OptionParser}; |
4979 | + |
4980 | + $args->{_explicit_recursion_method} = $o->got('recursion-method') |
4981 | + if $o; |
4982 | + |
4983 | + return $args; |
4984 | +} |
4985 | + |
4986 | sub check_recursion_method { |
4987 | my ($methods) = @_; |
4988 | |
4989 | @@ -1911,19 +2210,6 @@ |
4990 | } |
4991 | } |
4992 | |
4993 | -sub new { |
4994 | - my ( $class, %args ) = @_; |
4995 | - my @required_args = qw(OptionParser DSNParser Quoter); |
4996 | - foreach my $arg ( @required_args ) { |
4997 | - die "I need a $arg argument" unless $args{$arg}; |
4998 | - } |
4999 | - my $self = { |
5000 | - %args, |
Typo: http:// bazaar. launchpad. net/~percona- toolkit- dev/percona- toolkit/ masterslave- redesign- prototype/ revision/ 356#lib/ TableSyncer. pm
Moving is_replication_ thread to Processlist.pm is good.
What does Percona::Object do? It looks like if an obj has => 'foo_bar' and it also has OptionParser then it sets its foo_bar = $o->get('foo-bar')?
# Internal caches
has [qw( _not_a_master _not_a_slave _sths )] => (
^ Get rid of all caching. Well not *all* caching: stuff like server_ids_seen is required. What I mean is "global" caches, i.e. stuff that returns cached results (problematic) vs. keeping the code from getting itself in loops (which is what server_ids_seen does).
_repl_posn() -- is now pseudo-private (leading underscore)? (Re _pos_cmp())
Re short_host(): I think pt-slave-find needs to use Cxn and then move short_hosts()'s logic into Cxn::name() and enable via an optional arg like name(short => 1);
Overall, looks good. Make sure to update MasterSlave in all tools and then run a full test because messing with MasterSlave can introduce subtle bugs that only come up in certain cases.