Bug #26379 | Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table | ||
---|---|---|---|
Submitted: | 14 Feb 2007 19:59 | Modified: | 3 Dec 2007 9:33 |
Reporter: | Dean Ellis | Email Updates: | |
Status: | Closed | Impact on me: | |
Category: | MySQL Server: Merge storage engine | Severity: | S1 (Critical) |
Version: | 4.0 4.1 5.0 5.1 | OS: | Any |
Assigned to: | Ingo Strüwing | CPU Architecture: | Any |
Tags: | bfsm_2007_03_01 |
[14 Feb 2007 19:59]
Dean Ellis
[3 Apr 2007 12:35]
Ingo Strüwing
This bug report revealed three problems: 1. A thread trying to lock a MERGE table performs busy waiting while REPAIR TABLE or a similar table administration task is ongoing on one or more of its MyISAM tables. 2. A thread trying to lock a MERGE table performs busy waiting until all threads that did REPAIR TABLE or similar table administration tasks on one or more of its MyISAM tables in LOCK TABLES segments do UNLOCK TABLES. The difference against problem #1 is that the busy waiting takes place *after* the administration task. It is terminated by UNLOCK TABLES only. 3. Two FLUSH TABLES within a LOCK TABLES segment can invalidate the lock. This does *not* require a MERGE table. The first FLUSH TABLES can be replaced by any statement that requires other threads to reopen the table. In 5.0 and 5.1 a single FLUSH TABLES can provoke the problem. Except of #2 it is not easy to repeat the problem or to watch it. One can repeat #1 easily with a some seconds taking repair. But how will you tell if the CPU load comes from the repair or the mentioned problem? Repeating #3 is even more difficult as it requires the (second) FLUSH TABLES to run at a certain moment in the execution of another thread. In the original test case this was possible by exploiting problem #2 and driving the other thread into a busy waiting. It passed the critical code region so often that the probability to issue FLUSH TABLES in the right moment was quite good. However this is still not a 100% repeatable test as the number of FLUSH TABLES until the problem triggers is random. Fortunately I found places in the code where the insertion of sleep() made the problems 100% repeatable. For #1 and #2 use a system monitor to watch the CPU load. Modify the sleep times to see that the CPU is busy for these times. 1. To allow for observability it is necessary to enter a multi-second sleep in mysql_admin_table() after remove_table_from_cache(), which comes after mysql_abort_lock(). The sleep fakes a long running operation. One can watch a high CPU load during the sleep time. The problem is that mysql_abort_lock() upgrades the write lock to TL_WRITE_ONLY. This lock type persists until the final unlock at the end of the administration task. The effect of TL_WRITE_ONLY is to reject any attempt to lock the table. The trying thread must close the table and wait until it is no longer used. Unfortunately there is no way to detect that one of the MyISAM tables of a MERGE table is in use. When trying to lock the MERGE table, all MyISAM tables are locked. If one fails on TL_WRITE_ONLY, all locks are aborted and wait_for_tables() is entered. But this doesn't see the MERGE table as used, so it seems appropriate to retry a lock... CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION=(t1); send REPAIR TABLE t1; connection con1; INSERT INTO t2 VALUES (1); connection default; reap; DROP TABLE t1, t2; 2. No sleep() in the code required. The problem is the same as #1, but TL_READ_ONLY persists beyond the end of the administration task until UNLOCK TABLES. CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; CREATE TABLE t2 (c1 INT) ENGINE= MRG_MYISAM UNION=(t1); LOCK TABLE t1 WRITE; send REPAIR TABLE t1; connection con1; send INSERT INTO t2 VALUES (1); connection default; reap; sleep 4; # con1 performs busy waiting during this sleep. UNLOCK TABLES; connection con1; reap; connection default; DROP TABLE t1, t2; 3. To increase reproducibility it is necessary to enter a sleep of 2 seconds at the end of wait_for_tables() after unlock of LOCK_open. In 5.0 and 5.1 the sleep must be inserted in open_and_lock_tables() after open_tables() instead. wait_for_tables() is not used in this case. The problem is that FLUSH TABLES releases LOCK_open while having unlocked and closed all tables. When this happens while a thread is in the loop in mysql_lock_tables() right after wait_for_tables() and before retrying to lock, the thread got the lock. (Translate to similar code places in 5.0 and 5.1). And it does not notice that the table needs a refresh. So it executes its statement on the table. The first FLUSH TABLES kicks the INSERT out of thr_multi_lock() and let it wait in wait_for_tables(). (open_table() in 5.0 and 5.1). The second FLUSH TABLES must happen while the INSERT was on its way from wait_for_tables() (open_tables() in 5.0 and 5.1) to the next call of thr_multi_lock(). This needs to be supported by a sleep to make it repeatable. CREATE TABLE t1 (c1 INT) ENGINE= MyISAM; LOCK TABLE t1 WRITE; connection con1; send INSERT INTO t1 VALUES (1); sleep 1; # Let INSERT go into thr_multi_lock(). connection default; FLUSH TABLES; sleep 1; # Let INSERT go through wait_for_tables() where it sleeps. FLUSH TABLES; # This should give no result. But it will with sleep(2) at the right place. SELECT * FROM t1; UNLOCK TABLES; connection con1; reap; connection default; DROP TABLE t1; The fix for #3 seems to be simple. I do not expect it to raise new problems. I propose to set THD::some_tables_deleted for all threads with open tables before releasing LOCK_open for the first time. This will not prevent other threads from getting locks on the tables that should be under control of LOCK TABLES, but they will detect THD::some_tables_deleted after locking and immediately release the locks again. They will close all tables and detect the changed refresh_version. The fix for #1 and #2 is also simple. But it has the potential to break things at other places. Currently administration tasks like REPAIR TABLE abort locks on the table before the operation starts. A comment in mysql_admin_table() says "Close all instances of the table to allow repair to rename files". If this comment is correct, it means that it is expected to raise problems when trying to rename files that are open. So we abort the locks so that no thread can sit and wait for a lock on the table. Tables are opened before locking. A thread that waits for a lock will keep the table and its files open all the time. If the lock fails as aborted, it will close the table and reopen it only after it sees the table as unused. BUT: Aborting every lock request does not prevent threads from opening the table before trying to lock it. So there are moments where the table can be open by other threads in spite of aborting all locks on the table. Either we get around this by retrying the rename until we meet a moment where the table is closed, or we assume that renaming blocks until the file is closed or we live with the risk that the rename can fail in spite of lock aborting. However, I did not find any repetition of renaming in the code. Blocking on rename is probably highly platform dpendend, but perhaps we have it where necessary. If we do indeed have that rename problem on platforms relevant for MySQL, then there might be no solution for problem #1. But as far as I know we have methods for overcoming the rename/delete restriction at least in 5.0 and 5.1. We have my_delete_allow_opened(), which is the base for other functions around replacing old table files by new ones. It could be backported to 4.1 or we could forgo fixing #1 in 4.1. It can make table administration operations slower, but it does not hurt data. I propose to move the branch with mysql_lock_abort and remove_table_from_cache behind the administration operation. Immediately after this we should reset the lock type of TL_WRITE_ONLY back to a normal TL_WRITE. This should happen still within LOCK_open so that there is no chance for busy waiting even for a short moment. This would fix #1 and #2. If we cannot make the move in 4.1, then I propose to reset the lock type of TL_WRITE_ONLY back to a normal TL_WRITE after the administration operation. This would at least fix #2.
[3 Apr 2007 12:36]
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/23668 ChangeSet@1.2620, 2007-04-03 14:35:50+02:00, istruewing@chilla.local +10 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table This bug report revealed three problems: 1. A thread trying to lock a MERGE table performs busy waiting while REPAIR TABLE or a similar table administration task is ongoing on one or more of its MyISAM tables. 2. A thread trying to lock a MERGE table performs busy waiting until all threads that did REPAIR TABLE or similar table administration tasks on one or more of its MyISAM tables in LOCK TABLES segments do UNLOCK TABLES. The difference against problem #1 is that the busy waiting takes place *after* the administration task. It is terminated by UNLOCK TABLES only. 3. Two FLUSH TABLES within a LOCK TABLES segment can invalidate the lock. This does *not* require a MERGE table. The first FLUSH TABLES can be replaced by any statement that requires other threads to reopen the table. In 5.0 and 5.1 a single FLUSH TABLES can provoke the problem. Problem #1 and #2 are fixed by moving abortion of locks behind the table administration operation and undoing the lock abort immediately after all threads have closed the table. Problem #3 is fixed by setting THD::some_tables_deleted for all threads with open tables before releasing LOCK_open when waiting for all threads to close their tables. No test case. The misbehaviour cannot be repeated reliably by a deterministic test without sleep()-injection in the code. Please see the bug report for more information and tests for manual testing.
[3 Apr 2007 14:22]
Ingo Strüwing
The patch was rejected by Monty. It turned out that I was wrong in thinking that a table can be opened by other threads after remove_table_from_cache. The table version is set to zero, which prevents a new open. Just MERGE tables bypass this check. The correct fix is to extend MERGE table open so that it can check the table cache entry of all its MyISAM tables.
[4 Apr 2007 7:53]
Michael Widenius
I believe that the current solution for problem #1 and #2 is not a desired one. The reason is that many of the admin commands, like repair, check, assumes that the admin thread is the only one who is accessing the table so that it can freely manipulate the table files and objects (on disk and in memory). The proposed patch would change this with probably drastic consequences. Another problem is that the patch doesn't solve the problem with MERGE tables, only makes it less likely to happen; Ie, we can still have a REPAIR and a open of a MyISAM table (through a MERGE table) happening at the same time. If this happens at the same time REPAIR is creating new files or updating the MyISAM share, we can get strange effects. A better solution, which is also in line with the long time view of MERGE tables, is to add a callback function to MERGE table open, so that we can do checks between the opened tables and tables in the table cache and have the open fail if there is any conflicts (like too old table->version). Ingo and I have talked about this and he will try this solution.
[28 Apr 2007 12:32]
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/25671 ChangeSet@1.2630, 2007-04-28 14:31:55+02:00, istruewing@chilla.local +22 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Not to be pushed. This is kind of a technology preview. It is not yet complete. Though it does not seem to make anything really worse, it turned out that at least another step is required: When closing and opening the MERGE child tables, it must be taken into account if a real open/close or a reopen/close_old_data_files is going on. In the latter case the child tables must not be deleted, but kept for reopen. In reopen they must be picked up again. This bug report revealed three problems: 1. A thread trying to lock a MERGE table performs busy waiting while REPAIR TABLE or a similar table administration task is ongoing on one or more of its MyISAM tables. 2. A thread trying to lock a MERGE table performs busy waiting until all threads that did REPAIR TABLE or similar table administration tasks on one or more of its MyISAM tables in LOCK TABLES segments do UNLOCK TABLES. The difference against problem #1 is that the busy waiting takes place *after* the administration task. It is terminated by UNLOCK TABLES only. 3. Two FLUSH TABLES within a LOCK TABLES segment can invalidate the lock. This does *not* require a MERGE table. The first FLUSH TABLES can be replaced by any statement that requires other threads to reopen the table. In 5.0 and 5.1 a single FLUSH TABLES can provoke the problem. Problem #1 and #2 are fixed by opening the MERGE child tables through the table cache. So they are visible to other threads and even for certain statements in the same thread. The child tables are chained to the parent table, but do not appear in thd->open_tables nor thd->locked_tables. This does also fix the bugs 26377, 26867, and 8306 at least. I reverted the preliminary fix for 8306. Problem #3 is fixed by setting THD::some_tables_deleted for all threads with open tables before releasing LOCK_open when waiting for all threads to close their tables. No test case. The misbehaviour cannot be repeated reliably by a deterministic test without sleep()-injection in the code. Please see the bug report for more information and tests for manual testing.
[6 May 2007 12:04]
Rene Vogt
Same here: I have a merge table that contains about 25 sub-tables. Each contains about half a million entries. In general this works great. However today I detected that if I do a repair on one of the 25 sub-tables while someone else is doing a select on the main merge table the repair fails with status code 13. And all the data in the corresponding sub-table are lost. (the MYD-file is missing and there is a 0 byte TMD file) Subsequent repairs to this table fails with the message File '.\dbname\tablename.MYD' not found (Errcode: 2) Subsequent select queries on the merge table gives the following error message: ERROR 144 (HY000): Table '.\dbname\tablename' is marked as crashed and last (automatic?) repair failed For me this is reproducible and only happens if I make a select query on the merge table while repairing. Tested against mysql 5.0.37 on windows 2003 server.
[21 May 2007 15:48]
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/27080 ChangeSet@1.2630, 2007-05-21 17:48:22+02:00, istruewing@chilla.local +23 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table This bug report revealed three problems: 1. A thread trying to lock a MERGE table performs busy waiting while REPAIR TABLE or a similar table administration task is ongoing on one or more of its MyISAM tables. 2. A thread trying to lock a MERGE table performs busy waiting until all threads that did REPAIR TABLE or similar table administration tasks on one or more of its MyISAM tables in LOCK TABLES segments do UNLOCK TABLES. The difference against problem #1 is that the busy waiting takes place *after* the administration task. It is terminated by UNLOCK TABLES only. 3. Two FLUSH TABLES within a LOCK TABLES segment can invalidate the lock. This does *not* require a MERGE table. The first FLUSH TABLES can be replaced by any statement that requires other threads to reopen the table. In 5.0 and 5.1 a single FLUSH TABLES can provoke the problem. Problem #1 and #2 are fixed by opening the MERGE child tables through the table cache. So they are visible to other threads and even for certain statements in the same thread. The child tables are chained to the parent table, but do not appear in thd->open_tables nor thd->locked_tables. This does also fix the bugs 8306, 25038, 25700, 26377, 26867, and 27660. I reverted the preliminary fix for 8306. Problem #3 is fixed by setting THD::some_tables_deleted for all threads with open tables before releasing LOCK_open when waiting for all threads to close their tables. The misbehaviour for problems #1 and #3 can only be repeated reliably with some sleep()-injection in the code. Problem #2 can be watched by enlarging the sleep time in the test file. Please see the bug report for more information and tests for manual testing. See bug 25700 for a stress test.
[13 Jul 2007 15:59]
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/30888 ChangeSet@1.2530, 2007-07-13 17:58:57+02:00, istruewing@chilla.local +9 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Not to be pushed. This is a partial implementation of a new MERGE table open approach. For intermediate review only. When opening a MERGE table in open_tables() we do now add the child tables to the list of tables to be opened by open_tables(). Nothing else is done with the MERGE table at this stage. I.e. no storage engine object is created yet. When the last child is opened, we remove the children from the list again and finalize the MERGE table open. This behaves basically like the old open. However it does not open the MyISAM tables directly, but grabs them from the already open children.
[13 Jul 2007 16:02]
Ingo Strüwing
Back "in progress". The patch is just an intermediate study.
[13 Aug 2007 7:50]
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/32431 ChangeSet@1.2585, 2007-08-13 09:49:54+02:00, istruewing@synthia.local +11 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Not to be pushed. This is a partial implementation of a new MERGE table open approach. For intermediate review only. It replaces/supersedes the former patch entirely. Changed/added test cases not included for easier review. Changes over the former patch: - Renamed "finalize open (with cannot use option)" into "attach/detach children". - On parent open the storage engine structures are allocated and initialized. They stay with the open table until its final close. - Table layout conversion (table2myisam) is done on first attach only. The result is stored in the handler object for reuse with later attaches. - Added detach functionality to close_thread_table(). - The patch survives the whole test suite + bug25038 + bug26379 + bug26867 + bug27660, except of the few places where a MERGE table is opened with open_ltable() or reopen_table(). Test cases: delayed (open_ltable), merge (ALTER/open_ltable), bug25700 (reopen_table). As suggested these are to be fixed in a second patch. myisam passes after reverting/fixing test and result for bug8306. However the test for bug26377 revealed an unexpected problem: FLUSH t1 after LOCK t1, m1(t1). The child is closed, but the parent doesn't notice it. It won't re-attach its children as long as LOCK TABLES is in effect. General patch description: When opening a MERGE table in open_tables() we do now add the child tables to the list of tables to be opened by open_tables(). The children are not opened at this stage. When the last child is opened, we remove the children from the list again and attach the children to the parent. This behaves similar to the old open. However it does not open the MyISAM tables directly, but grabs them from the already open children. When closing a MERGE table in close_thread_table() we detach the children only. Closing of the children is done implicitly because they are in thd->open_tables.
[12 Sep 2007 20:12]
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/34133 ChangeSet@1.2609, 2007-09-12 22:11:47+02:00, istruewing@stella.local +20 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Not to be pushed. This is a partial implementation of a new MERGE table open approach. For intermediate review only. This is part 2 (open_ltable(), reopen_tables()). I did not change reopen_name_locked_table(). It works similar to reopen_table(), but does not copy many important elements from the old table. It is unclear which MERGE relevant elements need to be copied. The only test I found for it showed that there was something to implement for CREATE...SELECT. So I put it into create_table_from_items() behind reopen_name_locked_table(). Fixes of first patch became necessary because the part 2 changes allowed more tests to run, which revealed new problems: - Added elements 'parent' and 'children_attached' to TABLE. 'parent' is required to identify MERGE children when the TABLE_LIST object is no longer accessible (e.g. within a LOCK TABLES ... UNLOCK TABLES sequence). This is required to avoid tampering with children locks and to forward lock changes and table refreshs to the parent. Since all this is relevant in reopen situations, this change may indeed belong to this patch instead of part 1. 'children_attached' enables the server to keep track of the MERGE table state. So it can avoid to perform inappropriate handler calls. - Temporary table handling is new. - Respect thd->open_options in attach. Cached MERGE parent may be used by other threads or thd->open_options change between statements. - Handle empty MERGE union by always have children_attached= true. This avoids detach/[re-]attach of non-existing children. The parent is usually detected by table->child_l, which is NULL for an empty union set. In effect the table is not treated as a MERGE table, but some handler methods must neverthless work and survive DBUG_ASSERT(children_attached) (e.g. ::info, ::close). General patch description: Changed from open_ltable() to open_and_lock_tables() in all places that can be relevant for MERGE tables. The latter can handle tables added to the list on the fly. When open_ltable() was used in a loop over a list of tables, the list must be temporarily terminated after every table for open_and_lock_tables(). table_list->required_type is set to FRMTYPE_TABLE to avoid open of special tables. Handling of derived tables is suppressed. In reopen_tables() some of the tables open by a thread can be closed and reopened. When a MERGE child is affected, the parent must be closed and reopened too. Closing of the parent is forced before the first child is closed. Reopen happens in the order of thd->open_tables. MERGE parents do not attach their children automatically at open. This is done after all tables are reopened. So all children are open when attaching them. There are some more places where MERGE children need to be detached. Special lock handling like mysql_lock_abort() or mysql_lock_remove() need to be suppressed for MERGE children or forwarded to the parent. This depends on the situation. In loops over all open tables one suppresses child lock handling. When a single table is touched, forwarding is done. Bugs fixed with this patch (plus part 1): 19627, 25038, 25700, 26377, 26379, 26867, 27660, 30275, and 30273.
[22 Sep 2007 9:09]
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/34477 ChangeSet@1.2610, 2007-09-22 11:08:21+02:00, istruewing@synthia.local +3 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Not to be pushed. These are fixes on top of the second patch. They fix problems detected during first coarse review.
[24 Sep 2007 13:08]
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/34520 ChangeSet@1.2610, 2007-09-24 15:07:25+02:00, istruewing@synthia.local +1 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Not to be pushed. Internal TEST only. Stupid "test" lines follow: The problem was missing dump layout I added dump layout No test case. layout is used during debugging only.
[4 Oct 2007 18:38]
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/34923 ChangeSet@1.2610, 2007-10-04 20:38:09+02:00, istruewing@stella.local +3 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Not to be pushed. For intermediate review only. This is a technology preview for removing wait_for_tables() from mysql_lock_tables(). Done during my Heidelberg stay. All calls to mysql_lock_tables() do now use the flag MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN. This means that wait_for_tables() is not called any more. The next step would be to remove the flag and the call to wait_for_tables() and the function itself alltogether. It is not used elsewhere. Added MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN flag to mysql_lock_tables() and added restart code where necessary.
[13 Oct 2007 10:04]
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/35507 ChangeSet@1.2569, 2007-10-13 12:03:50+02:00, istruewing@stella.local +25 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Not to be pushed. Internal commit. Please ignore. Contains more post-review fixes. Will commit a combined patch soon. Still to be done: - Work on review comments regarding the tests. - Test if tampering with a child from another thread can require to detach (and re-attach later in reopen_tables()) the children in close_handle_and_leave_table_as_lock(). - Test if another thread can break into CREATE...MERGE...SELECT as I need to unlock the children in create_table_from_items(). - Test ALTER TABLE (fast + copy) within LOCK TABLES to see if I need to re-attach the children after reopen_table(). - Decide what value ::reset() should return if children are detached. Forgot to mention in last patch comment: Temporary MERGE must have temporary children. This is a new behavior. The old behavior was wrong. See Bug 19627 - temporary merge table locking.
[15 Oct 2007 14:03]
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/35566 ChangeSet@1.2576, 2007-10-15 16:02:51+02:00, istruewing@stella.local +28 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Bug 26377 - Deadlock with MERGE and FLUSH TABLE Bug 25038 - Waiting TRUNCATE Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Bug 19627 - temporary merge table locking Bug 27660 - Falcon: merge table possible Bug 30273 - merge tables: Can't lock file (errno: 155) Not to be pushed. For intermediate review only. This is an implementation of a new MERGE table open approach. Still to be done: - Work on review comments regarding the tests. - Test if tampering with a child from another thread can require to detach (and re-attach later in reopen_tables()) the children in close_handle_and_leave_table_as_lock(). - Test if another thread can break into CREATE...MERGE...SELECT as I need to unlock the children in create_table_from_items(). - Test ALTER TABLE (fast + copy) within LOCK TABLES to see if I need to re-attach the children after reopen_table(). - Decide what value ::reset() should return if children are detached. General patch description: When opening a MERGE table in open_tables() we do now add the child tables to the list of tables to be opened by open_tables() (the "query_list"). The children are not opened in the handler at this stage. After opening the parent, open_tables() opens each child from the now extended query_list. When the last child is opened, we remove the children from the query_list again and attach the children to the parent. This behaves similar to the old open. However it does not open the MyISAM tables directly, but grabs them from the already open children. When closing a MERGE table in close_thread_table() we detach the children only. Closing of the children is done implicitly because they are in thd->open_tables. For more detail see the comment at the top of ha_myisammrg.cc. Changed from open_ltable() to open_and_lock_tables() in all places that can be relevant for MERGE tables. The latter can handle tables added to the list on the fly. When open_ltable() was used in a loop over a list of tables, the list must be temporarily terminated after every table for open_and_lock_tables(). table_list->required_type is set to FRMTYPE_TABLE to avoid open of special tables. Handling of derived tables is suppressed. These details are handled by the new function open_n_lock_single_table(), which has nearly the same signature as open_ltable() and can replace it in most cases. In reopen_tables() some of the tables open by a thread can be closed and reopened. When a MERGE child is affected, the parent must be closed and reopened too. Closing of the parent is forced before the first child is closed. Reopen happens in the order of thd->open_tables. MERGE parents do not attach their children automatically at open. This is done after all tables are reopened. So all children are open when attaching them. Special lock handling like mysql_lock_abort() or mysql_lock_remove() needs to be suppressed for MERGE children or forwarded to the parent. This depends on the situation. In loops over all open tables one suppresses child lock handling. When a single table is touched, forwarding is done. This patch changes the behavior of temporary MERGE tables. Temporary MERGE must have temporary children. The old behavior was wrong. A temporary table is not locked. Hence even non-temporary children were not locked. See Bug 19627 - temporary merge table locking.
[21 Oct 2007 15:17]
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/35994 ChangeSet@1.2576, 2007-10-21 17:16:52+02:00, istruewing@stella.local +31 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Bug 26377 - Deadlock with MERGE and FLUSH TABLE Bug 25038 - Waiting TRUNCATE Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Bug 19627 - temporary merge table locking Bug 27660 - Falcon: merge table possible Bug 30273 - merge tables: Can't lock file (errno: 155) Not to be pushed. For intermediate review only. This is an implementation of a new MERGE table open approach. This patch was committed to document the current state of work. Still to be done: - Work on review comments regarding the tests. - Decide what value ::reset() should return if children are detached. General patch description: When opening a MERGE table in open_tables() we do now add the child tables to the list of tables to be opened by open_tables() (the "query_list"). The children are not opened in the handler at this stage. After opening the parent, open_tables() opens each child from the now extended query_list. When the last child is opened, we remove the children from the query_list again and attach the children to the parent. This behaves similar to the old open. However it does not open the MyISAM tables directly, but grabs them from the already open children. When closing a MERGE table in close_thread_table() we detach the children only. Closing of the children is done implicitly because they are in thd->open_tables. For more detail see the comment at the top of ha_myisammrg.cc. Changed from open_ltable() to open_and_lock_tables() in all places that can be relevant for MERGE tables. The latter can handle tables added to the list on the fly. When open_ltable() was used in a loop over a list of tables, the list must be temporarily terminated after every table for open_and_lock_tables(). table_list->required_type is set to FRMTYPE_TABLE to avoid open of special tables. Handling of derived tables is suppressed. These details are handled by the new function open_n_lock_single_table(), which has nearly the same signature as open_ltable() and can replace it in most cases. In reopen_tables() some of the tables open by a thread can be closed and reopened. When a MERGE child is affected, the parent must be closed and reopened too. Closing of the parent is forced before the first child is closed. Reopen happens in the order of thd->open_tables. MERGE parents do not attach their children automatically at open. This is done after all tables are reopened. So all children are open when attaching them. Special lock handling like mysql_lock_abort() or mysql_lock_remove() needs to be suppressed for MERGE children or forwarded to the parent. This depends on the situation. In loops over all open tables one suppresses child lock handling. When a single table is touched, forwarding is done. This patch changes the behavior of temporary MERGE tables. Temporary MERGE must have temporary children. The old behavior was wrong. A temporary table is not locked. Hence even non-temporary children were not locked. See Bug 19627 - temporary merge table locking.
[22 Oct 2007 7:17]
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/36004 ChangeSet@1.2577, 2007-10-22 09:17:16+02:00, istruewing@stella.local +2 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Forgot to include this new test case in the last commit.
[2 Nov 2007 16:29]
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/37002 ChangeSet@1.2595, 2007-11-02 17:28:19+01:00, istruewing@stella.local +34 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Bug 26377 - Deadlock with MERGE and FLUSH TABLE Bug 25038 - Waiting TRUNCATE Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Bug 19627 - temporary merge table locking Bug 27660 - Falcon: merge table possible Bug 30273 - merge tables: Can't lock file (errno: 155) The problems were: Bug 26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table 1. A thread trying to lock a MERGE table performs busy waiting while REPAIR TABLE or a similar table administration task is ongoing on one or more of its MyISAM tables. 2. A thread trying to lock a MERGE table performs busy waiting until all threads that did REPAIR TABLE or similar table administration tasks on one or more of its MyISAM tables in LOCK TABLES segments do UNLOCK TABLES. The difference against problem #1 is that the busy waiting takes place *after* the administration task. It is terminated by UNLOCK TABLES only. 3. Two FLUSH TABLES within a LOCK TABLES segment can invalidate the lock. This does *not* require a MERGE table. The first FLUSH TABLES can be replaced by any statement that requires other threads to reopen the table. In 5.0 and 5.1 a single FLUSH TABLES can provoke the problem. Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Trying DML on a MERGE table, which has a child locked and repaired by another thread, made an infinite loop in the server. Bug 26377 - Deadlock with MERGE and FLUSH TABLE Locking a MERGE table and its children in parent-child order and flushing the child deadlocked the server. Bug 25038 - Waiting TRUNCATE Truncating a MERGE child, while the MERGE table was in use, let the truncate fail instead of waiting for the table to become free. Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Repairing a child of an open MERGE table corrupted the child. It was necessary to FLUSH the child first. Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Flushing and optimizing locked MERGE children crashed the server. Bug 19627 - temporary merge table locking Use of a temporary MERGE table with non-temporary children could corrupt the children. Temporary tables are never locked. So we do now prohibit non-temporary chidlren of a temporary MERGE table. Bug 27660 - Falcon: merge table possible It was possible to create a MERGE table with non-MyISAM children. Bug 30273 - merge tables: Can't lock file (errno: 155) This was a Windows-only bug. Table administration statements sometimes failed with "Can't lock file (errno: 155)". These bugs are fixed by a new implementation of MERGE table open. When opening a MERGE table in open_tables() we do now add the child tables to the list of tables to be opened by open_tables() (the "query_list"). The children are not opened in the handler at this stage. After opening the parent, open_tables() opens each child from the now extended query_list. When the last child is opened, we remove the children from the query_list again and attach the children to the parent. This behaves similar to the old open. However it does not open the MyISAM tables directly, but grabs them from the already open children. When closing a MERGE table in close_thread_table() we detach the children only. Closing of the children is done implicitly because they are in thd->open_tables. For more detail see the comment at the top of ha_myisammrg.cc. Changed from open_ltable() to open_and_lock_tables() in all places that can be relevant for MERGE tables. The latter can handle tables added to the list on the fly. When open_ltable() was used in a loop over a list of tables, the list must be temporarily terminated after every table for open_and_lock_tables(). table_list->required_type is set to FRMTYPE_TABLE to avoid open of special tables. Handling of derived tables is suppressed. These details are handled by the new function open_n_lock_single_table(), which has nearly the same signature as open_ltable() and can replace it in most cases. In reopen_tables() some of the tables open by a thread can be closed and reopened. When a MERGE child is affected, the parent must be closed and reopened too. Closing of the parent is forced before the first child is closed. Reopen happens in the order of thd->open_tables. MERGE parents do not attach their children automatically at open. This is done after all tables are reopened. So all children are open when attaching them. Special lock handling like mysql_lock_abort() or mysql_lock_remove() needs to be suppressed for MERGE children or forwarded to the parent. This depends on the situation. In loops over all open tables one suppresses child lock handling. When a single table is touched, forwarding is done. This patch changes the behavior of temporary MERGE tables. Temporary MERGE must have temporary children. The old behavior was wrong. A temporary table is not locked. Hence even non-temporary children were not locked. See Bug 19627 - temporary merge table locking.
[14 Nov 2007 18:37]
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/37766 ChangeSet@1.2596, 2007-11-14 19:37:36+01:00, istruewing@stella.local +16 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Not to be pushed as is. Post-review fixes. Intermediate patch for review purpose. Still open issue: Move temporary detach to mark_temp_tables_as_free_for_reuse(). This requires a current repository. Not contained in this patch: Reverted creation of new test case rpl_merge. It was for CREATE...SELECT only.
[15 Nov 2007 9:13]
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/37833 ChangeSet@1.2617, 2007-11-15 10:13:02+01:00, istruewing@stella.local +2 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Not to be pushed as is. Final post-review fixes. Intermediate patch for review purpose.
[15 Nov 2007 18:46]
Konstantin Osipov
Approved by email.
[15 Nov 2007 19:27]
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/37883 ChangeSet@1.2619, 2007-11-15 20:25:43+01:00, istruewing@stella.local +35 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Bug 26377 - Deadlock with MERGE and FLUSH TABLE Bug 25038 - Waiting TRUNCATE Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Bug 19627 - temporary merge table locking Bug 27660 - Falcon: merge table possible Bug 30273 - merge tables: Can't lock file (errno: 155) The problems were: Bug 26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table 1. A thread trying to lock a MERGE table performs busy waiting while REPAIR TABLE or a similar table administration task is ongoing on one or more of its MyISAM tables. 2. A thread trying to lock a MERGE table performs busy waiting until all threads that did REPAIR TABLE or similar table administration tasks on one or more of its MyISAM tables in LOCK TABLES segments do UNLOCK TABLES. The difference against problem #1 is that the busy waiting takes place *after* the administration task. It is terminated by UNLOCK TABLES only. 3. Two FLUSH TABLES within a LOCK TABLES segment can invalidate the lock. This does *not* require a MERGE table. The first FLUSH TABLES can be replaced by any statement that requires other threads to reopen the table. In 5.0 and 5.1 a single FLUSH TABLES can provoke the problem. Bug 26867 - LOCK TABLES + REPAIR + merge table result in memory/cpu hogging Trying DML on a MERGE table, which has a child locked and repaired by another thread, made an infinite loop in the server. Bug 26377 - Deadlock with MERGE and FLUSH TABLE Locking a MERGE table and its children in parent-child order and flushing the child deadlocked the server. Bug 25038 - Waiting TRUNCATE Truncating a MERGE child, while the MERGE table was in use, let the truncate fail instead of waiting for the table to become free. Bug 25700 - merge base tables get corrupted by optimize/analyze/repair table Repairing a child of an open MERGE table corrupted the child. It was necessary to FLUSH the child first. Bug 30275 - Merge tables: flush tables or unlock tables causes server to crash Flushing and optimizing locked MERGE children crashed the server. Bug 19627 - temporary merge table locking Use of a temporary MERGE table with non-temporary children could corrupt the children. Temporary tables are never locked. So we do now prohibit non-temporary chidlren of a temporary MERGE table. Bug 27660 - Falcon: merge table possible It was possible to create a MERGE table with non-MyISAM children. Bug 30273 - merge tables: Can't lock file (errno: 155) This was a Windows-only bug. Table administration statements sometimes failed with "Can't lock file (errno: 155)". These bugs are fixed by a new implementation of MERGE table open. When opening a MERGE table in open_tables() we do now add the child tables to the list of tables to be opened by open_tables() (the "query_list"). The children are not opened in the handler at this stage. After opening the parent, open_tables() opens each child from the now extended query_list. When the last child is opened, we remove the children from the query_list again and attach the children to the parent. This behaves similar to the old open. However it does not open the MyISAM tables directly, but grabs them from the already open children. When closing a MERGE table in close_thread_table() we detach the children only. Closing of the children is done implicitly because they are in thd->open_tables. For more detail see the comment at the top of ha_myisammrg.cc. Changed from open_ltable() to open_and_lock_tables() in all places that can be relevant for MERGE tables. The latter can handle tables added to the list on the fly. When open_ltable() was used in a loop over a list of tables, the list must be temporarily terminated after every table for open_and_lock_tables(). table_list->required_type is set to FRMTYPE_TABLE to avoid open of special tables. Handling of derived tables is suppressed. These details are handled by the new function open_n_lock_single_table(), which has nearly the same signature as open_ltable() and can replace it in most cases. In reopen_tables() some of the tables open by a thread can be closed and reopened. When a MERGE child is affected, the parent must be closed and reopened too. Closing of the parent is forced before the first child is closed. Reopen happens in the order of thd->open_tables. MERGE parents do not attach their children automatically at open. This is done after all tables are reopened. So all children are open when attaching them. Special lock handling like mysql_lock_abort() or mysql_lock_remove() needs to be suppressed for MERGE children or forwarded to the parent. This depends on the situation. In loops over all open tables one suppresses child lock handling. When a single table is touched, forwarding is done. Behavioral changes: =================== This patch changes the behavior of temporary MERGE tables. Temporary MERGE must have temporary children. The old behavior was wrong. A temporary table is not locked. Hence even non-temporary children were not locked. See Bug 19627 - temporary merge table locking. You cannot change the union list of a non-temporary MERGE table when LOCK TABLES is in effect. The following does *not* work: CREATE TABLE m1 ... ENGINE=MRG_MYISAM ...; LOCK TABLES t1 WRITE, t2 WRITE, m1 WRITE; ALTER TABLE m1 ... UNION=(t1,t2) ...; However, you can do this with a temporary MERGE table. You cannot create a MERGE table with CREATE ... SELECT, neither as a temporary MERGE table, nor as a non-temporary MERGE table. CREATE TABLE m1 ... ENGINE=MRG_MYISAM ... SELECT ...; Gives error message: table is not BASE TABLE.
[15 Nov 2007 20:19]
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/37887 ChangeSet@1.2668, 2007-11-15 21:19:05+01:00, istruewing@stella.local +2 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Post-merge fix
[16 Nov 2007 8:06]
Ingo Strüwing
Queued to 6.0-engines, 5.1-engines.
[18 Nov 2007 19:28]
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/38012 ChangeSet@1.2620, 2007-11-18 20:28:37+01:00, istruewing@stella.local +3 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Post-pushbuild fix for a Valgrind warning.
[22 Nov 2007 20:39]
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/38311 ChangeSet@1.2622, 2007-11-22 21:39:28+01:00, istruewing@stella.local +1 -0 Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table Post-pushbuild fix. The merge test failed on Windows. The MoveFile() function returned the error code ERROR_ACCESS_DENIED. The fix is to use a different name for the file to be deleted. This is the same trick as we use for the error code ERROR_ALREADY_EXISTS. Added ERROR_ACCESS_DENIED to the list of error codes that require to change the name of the file to be deleted.
[28 Nov 2007 10:24]
Bugs System
Pushed into 6.0.4-alpha
[28 Nov 2007 10:26]
Bugs System
Pushed into 5.1.23-rc
[3 Dec 2007 9:33]
MC Brown
A note has been added to the 5.1.23 and 6.0.4 changelogs: Important Change: Incompatible Change: A number of problems existed in the implementation of MERGE tables that could cause problems. The problems are summarized below: Bug#26379 - Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table. This was caused in a number of situations: A thread trying to lock a MERGE table performs busy waiting while REPAIR TABLE or a similar table administration task is ongoing on one or more of its MyISAM tables. A thread trying to lock a MERGE table performs busy waiting until all threads that did REPAIR TABLE or similar table administration tasks on one or more of its MyISAM tables in LOCK TABLES segments do UNLOCK TABLES. The difference against problem #1 is that the busy waiting takes place after the administration task. It is terminated by UNLOCK TABLES only. Two FLUSH TABLES within a LOCK TABLES segment can invalidate the lock. This does not require a MERGE table. The first FLUSH TABLES can be replaced by any statement that requires other threads to reopen the table. In 5.0 and 5.1 a single FLUSH TABLES can provoke the problem. Bug#26867 - Simultaneously executing LOCK TABLES and REPAIR TABLE on a MERGE table would result in memory/cpu hogging. Trying DML on a MERGE table, which has a child locked and repaired by another thread, made an infinite loop in the server. Bug#26377 - Deadlock with MERGE and FLUSH TABLE Locking a MERGE table and its children in parent-child order and flushing the child deadlocked the server. Bug#25038 - Waiting TRUNCATE Truncating a MERGE child, while the MERGE table was in use, let the truncate fail instead of waiting for the table to become free. Bug#25700 - MERGE base tables get corrupted by OPTIMIZE/ANALYZE/REPAIR TABLE Repairing a child of an open MERGE table corrupted the child. It was necessary to FLUSH the child first. Bug#30275 - MERGE tables: FLUSH TABLES or UNLOCK TABLES causes server to crash. Flushing and optimizing locked MERGE children crashed the server. Bug#19627 - temporary merge table locking Use of a temporary MERGE table with non-temporary children could corrupt the children. Temporary tables are never locked. Creation of tables with non-temporary chidlren of a temporary MERGE table is now prohibited. Bug#27660 - Falcon: MERGE table possible It was possible to create a MERGE table with non-MyISAM children. Bug#30273 - MERGE tables: Can't lock file (errno: 155) This was a Windows-only bug. Table administration statements sometimes failed with "Can't lock file (errno: 155)". The fix introduces the following changes in behavior: This patch changes the behavior of temporary MERGE tables. Temporary MERGE must have temporary children. The old behavior was wrong. A temporary table is not locked. Hence even non-temporary children were not locked. See Bug#19627. You cannot change the union list of a non-temporary MERGE table when LOCK TABLES is in effect. The following does not work: CREATE TABLE m1 ... ENGINE=MRG_MYISAM ...; LOCK TABLES t1 WRITE, t2 WRITE, m1 WRITE; ALTER TABLE m1 ... UNION=(t1,t2) ...; However, you can do this with a temporary MERGE table. You cannot create a MERGE table with CREATE ... SELECT, neither as a temporary MERGE table, nor as a non-temporary MERGE table. For example: CREATE TABLE m1 ... ENGINE=MRG_MYISAM ... SELECT ...; Gives error message: table is not BASE TABLE.
[14 Jan 2008 17:58]
Jon Stephens
Bug #30491 (q.v.) was also fixed with this bug.