Bug #41029 | MRR: SELECT FOR UPDATE fails to lock gaps (InnoDB table) | ||
---|---|---|---|
Submitted: | 25 Nov 2008 19:37 | Modified: | 22 Nov 2010 1:31 |
Reporter: | Mikhail Izioumtchenko | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: Optimizer | Severity: | S3 (Non-critical) |
Version: | 6.0-bzr | OS: | Any |
Assigned to: | Olav Sandstå | CPU Architecture: | Any |
Tags: | mrr, optimizer_switch |
[25 Nov 2008 19:37]
Mikhail Izioumtchenko
[26 Nov 2008 9:08]
Valeriy Kravchuk
With 6.0.9-debug I've got a crash on INSERT (2,2,2) from the second session (even with MRR disabled): Version: '6.0.9-alpha-debug' socket: '/tmp/mysql.sock' port: 3306 Source distribution /home2/openxs/dbs/6.0/libexec/mysqld(my_print_stacktrace+0x29)[0x890486c] /home2/openxs/dbs/6.0/libexec/mysqld(handle_segfault+0x271)[0x82f43b7] [0xffffe420] /home2/openxs/dbs/6.0/libexec/mysqld[0x872a067] /home2/openxs/dbs/6.0/libexec/mysqld(row_ins+0x8d)[0x872a261] /home2/openxs/dbs/6.0/libexec/mysqld(row_ins_step+0x144)[0x872a3f5] /home2/openxs/dbs/6.0/libexec/mysqld(row_insert_for_mysql+0x201)[0x872ba8f] /home2/openxs/dbs/6.0/libexec/mysqld(_ZN11ha_innobase9write_rowEPh+0x3ae)[0x86cd59e] /home2/openxs/dbs/6.0/libexec/mysqld(_ZN7handler12ha_write_rowEPh+0x57)[0x8427bd9] /home2/openxs/dbs/6.0/libexec/mysqld(_Z12write_recordP3THDP5TABLEP12st_copy_info+0x98c)[0x8396bc8] /home2/openxs/dbs/6.0/libexec/mysqld(_Z12mysql_insertP3THDP10TABLE_LISTR4ListI4ItemERS3_IS5_ES6_S6_15enum_duplicatesb+0xad5)[0x83950bb] /home2/openxs/dbs/6.0/libexec/mysqld(_Z21mysql_execute_commandP3THD+0x2d51)[0x8307551] /home2/openxs/dbs/6.0/libexec/mysqld(_Z11mysql_parseP3THDPKcjPS2_+0x1d0)[0x830e1fc] /home2/openxs/dbs/6.0/libexec/mysqld(_Z16dispatch_command19enum_server_commandP3THDPcj+0x84f)[0x8302f4d] /home2/openxs/dbs/6.0/libexec/mysqld(_Z10do_commandP3THD+0x229)[0x83024f5] /home2/openxs/dbs/6.0/libexec/mysqld(handle_one_connection+0x125)[0x8300105] /lib/tls/libpthread.so.0[0x40050aa7] /lib/tls/libc.so.6(__clone+0x5e)[0x40211c2e] 081030 14:43:52 - mysqld got signal 11 ; This could be because you hit a bug. It is also possible that this binary or one of the libraries it was linked against is corrupt, improperly built, or misconfigured. This error can also be caused by malfunctioning hardware. We will try our best to scrape up some info that will hopefully help diagnose the problem, but since we have already crashed, something is definitely wrong and this may fail. key_buffer_size=8388572 read_buffer_size=131072 max_used_connections=2 max_threads=151 thread_count=2 connection_count=2 It is possible that mysqld could use up to key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 337809 K bytes of memory Hope that's ok; if not, decrease some variables in the equation. thd: 0x9b99388 Attempting backtrace. You can use the following information to find out where mysqld died. If you see no messages after this, something went terribly wrong... stack_bottom = 0x46bcf030 thread_stack 0x30c00 Trying to get some variables. Some pointers may be invalid and cause the dump to abort... thd->query at 0x9bab890 = insert into t values(2,2,2) thd->thread_id=2 thd->killed=NOT_KILLED Crash looks related to InnoDB. Please, check.
[26 Nov 2008 13:35]
Mikhail Izioumtchenko
another known problematic area is engine condition pushdown. Could you repeat your test with engine_condition_pushdown=OFF; and see if there's still a crash. What does -debug in 6.0.9-debug mean?
[5 Dec 2008 14:53]
MySQL Verification Team
Thank you for the bug report. Verified as described: miguel@hegel:~/dbs$ 6.0/bin/mysql -uroot --prompt='conn 2 >' test Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 3 Server version: 6.0.9-alpha-debug Source distribution Type 'help;' or '\h' for help. Type '\c' to clear the buffer. conn 2 >use test; Database changed conn 2 >set autocommit=0; Query OK, 0 rows affected (0.00 sec) conn 2 >start transaction; Query OK, 0 rows affected (0.00 sec) conn 2 >select 1; +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec) conn 2 >insert into t values(2,2,2); Query OK, 1 row affected (0.00 sec) conn 2 >select 2; +---+ | 2 | +---+ | 2 | +---+ 1 row in set (0.00 sec) conn 2 >insert into t values(0,0,0); Query OK, 1 row affected (0.00 sec) conn 2 >select 3; +---+ | 3 | +---+ | 3 | +---+ 1 row in set (0.00 sec) conn 2 >select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set (0.00 sec) conn 2 >select * from t; +-------+------+------+ | dummy | a | b | +-------+------+------+ | 0 | 0 | 0 | | 1 | 1 | 1 | | 2 | 2 | 2 | | 3 | 3 | 3 | | 5 | 5 | 5 | +-------+------+------+ 5 rows in set (0.00 sec) conn 2 >exit Bye miguel@hegel:~/dbs$ 5.1/bin/mysql -uroot --prompt='conn 2 >' test Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 2 Server version: 5.1.31-debug Source distribution Type 'help;' or '\h' for help. Type '\c' to clear the buffer. conn 2 >use test; Database changed conn 2 >set autocommit=0; Query OK, 0 rows affected (0.00 sec) conn 2 >start transaction; Query OK, 0 rows affected (0.00 sec) conn 2 >select 1; +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec) conn 2 >insert into t values(2,2,2); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction conn 2 >select 2; +---+ | 2 | +---+ | 2 | +---+ 1 row in set (0.00 sec) conn 2 >insert into t values(0,0,0); Query OK, 1 row affected (0.00 sec) conn 2 >select 3; +---+ | 3 | +---+ | 3 | +---+ 1 row in set (0.00 sec) conn 2 >select @@tx_isolation; +-----------------+ | @@tx_isolation | +-----------------+ | REPEATABLE-READ | +-----------------+ 1 row in set (0.00 sec) conn 2 >select * from t; +-------+------+------+ | dummy | a | b | +-------+------+------+ | 0 | 0 | 0 | | 1 | 1 | 1 | | 3 | 3 | 3 | | 5 | 5 | 5 | +-------+------+------+ 4 rows in set (0.00 sec) conn 2 >
[8 Oct 2009 14:10]
Guilhem Bichot
goes away / comes back when disabling/enabling MRR in InnoDB
[21 May 2010 13:11]
Olav Sandstå
When MRR is used a second handler object is created. This handler object is a "clone" of the first handler object and the MRR code use this for executing some of the operations. This second handler object is created in DsMRR_impl::dsmrr_init() (in handler.cc) by "cloning" the initial handler object: if (!(new_h2= h->clone(thd->mem_root)) || new_h2->ha_external_lock(thd, F_RDLCK)) The important thing about the above code is that on this cloned handler object a call to ha_external_lock() is done with a request to use a read lock (F_RDLCK). Later, this handler (h2) object is used in DsMrr_impl::dsmrr_fill_buffer() for doing the initial search for the first record in the range interval. Since the call to ha_external_lock() only requested a read lock the index search done by InnoDB will be done without setting an exclusive lock on the search interval. This allows the second transaction to succeed to insert data within the "read interval" of the first transaction.
[21 May 2010 14:46]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/108878 3182 Olav Sandstaa 2010-05-21 Fix for Bug#41029 MRR: SELECT FOR UPDATE fails to lock gaps (InnoDB table) This problem occurs due to MRR is creating a second handler object. This handler object is a "clone" of the first/initial handler object and the MRR code use this second handler object for executing some of the operations. The second handler object is created in DsMRR_impl::dsmrr_init() (in handler.cc) by "cloning" the initial handler object: if (!(new_h2= h->clone(thd->mem_root)) || new_h2->ha_external_lock(thd, F_RDLCK)) The important thing about the above code is that on this cloned handler object a call to ha_external_lock() is done with a request to use a read lock (F_RDLCK). When this handler object (h2) is used in DsMrr_impl::dsmrr_fill_buffer() for doing the initial search for the first record in the range interval, the index search done by InnoDB will be done without setting any exclusive lock on the search interval. This allows a second transaction to succeed to insert data within the "read interval" of the first transaction. The fix for this problem is to use the same lock type when calling new_h2->ha_external_lock() as was done on the original handler object. To do this we store (in handler::ha_external_lock()) the original lock type in a new member variable in the handler object (m_lock_type) and use this lock type when "cloning" the handler object in DsMRR_impl::dsmrr_init(). Note to reviewers (and myself): 1. The included test case should be will be included in the innodb_mrr test before the fix is pushed but due to innodb_mrr test being unstable I have included as a separate test case. 2. The final commit/push should also remove the test and result files for this bug from the optimizer_unfixed_bugs suite. @ mysql-test/r/bug41029.result Result file for test of Bug#41029 MRR: SELECT FOR UPDATE fails to lock gaps (InnoDB table) @ mysql-test/t/bug41029-master.opt Option file for reducing the default lock timeout in order to reduce the time for running the test. @ mysql-test/t/bug41029.test Test for Bug#41029 MRR: SELECT FOR UPDATE fails to lock gaps (InnoDB table) @ sql/handler.cc When MRR is used a second handler object is cloned based on the original handler object. This second handler object is used for the initial navigation of the underlying storage engine's data. When this second handler object was created a call to ha_external_lock() was done with F_RDLCK as the lock type. Since F_RDLCK was always used this made InnoDB not put gap lock on the search interval and allowed concurrent transactions to insert data into the search interval of transasactions running SELECT FOR UPDATE statements in REPEATABLE READ mode. To ensure the second handler object created when MRR is used is getting the same lock type as the initial handler the following changes are implemented: 1. Extend handler::ha_external_lock() to save the lock type in a member variable (m_lock_type). 2. Extend DsMRR_impl::dsmrr_init() so that it uses the same lock type when cloning a second handler object as used for the initial handler object (instead of always using F_RDLCK). @ sql/handler.h Add a new member variable to the handler class to store the lock type set by the call to handler::ha_external_lock(). This lock type is needed by the MRR implementation when creating a second handler object by cloning the initial handler.
[7 Jun 2010 9:16]
Bugs System
A patch for this bug has been committed. After review, it may be pushed to the relevant source trees for release in the next version. You can access the patch from: http://lists.mysql.com/commits/110319 3186 Olav Sandstaa 2010-06-07 Fix for Bug#41029 MRR: SELECT FOR UPDATE fails to lock gaps (InnoDB table) This problem occurs due to MRR is creating a second handler object. This handler object is a "clone" of the first/initial handler object and the MRR code use this second handler object for executing some of the operations. The second handler object is created in DsMRR_impl::dsmrr_init() (in handler.cc) by "cloning" the initial handler object: if (!(new_h2= h->clone(thd->mem_root)) || new_h2->ha_external_lock(thd, F_RDLCK)) The important thing about the above code is that on this cloned handler object a call to ha_external_lock() is done with a request to use a read lock (F_RDLCK). When this handler object (h2) is used in DsMrr_impl::dsmrr_fill_buffer() for doing the initial search for the first record in the range interval, the index search done by InnoDB will be done without setting any exclusive lock on the search interval. This allows a second transaction to succeed to insert data within the "read interval" of the first transaction. The fix for this problem is to use the same lock type when calling new_h2->ha_external_lock() as was done on the original handler object. To do this we store (in handler::ha_external_lock()) the original lock type in a new member variable in the handler object (m_lock_type) and use this lock type when "cloning" the handler object in DsMRR_impl::dsmrr_init(). @ mysql-test/r/innodb_mrr.result Result file for test of Bug#41029 MRR: SELECT FOR UPDATE fails to lock gaps (InnoDB table). @ mysql-test/suite/optimizer_unfixed_bugs/r/bug41029.result Test case moved to innodb_mrr test. @ mysql-test/suite/optimizer_unfixed_bugs/t/bug41029-master.opt Test case moved to innodb_mrr test. @ mysql-test/suite/optimizer_unfixed_bugs/t/bug41029.test Test case moved to innodb_mrr test. @ mysql-test/t/innodb_mrr-master.opt Add an option file for the innodb_mrr test. For testing of Bug#41029 we reduce the default InnoDB lock timeout in order to reduce the time for running the test. @ mysql-test/t/innodb_mrr.test Test for Bug#41029 MRR: SELECT FOR UPDATE fails to lock gaps (InnoDB table). @ sql/handler.cc When MRR is used, a second handler object is cloned based on the original handler object. This second handler object is used for the initial navigation of the underlying storage engine's data. When this second handler object was created a call to ha_external_lock() was done with F_RDLCK as the lock type. Since F_RDLCK was always used, this made InnoDB not put gap lock on the search interval and allowed concurrent transactions to insert data into the search interval of transasactions running SELECT FOR UPDATE statements in REPEATABLE READ mode. To ensure the second handler object created when MRR is used is getting the same lock type as the initial handler, the following changes are implemented: 1. Extend handler::ha_external_lock() to save the lock type in a member variable (m_lock_type). 2. Extend DsMRR_impl::dsmrr_init() so that it uses the same lock type when cloning a second handler object as used for the initial handler object (instead of always using F_RDLCK). @ sql/handler.h Add a new member variable to the handler class to store the lock type set by the call to handler::ha_external_lock(). This lock type is needed by the MRR implementation when creating a second handler object by cloning the initial handler.
[16 Aug 2010 6:38]
Bugs System
Pushed into mysql-next-mr (revid:alik@sun.com-20100816062819-bluwgdq8q4xysmlg) (version source revid:alik@sun.com-20100816062612-enatdwnv809iw3s9) (pib:20)
[13 Nov 2010 16:14]
Bugs System
Pushed into mysql-trunk 5.6.99-m5 (revid:alexander.nozdrin@oracle.com-20101113155825-czmva9kg4n31anmu) (version source revid:vasil.dimov@oracle.com-20100629074804-359l9m9gniauxr94) (merge vers: 5.6.99-m4) (pib:21)
[22 Nov 2010 1:31]
Paul DuBois
Bug is not in any released 5.6.x version. No changelog entry needed.