Merge lp:~laurynas-biveinis/percona-server/26611-bug917942-5.1 into lp:percona-server/5.1

Proposed by Laurynas Biveinis
Status: Merged
Approved by: Stewart Smith
Approved revision: no longer in the source branch.
Merged at revision: 498
Proposed branch: lp:~laurynas-biveinis/percona-server/26611-bug917942-5.1
Merge into: lp:percona-server/5.1
Prerequisite: lp:~laurynas-biveinis/percona-server/bug1064326-5.1
Diff against target: 395 lines (+288/-7)
7 files modified
Percona-Server/mysql-test/r/percona_innodb_fake_changes_bug_917942.result (+151/-0)
Percona-Server/mysql-test/t/percona_innodb_fake_changes_bug_917942-master.opt (+1/-0)
Percona-Server/mysql-test/t/percona_innodb_fake_changes_bug_917942.test (+103/-0)
Percona-Server/storage/innodb_plugin/btr/btr0cur.c (+28/-4)
Percona-Server/storage/innodb_plugin/btr/btr0pcur.c (+1/-0)
Percona-Server/storage/innodb_plugin/ibuf/ibuf0ibuf.c (+1/-1)
Percona-Server/storage/innodb_plugin/include/btr0cur.h (+3/-2)
To merge this branch: bzr merge lp:~laurynas-biveinis/percona-server/26611-bug917942-5.1
Reviewer Review Type Date Requested Status
Stewart Smith (community) Approve
Review via email: mp+130074@code.launchpad.net

Description of the change

Fix bug 917942 (fake-changes infinite loop on update, allocates many
extents).

The bug was reported by, and the fix and testcase are adopted from
Facebook MySQL branch, revision 3787 of lp:mysqlatfacebook/51.

The issue is btr_cur_pessimistic_update() has uninitialized
cursor->tree_height when fake changes enabled, which is then used to
compute the number of extents to allocate. Other similar functions
(btr_cur_pessimistic_insert() and btr_cur_pessimistic_delete()) seem
to be unaffected.

Fixed by initializing cursor->tree_height to ULINT_UNDEFINED in
btr_pcur_create_for_mysql(), testing for this value in
btr_cur_pessimistic_update() and allocating 3 extents in the case of
fake changes. Added checks in btr_cur_pessimistic_insert() and
btr_cur_pessimistic_delete() that tree_height != ULINT_UNDEFINED.

Make btr_cur_update_alloc_zip() not call
page_zip_compress_write_log_no_data() as fake transactions should not
write to log and to avoid hitting asserts. For that, pass trx to the
former and update all callers.

Add a testcase for the bug.

http://jenkins.percona.com/job/percona-server-5.1-param/447/

26611

To post a comment you must log in.
Revision history for this message
Stewart Smith (stewart) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'Percona-Server/mysql-test/r/percona_innodb_fake_changes_bug_917942.result'
2--- Percona-Server/mysql-test/r/percona_innodb_fake_changes_bug_917942.result 1970-01-01 00:00:00 +0000
3+++ Percona-Server/mysql-test/r/percona_innodb_fake_changes_bug_917942.result 2012-10-17 10:27:31 +0000
4@@ -0,0 +1,151 @@
5+DROP TABLE IF EXISTS t1;
6+Test compressed
7+CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, v VARCHAR(200), t TEXT) ENGINE=INNODB KEY_BLOCK_SIZE=8;
8+INSERT INTO t1 VALUES(NULL, LPAD("v", 2, "b"), LPAD("a", 100, "b"));
9+SET innodb_fake_changes=1;
10+INSERT INTO t1 VALUES(1, "foo", LPAD("a", 4600, "b")) ON DUPLICATE KEY UPDATE T = VALUES(T);
11+ERROR HY000: Got error 131 during COMMIT
12+UPDATE t1 SET T = LPAD("A", 4600, "b");
13+ERROR HY000: Got error 131 during COMMIT
14+SET innodb_fake_changes=0;
15+CHECK TABLE t1;
16+Table Op Msg_type Msg_text
17+test.t1 check status OK
18+should_be_1
19+1
20+DROP TABLE t1;
21+Test for uncompressed
22+CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, v VARCHAR(200), t TEXT) ENGINE=INNODB;
23+INSERT INTO t1 VALUES(NULL, LPAD("v", 2, "b"), LPAD("a", 100, "b"));
24+SET innodb_fake_changes=1;
25+ERROR HY000: Got error 131 during COMMIT
26+ERROR HY000: Got error 131 during COMMIT
27+ERROR HY000: Got error 131 during COMMIT
28+ERROR HY000: Got error 131 during COMMIT
29+ERROR HY000: Got error 131 during COMMIT
30+ERROR HY000: Got error 131 during COMMIT
31+ERROR HY000: Got error 131 during COMMIT
32+ERROR HY000: Got error 131 during COMMIT
33+ERROR HY000: Got error 131 during COMMIT
34+ERROR HY000: Got error 131 during COMMIT
35+ERROR HY000: Got error 131 during COMMIT
36+ERROR HY000: Got error 131 during COMMIT
37+ERROR HY000: Got error 131 during COMMIT
38+ERROR HY000: Got error 131 during COMMIT
39+ERROR HY000: Got error 131 during COMMIT
40+ERROR HY000: Got error 131 during COMMIT
41+ERROR HY000: Got error 131 during COMMIT
42+ERROR HY000: Got error 131 during COMMIT
43+ERROR HY000: Got error 131 during COMMIT
44+ERROR HY000: Got error 131 during COMMIT
45+ERROR HY000: Got error 131 during COMMIT
46+ERROR HY000: Got error 131 during COMMIT
47+ERROR HY000: Got error 131 during COMMIT
48+ERROR HY000: Got error 131 during COMMIT
49+ERROR HY000: Got error 131 during COMMIT
50+ERROR HY000: Got error 131 during COMMIT
51+ERROR HY000: Got error 131 during COMMIT
52+ERROR HY000: Got error 131 during COMMIT
53+ERROR HY000: Got error 131 during COMMIT
54+ERROR HY000: Got error 131 during COMMIT
55+ERROR HY000: Got error 131 during COMMIT
56+ERROR HY000: Got error 131 during COMMIT
57+ERROR HY000: Got error 131 during COMMIT
58+ERROR HY000: Got error 131 during COMMIT
59+ERROR HY000: Got error 131 during COMMIT
60+ERROR HY000: Got error 131 during COMMIT
61+ERROR HY000: Got error 131 during COMMIT
62+ERROR HY000: Got error 131 during COMMIT
63+ERROR HY000: Got error 131 during COMMIT
64+ERROR HY000: Got error 131 during COMMIT
65+ERROR HY000: Got error 131 during COMMIT
66+ERROR HY000: Got error 131 during COMMIT
67+ERROR HY000: Got error 131 during COMMIT
68+ERROR HY000: Got error 131 during COMMIT
69+ERROR HY000: Got error 131 during COMMIT
70+ERROR HY000: Got error 131 during COMMIT
71+ERROR HY000: Got error 131 during COMMIT
72+ERROR HY000: Got error 131 during COMMIT
73+ERROR HY000: Got error 131 during COMMIT
74+ERROR HY000: Got error 131 during COMMIT
75+ERROR HY000: Got error 131 during COMMIT
76+ERROR HY000: Got error 131 during COMMIT
77+ERROR HY000: Got error 131 during COMMIT
78+ERROR HY000: Got error 131 during COMMIT
79+ERROR HY000: Got error 131 during COMMIT
80+ERROR HY000: Got error 131 during COMMIT
81+ERROR HY000: Got error 131 during COMMIT
82+ERROR HY000: Got error 131 during COMMIT
83+ERROR HY000: Got error 131 during COMMIT
84+ERROR HY000: Got error 131 during COMMIT
85+ERROR HY000: Got error 131 during COMMIT
86+ERROR HY000: Got error 131 during COMMIT
87+ERROR HY000: Got error 131 during COMMIT
88+ERROR HY000: Got error 131 during COMMIT
89+ERROR HY000: Got error 131 during COMMIT
90+ERROR HY000: Got error 131 during COMMIT
91+ERROR HY000: Got error 131 during COMMIT
92+ERROR HY000: Got error 131 during COMMIT
93+ERROR HY000: Got error 131 during COMMIT
94+ERROR HY000: Got error 131 during COMMIT
95+ERROR HY000: Got error 131 during COMMIT
96+ERROR HY000: Got error 131 during COMMIT
97+ERROR HY000: Got error 131 during COMMIT
98+ERROR HY000: Got error 131 during COMMIT
99+ERROR HY000: Got error 131 during COMMIT
100+ERROR HY000: Got error 131 during COMMIT
101+ERROR HY000: Got error 131 during COMMIT
102+ERROR HY000: Got error 131 during COMMIT
103+ERROR HY000: Got error 131 during COMMIT
104+ERROR HY000: Got error 131 during COMMIT
105+ERROR HY000: Got error 131 during COMMIT
106+ERROR HY000: Got error 131 during COMMIT
107+ERROR HY000: Got error 131 during COMMIT
108+ERROR HY000: Got error 131 during COMMIT
109+ERROR HY000: Got error 131 during COMMIT
110+ERROR HY000: Got error 131 during COMMIT
111+ERROR HY000: Got error 131 during COMMIT
112+ERROR HY000: Got error 131 during COMMIT
113+ERROR HY000: Got error 131 during COMMIT
114+ERROR HY000: Got error 131 during COMMIT
115+ERROR HY000: Got error 131 during COMMIT
116+ERROR HY000: Got error 131 during COMMIT
117+ERROR HY000: Got error 131 during COMMIT
118+ERROR HY000: Got error 131 during COMMIT
119+ERROR HY000: Got error 131 during COMMIT
120+ERROR HY000: Got error 131 during COMMIT
121+ERROR HY000: Got error 131 during COMMIT
122+ERROR HY000: Got error 131 during COMMIT
123+ERROR HY000: Got error 131 during COMMIT
124+ERROR HY000: Got error 131 during COMMIT
125+SET innodb_fake_changes=0;
126+CHECK TABLE t1;
127+Table Op Msg_type Msg_text
128+test.t1 check status OK
129+should_be_1
130+1
131+DROP TABLE t1;
132+CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, v VARCHAR(200), t TEXT) ENGINE=INNODB KEY_BLOCK_SIZE=8;
133+INSERT INTO t1 VALUES(2, LPAD("v", 2, "b"), LPAD("a", 200, "b"));
134+SET innodb_fake_changes=1;
135+INSERT INTO t1 VALUES (3, "foo", LPAD("a", 4600, "b"));
136+ERROR HY000: Got error 131 during COMMIT
137+SET innodb_fake_changes=0;
138+CHECK TABLE t1;
139+Table Op Msg_type Msg_text
140+test.t1 check status OK
141+should_be_1
142+1
143+DROP TABLE t1;
144+CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, v VARCHAR(200), t TEXT) ENGINE=INNODB;
145+INSERT INTO t1 VALUES(2, LPAD("v", 3, "b"), LPAD("a", 100, "b"));
146+SET innodb_fake_changes=1;
147+INSERT INTO t1 VALUES (3, "foo", LPAD("a", 4600, "b"));
148+ERROR HY000: Got error 131 during COMMIT
149+SET innodb_fake_changes=0;
150+CHECK TABLE t1;
151+Table Op Msg_type Msg_text
152+test.t1 check status OK
153+should_be_1
154+1
155+DROP TABLE t1;
156
157=== added file 'Percona-Server/mysql-test/t/percona_innodb_fake_changes_bug_917942-master.opt'
158--- Percona-Server/mysql-test/t/percona_innodb_fake_changes_bug_917942-master.opt 1970-01-01 00:00:00 +0000
159+++ Percona-Server/mysql-test/t/percona_innodb_fake_changes_bug_917942-master.opt 2012-10-17 10:27:31 +0000
160@@ -0,0 +1,1 @@
161+--innodb_file_per_table --innodb_file_format=barracuda
162
163=== added file 'Percona-Server/mysql-test/t/percona_innodb_fake_changes_bug_917942.test'
164--- Percona-Server/mysql-test/t/percona_innodb_fake_changes_bug_917942.test 1970-01-01 00:00:00 +0000
165+++ Percona-Server/mysql-test/t/percona_innodb_fake_changes_bug_917942.test 2012-10-17 10:27:31 +0000
166@@ -0,0 +1,103 @@
167+# Test for bug 917942, based on FB percona_innodb_fake_changes_bugs2 and ... _bugs3 test cases.
168+# Meant to be run under Valgrind.
169+
170+--source include/have_innodb_plugin.inc
171+
172+--disable_warnings
173+DROP TABLE IF EXISTS t1;
174+--enable_warnings
175+
176+#
177+# Confirm that cursor->tree_height is initialized when calling btr_cur_pessimistic_update
178+# for transactions with innodb_fake_changes set
179+#
180+
181+--echo Test compressed
182+CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, v VARCHAR(200), t TEXT) ENGINE=INNODB KEY_BLOCK_SIZE=8;
183+
184+INSERT INTO t1 VALUES(NULL, LPAD("v", 2, "b"), LPAD("a", 100, "b"));
185+let $t1_checksum_1= `CHECKSUM TABLE t1 EXTENDED`;
186+
187+SET innodb_fake_changes=1;
188+
189+--error 1180
190+INSERT INTO t1 VALUES(1, "foo", LPAD("a", 4600, "b")) ON DUPLICATE KEY UPDATE T = VALUES(T);
191+--error 1180
192+UPDATE t1 SET T = LPAD("A", 4600, "b");
193+
194+SET innodb_fake_changes=0;
195+CHECK TABLE t1;
196+let $t1_checksum_2= `CHECKSUM TABLE t1 EXTENDED`;
197+--disable_query_log
198+eval SELECT "$t1_checksum_1" LIKE "$t1_checksum_2" AS should_be_1;
199+--enable_query_log
200+DROP TABLE t1;
201+
202+--echo Test for uncompressed
203+CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, v VARCHAR(200), t TEXT) ENGINE=INNODB;
204+
205+INSERT INTO t1 VALUES(NULL, LPAD("v", 2, "b"), LPAD("a", 100, "b"));
206+let $t1_checksum_1= `CHECKSUM TABLE t1 EXTENDED`;
207+
208+SET innodb_fake_changes=1;
209+
210+--disable_query_log
211+let $x = 50;
212+while ($x)
213+{
214+ --error 1180
215+ eval INSERT INTO t1 VALUES ($x, "foo", lpad("a", (($x * 100) + 4500) MOD 9000, "b")) ON DUPLICATE KEY UPDATE t = VALUES(t);
216+ --error 1180
217+ eval UPDATE t1 SET t = LPAD("a", (($x * 100) + 4500) MOD 9000, "b");
218+ dec $x;
219+}
220+--enable_query_log
221+
222+SET innodb_fake_changes=0;
223+CHECK TABLE t1;
224+let $t1_checksum_2= `CHECKSUM TABLE t1 EXTENDED`;
225+--disable_query_log
226+eval SELECT "$t1_checksum_1" LIKE "$t1_checksum_2" AS should_be_1;
227+--enable_query_log
228+DROP TABLE t1;
229+
230+#
231+# Confirm that cursor->tree_height is initialized when calling btr_cur_pessimistic_insert
232+# for transactions with innodb_fake_changes set
233+#
234+
235+CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, v VARCHAR(200), t TEXT) ENGINE=INNODB KEY_BLOCK_SIZE=8;
236+
237+INSERT INTO t1 VALUES(2, LPAD("v", 2, "b"), LPAD("a", 200, "b"));
238+let $t1_checksum_1= `CHECKSUM TABLE t1 EXTENDED`;
239+
240+SET innodb_fake_changes=1;
241+
242+--error 1180
243+INSERT INTO t1 VALUES (3, "foo", LPAD("a", 4600, "b"));
244+
245+SET innodb_fake_changes=0;
246+CHECK TABLE t1;
247+let $t1_checksum_2= `CHECKSUM TABLE t1 EXTENDED`;
248+--disable_query_log
249+eval SELECT "$t1_checksum_1" LIKE "$t1_checksum_2" AS should_be_1;
250+--enable_query_log
251+DROP TABLE t1;
252+
253+CREATE TABLE t1 (id INT PRIMARY KEY AUTO_INCREMENT, v VARCHAR(200), t TEXT) ENGINE=INNODB;
254+
255+INSERT INTO t1 VALUES(2, LPAD("v", 3, "b"), LPAD("a", 100, "b"));
256+let $t1_checksum_1= `CHECKSUM TABLE t1 EXTENDED`;
257+
258+SET innodb_fake_changes=1;
259+
260+--error 1180
261+INSERT INTO t1 VALUES (3, "foo", LPAD("a", 4600, "b"));
262+
263+SET innodb_fake_changes=0;
264+CHECK TABLE t1;
265+let $t1_checksum_2= `CHECKSUM TABLE t1 EXTENDED`;
266+--disable_query_log
267+eval SELECT "$t1_checksum_1" LIKE "$t1_checksum_2" AS should_be_1;
268+--enable_query_log
269+DROP TABLE t1;
270
271=== modified file 'Percona-Server/storage/innodb_plugin/btr/btr0cur.c'
272--- Percona-Server/storage/innodb_plugin/btr/btr0cur.c 2012-10-17 10:27:31 +0000
273+++ Percona-Server/storage/innodb_plugin/btr/btr0cur.c 2012-10-17 10:27:31 +0000
274@@ -1467,6 +1467,9 @@
275 }
276
277 if (!(flags & BTR_NO_UNDO_LOG_FLAG)) {
278+
279+ ut_a(cursor->tree_height != ULINT_UNDEFINED);
280+
281 /* First reserve enough free space for the file segments
282 of the index tree, so that the insert will not fail because
283 of lack of space */
284@@ -1761,7 +1764,8 @@
285 ulint length, /*!< in: size needed */
286 ibool create, /*!< in: TRUE=delete-and-insert,
287 FALSE=update-in-place */
288- mtr_t* mtr) /*!< in: mini-transaction */
289+ mtr_t* mtr, /*!< in: mini-transaction */
290+ trx_t* trx) /*!< in: NULL or transaction */
291 {
292 ut_a(page_zip == buf_block_get_page_zip(block));
293 ut_ad(page_zip);
294@@ -1778,6 +1782,14 @@
295 return(FALSE);
296 }
297
298+ if (trx && trx->fake_changes) {
299+ /* Don't call page_zip_compress_write_log_no_data as that has
300+ assert which would fail. Assume there won't be a compression
301+ failure. */
302+
303+ return TRUE;
304+ }
305+
306 if (!page_zip_compress(page_zip, buf_block_get_frame(block),
307 index, mtr)) {
308 /* Unable to compress the page */
309@@ -1861,7 +1873,8 @@
310 /* Check that enough space is available on the compressed page. */
311 if (page_zip
312 && !btr_cur_update_alloc_zip(page_zip, block, index,
313- rec_offs_size(offsets), FALSE, mtr)) {
314+ rec_offs_size(offsets), FALSE, mtr,
315+ trx)) {
316 return(DB_ZIP_OVERFLOW);
317 }
318
319@@ -2060,7 +2073,8 @@
320
321 if (page_zip
322 && !btr_cur_update_alloc_zip(page_zip, block, index,
323- new_rec_size, TRUE, mtr)) {
324+ new_rec_size, TRUE, mtr,
325+ thr_get_trx(thr))) {
326 err = DB_ZIP_OVERFLOW;
327 goto err_exit;
328 }
329@@ -2297,7 +2311,15 @@
330 of the index tree, so that the update will not fail because
331 of lack of space */
332
333- n_extents = cursor->tree_height / 16 + 3;
334+ if (UNIV_UNLIKELY(cursor->tree_height == ULINT_UNDEFINED)) {
335+ /* When the tree height is uninitialized due to fake
336+ changes, reserve some hardcoded number of extents. */
337+ ut_a(thr && thr_get_trx(thr)->fake_changes);
338+ n_extents = 3;
339+ }
340+ else {
341+ n_extents = cursor->tree_height / 16 + 3;
342+ }
343
344 if (flags & BTR_NO_UNDO_LOG_FLAG) {
345 reserve_flag = FSP_CLEANING;
346@@ -3097,6 +3119,8 @@
347 of the index tree, so that the node pointer updates will
348 not fail because of lack of space */
349
350+ ut_a(cursor->tree_height != ULINT_UNDEFINED);
351+
352 n_extents = cursor->tree_height / 32 + 1;
353
354 success = fsp_reserve_free_extents(&n_reserved,
355
356=== modified file 'Percona-Server/storage/innodb_plugin/btr/btr0pcur.c'
357--- Percona-Server/storage/innodb_plugin/btr/btr0pcur.c 2012-05-09 04:14:12 +0000
358+++ Percona-Server/storage/innodb_plugin/btr/btr0pcur.c 2012-10-17 10:27:31 +0000
359@@ -47,6 +47,7 @@
360
361 pcur->btr_cur.index = NULL;
362 btr_pcur_init(pcur);
363+ pcur->btr_cur.tree_height = ULINT_UNDEFINED;
364
365 return(pcur);
366 }
367
368=== modified file 'Percona-Server/storage/innodb_plugin/ibuf/ibuf0ibuf.c'
369--- Percona-Server/storage/innodb_plugin/ibuf/ibuf0ibuf.c 2012-05-09 04:14:12 +0000
370+++ Percona-Server/storage/innodb_plugin/ibuf/ibuf0ibuf.c 2012-10-17 10:27:31 +0000
371@@ -3058,7 +3058,7 @@
372 update)
373 && (!page_zip || btr_cur_update_alloc_zip(
374 page_zip, block, index,
375- rec_offs_size(offsets), FALSE, mtr))) {
376+ rec_offs_size(offsets), FALSE, mtr, NULL))) {
377 /* This is the easy case. Do something similar
378 to btr_cur_update_in_place(). */
379 row_upd_rec_in_place(rec, index, offsets,
380
381=== modified file 'Percona-Server/storage/innodb_plugin/include/btr0cur.h'
382--- Percona-Server/storage/innodb_plugin/include/btr0cur.h 2012-04-02 02:09:15 +0000
383+++ Percona-Server/storage/innodb_plugin/include/btr0cur.h 2012-10-17 10:27:31 +0000
384@@ -258,8 +258,9 @@
385 ulint length, /*!< in: size needed */
386 ibool create, /*!< in: TRUE=delete-and-insert,
387 FALSE=update-in-place */
388- mtr_t* mtr) /*!< in: mini-transaction */
389- __attribute__((nonnull, warn_unused_result));
390+ mtr_t* mtr, /*!< in: mini-transaction */
391+ trx_t* trx) /*!< in: NULL or transaction */
392+ __attribute__((nonnull (1, 2, 3, 6), warn_unused_result));
393 /*************************************************************//**
394 Updates a record when the update causes no size changes in its fields.
395 @return DB_SUCCESS or error number */

Subscribers

People subscribed via source and target branches