Merge lp:~percona-toolkit-dev/percona-toolkit/empty-table-bug-987393-2.0 into lp:percona-toolkit/2.0

Proposed by Daniel Nichter
Status: Merged
Merged at revision: 229
Proposed branch: lp:~percona-toolkit-dev/percona-toolkit/empty-table-bug-987393-2.0
Merge into: lp:percona-toolkit/2.0
Diff against target: 195 lines (+108/-6)
5 files modified
bin/pt-table-checksum (+27/-3)
lib/NibbleIterator.pm (+2/-1)
t/lib/OobNibbleIterator.t (+24/-1)
t/pt-table-checksum/bugs.t (+37/-1)
t/pt-table-checksum/samples/empty-table-bug-987393.sql (+18/-0)
To merge this branch: bzr merge lp:~percona-toolkit-dev/percona-toolkit/empty-table-bug-987393-2.0
Reviewer Review Type Date Requested Status
Daniel Nichter Approve
Review via email: mp+105355@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Daniel Nichter (daniel-nichter) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/pt-table-checksum'
2--- bin/pt-table-checksum 2012-05-08 23:21:47 +0000
3+++ bin/pt-table-checksum 2012-05-10 16:49:30 +0000
4@@ -3530,8 +3530,9 @@
5
6 my $where = $o->get('where');
7 my ($row_est, $mysql_index) = get_row_estimate(%args, where => $where);
8+ my $chunk_size_limit = $o->get('chunk-size-limit') || 1;
9 my $one_nibble = !defined $args{one_nibble} || $args{one_nibble}
10- ? $row_est <= $chunk_size * $o->get('chunk-size-limit')
11+ ? $row_est <= $chunk_size * $chunk_size_limit
12 : 0;
13 PTDEBUG && _d('One nibble:', $one_nibble ? 'yes' : 'no');
14
15@@ -6283,10 +6284,29 @@
16 my $total_rows = 0;
17 my $total_time = 0;
18 my $total_rate = 0;
19- my $limit = $o->get('chunk-size-limit');
20 my $tn = new TableNibbler(TableParser => $tp, Quoter => $q);
21 my $retry = new Retry();
22
23+
24+ # --chunk-size-limit has two purposes. The 1st, as documented, is
25+ # to prevent oversized chunks when the chunk index is not unique.
26+ # The 2nd is to determine if the table can be processed in one chunk
27+ # (WHERE 1=1 instead of nibbling). This creates a problem when
28+ # the user does --chunk-size-limit=0 to disable the 1st, documented
29+ # purpose because, apparently, they're using non-unique indexes and
30+ # they don't care about potentially large chunks. But disabling the
31+ # 1st purpose adversely affects the 2nd purpose becuase 0 * the chunk size
32+ # will always be zero, so tables will only be single-chunked if EXPLAIN
33+ # says there are 0 rows, but sometimes EXPLAIN says there is 1 row
34+ # even when the table is empty. This wouldn't matter except that nibbling
35+ # an empty table doesn't currently work becuase there are no boundaries,
36+ # so no checksum is written for the empty table. To fix this and
37+ # preserve the two purposes of this option, usages of the 2nd purpose
38+ # do || 1 so the limit is never 0 and empty tables are single-chunked.
39+ # See: https://bugs.launchpad.net/percona-toolkit/+bug/987393
40+ # This is used for the 1st purpose of --chunk-size-limit:
41+ my $limit = $o->get('chunk-size-limit');
42+
43 # ########################################################################
44 # Callbacks for each table's nibble iterator. All checksum work is done
45 # in these callbacks and the subs that they call.
46@@ -6339,7 +6359,11 @@
47 else {
48 if ( $nibble_iter->one_nibble() ) {
49 PTDEBUG && _d('Getting table row estimate on replicas');
50- my $chunk_size_limit = $o->get('chunk-size-limit');
51+
52+ # This is used for the 2nd purpose for --chunkr-size-limit;
53+ # see the large comment block above near my $limit = $o->...
54+ my $chunk_size_limit = $o->get('chunk-size-limit') || 1;
55+
56 my @too_large;
57 foreach my $slave ( @$slaves ) {
58 my ($n_rows) = NibbleIterator::get_row_estimate(
59
60=== modified file 'lib/NibbleIterator.pm'
61--- lib/NibbleIterator.pm 2012-05-08 23:21:47 +0000
62+++ lib/NibbleIterator.pm 2012-05-10 16:49:30 +0000
63@@ -64,8 +64,9 @@
64
65 my $where = $o->get('where');
66 my ($row_est, $mysql_index) = get_row_estimate(%args, where => $where);
67+ my $chunk_size_limit = $o->get('chunk-size-limit') || 1;
68 my $one_nibble = !defined $args{one_nibble} || $args{one_nibble}
69- ? $row_est <= $chunk_size * $o->get('chunk-size-limit')
70+ ? $row_est <= $chunk_size * $chunk_size_limit
71 : 0;
72 PTDEBUG && _d('One nibble:', $one_nibble ? 'yes' : 'no');
73
74
75=== modified file 't/lib/OobNibbleIterator.t'
76--- t/lib/OobNibbleIterator.t 2011-12-30 16:23:41 +0000
77+++ t/lib/OobNibbleIterator.t 2012-05-10 16:49:30 +0000
78@@ -41,7 +41,7 @@
79 plan skip_all => 'Cannot connect to sandbox master';
80 }
81 else {
82- plan tests => 15;
83+ plan tests => 16;
84 }
85
86 my $q = new Quoter();
87@@ -248,6 +248,29 @@
88 );
89
90 # #############################################################################
91+# Empty table
92+# https://bugs.launchpad.net/percona-toolkit/+bug/987393
93+# #############################################################################
94+$sb->load_file('master', "t/pt-table-checksum/samples/empty-table-bug-987393.sql");
95+PerconaTest::wait_for_table($dbh, "test.test_full", "id=1");
96+
97+$ni = make_nibble_iter(
98+ db => 'test',
99+ tbl => 'test_empty',
100+ argv => [qw(--databases test --chunk-size-limit 0)],
101+);
102+
103+@rows = ();
104+for (1..5) {
105+ push @rows, $ni->next();
106+}
107+is_deeply(
108+ \@rows,
109+ [],
110+ "Empty table"
111+);
112+
113+# #############################################################################
114 # Done.
115 # #############################################################################
116 {
117
118=== modified file 't/pt-table-checksum/bugs.t'
119--- t/pt-table-checksum/bugs.t 2012-05-08 23:16:36 +0000
120+++ t/pt-table-checksum/bugs.t 2012-05-10 16:49:30 +0000
121@@ -38,7 +38,7 @@
122 plan skip_all => 'Cannot connect to sandbox slave1';
123 }
124 else {
125- plan tests => 2;
126+ plan tests => 5;
127 }
128
129 # The sandbox servers run with lock_wait_timeout=3 and it's not dynamic
130@@ -78,6 +78,42 @@
131 );
132
133 # #############################################################################
134+# https://bugs.launchpad.net/percona-toolkit/+bug/987393
135+# Empy tables cause "undefined value as an ARRAY" errors
136+# #############################################################################
137+$master_dbh->do("DROP DATABASE IF EXISTS percona"); # clear old checksums
138+$sb->load_file('master', "$sample/empty-table-bug-987393.sql");
139+PerconaTest::wait_for_table($slave_dbh, "test.test_full", "id=1");
140+
141+$output = output(
142+ sub { $exit_status = pt_table_checksum::main(
143+ @args, qw(-d test --chunk-size-limit 0)) },
144+ stderr => 1,
145+);
146+
147+is(
148+ $exit_status,
149+ 0,
150+ "Bug 987393 (empty table): zero exit status"
151+);
152+
153+is(
154+ PerconaTest::count_checksum_results($output, 'errors'),
155+ 0,
156+ "Bug 987393 (empty table): no errors"
157+);
158+
159+my $rows = $master_dbh->selectall_arrayref("SELECT db, tbl, chunk, master_crc, master_cnt FROM percona.checksums ORDER BY db, tbl, chunk");
160+is_deeply(
161+ $rows,
162+ [
163+ ['test', 'test_empty', '1', '0', '0'], # empty
164+ ['test', 'test_full', '1', 'ac967054', '1'], # row
165+ ],
166+ "Bug 987393 (empty table): checksums"
167+) or print STDERR Dumper($rows);
168+
169+# #############################################################################
170 # Done.
171 # #############################################################################
172 $sb->wipe_clean($master_dbh);
173
174=== added file 't/pt-table-checksum/samples/empty-table-bug-987393.sql'
175--- t/pt-table-checksum/samples/empty-table-bug-987393.sql 1970-01-01 00:00:00 +0000
176+++ t/pt-table-checksum/samples/empty-table-bug-987393.sql 2012-05-10 16:49:30 +0000
177@@ -0,0 +1,18 @@
178+DROP DATABASE IF EXISTS test;
179+CREATE DATABASE test;
180+USE test;
181+
182+CREATE TABLE `test_empty` (
183+ `id` int(11) NOT NULL AUTO_INCREMENT,
184+ PRIMARY KEY (`id`)
185+) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
186+
187+
188+CREATE TABLE `test_full` (
189+ `id` int(11) NOT NULL AUTO_INCREMENT,
190+ `d` text NOT NULL,
191+ PRIMARY KEY (`id`)
192+) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
193+
194+INSERT INTO `test_full` (`id`, `d`) VALUES
195+(1, '2');

Subscribers

People subscribed via source and target branches