Merge lp:~percona-toolkit-dev/percona-toolkit/fix-886059-pt-heartbeat-timezones into lp:percona-toolkit/2.1

Proposed by Daniel Nichter
Status: Merged
Approved by: Daniel Nichter
Approved revision: 505
Merged at revision: 507
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/fix-886059-pt-heartbeat-timezones
Merge into: lp:percona-toolkit/2.1
Diff against target: 198 lines (+127/-10)
2 files modified
bin/pt-heartbeat (+14/-10)
t/pt-heartbeat/bugs.t (+113/-0)
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/fix-886059-pt-heartbeat-timezones
Reviewer Review Type Date Requested Status
Brian Fraser (community) Approve
Daniel Nichter Approve
Review via email: mp+139737@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Daniel Nichter (daniel-nichter) :
review: Approve
Revision history for this message
Brian Fraser (fraserbn) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bin/pt-heartbeat'
--- bin/pt-heartbeat 2012-12-03 03:48:11 +0000
+++ bin/pt-heartbeat 2012-12-13 16:24:27 +0000
@@ -4734,7 +4734,7 @@
4734 $dbh->do($sql);4734 $dbh->do($sql);
47354735
4736 $sql = ($o->get('replace') ? "REPLACE" : "INSERT")4736 $sql = ($o->get('replace') ? "REPLACE" : "INSERT")
4737 . qq/ INTO $db_tbl (ts, server_id) VALUES (NOW(), $server_id)/;4737 . qq/ INTO $db_tbl (ts, server_id) VALUES (UTC_TIMESTAMP(), $server_id)/;
4738 PTDEBUG && _d($sql);4738 PTDEBUG && _d($sql);
4739 # This may fail if the table already existed and already had this row.4739 # This may fail if the table already existed and already had this row.
4740 # We eval to ignore this possibility.4740 # We eval to ignore this possibility.
@@ -4831,7 +4831,7 @@
4831 PTDEBUG && _d('No heartbeat row in table');4831 PTDEBUG && _d('No heartbeat row in table');
4832 if ( $o->get('insert-heartbeat-row') ) {4832 if ( $o->get('insert-heartbeat-row') ) {
4833 my $sql = "INSERT INTO $db_tbl ($pk_col, ts) "4833 my $sql = "INSERT INTO $db_tbl ($pk_col, ts) "
4834 . "VALUES ('$pk_val', NOW())";4834 . "VALUES ('$pk_val', UTC_TIMESTAMP())";
4835 PTDEBUG && _d($sql);4835 PTDEBUG && _d($sql);
4836 $dbh->do($sql);4836 $dbh->do($sql);
4837 }4837 }
@@ -4920,7 +4920,8 @@
4920 }4920 }
4921 }4921 }
49224922
4923 $sth->execute(ts(time), @vals);4923 my ($now) = $dbh->selectrow_array(q{SELECT UTC_TIMESTAMP()});
4924 $sth->execute($now, @vals);
4924 PTDEBUG && _d($sth->{Statement});4925 PTDEBUG && _d($sth->{Statement});
4925 $sth->finish();4926 $sth->finish();
49264927
@@ -4930,8 +4931,12 @@
4930 else { # --monitor or --check4931 else { # --monitor or --check
4931 my $dbi_driver = lc $o->get('dbi-driver');4932 my $dbi_driver = lc $o->get('dbi-driver');
49324933
4934 # UNIX_TIMESTAMP(UTC_TIMESTAMP()) instead of UNIX_TIMESTAMP() alone,
4935 # so we make sure that we aren't being fooled by a timezone.
4936 # UNIX_TIMESTAMP(ts) replaces unix_timestamp($ts) -- MySQL is the
4937 # authority here, so let it calculate everything.
4933 $heartbeat_sql4938 $heartbeat_sql
4934 = "SELECT ts"4939 = "SELECT UNIX_TIMESTAMP(UTC_TIMESTAMP()), UNIX_TIMESTAMP(ts)"
4935 . ($dbi_driver eq 'mysql' ? '/*!50038, @@hostname AS host*/' : '')4940 . ($dbi_driver eq 'mysql' ? '/*!50038, @@hostname AS host*/' : '')
4936 . ($id ? "" : ", server_id")4941 . ($id ? "" : ", server_id")
4937 . " FROM $db_tbl "4942 . " FROM $db_tbl "
@@ -4945,13 +4950,12 @@
4945 my ($sth) = @_;4950 my ($sth) = @_;
4946 $sth->execute();4951 $sth->execute();
4947 PTDEBUG && _d($sth->{Statement});4952 PTDEBUG && _d($sth->{Statement});
4948 my ($ts, $hostname, $server_id) = $sth->fetchrow_array();4953 my ($now, $ts, $hostname, $server_id) = $sth->fetchrow_array();
4949 my $now = time;
4950 PTDEBUG && _d("Heartbeat from server", $server_id, "\n",4954 PTDEBUG && _d("Heartbeat from server", $server_id, "\n",
4951 " now:", ts($now), "\n",4955 " now:", $now, "\n",
4952 " ts:", $ts, "\n",4956 " ts:", $ts, "\n",
4953 "skew:", $skew);4957 "skew:", $skew);
4954 my $delay = $now - unix_timestamp($ts) - $skew;4958 my $delay = $now - $ts - $skew;
4955 PTDEBUG && _d('Delay', sprintf('%.6f', $delay), 'on', $hostname);4959 PTDEBUG && _d('Delay', sprintf('%.6f', $delay), 'on', $hostname);
49564960
4957 # Because we adjust for skew, if the ts are less than skew seconds4961 # Because we adjust for skew, if the ts are less than skew seconds
@@ -5446,7 +5450,7 @@
5446The heartbeat table requires at least one row. If you manually create the5450The heartbeat table requires at least one row. If you manually create the
5447heartbeat table, then you must insert a row by doing:5451heartbeat table, then you must insert a row by doing:
54485452
5449 INSERT INTO heartbeat (ts, server_id) VALUES (NOW(), N);5453 INSERT INTO heartbeat (ts, server_id) VALUES (UTC_TIMESTAMP(), N);
54505454
5451where C<N> is the server's ID; do not use @@server_id because it will replicate5455where C<N> is the server's ID; do not use @@server_id because it will replicate
5452and slaves will insert their own server ID instead of the master's server ID.5456and slaves will insert their own server ID instead of the master's server ID.
@@ -5464,7 +5468,7 @@
5464of a multi-slave hierarchy like "master -> slave1 -> slave2".5468of a multi-slave hierarchy like "master -> slave1 -> slave2".
5465To manually insert the one required row into a legacy table:5469To manually insert the one required row into a legacy table:
54665470
5467 INSERT INTO heartbeat (id, ts) VALUES (1, NOW());5471 INSERT INTO heartbeat (id, ts) VALUES (1, UTC_TIMESTAMP());
54685472
5469The tool automatically detects if the heartbeat table is legacy.5473The tool automatically detects if the heartbeat table is legacy.
54705474
54715475
=== added file 't/pt-heartbeat/bugs.t'
--- t/pt-heartbeat/bugs.t 1970-01-01 00:00:00 +0000
+++ t/pt-heartbeat/bugs.t 2012-12-13 16:24:27 +0000
@@ -0,0 +1,113 @@
1#!/usr/bin/env perl
2
3BEGIN {
4 die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
5 unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
6 unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
7};
8
9use strict;
10use warnings FATAL => 'all';
11use English qw(-no_match_vars);
12use Test::More;
13
14use POSIX qw( tzset );
15use File::Temp qw(tempfile);
16
17use PerconaTest;
18use Sandbox;
19require "$trunk/bin/pt-heartbeat";
20
21my $dp = new DSNParser(opts=>$dsn_opts);
22my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
23my $master_dbh = $sb->get_dbh_for('master');
24my $slave1_dbh = $sb->get_dbh_for('slave1');
25my $slave2_dbh = $sb->get_dbh_for('slave2');
26
27if ( !$master_dbh ) {
28 plan skip_all => 'Cannot connect to sandbox master';
29}
30elsif ( !$slave1_dbh ) {
31 plan skip_all => 'Cannot connect to sandbox slave1';
32}
33elsif ( !$slave2_dbh ) {
34 plan skip_all => 'Cannot connect to sandbox slave2';
35}
36
37unlink '/tmp/pt-heartbeat-sentinel';
38$sb->create_dbs($master_dbh, ['test']);
39$sb->wait_for_slaves();
40
41my $output;
42my $base_pidfile = (tempfile("/tmp/pt-heartbeat-test.XXXXXXXX", OPEN => 0, UNLINK => 0))[1];
43my $master_port = $sb->port_for('master');
44
45my @exec_pids;
46my @pidfiles;
47
48sub start_update_instance {
49 my ($port) = @_;
50 my $pidfile = "$base_pidfile.$port.pid";
51 push @pidfiles, $pidfile;
52
53 my $pid = fork();
54 if ( $pid == 0 ) {
55 my $cmd = "$trunk/bin/pt-heartbeat";
56 exec { $cmd } $cmd, qw(-h 127.0.0.1 -u msandbox -p msandbox -P), $port,
57 qw(--database test --table heartbeat --create-table),
58 qw(--update --interval 0.5 --pid), $pidfile;
59 exit 1;
60 }
61 push @exec_pids, $pid;
62
63 PerconaTest::wait_for_files($pidfile);
64 ok(
65 -f $pidfile,
66 "--update on $port started"
67 );
68}
69
70sub stop_all_instances {
71 my @pids = @exec_pids, map { chomp; $_ } map { slurp_file($_) } @pidfiles;
72 diag(`$trunk/bin/pt-heartbeat --stop >/dev/null`);
73
74 waitpid($_, 0) for @pids;
75 PerconaTest::wait_until(sub{ !-e $_ }) for @pidfiles;
76
77 unlink '/tmp/pt-heartbeat-sentinel';
78}
79
80# ############################################################################
81# pt-heartbeat handles timezones inconsistently
82# https://bugs.launchpad.net/percona-toolkit/+bug/886059
83# ############################################################################
84
85start_update_instance( $master_port );
86
87my $slave1_dsn = $sb->dsn_for('slave1');
88# Using full_output here to work around a Perl bug: Only the first explicit
89# tzset works.
90($output) = full_output(sub {
91 local $ENV{TZ} = '-09:00';
92 tzset();
93 pt_heartbeat::main($slave1_dsn, qw(--database test --table heartbeat),
94 qw(--check --master-server-id), $master_port)
95});
96
97# If the servers use UTC then the lag should be 0.00, or at least
98# no greater than 9.99 for slow test boxes. When this fails, the
99# lag is like 25200.00 becaues the servers are hours off.
100like(
101 $output,
102 qr/\A\d.\d{2}$/,
103 "Bug 886059: pt-heartbeat doesn't get confused with differing timezones"
104);
105
106stop_all_instances();
107
108# ############################################################################
109# Done.
110# ############################################################################
111$sb->wipe_clean($master_dbh);
112ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
113done_testing;

Subscribers

People subscribed via source and target branches