Merge lp:~gryp/percona-toolkit/ptosc-lagwaiter-ptheartbeat into lp:~percona-toolkit-dev/percona-toolkit/release-2.2.8

Proposed by Daniel Nichter on 2014-04-24
Status: Merged
Approved by: Daniel Nichter on 2014-04-24
Approved revision: 603
Merged at revision: 599
Proposed branch: lp:~gryp/percona-toolkit/ptosc-lagwaiter-ptheartbeat
Merge into: lp:~percona-toolkit-dev/percona-toolkit/release-2.2.8
Diff against target: 448 lines (+301/-30)
6 files modified
bin/pt-online-schema-change (+23/-14)
bin/pt-table-checksum (+127/-16)
t/pt-online-schema-change/plugin.t (+1/-0)
t/pt-online-schema-change/samples/plugins/all_hooks.pm (+7/-0)
t/pt-table-checksum/plugin.t (+98/-0)
t/pt-table-checksum/samples/plugins/all_hooks.pm (+45/-0)
To merge this branch: bzr merge lp:~gryp/percona-toolkit/ptosc-lagwaiter-ptheartbeat
Reviewer Review Type Date Requested Status
Daniel Nichter Approve on 2014-04-24
Review via email: mp+217101@code.launchpad.net

This proposal supersedes a proposal from 2014-04-23.

Description of the change

Hi,

This merge request adds 2 features to 2 tools.

First of all, I have added --plugin to pt-table-checksum.
The necessary tests (similar to pt-osc) are added.

Then I have added a new --plugin call for pt-osc named 'get_slave_lag'.

Reason for this development is that a customer (issue #28725) wants to use the official percona toolkit packaged tools while using pt-heartbeat or tungsten to check slavelag. Both pt-osc and pt-tc need to have pt-heartbeat functionality which can be achieved with the --plugin and by overriding the $get_lag subroutine.

I have build an example plugin for both pt-osc and pt-tc in the following github branch: https://github.com/grypyrg/percona-toolkit-plugin-heartbeat/blob/master/pt-plugin-heartbeat.pm

I tried to do it as compatible as possible.
Implementing pt-heartbeat in the tools itself looked a bit more overkill and I think the plugin is a simple codechange which gives a lot of flexibility. Of course the flexibility means that this is not for novice users.

The changes that Daniel requested were implemented.

Kenny

To post a comment you must log in.
Daniel Nichter (daniel-nichter) wrote : Posted in a previous version of this proposal

This will make a good addition to the tool. Thanks for testing it. Before we merge, let's make the following changes:

1. Rename plugin hook to get-slave-lag

2. Rewrite code like:

my $get_lag;
if ( $plugin && $plugin->can('get-slave-lag') ) {
    $get_lag = $plugin->override_slavelag_check(oktorun => \$oktorun);
}
else {
    $get_lag = sub {...}
}

3. Break the code comments "# The plugin is able"... on/before 80 cols.

review: Resubmit
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/pt-online-schema-change'
2--- bin/pt-online-schema-change 2014-02-20 08:10:16 +0000
3+++ bin/pt-online-schema-change 2014-04-24 17:22:32 +0000
4@@ -8105,21 +8105,29 @@
5 return;
6 };
7
8- my $get_lag = sub {
9- my ($cxn) = @_;
10- my $dbh = $cxn->dbh();
11- if ( !$dbh || !$dbh->ping() ) {
12- eval { $dbh = $cxn->connect() }; # connect or die trying
13- if ( $EVAL_ERROR ) {
14- $oktorun = 0; # flag for cleanup tasks
15- chomp $EVAL_ERROR;
16- die "Lost connection to replica " . $cxn->name()
17- . " while attempting to get its lag ($EVAL_ERROR)\n";
18+ my $get_lag;
19+ # The plugin is able to override the slavelag check so tools like
20+ # pt-heartbeat or other replicators (Tungsten...) can be used to
21+ # measure replication lag
22+ if ( $plugin && $plugin->can('get_slave_lag') ) {
23+ $get_lag = $plugin->get_slave_lag(oktorun => \$oktorun);
24+ } else {
25+ $get_lag = sub {
26+ my ($cxn) = @_;
27+ my $dbh = $cxn->dbh();
28+ if ( !$dbh || !$dbh->ping() ) {
29+ eval { $dbh = $cxn->connect() }; # connect or die trying
30+ if ( $EVAL_ERROR ) {
31+ $oktorun = 0; # flag for cleanup tasks
32+ chomp $EVAL_ERROR;
33+ die "Lost connection to replica " . $cxn->name()
34+ . " while attempting to get its lag ($EVAL_ERROR)\n";
35+ }
36 }
37- }
38- return $ms->get_slave_lag($dbh);
39- };
40-
41+ return $ms->get_slave_lag($dbh);
42+ };
43+ }
44+
45 $replica_lag = new ReplicaLagWaiter(
46 slaves => $slave_lag_cxns,
47 max_lag => $o->get('max-lag'),
48@@ -11345,6 +11353,7 @@
49 after_drop_old_table
50 before_drop_triggers
51 before_exit
52+ get_slave_lag
53
54 Each hook is passed different arguments. To see which arguments are passed
55 to a hook, search for the hook's name in the tool's source code, like:
56
57=== modified file 'bin/pt-table-checksum'
58--- bin/pt-table-checksum 2014-02-20 08:10:16 +0000
59+++ bin/pt-table-checksum 2014-04-24 17:22:32 +0000
60@@ -9223,6 +9223,30 @@
61 my $slaves = []; # all slaves (that we can find)
62 my $slave_lag_cxns; # slaves whose lag we'll check
63
64+ # ########################################################################
65+ # Create --plugin.
66+ # ########################################################################
67+ my $plugin;
68+ if ( my $file = $o->get('plugin') ) {
69+ die "--plugin file $file does not exist\n" unless -f $file;
70+ eval {
71+ require $file;
72+ };
73+ die "Error loading --plugin $file: $EVAL_ERROR" if $EVAL_ERROR;
74+ eval {
75+ $plugin = pt_table_checksum_plugin->new(
76+ master_cxn => $master_cxn,
77+ explain => $o->get('explain'),
78+ quiet => $o->get('quiet'),
79+ resume => $o->get('resume'),
80+ Quoter => $q,
81+ TableParser => $tp,
82+ );
83+ };
84+ die "Error creating --plugin: $EVAL_ERROR" if $EVAL_ERROR;
85+ print "Created plugin from $file.\n";
86+ }
87+
88 my $replica_lag; # ReplicaLagWaiter object
89 my $replica_lag_pr; # Progress for ReplicaLagWaiter
90 my $sys_load; # MySQLStatusWaiter object
91@@ -9447,6 +9471,11 @@
92 # #####################################################################
93 if ( $o->get('replicate-check') && $o->get('replicate-check-only') ) {
94 PTDEBUG && _d('Will --replicate-check and exit');
95+
96+ # --plugin hook
97+ if ( $plugin && $plugin->can('before_replicate_check') ) {
98+ $plugin->before_replicate_check();
99+ }
100
101 foreach my $slave ( @$slaves ) {
102 my $diffs = $rc->find_replication_differences(
103@@ -9467,6 +9496,11 @@
104 }
105 }
106
107+ # --plugin hook
108+ if ( $plugin && $plugin->can('after_replicate_check') ) {
109+ $plugin->after_replicate_check();
110+ }
111+
112 PTDEBUG && _d('Exit status', $exit_status, 'oktorun', $oktorun);
113 return $exit_status;
114 }
115@@ -9545,23 +9579,31 @@
116 return;
117 };
118
119- my $get_lag = sub {
120- my ($cxn) = @_;
121- my $dbh = $cxn->dbh();
122- if ( !$dbh || !$dbh->ping() ) {
123- PTDEBUG && _d('Lost connection to slave', $cxn->name(),
124- 'while waiting for slave lag');
125- eval { $dbh = $cxn->connect() }; # connect or die trying
126- if ( $EVAL_ERROR ) {
127- $oktorun = 0; # Fatal error
128- chomp $EVAL_ERROR;
129- die "Lost connection to replica " . $cxn->name()
130- . " while attempting to get its lag ($EVAL_ERROR)";
131+ my $get_lag;
132+ # The plugin is able to override the slavelag check so tools like
133+ # pt-heartbeat or other replicators (Tungsten...) can be used to
134+ # measure replication lag
135+ if ( $plugin && $plugin->can('get_slave_lag') ) {
136+ $get_lag = $plugin->get_slave_lag(oktorun => \$oktorun);
137+ } else {
138+ $get_lag = sub {
139+ my ($cxn) = @_;
140+ my $dbh = $cxn->dbh();
141+ if ( !$dbh || !$dbh->ping() ) {
142+ PTDEBUG && _d('Lost connection to slave', $cxn->name(),
143+ 'while waiting for slave lag');
144+ eval { $dbh = $cxn->connect() }; # connect or die trying
145+ if ( $EVAL_ERROR ) {
146+ $oktorun = 0; # Fatal error
147+ chomp $EVAL_ERROR;
148+ die "Lost connection to replica " . $cxn->name()
149+ . " while attempting to get its lag ($EVAL_ERROR)";
150+ }
151 }
152- }
153- return $ms->get_slave_lag($dbh);
154- };
155-
156+ return $ms->get_slave_lag($dbh);
157+ };
158+ }
159+
160 $replica_lag = new ReplicaLagWaiter(
161 slaves => $slave_lag_cxns,
162 max_lag => $o->get('max-lag'),
163@@ -10169,6 +10211,19 @@
164 };
165
166 # ########################################################################
167+ # Init the --plugin.
168+ # ########################################################################
169+
170+ # --plugin hook
171+ if ( $plugin && $plugin->can('init') ) {
172+ $plugin->init(
173+ slaves => $slaves,
174+ slave_lag_cxns => $slave_lag_cxns,
175+ repl_table => $repl_table,
176+ );
177+ }
178+
179+ # ########################################################################
180 # Checksum each table.
181 # ########################################################################
182
183@@ -10272,6 +10327,12 @@
184 @$all_cols;
185 $tbl->{checksum_cols} = \@cols;
186
187+ # --plugin hook
188+ if ( $plugin && $plugin->can('before_checksum_table') ) {
189+ $plugin->before_checksum_table(
190+ tbl => $tbl);
191+ }
192+
193 # Finally, checksum the table.
194 # The "1 while" loop is necessary because we're executing REPLACE
195 # statements which don't return rows and NibbleIterator only
196@@ -10280,6 +10341,11 @@
197 # from the done callback, uses this start time.
198 $tbl->{checksum_results}->{start_time} = time;
199 1 while $nibble_iter->next();
200+
201+ # --plugin hook
202+ if ( $plugin && $plugin->can('after_checksum_table') ) {
203+ $plugin->after_checksum_table();
204+ }
205 }
206 };
207 if ( $EVAL_ERROR ) {
208@@ -12054,6 +12120,18 @@
209 tool will overwrite the PID file with the current PID. The PID file is
210 removed automatically when the tool exits.
211
212+=item --plugin
213+
214+type: string
215+
216+Perl module file that defines a C<pt_table_checksum_plugin> class.
217+A plugin allows you to write a Perl module that can hook into many parts
218+of pt-table-checksum. This requires a good knowledge of Perl and
219+Percona Toolkit conventions, which are beyond this scope of this
220+documentation. Please contact Percona if you have questions or need help.
221+
222+See L<"PLUGIN"> for more information.
223+
224 =item --port
225
226 short form: -P; type: int; group: Connection
227@@ -12402,6 +12480,39 @@
228
229 =back
230
231+=head1 PLUGIN
232+
233+The file specified by L<"--plugin"> must define a class (i.e. a package)
234+called C<pt_table_checksum_plugin> with a C<new()> subroutine.
235+The tool will create an instance of this class and call any hooks that
236+it defines. No hooks are required, but a plugin isn't very useful without
237+them.
238+
239+These hooks, in this order, are called if defined:
240+
241+ init
242+ before_replicate_check
243+ after_replicate_check
244+ get_slave_lag
245+ before_checksum_table
246+ after_checksum_table
247+
248+Each hook is passed different arguments. To see which arguments are passed
249+to a hook, search for the hook's name in the tool's source code, like:
250+
251+ # --plugin hook
252+ if ( $plugin && $plugin->can('init') ) {
253+ $plugin->init(
254+ slaves => $slaves,
255+ slave_lag_cxns => $slave_lag_cxns,
256+ repl_table => $repl_table,
257+ );
258+ }
259+
260+The comment C<# --plugin hook> precedes every hook call.
261+
262+Please contact Percona if you have questions or need help.
263+
264 =head1 DSN OPTIONS
265
266 These DSN options are used to create a DSN. Each option is given like
267
268=== modified file 't/pt-online-schema-change/plugin.t'
269--- t/pt-online-schema-change/plugin.t 2013-03-01 00:27:39 +0000
270+++ t/pt-online-schema-change/plugin.t 2014-04-24 17:22:32 +0000
271@@ -56,6 +56,7 @@
272 is_deeply(
273 \@called,
274 [
275+ 'PLUGIN get_slave_lag',
276 'PLUGIN init',
277 'PLUGIN before_create_new_table',
278 'PLUGIN after_create_new_table',
279
280=== modified file 't/pt-online-schema-change/samples/plugins/all_hooks.pm'
281--- t/pt-online-schema-change/samples/plugins/all_hooks.pm 2013-03-01 00:27:39 +0000
282+++ t/pt-online-schema-change/samples/plugins/all_hooks.pm 2014-04-24 17:22:32 +0000
283@@ -96,4 +96,11 @@
284 print "PLUGIN before_exit\n";
285 }
286
287+sub get_slave_lag {
288+ my ($self, %args) = @_;
289+ print "PLUGIN get_slave_lag\n";
290+
291+ return sub { return 0; };
292+}
293+
294 1;
295
296=== added file 't/pt-table-checksum/plugin.t'
297--- t/pt-table-checksum/plugin.t 1970-01-01 00:00:00 +0000
298+++ t/pt-table-checksum/plugin.t 2014-04-24 17:22:32 +0000
299@@ -0,0 +1,98 @@
300+#!/usr/bin/env perl
301+
302+BEGIN {
303+ die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
304+ unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
305+ unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
306+};
307+
308+use strict;
309+use warnings FATAL => 'all';
310+use English qw(-no_match_vars);
311+use Test::More;
312+
313+use PerconaTest;
314+use Sandbox;
315+require "$trunk/bin/pt-table-checksum";
316+
317+use Data::Dumper;
318+$Data::Dumper::Indent = 1;
319+$Data::Dumper::Sortkeys = 1;
320+$Data::Dumper::Quotekeys = 0;
321+
322+my $dp = new DSNParser(opts=>$dsn_opts);
323+my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
324+my $master_dbh = $sb->get_dbh_for('master');
325+
326+if ( !$master_dbh ) {
327+ plan skip_all => 'Cannot connect to sandbox master';
328+}
329+
330+my $output;
331+my $master_dsn = $sb->dsn_for('master');
332+my $sample = "t/pt-table-checksum/samples";
333+my $plugin = "$trunk/$sample/plugins";
334+my $exit;
335+my $rows;
336+
337+$master_dbh->prepare("drop database if exists percona")->execute();
338+$master_dbh->prepare("create database percona")->execute();
339+$master_dbh->prepare("create table if not exists percona.t ( a int primary key);")->execute();
340+$master_dbh->prepare("insert into percona.t values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)")->execute();
341+$master_dbh->prepare("analyze table percona.t;")->execute();
342+
343+# #############################################################################
344+# all_hooks.pm
345+# #############################################################################
346+
347+($output) = full_output(
348+ sub { pt_table_checksum::main(
349+ "$master_dsn",
350+ '--databases', 'percona',
351+ '--plugin', "$plugin/all_hooks.pm",
352+ )},
353+ stderr => 1,
354+);
355+
356+my @called = $output =~ m/^PLUGIN \S+$/gm;
357+
358+is_deeply(
359+ \@called,
360+ [
361+ 'PLUGIN get_slave_lag',
362+ 'PLUGIN init',
363+ 'PLUGIN before_checksum_table',
364+ 'PLUGIN after_checksum_table',
365+ ],
366+ "Called all plugins on basic run"
367+) or diag(Dumper($output));
368+
369+
370+($output) = full_output(
371+ sub { pt_table_checksum::main(
372+ "$master_dsn",
373+ '--replicate-check', '--replicate-check-only',
374+ '--databases', 'percona',
375+ '--plugin', "$plugin/all_hooks.pm",
376+ )},
377+ stderr => 1,
378+);
379+
380+@called = $output =~ m/^PLUGIN \S+$/gm;
381+
382+is_deeply(
383+ \@called,
384+ [
385+ 'PLUGIN before_replicate_check',
386+ 'PLUGIN after_replicate_check',
387+ ],
388+ "Called all plugins on replicate-check run"
389+) or diag(Dumper($output));
390+
391+
392+# #############################################################################
393+# Done.
394+# #############################################################################
395+$sb->wipe_clean($master_dbh);
396+ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
397+done_testing;
398
399=== added directory 't/pt-table-checksum/samples/plugins'
400=== added file 't/pt-table-checksum/samples/plugins/all_hooks.pm'
401--- t/pt-table-checksum/samples/plugins/all_hooks.pm 1970-01-01 00:00:00 +0000
402+++ t/pt-table-checksum/samples/plugins/all_hooks.pm 2014-04-24 17:22:32 +0000
403@@ -0,0 +1,45 @@
404+package pt_table_checksum_plugin;
405+
406+use strict;
407+use warnings FATAL => 'all';
408+use English qw(-no_match_vars);
409+use constant PTDEBUG => $ENV{PTDEBUG} || 0;
410+
411+sub new {
412+ my ($class, %args) = @_;
413+ my $self = { %args };
414+ return bless $self, $class;
415+}
416+
417+sub init {
418+ my ($self, %args) = @_;
419+ print "PLUGIN init\n";
420+}
421+
422+sub before_replicate_check {
423+ my ($self, %args) = @_;
424+ print "PLUGIN before_replicate_check\n";
425+}
426+
427+sub after_replicate_check {
428+ my ($self, %args) = @_;
429+ print "PLUGIN after_replicate_check\n";
430+}
431+
432+sub get_slave_lag {
433+ my ($self, %args) = @_;
434+ print "PLUGIN get_slave_lag\n";
435+ return sub { return 0; };
436+}
437+
438+sub before_checksum_table {
439+ my ($self, %args) = @_;
440+ print "PLUGIN before_checksum_table\n";
441+}
442+
443+sub after_checksum_table {
444+ my ($self, %args) = @_;
445+ print "PLUGIN after_checksum_table\n";
446+}
447+
448+1;

Subscribers

People subscribed via source and target branches