bool MDL_context::try_acquire_lock(MDL_request*)
This assert was triggered in the following way:
1) HANDLER OPEN t1 from connection 1
2) DROP TABLE t1 from connection 2. This will block due to the metadata lock
held by the open handler in connection 1.
3) DML statement (e.g. INSERT) from connection 1. This will close the table
opened by the HANDLER in 1) and release its metadata lock. This is done due
to the pending exclusive metadata lock from 2).
4) DROP TABLE t1 from connection 2 now completes and removes table t1.
5) HANDLER READ from connection 1. Since the handler table was closed in 3),
the handler code will try to reopen the table. First a new metadata lock on
t1 will be granted before the command fails since the table was removed in 4).
6) HANDLER READ from connection 1. This caused the assert.
The reason for the assert was that the MDL_request's pointer to the lock
ticket was not reset when the statement failed. HANDLER READ then tried to
acquire a lock using the same MDL_request object, triggering the assert.
This bug was only noticeable on debug builds and did not cause any problems
on release builds.
This patch fixes the problem by assuring that the pointer to the metadata
lock ticket is reset when reopening of handler tables fails.
Test case added to handler.inc
bool MDL_context::try_acquire_lock(MDL_request*)
This assert was triggered in the following way:
1) HANDLER OPEN t1 from connection 1
2) DROP TABLE t1 from connection 2. This will block due to the metadata lock
held by the open handler in connection 1.
3) DML statement (e.g. INSERT) from connection 1. This will close the table
opened by the HANDLER in 1) and release its metadata lock. This is done due
to the pending exclusive metadata lock from 2).
4) DROP TABLE t1 from connection 2 now completes and removes table t1.
5) HANDLER READ from connection 1. Since the handler table was closed in 3),
the handler code will try to reopen the table. First a new metadata lock on
t1 will be granted before the command fails since the table was removed in 4).
6) HANDLER READ from connection 1. This caused the assert.
The reason for the assert was that the MDL_request's pointer to the lock
ticket was not reset when the statement failed. HANDLER READ then tried to
acquire a lock using the same MDL_request object, triggering the assert.
This bug was only noticeable on debug builds and did not cause any problems
on release builds.
This patch fixes the problem by assuring that the pointer to the metadata
lock ticket is reset when reopening of handler tables fails.
Test case added to handler.inc
failed in enter_locked_tables_mode".
Server was aborted due to assertion failure when one tried to
execute statement requiring prelocking (i.e. firing triggers
or using stored functions) while having open HANDLERs.
The problem was that THD::enter_locked_tables_mode() method
which was called at the beginning of execution of prelocked
statement assumed there are no open HANDLERs. It had to do
so because corresponding THD::leave_locked_tables_mode()
method was unable to properly restore MDL sentinel when
leaving LOCK TABLES/prelocked mode in the presence of open
HANDLERs.
This patch solves this problem by changing the latter method
to properly restore MDL sentinel and thus removing need for
this assumption. As a side-effect, it lifts unjustified
limitation by allowing to keep HANDLERs open when entering
LOCK TABLES mode.
mysql-test/include/handler.inc:
Adjusted tests after making LOCK TABLES not to close
open HANDLERs. Added coverage for bug #50908
"Assertion `handler_tables_hash.records == 0' failed
in enter_locked_tables_mode".
mysql-test/r/handler_innodb.result:
Updated test results (see include/handler.inc).
mysql-test/r/handler_myisam.result:
Updated test results (see include/handler.inc).
sql/mysql_priv.h:
Introduced mysql_ha_move_tickets_after_trans_sentinel()
routine which allows to move tickets for metadata locks
corresponding to open HANDLERs after transaction sentinel.
sql/sql_class.cc:
Changed THD::leave_locked_tables_mode() to correctly restore
MDL sentinel value in the presence of open HANDLERs.
sql/sql_class.h:
Removed assert from THD::enter_locked_tables_mode() as we
no longer have to close HANDLERs when entering LOCK TABLES
or prelocked modes. Instead we keep them open and correctly
restore MDL sentinel value after leaving them.
Removal of assert also fixes problem from the bug report.
sql/sql_handler.cc:
Introduced mysql_ha_move_tickets_after_trans_sentinel()
routine which allows to move tickets for metadata locks
corresponding to open HANDLERs after transaction sentinel.
sql/sql_parse.cc:
We no longer have to close HANDLERs when entering LOCK TABLES
mode. Instead we keep them open and simply correctly restore
MDL sentinel value after leaving this mode.
failed in enter_locked_tables_mode".
Server was aborted due to assertion failure when one tried to
execute statement requiring prelocking (i.e. firing triggers
or using stored functions) while having open HANDLERs.
The problem was that THD::enter_locked_tables_mode() method
which was called at the beginning of execution of prelocked
statement assumed there are no open HANDLERs. It had to do
so because corresponding THD::leave_locked_tables_mode()
method was unable to properly restore MDL sentinel when
leaving LOCK TABLES/prelocked mode in the presence of open
HANDLERs.
This patch solves this problem by changing the latter method
to properly restore MDL sentinel and thus removing need for
this assumption. As a side-effect, it lifts unjustified
limitation by allowing to keep HANDLERs open when entering
LOCK TABLES mode.
and MDL".
Concurrent execution of a multi-DELETE statement and ALTER
TABLE statement which affected one of the tables used in
the multi-DELETE sometimes led to deadlock.
Similar deadlocks might have occured when one performed
INSERT/UPDATE/DELETE on a view and concurrently executed
ALTER TABLE for the view's underlying table, or when one
concurrently executed TRUNCATE TABLE for InnoDB table and
ALTER TABLE for the same table.
These deadlocks were caused by a discrepancy between types of
metadata and thr_lock.cc locks acquired by those statements.
What happened was that multi-DELETE/TRUNCATE/DML-through-the-
view statement in the first connection acquired SR lock on a
table, then ALTER TABLE would come in in the second connection
and acquire SNW metadata lock and TL_WRITE_ALLOW_READ
thr_lock.c lock and then would start waiting for the first
connection during lock upgrade. After that the statement in
the first connection would try to acquire TL_WRITE lock on
table and would start waiting for the second connection,
creating a deadlock.
This patch solves this problem by ensuring that we acquire
SW metadata lock in all cases in which we acquiring write
thr_lock.c lock. This guarantees that deadlocks like the
one described above won't occur since all lock conflicts
in such situation are resolved within MDL subsystem.
This patch also adds assert which should guarantee that
such situations won't arise in future.
mysql-test/r/lock_multi.result:
Added main test for bug #50913 "Deadlock between
open_and_lock_tables_derived and MDL".
mysql-test/r/mdl_sync.result:
Added additional coverage for bug #50913 "Deadlock
between open_and_lock_tables_derived and MDL".
mysql-test/t/lock_multi.test:
Added main test for bug #50913 "Deadlock between
open_and_lock_tables_derived and MDL".
mysql-test/t/mdl_sync.test:
Added additional coverage for bug #50913 "Deadlock
between open_and_lock_tables_derived and MDL".
sql/lock.cc:
Added assert that enforces that when we are locking
a non-temporary table we have an appropriate type of
metadata lock on this table.
sql/mysql_priv.h:
Added separate flag for open_tables() to be able specify that
SH metadata locks on table to be open should be acquired.
We can no longer use MYSQL_LOCK_IGNORE_FLUSH flag for this
as in addition to use in I_S implementation it is also used
for opening system tables. Since in the latter case we also
acquire thr_lock.c locks using SH metadata lock in it instead
of SR or SW locks may lead to deadlock.
sql/sql_base.cc:
When opening tables don't interpret MYSQL_LOCK_IGNORE_FLUSH
flag as request to acquire SH metadata locks. This flag is
also used for opening system tables for which we also take
thr_lock.c locks and thus proper metadata lock to take in
this case is SR or SW lock (otherwise deadlocks can occur).
In cases when SH lock is really required (e.g. when tables
are open by I_S implementation) we rely on that newly
introduced MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL flag is
used.
sql/sql_delete.cc:
mysql_truncate_by_delete():
Adjust type of metadata lock to be requested after changing
type of thr_lock.c lock for table list element from one
which was set in parser to TL_WRITE.
This removes discrepancy between types of these locks which
allowed deadlocks to creep in.
sql/sql_handler.cc:
When closing table which was open by HANDLER statement clear
TABLE::open_by_handler flag. This allows to use this flag as
a reliable indication that TABLE instance was open by HANDLER
statement in assert which was added to mysql_lock_tables().
sql/sql_parse.cc:
multi_delete_set_locks_and_link_aux_tables():
Adjust type of metadata lock to be requested after changing
type of thr_lock.c lock for table list element from one
which was set in parser to TL_WRITE.
This removes discrepancy between types of these locks which
allowed deadlocks to creep in.
sql/sql_show.cc:
Use newly introduced MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL
flag in order to acquire SH metadata locks when opening tables
in I_S implementation.
sql/sql_update.cc:
Added comment explaining why in multi-update after deciding
that we need weaker thr_lock.c lock on a table we don't
downgrade metadata lock on it.
sql/sql_view.cc:
When merging view into main statement adjust type of metadata
lock to be requested after changing type of thr_lock.c lock
for table. This removes discrepancy between types of these
locks which allowed deadlocks to creep in.
and MDL".
Concurrent execution of a multi-DELETE statement and ALTER
TABLE statement which affected one of the tables used in
the multi-DELETE sometimes led to deadlock.
Similar deadlocks might have occured when one performed
INSERT/UPDATE/DELETE on a view and concurrently executed
ALTER TABLE for the view's underlying table, or when one
concurrently executed TRUNCATE TABLE for InnoDB table and
ALTER TABLE for the same table.
These deadlocks were caused by a discrepancy between types of
metadata and thr_lock.cc locks acquired by those statements.
What happened was that multi-DELETE/TRUNCATE/DML-through-the-
view statement in the first connection acquired SR lock on a
table, then ALTER TABLE would come in in the second connection
and acquire SNW metadata lock and TL_WRITE_ALLOW_READ
thr_lock.c lock and then would start waiting for the first
connection during lock upgrade. After that the statement in
the first connection would try to acquire TL_WRITE lock on
table and would start waiting for the second connection,
creating a deadlock.
This patch solves this problem by ensuring that we acquire
SW metadata lock in all cases in which we acquiring write
thr_lock.c lock. This guarantees that deadlocks like the
one described above won't occur since all lock conflicts
in such situation are resolved within MDL subsystem.
This patch also adds assert which should guarantee that
such situations won't arise in future.
HANDLER OPEN
The problem was a too restrictive assert in the code for
HANDLER ... OPEN and HANDLER ... READ that checked table->next
to verify that we didn't open views or merge tables.
This pointer is also used to link temporary tables together
(see thd->temporary_tables). In this case TABLE::next can be
set even if we're trying to open a single table.
This patch adjust the two asserts to also check for the presence
of temporary tables.
Test case added to handler_myisam.test.
HANDLER OPEN
The problem was a too restrictive assert in the code for
HANDLER ... OPEN and HANDLER ... READ that checked table->next
to verify that we didn't open views or merge tables.
This pointer is also used to link temporary tables together
(see thd->temporary_tables). In this case TABLE::next can be
set even if we're trying to open a single table.
This patch adjust the two asserts to also check for the presence
of temporary tables.
Test case added to handler_myisam.test.
Add a wait-for graph based deadlock detector to the
MDL subsystem.
Fixes bug #46272 "MySQL 5.4.4, new MDL: unnecessary deadlock" and
bug #37346 "innodb does not detect deadlock between update and
alter table".
The first bug manifested itself as an unwarranted abort of a
transaction with ER_LOCK_DEADLOCK error by a concurrent ALTER
statement, when this transaction tried to repeat use of a
table, which it has already used in a similar fashion before
ALTER started.
The second bug showed up as a deadlock between table-level
locks and InnoDB row locks, which was "detected" only after
innodb_lock_wait_timeout timeout.
A transaction would start using the table and modify a few
rows.
Then ALTER TABLE would come in, and start copying rows
into a temporary table. Eventually it would stumble on
the modified records and get blocked on a row lock.
The first transaction would try to do more updates, and get
blocked on thr_lock.c lock.
This situation of circular wait would only get resolved
by a timeout.
Both these bugs stemmed from inadequate solutions to the
problem of deadlocks occurring between different
locking subsystems.
In the first case we tried to avoid deadlocks between metadata
locking and table-level locking subsystems, when upgrading shared
metadata lock to exclusive one.
Transactions holding the shared lock on the table and waiting for
some table-level lock used to be aborted too aggressively.
We also allowed ALTER TABLE to start in presence of transactions
that modify the subject table. ALTER TABLE acquires
TL_WRITE_ALLOW_READ lock at start, and that block all writes
against the table (naturally, we don't want any writes to be lost
when switching the old and the new table). TL_WRITE_ALLOW_READ
lock, in turn, would block the started transaction on thr_lock.c
lock, should they do more updates. This, again, lead to the need
to abort such transactions.
The second bug occurred simply because we didn't have any
mechanism to detect deadlocks between the table-level locks
in thr_lock.c and row-level locks in InnoDB, other than
innodb_lock_wait_timeout.
This patch solves both these problems by moving lock conflicts
which are causing these deadlocks into the metadata locking
subsystem, thus making it possible to avoid or detect such
deadlocks inside MDL.
To do this we introduce new type-of-operation-aware metadata
locks, which allow MDL subsystem to know not only the fact that
transaction has used or is going to use some object but also what
kind of operation it has carried out or going to carry out on the
object.
This, along with the addition of a special kind of upgradable
metadata lock, allows ALTER TABLE to wait until all
transactions which has updated the table to go away.
This solves the second issue.
Another special type of upgradable metadata lock is acquired
by LOCK TABLE WRITE. This second lock type allows to solve the
first issue, since abortion of table-level locks in event of
DDL under LOCK TABLES becomes also unnecessary.
Below follows the list of incompatible changes introduced by
this patch:
- From now on, ALTER TABLE and CREATE/DROP TRIGGER SQL (i.e. those
statements that acquire TL_WRITE_ALLOW_READ lock)
wait for all transactions which has *updated* the table to
complete.
- From now on, LOCK TABLES ... WRITE, REPAIR/OPTIMIZE TABLE
(i.e. all statements which acquire TL_WRITE table-level lock) wait
for all transaction which *updated or read* from the table
to complete.
As a consequence, innodb_table_locks=0 option no longer applies
to LOCK TABLES ... WRITE.
- DROP DATABASE, DROP TABLE, RENAME TABLE no longer abort
statements or transactions which use tables being dropped or
renamed, and instead wait for these transactions to complete.
- Since LOCK TABLES WRITE now takes a special metadata lock,
not compatible with with reads or writes against the subject table
and transaction-wide, thr_lock.c deadlock avoidance algorithm
that used to ensure absence of deadlocks between LOCK TABLES
WRITE and other statements is no longer sufficient, even for
MyISAM. The wait-for graph based deadlock detector of MDL
subsystem may sometimes be necessary and is involved. This may
lead to ER_LOCK_DEADLOCK error produced for multi-statement
transactions even if these only use MyISAM:
session 1: session 2:
begin;
update t1 ... lock table t2 write, t1 write;
-- gets a lock on t2, blocks on t1
update t2 ...
(ER_LOCK_DEADLOCK)
- Finally, support of LOW_PRIORITY option for LOCK TABLES ... WRITE
was abandoned.
LOCK TABLE ... LOW_PRIORITY WRITE from now on has the same
priority as the usual LOCK TABLE ... WRITE.
SELECT HIGH PRIORITY no longer trumps LOCK TABLE ... WRITE in
the wait queue.
- We do not take upgradable metadata locks on implicitly
locked tables. So if one has, say, a view v1 that uses
table t1, and issues:
LOCK TABLE v1 WRITE;
FLUSH TABLE t1; -- (or just 'FLUSH TABLES'),
an error is produced.
In order to be able to perform DDL on a table under LOCK TABLES,
the table must be locked explicitly in the LOCK TABLES list.
mysql-test/include/handler.inc:
Adjusted test case to trigger an execution path on which bug 41110
"crash with handler command when used concurrently with alter
table" and bug 41112 "crash in mysql_ha_close_table/get_lock_data
with alter table" were originally discovered. Left old test case
which no longer triggers this execution path for the sake of
coverage.
Added test coverage for HANDLER SQL statements and type-aware
metadata locks.
Added a test for the global shared lock and HANDLER SQL.
Updated tests to take into account that the old simple deadlock
detection heuristics was replaced with a graph-based deadlock
detector.
mysql-test/r/debug_sync.result:
Updated results (see debug_sync.test).
mysql-test/r/handler_innodb.result:
Updated results (see handler.inc test).
mysql-test/r/handler_myisam.result:
Updated results (see handler.inc test).
mysql-test/r/innodb-lock.result:
Updated results (see innodb-lock.test).
mysql-test/r/innodb_mysql_lock.result:
Updated results (see innodb_mysql_lock.test).
mysql-test/r/lock.result:
Updated results (see lock.test).
mysql-test/r/lock_multi.result:
Updated results (see lock_multi.test).
mysql-test/r/lock_sync.result:
Updated results (see lock_sync.test).
mysql-test/r/mdl_sync.result:
Updated results (see mdl_sync.test).
mysql-test/r/sp-threads.result:
SHOW PROCESSLIST output has changed due to the fact that waiting
for LOCK TABLES WRITE now happens within metadata locking
subsystem.
mysql-test/r/truncate_coverage.result:
Updated results (see truncate_coverage.test).
mysql-test/suite/funcs_1/datadict/processlist_val.inc:
SELECT FROM I_S.PROCESSLIST output has changed due to fact that
waiting for LOCK TABLES WRITE now happens within metadata locking
subsystem.
mysql-test/suite/funcs_1/r/processlist_val_no_prot.result:
SELECT FROM I_S.PROCESSLIST output has changed due to fact that
waiting for LOCK TABLES WRITE now happens within metadata locking
subsystem.
mysql-test/suite/rpl/t/rpl_sp.test:
Updated to a new SHOW PROCESSLIST state name.
mysql-test/t/debug_sync.test:
Use LOCK TABLES READ instead of LOCK TABLES WRITE as the latter
no longer allows to trigger execution path involving waiting on
thr_lock.c lock and therefore reaching debug sync-point covered
by this test.
mysql-test/t/innodb-lock.test:
Adjusted test case to the fact that innodb_table_locks=0 option is
no longer supported, since LOCK TABLES WRITE handles all its
conflicts within MDL subsystem.
mysql-test/t/innodb_mysql_lock.test:
Added test for bug #37346 "innodb does not detect deadlock between
update and alter table".
mysql-test/t/lock.test:
Added test coverage which checks the fact that we no longer support
DDL under LOCK TABLES on tables which were locked implicitly.
Adjusted existing test cases accordingly.
mysql-test/t/lock_multi.test:
Added test for bug #46272 "MySQL 5.4.4, new MDL: unnecessary
deadlock". Adjusted other test cases to take into account the
fact that waiting for LOCK TABLES ... WRITE now happens within MDL
subsystem.
mysql-test/t/lock_sync.test:
Since LOCK TABLES ... WRITE now takes SNRW metadata lock for
tables locked explicitly we have to implicitly lock InnoDB tables
(through view) to trigger the table-level lock conflict between
TL_WRITE and TL_WRITE_ALLOW_WRITE.
mysql-test/t/mdl_sync.test:
Added basic test coverage for type-of-operation-aware metadata
locks. Also covered with tests some use cases involving HANDLER
statements in which a deadlock could arise.
Adjusted existing tests to take type-of-operation-aware MDL into
account.
mysql-test/t/multi_update.test:
Update to a new SHOW PROCESSLIST state name.
mysql-test/t/truncate_coverage.test:
Adjusted test case after making LOCK TABLES WRITE to wait until
transactions that use the table to be locked are completed.
Updated to the changed name of DEBUG_SYNC point.
sql/handler.cc:
Global read lock functionality has been
moved into a class.
sql/lock.cc:
Global read lock functionality has been
moved into a class.
Updated code to use the new MDL API.
sql/mdl.cc:
Introduced new type-of-operation aware metadata locks.
To do this:
- Changed MDL_lock to use one list for waiting requests and one
list for granted requests. For each list, added a bitmap
that holds information what lock types a list contains.
Added a helper class MDL_lock::List to manipulate with granted
and waited lists while keeping the bitmaps in sync
with list contents.
- Changed lock-compatibility functions to use bitmaps that
define compatibility.
- Introduced a graph based deadlock detector inspired by
waiting_threads.c from Maria implementation.
- Now that we have a deadlock detector, and no longer have
a global lock to protect individual lock objects, but rather
use an rw lock per object, removed redundant code for upgrade,
and the global read lock. Changed the MDL API to
no longer require the caller to acquire the global
intention exclusive lock by means of a separate method.
Removed a few more methods that became redundant.
- Removed deadlock detection heuristic, it has been made
obsolete by the deadlock detector.
- With operation-type-aware metadata locks, MDL subsystem has
become aware of potential conflicts between DDL and open
transactions. This made it possible to remove calls to
mysql_abort_transactions_with_shared_lock() from acquisition
paths for exclusive lock and lock upgrade. Now we can simply
wait for these transactions to complete without fear of
deadlock. Function mysql_lock_abort() has also become
unnecessary for all conflicting cases except when a DDL
conflicts with a connection that has an open HANDLER.
sql/mdl.h:
Introduced new type-of-operation aware metadata locks.
Introduced a graph based deadlock detector and supporting
methods.
Added comments.
God rid of redundant API calls.
Renamed m_lt_or_ha_sentinel to m_trans_sentinel,
since now it guards the global read lock as well as
LOCK TABLES and HANDLER locks.
sql/mysql_priv.h:
Moved the global read lock functionality into a
class.
Added MYSQL_OPEN_FORCE_SHARED_MDL flag which forces
open_tables() to take MDL_SHARED on tables instead of
metadata locks specified in the parser. We use this to
allow PREPARE run concurrently in presence of
LOCK TABLES ... WRITE.
Added signature for find_table_for_mdl_ugprade().
sql/set_var.cc:
Global read lock functionality has been
moved into a class.
sql/sp_head.cc:
When creating TABLE_LIST elements for prelocking or
system tables set the type of request for metadata
lock according to the operation that will be performed
on the table.
sql/sql_base.cc:
- Updated code to use the new MDL API.
- In order to avoid locks starvation we take upgradable
locks all at once. As result implicitly locked tables no
longer get an upgradable lock. Consequently DDL and FLUSH
TABLES for such tables is prohibited.
find_write_locked_table() was replaced by
find_table_for_mdl_upgrade() function.
open_table() was adjusted to return TABLE instance with
upgradable ticket when necessary.
- We no longer wait for all locks on OT_WAIT back off
action -- only on the lock that caused the wait
conflict. Moreover, now we distinguish cases when we
have to wait due to conflict in MDL and old version
of table in TDC.
- Upate mysql_notify_threads_having_share_locks()
to only abort thr_lock.c waits of threads that
have open HANDLERs, since lock conflicts with only
these threads now can lead to deadlocks not detectable
by the MDL deadlock detector.
- Remove mysql_abort_transactions_with_shared_locks()
which is no longer needed.
sql/sql_class.cc:
Global read lock functionality has been moved into a class.
Re-arranged code in THD::cleanup() to simplify assert.
sql/sql_class.h:
Introduced class to incapsulate global read lock
functionality.
Now sentinel in MDL subsystem guards the global read lock
as well as LOCK TABLES and HANDLER locks. Adjusted code
accordingly.
sql/sql_db.cc:
Global read lock functionality has been moved into a class.
sql/sql_delete.cc:
We no longer acquire upgradable metadata locks on tables
which are locked by LOCK TABLES implicitly. As result
TRUNCATE TABLE is no longer allowed for such tables.
Updated code to use the new MDL API.
sql/sql_handler.cc:
Inform MDL_context about presence of open HANDLERs.
Since HANLDERs break MDL protocol by acquiring table-level
lock while holding only S metadata lock on a table MDL
subsystem should take special care about such contexts (Now
this is the only case when mysql_lock_abort() is used).
sql/sql_parse.cc:
Global read lock functionality has been moved into a class.
Do not take upgradable metadata locks when opening tables
for CREATE TABLE SELECT as it is not necessary and limits
concurrency.
When initializing TABLE_LIST objects before adding them
to the table list set the type of request for metadata lock
according to the operation that will be performed on the
table.
We no longer acquire upgradable metadata locks on tables
which are locked by LOCK TABLES implicitly. As result FLUSH
TABLES is no longer allowed for such tables.
sql/sql_prepare.cc:
Use MYSQL_OPEN_FORCE_SHARED_MDL flag when opening
tables during PREPARE. This allows PREPARE to run
concurrently in presence of LOCK TABLES ... WRITE.
sql/sql_rename.cc:
Global read lock functionality has been moved into a class.
sql/sql_show.cc:
Updated code to use the new MDL API.
sql/sql_table.cc:
Global read lock functionality has been moved into a class.
We no longer acquire upgradable metadata locks on tables
which are locked by LOCK TABLES implicitly. As result DROP
TABLE is no longer allowed for such tables.
Updated code to use the new MDL API.
sql/sql_trigger.cc:
Global read lock functionality has been moved into a class.
We no longer acquire upgradable metadata locks on tables
which are locked by LOCK TABLES implicitly. As result
CREATE/DROP TRIGGER is no longer allowed for such tables.
Updated code to use the new MDL API.
sql/sql_view.cc:
Global read lock functionality has been moved into a class.
Fixed results of wrong merge that led to misuse of GLR API.
CREATE VIEW statement is not a commit statement.
sql/table.cc:
When resetting TABLE_LIST objects for PS or SP re-execution
set the type of request for metadata lock according to the
operation that will be performed on the table. Do the same
in auxiliary function initializing metadata lock requests
in a table list.
sql/table.h:
When initializing TABLE_LIST objects set the type of request
for metadata lock according to the operation that will be
performed on the table.
sql/transaction.cc:
Global read lock functionality has been moved into a class.
Add a wait-for graph based deadlock detector to the
MDL subsystem.
Fixes bug #46272 "MySQL 5.4.4, new MDL: unnecessary deadlock" and
bug #37346 "innodb does not detect deadlock between update and
alter table".
The first bug manifested itself as an unwarranted abort of a
transaction with ER_LOCK_DEADLOCK error by a concurrent ALTER
statement, when this transaction tried to repeat use of a
table, which it has already used in a similar fashion before
ALTER started.
The second bug showed up as a deadlock between table-level
locks and InnoDB row locks, which was "detected" only after
innodb_lock_wait_timeout timeout.
A transaction would start using the table and modify a few
rows.
Then ALTER TABLE would come in, and start copying rows
into a temporary table. Eventually it would stumble on
the modified records and get blocked on a row lock.
The first transaction would try to do more updates, and get
blocked on thr_lock.c lock.
This situation of circular wait would only get resolved
by a timeout.
Both these bugs stemmed from inadequate solutions to the
problem of deadlocks occurring between different
locking subsystems.
In the first case we tried to avoid deadlocks between metadata
locking and table-level locking subsystems, when upgrading shared
metadata lock to exclusive one.
Transactions holding the shared lock on the table and waiting for
some table-level lock used to be aborted too aggressively.
We also allowed ALTER TABLE to start in presence of transactions
that modify the subject table. ALTER TABLE acquires
TL_WRITE_ALLOW_READ lock at start, and that block all writes
against the table (naturally, we don't want any writes to be lost
when switching the old and the new table). TL_WRITE_ALLOW_READ
lock, in turn, would block the started transaction on thr_lock.c
lock, should they do more updates. This, again, lead to the need
to abort such transactions.
The second bug occurred simply because we didn't have any
mechanism to detect deadlocks between the table-level locks
in thr_lock.c and row-level locks in InnoDB, other than
innodb_lock_wait_timeout.
This patch solves both these problems by moving lock conflicts
which are causing these deadlocks into the metadata locking
subsystem, thus making it possible to avoid or detect such
deadlocks inside MDL.
To do this we introduce new type-of-operation-aware metadata
locks, which allow MDL subsystem to know not only the fact that
transaction has used or is going to use some object but also what
kind of operation it has carried out or going to carry out on the
object.
This, along with the addition of a special kind of upgradable
metadata lock, allows ALTER TABLE to wait until all
transactions which has updated the table to go away.
This solves the second issue.
Another special type of upgradable metadata lock is acquired
by LOCK TABLE WRITE. This second lock type allows to solve the
first issue, since abortion of table-level locks in event of
DDL under LOCK TABLES becomes also unnecessary.
Below follows the list of incompatible changes introduced by
this patch:
- From now on, ALTER TABLE and CREATE/DROP TRIGGER SQL (i.e. those
statements that acquire TL_WRITE_ALLOW_READ lock)
wait for all transactions which has *updated* the table to
complete.
- From now on, LOCK TABLES ... WRITE, REPAIR/OPTIMIZE TABLE
(i.e. all statements which acquire TL_WRITE table-level lock) wait
for all transaction which *updated or read* from the table
to complete.
As a consequence, innodb_table_locks=0 option no longer applies
to LOCK TABLES ... WRITE.
- DROP DATABASE, DROP TABLE, RENAME TABLE no longer abort
statements or transactions which use tables being dropped or
renamed, and instead wait for these transactions to complete.
- Since LOCK TABLES WRITE now takes a special metadata lock,
not compatible with with reads or writes against the subject table
and transaction-wide, thr_lock.c deadlock avoidance algorithm
that used to ensure absence of deadlocks between LOCK TABLES
WRITE and other statements is no longer sufficient, even for
MyISAM. The wait-for graph based deadlock detector of MDL
subsystem may sometimes be necessary and is involved. This may
lead to ER_LOCK_DEADLOCK error produced for multi-statement
transactions even if these only use MyISAM:
session 1: session 2:
begin;
update t1 ... lock table t2 write, t1 write;
-- gets a lock on t2, blocks on t1
update t2 ...
(ER_LOCK_DEADLOCK)
- Finally, support of LOW_PRIORITY option for LOCK TABLES ... WRITE
was abandoned.
LOCK TABLE ... LOW_PRIORITY WRITE from now on has the same
priority as the usual LOCK TABLE ... WRITE.
SELECT HIGH PRIORITY no longer trumps LOCK TABLE ... WRITE in
the wait queue.
- We do not take upgradable metadata locks on implicitly
locked tables. So if one has, say, a view v1 that uses
table t1, and issues:
LOCK TABLE v1 WRITE;
FLUSH TABLE t1; -- (or just 'FLUSH TABLES'),
an error is produced.
In order to be able to perform DDL on a table under LOCK TABLES,
the table must be locked explicitly in the LOCK TABLES list.
"HANDLER statements within a transaction might lead to deadlocks".
Introduce a notion of a sentinel to MDL_context. A sentinel
is a ticket that separates all tickets in the context into two
groups: before and after it. Currently we can have (and need) only
one designated sentinel -- it separates all locks taken by LOCK
TABLE or HANDLER statement, which must survive COMMIT and ROLLBACK
and all other locks, which must be released at COMMIT or ROLLBACK.
The tricky part is maintaining the sentinel up to date when
someone release its corresponding ticket. This can happen, e.g.
if someone issues DROP TABLE under LOCK TABLES (generally,
see all calls to release_all_locks_for_name()).
MDL_context::release_ticket() is modified to take care of it.
******
A fix and a test case for Bug#46224 "HANDLER statements within a
transaction might lead to deadlocks".
An attempt to mix HANDLER SQL statements, which are transaction-
agnostic, an open multi-statement transaction,
and DDL against the involved tables (in a concurrent connection)
could lead to a deadlock. The deadlock would occur when
HANDLER OPEN or HANDLER READ would have to wait on a conflicting
metadata lock. If the connection that issued HANDLER statement
also had other metadata locks (say, acquired in scope of a
transaction), a classical deadlock situation of mutual wait
could occur.
Incompatible change: entering LOCK TABLES mode automatically
closes all open HANDLERs in the current connection.
Incompatible change: previously an attempt to wait on a lock
in a connection that has an open HANDLER statement could wait
indefinitely/deadlock. After this patch, an error ER_LOCK_DEADLOCK
is produced.
The idea of the fix is to merge thd->handler_mdl_context
with the main mdl_context of the connection, used for transactional
locks. This makes deadlock detection possible, since all waits
with locks are "visible" and available to analysis in a single
MDL context of the connection.
Since HANDLER locks and transactional locks have a different life
cycle -- HANDLERs are explicitly open and closed, and so
are HANDLER locks, explicitly acquired and released, whereas
transactional locks "accumulate" till the end of a transaction
and are released only with COMMIT, ROLLBACK and ROLLBACK TO SAVEPOINT,
a concept of "sentinel" was introduced to MDL_context.
All locks, HANDLER and others, reside in the same linked list.
However, a selected element of the list separates locks with
different life cycle. HANDLER locks always reside at the
end of the list, after the sentinel. Transactional locks are
prepended to the beginning of the list, before the sentinel.
Thus, ROLLBACK, COMMIT or ROLLBACK TO SAVEPOINT, only
release those locks that reside before the sentinel. HANDLER locks
must be released explicitly as part of HANDLER CLOSE statement,
or an implicit close.
The same approach with sentinel
is also employed for LOCK TABLES locks. Since HANDLER and LOCK TABLES
statement has never worked together, the implementation is
made simple and only maintains one sentinel, which is used either
for HANDLER locks, or for LOCK TABLES locks.
mysql-test/include/handler.inc:
Add test coverage for Bug#46224 "HANDLER statements within a
transaction might lead to deadlocks".
Extended HANDLER coverage to cover a mix of HANDLER, transactions
and DDL statements.
mysql-test/r/handler_innodb.result:
Update results (Bug#46224).
mysql-test/r/handler_myisam.result:
Update results (Bug#46224).
sql/lock.cc:
Remove thd->some_tables_deleted, it's never used.
sql/log_event.cc:
No need to check for thd->locked_tables_mode,
it's done inside release_transactional_locks().
sql/mdl.cc:
Implement the concept of HANDLER and LOCK TABLES "sentinel".
Implement a method to clone an acquired ticket.
Do not return tickets beyond the sentinel when acquiring
locks, create a copy.
Remove methods to merge and backup MDL_context, they are now
not used (Hurra!). This opens a path to a proper constructor
and destructor of class MDL_context (to be done in a separate
patch).
Modify find_ticket() to provide information about where
the ticket position is with regard to the sentinel.
sql/mdl.h:
Add declarations necessary for the implementation of the concept
of "sentinel", a dedicated ticket separating transactional and
non-transactional locks.
sql/mysql_priv.h:
Add mark_tmp_table_for_reuse() declaration,
a function to "close" a single session (temporary) table.
sql/sql_base.cc:
Remove thd->some_tables_deleted.
Modify deadlock-prevention asserts and deadlock detection
heuristics to take into account that from now on HANDLER locks
reside in the same locking context.
Add broadcast_refresh() to mysql_notify_thread_having_shared_lock():
this is necessary for the case when a thread having a shared lock
is asleep in tdc_wait_for_old_versions(). This situation is only
possible with HANDLER t1 OPEN; FLUSH TABLE (since all over code paths
that lead to tdc_wait_for_old_versions() always have an
empty MDL_context). Previously the server would simply deadlock
in this situation.
sql/sql_class.cc:
Remove now unused member "THD::some_tables_deleted".
Move mysql_ha_cleanup() a few lines above in THD::cleanup()
to make sure that all handlers are closed when it's time to
destroy the MDL_context of this connection.
Remove handler_mdl_context and handler_tables.
sql/sql_class.h:
Remove THD::handler_tables, THD::handler_mdl_context,
THD::some_tables_deleted.
sql/sql_handler.cc:
Remove thd->handler_tables.
Remove thd->handler_mdl_context.
Rewrite mysql_ha_open() to have no special provision for MERGE
tables, now that we don't have to manipulate with thd->handler_tables
it's easy to do.
Remove dead code.
Fix a bug in mysql_ha_flush() when we would always flush
a temporary HANDLER when mysql_ha_flush() is called (actually
mysql_ha_flush() never needs to flush temporary tables).
sql/sql_insert.cc:
Update a comment, no more thd->some_tables_deleted.
sql/sql_parse.cc:
Implement an incompatible change: entering LOCK TABLES closes
active HANDLERs, if any.
Now that we have a sentinel, we don't need to check
for thd->locked_tables_mode when releasing metadata locks in
COMMIT/ROLLBACK.
sql/sql_plist.h:
Add new (now necessary) methods to the list class.
sql/sql_prepare.cc:
Make sure we don't release HANDLER locks when rollback to a
savepoint, set to not keep locks taken at PREPARE.
sql/sql_servers.cc:
Update to a new signature of MDL_context::release_all_locks().
sql/sql_table.cc:
Remove thd->some_tables_deleted.
sql/transaction.cc:
Add comments.
Make sure rollback to (MDL) savepoint works under LOCK TABLES and
with HANDLER tables.
"HANDLER statements within a transaction might lead to deadlocks".
Introduce a notion of a sentinel to MDL_context. A sentinel
is a ticket that separates all tickets in the context into two
groups: before and after it. Currently we can have (and need) only
one designated sentinel -- it separates all locks taken by LOCK
TABLE or HANDLER statement, which must survive COMMIT and ROLLBACK
and all other locks, which must be released at COMMIT or ROLLBACK.
The tricky part is maintaining the sentinel up to date when
someone release its corresponding ticket. This can happen, e.g.
if someone issues DROP TABLE under LOCK TABLES (generally,
see all calls to release_all_locks_for_name()).
MDL_context::release_ticket() is modified to take care of it.
******
A fix and a test case for Bug#46224 "HANDLER statements within a
transaction might lead to deadlocks".
An attempt to mix HANDLER SQL statements, which are transaction-
agnostic, an open multi-statement transaction,
and DDL against the involved tables (in a concurrent connection)
could lead to a deadlock. The deadlock would occur when
HANDLER OPEN or HANDLER READ would have to wait on a conflicting
metadata lock. If the connection that issued HANDLER statement
also had other metadata locks (say, acquired in scope of a
transaction), a classical deadlock situation of mutual wait
could occur.
Incompatible change: entering LOCK TABLES mode automatically
closes all open HANDLERs in the current connection.
Incompatible change: previously an attempt to wait on a lock
in a connection that has an open HANDLER statement could wait
indefinitely/deadlock. After this patch, an error ER_LOCK_DEADLOCK
is produced.
The idea of the fix is to merge thd->handler_mdl_context
with the main mdl_context of the connection, used for transactional
locks. This makes deadlock detection possible, since all waits
with locks are "visible" and available to analysis in a single
MDL context of the connection.
Since HANDLER locks and transactional locks have a different life
cycle -- HANDLERs are explicitly open and closed, and so
are HANDLER locks, explicitly acquired and released, whereas
transactional locks "accumulate" till the end of a transaction
and are released only with COMMIT, ROLLBACK and ROLLBACK TO SAVEPOINT,
a concept of "sentinel" was introduced to MDL_context.
All locks, HANDLER and others, reside in the same linked list.
However, a selected element of the list separates locks with
different life cycle. HANDLER locks always reside at the
end of the list, after the sentinel. Transactional locks are
prepended to the beginning of the list, before the sentinel.
Thus, ROLLBACK, COMMIT or ROLLBACK TO SAVEPOINT, only
release those locks that reside before the sentinel. HANDLER locks
must be released explicitly as part of HANDLER CLOSE statement,
or an implicit close.
The same approach with sentinel
is also employed for LOCK TABLES locks. Since HANDLER and LOCK TABLES
statement has never worked together, the implementation is
made simple and only maintains one sentinel, which is used either
for HANDLER locks, or for LOCK TABLES locks.
Bug #46654 False deadlock on concurrent DML/DDL with partitions,
inconsistent behavior
The problem was that if one connection is running a multi-statement
transaction which involves a single partitioned table, and another
connection attempts to alter the table, the first connection gets
ER_LOCK_DEADLOCK and cannot proceed anymore, even when the ALTER TABLE
statement in another connection has timed out or failed.
The reason for this was that the prepare phase for ALTER TABLE for
partitioned tables removed all instances of the table from the table
definition cache before it started waiting on the lock. The transaction
running in the first connection would notice this and report ER_LOCK_DEADLOCK.
This patch changes the prep_alter_part_table() ALTER TABLE code so that
tdc_remove_table() is no longer called. Instead, only the TABLE instance
changed by prep_alter_part_table() is marked as needing reopen.
The patch also removes an unnecessary call to tdc_remove_table() from
mysql_unpack_partition() as the changed TABLE object is destroyed by the
caller at a later point.
Test case added in partition_sync.test.
Bug #46654 False deadlock on concurrent DML/DDL with partitions,
inconsistent behavior
The problem was that if one connection is running a multi-statement
transaction which involves a single partitioned table, and another
connection attempts to alter the table, the first connection gets
ER_LOCK_DEADLOCK and cannot proceed anymore, even when the ALTER TABLE
statement in another connection has timed out or failed.
The reason for this was that the prepare phase for ALTER TABLE for
partitioned tables removed all instances of the table from the table
definition cache before it started waiting on the lock. The transaction
running in the first connection would notice this and report ER_LOCK_DEADLOCK.
This patch changes the prep_alter_part_table() ALTER TABLE code so that
tdc_remove_table() is no longer called. Instead, only the TABLE instance
changed by prep_alter_part_table() is marked as needing reopen.
The patch also removes an unnecessary call to tdc_remove_table() from
mysql_unpack_partition() as the changed TABLE object is destroyed by the
caller at a later point.
Test case added in partition_sync.test.
------------------------------------------------------------
revno: 2617.68.25
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-next-bg-pre2-2
timestamp: Wed 2009-09-16 18:26:50 +0400
message:
Follow-up for one of pre-requisite patches for fixing bug #30977
"Concurrent statement using stored function and DROP FUNCTION
breaks SBR".
Made enum_mdl_namespace enum part of MDL_key class and removed MDL_
prefix from the names of enum members. In order to do the latter
changed name of PROCEDURE symbol to PROCEDURE_SYM (otherwise macro
which was automatically generated for this symbol conflicted with
MDL_key::PROCEDURE enum member).
------------------------------------------------------------
revno: 2617.68.25
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-next-bg-pre2-2
timestamp: Wed 2009-09-16 18:26:50 +0400
message:
Follow-up for one of pre-requisite patches for fixing bug #30977
"Concurrent statement using stored function and DROP FUNCTION
breaks SBR".
Made enum_mdl_namespace enum part of MDL_key class and removed MDL_
prefix from the names of enum members. In order to do the latter
changed name of PROCEDURE symbol to PROCEDURE_SYM (otherwise macro
which was automatically generated for this symbol conflicted with
MDL_key::PROCEDURE enum member).
---------------------------------------------
This is a patch for bug#47098 assert in MDL_context::destroy on
HANDLER <damaged merge table> OPEN.
The assert occurs in MDL_context::destroy when the connection is terminated,
because all mdl_tickets have not been released.
MERGE tables do not support being opened using the HANDLER ... OPEN command,
and trying to do so will result in an error. In the event of an error, all
tables that are opened, should be closed again. The fix for bug#45781 made
sure that this also works for MERGE tables, which causes multiple tables to
be opened.
This fix extends the fix for bug#45781, by ensuring that also all locks are
released, when MERGE tables are involved.
mysql-test/r/merge.result:
The result of the test.
mysql-test/t/merge.test:
Added a test based on the bug report, as well as a test of the more general
scenario.
sql/sql_handler.cc:
Added code to release all the locks.
---------------------------------------------
This is a patch for bug#47098 assert in MDL_context::destroy on
HANDLER <damaged merge table> OPEN.
The assert occurs in MDL_context::destroy when the connection is terminated,
because all mdl_tickets have not been released.
MERGE tables do not support being opened using the HANDLER ... OPEN command,
and trying to do so will result in an error. In the event of an error, all
tables that are opened, should be closed again. The fix for bug#45781 made
sure that this also works for MERGE tables, which causes multiple tables to
be opened.
This fix extends the fix for bug#45781, by ensuring that also all locks are
released, when MERGE tables are involved.
----------------------------------------------------------
revno: 2617.69.25
committer: Konstantin Osipov <kostja@sun.com>
branch nick: 5.4-42546
timestamp: Fri 2009-08-14 23:52:00 +0400
message:
A cleanup in open_tables() and lock_tables():
change return type of these functions to bool from int,
to follow convention in the rest of the code.
(Part of WL#4284 review fixes).
sql/mysql_priv.h:
Change return type of open_talbes() and lock_tables() from
int to bool.
sql/sql_base.cc:
Change return type of open_tables() and lock_tables() from int to bool.
sql/sql_handler.cc:
Use bool now that open_tables() returns bool.
----------------------------------------------------------
revno: 2617.69.25
committer: Konstantin Osipov <kostja@sun.com>
branch nick: 5.4-42546
timestamp: Fri 2009-08-14 23:52:00 +0400
message:
A cleanup in open_tables() and lock_tables():
change return type of these functions to bool from int,
to follow convention in the rest of the code.
(Part of WL#4284 review fixes).
A pre-requisite patch for Bug#30977 "Concurrent statement using
stored function and DROP FUNCTION breaks SBR".
This patch changes the MDL API by introducing a namespace for
lock keys: MDL_TABLE for tables and views and MDL_PROCEDURE
for stored procedures and functions. The latter is needed for
the fix for Bug#30977.
A pre-requisite patch for Bug#30977 "Concurrent statement using
stored function and DROP FUNCTION breaks SBR".
This patch changes the MDL API by introducing a namespace for
lock keys: MDL_TABLE for tables and views and MDL_PROCEDURE
for stored procedures and functions. The latter is needed for
the fix for Bug#30977.
Bug #43272 HANDLER SQL command does not work under LOCK TABLES
HANDLER commands are now explicitly disallowed in LOCK TABLES mode.
Before, HANDLER OPEN gave the misleading error message: "Table x was
not locked with LOCK TABLES". This patch changes HANDLER OPEN/READ/CLOSE
to give ER_LOCK_OR_ACTIVE_TRANSACTION "Can't execute the given command
because you have active locked tables or an active transaction" in
LOCK TABLES mode.
Test case added to lock.test.
Bug #43272 HANDLER SQL command does not work under LOCK TABLES
HANDLER commands are now explicitly disallowed in LOCK TABLES mode.
Before, HANDLER OPEN gave the misleading error message: "Table x was
not locked with LOCK TABLES". This patch changes HANDLER OPEN/READ/CLOSE
to give ER_LOCK_OR_ACTIVE_TRANSACTION "Can't execute the given command
because you have active locked tables or an active transaction" in
LOCK TABLES mode.
Test case added to lock.test.
----------------------------------------------------------
revno: 2617.69.20
committer: Konstantin Osipov <kostja@sun.com>
branch nick: 5.4-4284-1-assert
timestamp: Thu 2009-08-13 18:29:55 +0400
message:
WL#4284 "Transactional DDL locking"
A review fix.
Since WL#4284 implementation separated MDL_request and MDL_ticket,
MDL_request becamse a utility object necessary only to get a ticket.
Store it by-value in TABLE_LIST with the intent to merge
MDL_request::key with table_list->table_name and table_list->db
in future.
Change the MDL subsystem to not require MDL_requests to
stay around till close_thread_tables().
Remove the list of requests from the MDL context.
Requests for shared metadata locks acquired in open_tables()
are only used as a list in recover_from_failed_open_table_attempt(),
which calls mdl_context.wait_for_locks() for this list.
To keep such list for recover_from_failed_open_table_attempt(),
introduce a context class (Open_table_context), that collects
all requests.
A lot of minor cleanups and simplications that became possible
with this change.
sql/event_db_repository.cc:
Remove alloc_mdl_requests(). Now MDL_request instance is a member
of TABLE_LIST, and init_one_table() initializes it.
sql/ha_ndbcluster_binlog.cc:
Remove now unnecessary declaration and initialization
of binlog_mdl_request.
sql/lock.cc:
No need to allocate MDL requests in lock_table_names() now.
sql/log.cc:
Use init_one_table() method, remove alloc_mdl_requests(),
which is now unnecessary.
sql/log_event.cc:
No need to allocate mdl_request separately now.
Use init_one_table() method.
sql/log_event_old.cc:
Update to the new signature of close_tables_for_reopen().
sql/mdl.cc:
Update try_acquire_exclusive_lock() to be more easy to use.
Function lock_table_name_if_not_cached() has been removed.
Make acquire_shared_lock() signature consistent with
try_acquire_exclusive_lock() signature.
Remove methods that are no longer used.
Update comments.
sql/mdl.h:
Implement an assignment operator that doesn't
copy MDL_key (MDL_key::operator= is private and
should remain private).
This is a hack to work-around assignment of TABLE_LIST
by value in several places. Such assignments violate
encapsulation, since only perform a shallow copy.
In most cases these assignments are a hack on their own.
sql/mysql_priv.h:
Update signatures of close_thread_tables() and close_tables_for_reopen().
sql/sp.cc:
Allocate TABLE_LIST in thd->mem_root.
Use init_one_table().
sql/sp_head.cc:
Use init_one_table().
Remove thd->locked_tables_root, it's no longer needed.
sql/sql_acl.cc:
Use init_mdl_requests() and init_one_table().
sql/sql_base.cc:
Update to new signatures of try_acquire_shared_lock() and
try_acquire_exclusive_lock().
Remove lock_table_name_if_not_cached().
Fix a bug in open_ltable() that would not return ER_LOCK_DEADLOCK
in case of a failed lock_tables() and a multi-statement
transaction.
Fix a bug in open_and_lock_tables_derived() that would
not return ER_LOCK_DEADLOCK in case of a multi-statement
transaction and a failure of lock_tables().
Move assignment of enum_open_table_action to a method of Open_table_context, a new class that maintains information
for backoff actions.
Minor rearrangements of the code.
Remove alloc_mdl_requests() in functions that work with system
tables: instead the patch ensures that callers always initialize
TABLE_LIST argument.
sql/sql_class.cc:
THD::locked_tables_root is no more.
sql/sql_class.h:
THD::locked_tables_root is no more.
Add a declaration for Open_table_context class.
sql/sql_delete.cc:
Update to use the simplified MDL API.
sql/sql_handler.cc:
TABLE_LIST::mdl_request is stored by-value now.
Ensure that mdl_request.ticket is NULL for every request
that is passed into MDL, to satisfy MDL asserts.
@ sql/sql_help.cc
Function open_system_tables_for_read() no longer initializes
mdl_requests.
Move TABLE_LIST::mdl_request initialization closer to
TABLE_LIST initialization.
sql/sql_help.cc:
Function open_system_tables_for_read() no longer initializes
mdl_requests.
Move TABLE_LIST::mdl_request initialization closer to
TABLE_LIST initialization.
sql/sql_insert.cc:
Remove assignment by-value of TABLE_LIST in
TABLEOP_HOOKS. We can't carry over a granted
MDL ticket from one table list to another.
sql/sql_parse.cc:
Change alloc_mdl_requests() -> init_mdl_requests().
@todo We can remove init_mdl_requests() altogether
in some places: all places that call add_table_to_list()
already have mdl requests initialized.
sql/sql_plugin.cc:
Use init_one_table().
THD::locked_tables_root is no more.
sql/sql_servers.cc:
Use init_one_table().
sql/sql_show.cc:
Update acquire_high_priority_shared_lock() to use
TABLE_LIST::mdl_request rather than allocate an own.
Fix get_trigger_table_impl() to use init_one_table(),
check for out of memory, follow the coding style.
sql/sql_table.cc:
Update to work with TABLE_LIST::mdl_request by-value.
Remove lock_table_name_if_not_cached().
The code that used to delegate to it is quite simple and
concise without it now.
sql/sql_udf.cc:
Use init_one_table().
sql/sql_update.cc:
Update to use the new signature of close_tables_for_reopen().
sql/table.cc:
Move re-setting of mdl_requests for prepared statements
and stored procedures from close_thread_tables() to
reinit_stmt_before_use().
Change alloc_mdl_requests() to init_mdl_requests().
init_mdl_requests() is a hack that can't be deleted
until we don't have a list-aware TABLE_LIST constructor.
Hopefully its use will be minimal
sql/table.h:
Change alloc_mdl_requests() to init_mdl_requests()
TABLE_LIST::mdl_request is stored by value.
sql/tztime.cc:
We no longer initialize mdl requests in open_system_tables_for*()
functions. Move this initialization closer to initialization
of the rest of TABLE_LIST members.
storage/myisammrg/ha_myisammrg.cc:
Simplify mdl_request initialization.
----------------------------------------------------------
revno: 2617.69.20
committer: Konstantin Osipov <kostja@sun.com>
branch nick: 5.4-4284-1-assert
timestamp: Thu 2009-08-13 18:29:55 +0400
message:
WL#4284 "Transactional DDL locking"
A review fix.
Since WL#4284 implementation separated MDL_request and MDL_ticket,
MDL_request becamse a utility object necessary only to get a ticket.
Store it by-value in TABLE_LIST with the intent to merge
MDL_request::key with table_list->table_name and table_list->db
in future.
Change the MDL subsystem to not require MDL_requests to
stay around till close_thread_tables().
Remove the list of requests from the MDL context.
Requests for shared metadata locks acquired in open_tables()
are only used as a list in recover_from_failed_open_table_attempt(),
which calls mdl_context.wait_for_locks() for this list.
To keep such list for recover_from_failed_open_table_attempt(),
introduce a context class (Open_table_context), that collects
all requests.
A lot of minor cleanups and simplications that became possible
with this change.
----------------------------------------------------------
revno: 2617.23.20
committer: Konstantin Osipov <kostja@sun.com>
branch nick: mysql-6.0-runtime
timestamp: Wed 2009-03-04 16:31:31 +0300
message:
WL#4284 "Transactional DDL locking"
Review comments: "Objectify" the MDL API.
MDL_request and MDL_context still need manual construction and
destruction, since they are used in environment that is averse
to constructors/destructors.
sql/mdl.cc:
Improve comments.
Add asserts to backup()/restore_from_backup()/merge() methods.
Fix an order bug in the error path of mdl_acquire_exclusive_locks():
we used to first free a ticket object, and only then exclude
it from the list of tickets.
----------------------------------------------------------
revno: 2617.23.20
committer: Konstantin Osipov <kostja@sun.com>
branch nick: mysql-6.0-runtime
timestamp: Wed 2009-03-04 16:31:31 +0300
message:
WL#4284 "Transactional DDL locking"
Review comments: "Objectify" the MDL API.
MDL_request and MDL_context still need manual construction and
destruction, since they are used in environment that is averse
to constructors/destructors.
------------------------------------------------------------
revno: 2617.23.18
committer: Davi Arnaut <Davi.Arnaut@Sun.COM>
branch nick: 4284-6.0
timestamp: Mon 2009-03-02 18:18:26 -0300
message:
Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order
WL#4284: Transactional DDL locking
This is a prerequisite patch:
These changes are intended to split lock requests from granted
locks and to allow the memory and lifetime of granted locks to
be managed within the MDL subsystem. Furthermore, tickets can
now be shared and therefore are used to satisfy multiple lock
requests, but only shared locks can be recursive.
The problem is that the MDL subsystem morphs lock requests into
granted locks locks but does not manage the memory and lifetime
of lock requests, and hence, does not manage the memory of
granted locks either. This can be problematic because it puts the
burden of tracking references on the users of the subsystem and
it can't be easily done in transactional contexts where the locks
have to be kept around for the duration of a transaction.
Another issue is that recursive locks (when the context trying to
acquire a lock already holds a lock on the same object) requires
that each time the lock is granted, a unique lock request/granted
lock structure structure must be kept around until the lock is
released. This can lead to memory leaks in transactional contexts
as locks taken during the transaction should only be released at
the end of the transaction. This also leads to unnecessary wake
ups (broadcasts) in the MDL subsystem if the context still holds
a equivalent of the lock being released.
These issues are exacerbated due to the fact that WL#4284 low-level
design says that the implementation should "2) Store metadata locks
in transaction memory root, rather than statement memory root" but
this is not possible because a memory root, as implemented in mysys,
requires all objects allocated from it to be freed all at once.
This patch combines review input and significant code contributions
from Konstantin Osipov (kostja) and Dmitri Lenev (dlenev).
mysql-test/r/mdl_sync.result:
Add test case result.
mysql-test/t/mdl_sync.test:
Add test case for shared lock upgrade case.
sql/event_db_repository.cc:
Rename mdl_alloc_lock to mdl_request_alloc.
sql/ha_ndbcluster_binlog.cc:
Use new function names to initialize MDL lock requests.
sql/lock.cc:
Rename MDL functions.
sql/log_event.cc:
The MDL request now holds the table and database name data (MDL_KEY).
sql/mdl.cc:
Move the MDL key to the MDL_LOCK structure in order to make the
object suitable for allocation from a fixed-size allocator. This
allows the simplification of the lists in the MDL_LOCK object,
which now are just two, one for granted tickets and other for
waiting (upgraders) tickets.
Recursive requests for a shared lock on the same object can now
be granted using the same lock ticket. This schema is only used
for shared locks because that the only case that matters. This
is used to avoid waste of resources in case a context (connection)
already holds a shared lock on a object.
sql/mdl.h:
Introduce a metadata lock object key which is used to uniquely
identify lock objects.
Separate the structure used to represent pending lock requests
from the structure used to represent granted metadata locks.
Rename functions used to manipulate locks requests in order to
have a more consistent function naming schema.
sql/sp_head.cc:
Rename mdl_alloc_lock to mdl_request_alloc.
sql/sql_acl.cc:
Rename alloc_mdl_locks to alloc_mdl_requests.
sql/sql_base.cc:
Various changes to accommodate that lock requests are separated
from lock tickets (granted locks).
sql/sql_class.h:
Last acquired lock before the savepoint was set.
sql/sql_delete.cc:
Various changes to accommodate that lock requests are separated
from lock tickets (granted locks).
sql/sql_handler.cc:
Various changes to accommodate that lock requests are separated
from lock tickets (granted locks).
sql/sql_insert.cc:
Rename alloc_mdl_locks to alloc_mdl_requests.
sql/sql_parse.cc:
Rename alloc_mdl_locks to alloc_mdl_requests.
sql/sql_plist.h:
Typedef for iterator type.
sql/sql_plugin.cc:
Rename alloc_mdl_locks to alloc_mdl_requests.
sql/sql_servers.cc:
Rename alloc_mdl_locks to alloc_mdl_requests.
sql/sql_show.cc:
Various changes to accommodate that lock requests are separated
from lock tickets (granted locks).
sql/sql_table.cc:
Various changes to accommodate that lock requests are separated
from lock tickets (granted locks).
sql/sql_trigger.cc:
Save reference to the lock ticket so it can be downgraded later.
sql/sql_udf.cc:
Rename alloc_mdl_locks to alloc_mdl_requests.
sql/table.cc:
Rename mdl_alloc_lock to mdl_request_alloc.
sql/table.h:
Separate MDL lock requests from lock tickets (granted locks).
storage/myisammrg/ha_myisammrg.cc:
Rename alloc_mdl_locks to alloc_mdl_requests.
------------------------------------------------------------
revno: 2617.23.18
committer: Davi Arnaut <Davi.Arnaut@Sun.COM>
branch nick: 4284-6.0
timestamp: Mon 2009-03-02 18:18:26 -0300
message:
Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order
WL#4284: Transactional DDL locking
This is a prerequisite patch:
These changes are intended to split lock requests from granted
locks and to allow the memory and lifetime of granted locks to
be managed within the MDL subsystem. Furthermore, tickets can
now be shared and therefore are used to satisfy multiple lock
requests, but only shared locks can be recursive.
The problem is that the MDL subsystem morphs lock requests into
granted locks locks but does not manage the memory and lifetime
of lock requests, and hence, does not manage the memory of
granted locks either. This can be problematic because it puts the
burden of tracking references on the users of the subsystem and
it can't be easily done in transactional contexts where the locks
have to be kept around for the duration of a transaction.
Another issue is that recursive locks (when the context trying to
acquire a lock already holds a lock on the same object) requires
that each time the lock is granted, a unique lock request/granted
lock structure structure must be kept around until the lock is
released. This can lead to memory leaks in transactional contexts
as locks taken during the transaction should only be released at
the end of the transaction. This also leads to unnecessary wake
ups (broadcasts) in the MDL subsystem if the context still holds
a equivalent of the lock being released.
These issues are exacerbated due to the fact that WL#4284 low-level
design says that the implementation should "2) Store metadata locks
in transaction memory root, rather than statement memory root" but
this is not possible because a memory root, as implemented in mysys,
requires all objects allocated from it to be freed all at once.
This patch combines review input and significant code contributions
from Konstantin Osipov (kostja) and Dmitri Lenev (dlenev).
------------------------------------------------------------
revno: 2630.4.33
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w2
timestamp: Fri 2008-06-20 17:11:20 +0400
message:
WL#3726 "DDL locking for all metadata objects".
After-review fixes in progress.
Minimized dependency of mdl.cc on other modules (particularly
made it independant of mysql_priv.h) in order to be able
write unit tests for metadata locking subsystem.
sql/ha_ndbcluster_binlog.cc:
Use newly introduced MAX_MDLKEY_LENGTH constant for allocating
buffer for object key for metadata locking subsystem.
sql/log_event.cc:
Use newly introduced MAX_MDLKEY_LENGTH constant for allocating
buffer for object key for metadata locking subsystem.
sql/mdl.cc:
Removed dependency on THD class (and thus on mysql_priv.h)
by using direct access to members of st_my_thread_var instead
of accessing THD::killed/enter_cond()/exit_cond().
sql/mdl.h:
Added MAX_MDLKEY_LENGTH constant to be used for allocating
buffers for key for metadata locking subsystem.
Added declarations of server kernel functions used by metadata
locking subsystem to mdl.h in order to decrease dependency of
mdl.cc on other files.
sql/mysql_priv.h:
Moved declaration of notify_thread_having_shared_lock() to the
mdl.h (also renamed it to make clear in metadata locking code
that it is a callback to SQL-layer).
sql/sql_base.cc:
Renamed notify_thread_having_shared_lock() to make it clear
in metadata locking subsystem code that it is a callback
to SQL layer.
sql/sql_handler.cc:
Use newly introduced MAX_MDLKEY_LENGTH constant for allocating
buffer for object key for metadata locking subsystem.
sql/sql_show.cc:
Use newly introduced MAX_MDLKEY_LENGTH constant for allocating
buffer for object key for metadata locking subsystem.
------------------------------------------------------------
revno: 2630.4.33
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w2
timestamp: Fri 2008-06-20 17:11:20 +0400
message:
WL#3726 "DDL locking for all metadata objects".
After-review fixes in progress.
Minimized dependency of mdl.cc on other modules (particularly
made it independant of mysql_priv.h) in order to be able
write unit tests for metadata locking subsystem.
------------------------------------------------------------
revno: 2630.4.32
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w2
timestamp: Thu 2008-06-19 16:39:58 +0400
message:
WL#3726 "DDL locking for all metadata objects".
After-review fixes in progress.
Ensure that metadata locking subsystem properly handles
out-of-memory conditions. Clarified MDL interface by
separating release of locks and removal of lock requests
from the context.
sql/lock.cc:
mdl_release_lock(), mdl_acquire_exclusive_locks() and
mdl_try_acquire_exclusive_lock() are no longer responsible
for removal of metadata lock requests from the context.
One should explicitly call mdl_remove_all_locks() and
mdl_remove_lock() to do this.
sql/mdl.cc:
Ensured that metadata locking subsystem properly handles
out-of-memory conditions.
Introduced new MDL_INITIALIZED state for metadata lock
request which is used in all cases when lock is not acquired
and we have not associated request with object respesenting
lock.
MDL_PENDING is now only used for requests for exclusive locks
which are added to the MDL_LOCK::waiting_exclusive queue.
mdl_release_lock(), mdl_acquire_exclusive_locks() and
mdl_try_acquire_exclusive_lock() are no longer responsible
for removal of metadata lock requests from the context.
One should explicitly call mdl_remove_all_locks() and
newly introduced mdl_remove_lock() to do this.
Also renamed mdl_release_all_locks_for_name() to
emphasize that it also actually removes lock requests
from the context.
Finally mdl_try_acquire_exclusive_lock() is now returs
information about encountered lock conflict in separate
out parameter since its return value is used for distinguishing
between error (e.g. due to OOM) and success.
sql/mdl.h:
Introduced new MDL_INITIALIZED state for metadata lock
request which is used in all cases when lock is not acquired
and we have not associated request with object respesenting
lock.
MDL_PENDING is now only used for requests for exclusive locks
which are added to the MDL_LOCK::waiting_exclusive queue.
mdl_release_lock(), mdl_acquire_exclusive_locks() and
mdl_try_acquire_exclusive_lock() are no longer responsible
for removal of metadata lock requests from the context.
One should explicitly call mdl_remove_all_locks() and
newly introduced mdl_remove_lock() to do this.
Also renamed mdl_release_all_locks_for_name() to
emphasize that it also actually removes lock requests
from the context.
Finally mdl_try_acquire_exclusive_lock() is now returs
information about encountered lock conflict in separate
out parameter since its return value is used for distinguishing
between error (e.g. due to OOM) and success.
sql/sql_base.cc:
mdl_release_lock(), mdl_acquire_exclusive_locks() and mdl_try_acquire_exclusive_lock() are no longer responsible
for removal of metadata lock requests from the context.
One should explicitly call mdl_remove_all_locks() and
mdl_remove_lock() to do this.
Also adjusted open_table() to ensure that it
releases/removes metadata locks in case of error
after adding/acquiring them (unless keeping these
lock requests is required for recovering action).
sql/sql_delete.cc:
mdl_release_lock(), mdl_acquire_exclusive_locks() and mdl_try_acquire_exclusive_lock() are no longer responsible
for removal of metadata lock requests from the context.
One should explicitly call mdl_remove_all_locks() and
mdl_remove_lock() to do this.
sql/sql_handler.cc:
mdl_release_lock(), mdl_acquire_exclusive_locks() and mdl_try_acquire_exclusive_lock() are no longer responsible
for removal of metadata lock requests from the context.
One should explicitly call mdl_remove_all_locks() and
mdl_remove_lock() to do this.
sql/sql_show.cc:
mdl_release_lock(), mdl_acquire_exclusive_locks() and mdl_try_acquire_exclusive_lock() are no longer responsible
for removal of metadata lock requests from the context.
One should explicitly call mdl_remove_all_locks() and
mdl_remove_lock() to do this.
sql/sql_table.cc:
Renamed mdl_release_all_locks_for_name() to emphasize
that it also actually removes lock requests from the context.
mdl_release_lock(), mdl_acquire_exclusive_locks() and mdl_try_acquire_exclusive_lock() are no longer responsible
for removal of metadata lock requests from the context.
One should explicitly call mdl_remove_all_locks() and
mdl_remove_lock() to do this.
Finally mdl_try_acquire_exclusive_lock() is now returs
information about encountered lock conflict in separate
out parameter since its return value is used for distinguishing
between error (e.g. due to OOM) and success.
------------------------------------------------------------
revno: 2630.8.3
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w4
timestamp: Thu 2008-06-05 10:48:36 +0400
message:
WL#3726 "DDL locking for all metadata objects".
After-review fixes in progress.
Adjust some comments that were using old terminology
(name locks instead of exclusive metadata locks), brought
some of them up-to-date with current situation in code.
sql/sql_base.cc:
Adjusted comments to use proper terminology.
sql/sql_delete.cc:
Adjusted comments to use proper terminology.
sql/sql_handler.cc:
Adjusted comments to use proper terminology.
sql/sql_partition.cc:
Adjusted comments to use proper terminology also fixed
one comment to correspond to what really happens in code.
sql/sql_show.cc:
Adjusted comments to use proper terminology.
sql/sql_table.cc:
Adjusted comments to use proper terminology, brought
one of them up-to-date with current situation.
sql/sql_trigger.cc:
Adjusted comments to use proper terminology.
sql/table.h:
Removed two unused members of TABLE_SHARE struct.
------------------------------------------------------------
revno: 2630.8.3
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w4
timestamp: Thu 2008-06-05 10:48:36 +0400
message:
WL#3726 "DDL locking for all metadata objects".
After-review fixes in progress.
Adjust some comments that were using old terminology
(name locks instead of exclusive metadata locks), brought
some of them up-to-date with current situation in code.
------------------------------------------------------------
revno: 2630.4.17
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w2
timestamp: Thu 2008-05-29 16:52:56 +0400
message:
WL#3726 "DDL locking for all metadata objects".
After review fixes in progress.
"The great correction of names".
Renamed MDL_LOCK and MDL_LOCK_DATA classes to make usage of
these names in metadata locking subsystem consistent with
other parts of server (i.e. thr_lock.cc). Now we MDL_LOCK_DATA
corresponds to request for a lock and MDL_LOCK to the lock
itself. Adjusted code in MDL subsystem and other places
using these classes accordingly.
Did similar thing for GLOBAL_MDL_LOCK_DATA class and also
changed name of its members to correspond to names of
MDL_LOCK_DATA members.
Finally got rid of usage of one letter variables in MDL
code since it makes code harder to search in (according
to reviewer).
------------------------------------------------------------
revno: 2630.4.17
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w2
timestamp: Thu 2008-05-29 16:52:56 +0400
message:
WL#3726 "DDL locking for all metadata objects".
After review fixes in progress.
"The great correction of names".
Renamed MDL_LOCK and MDL_LOCK_DATA classes to make usage of
these names in metadata locking subsystem consistent with
other parts of server (i.e. thr_lock.cc). Now we MDL_LOCK_DATA
corresponds to request for a lock and MDL_LOCK to the lock
itself. Adjusted code in MDL subsystem and other places
using these classes accordingly.
Did similar thing for GLOBAL_MDL_LOCK_DATA class and also
changed name of its members to correspond to names of
MDL_LOCK_DATA members.
Finally got rid of usage of one letter variables in MDL
code since it makes code harder to search in (according
to reviewer).
------------------------------------------------------------
revno: 2630.4.16
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w
timestamp: Thu 2008-05-29 09:45:02 +0400
message:
WL#3726 "DDL locking for all metadata objects".
After review changes in progress.
Tweaked some comments and did some renames to
avoid ambiguites.
sql/mysql_priv.h:
Removed name_lock_locked_table() function.
sql/sql_base.cc:
Got rid of name_lock_locked_table() function after replacing
the only call to it with its body.
Simplified open_table() code by making "action" argument
mandatory (i.e. one now should always pass non-0 pointer
in this argument).
Renamed TABLE_LIST::open_table_type to open_type to
avoid confusing it with type of table.
Adjusted comments according to review.
sql/sql_handler.cc:
Added comment clarifying in which cases we can have TABLE::mdl_lock
set to 0.
sql/sql_insert.cc:
Now the 4th argument of open_table() is mandatory (it makes
no sense to complicate open_table() code when we can simply
pass dummy variable).
sql/sql_parse.cc:
Renamed TABLE_LIST::open_table_type to open_type to
avoid confusing it with type of table.
sql/sql_prepare.cc:
Renamed TABLE_LIST::open_table_type to open_type to
avoid confusing it with type of table.
sql/sql_table.cc:
Now the 4th argument of open_table() is mandatory (it makes
no sense to complicate open_table() code when we can simply
pass dummy variable).
sql/sql_trigger.cc:
Replaced the only call to name_lock_locked_table() function
with its body.
sql/sql_view.cc:
Renamed TABLE_LIST::open_table_type to open_type to
avoid confusing it with type of table.
sql/table.h:
Renamed TABLE_LIST::open_table_type to open_type (to
avoid confusing it with type of table) and improved
comments describing this member.
------------------------------------------------------------
revno: 2630.4.16
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w
timestamp: Thu 2008-05-29 09:45:02 +0400
message:
WL#3726 "DDL locking for all metadata objects".
After review changes in progress.
Tweaked some comments and did some renames to
avoid ambiguites.