mirror of
https://github.com/MariaDB/server.git
synced 2026-04-24 09:15:30 +02:00
MDEV-136 Non-blocking "set read_only"
backport dmitry.shulga@oracle.com-20120209125742-w7hdxv0103ymb8ko from mysql-trunk: Patch for bug#11764747 (formerly known as 57612): SET GLOBAL READ_ONLY=1 cannot progress when a table is locked with LOCK TABLES. The reason for the bug was that mysql server makes a flush of all open tables during handling of statement 'SET GLOBAL READ_ONLY=1'. Therefore if some of these tables were locked by "LOCK TABLE ... READ" from a different connection, then execution of statement 'SET GLOBAL READ_ONLY=1' would be waiting for the lock for such table even if the table was locked in a compatible read mode. Flushing of all open tables before setting of read_only system variable is inherited from 5.1 implementation since this was the only possible approach to ensure that there isn't any pending write operations on open tables. Start from version 5.5 and above such behaviour is guaranteed by the fact that we acquire global_read_lock before setting read_only flag. Since acquiring of global_read_lock is successful only when there isn't any active write operation then we can remove flushing of open tables from processing of SET GLOBAL READ_ONLY=1. This modification changes the server behavior so that read locks held by other connections (LOCK TABLE ... READ) no longer will block attempts to enable read_only.
This commit is contained in:
parent
3e3606d21d
commit
4361c8645b
5 changed files with 19 additions and 57 deletions
|
|
@ -75,18 +75,16 @@ connection default;
|
|||
set global read_only=1;
|
||||
ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
|
||||
unlock tables ;
|
||||
send set global read_only=1;
|
||||
set global read_only=1;
|
||||
connection con1;
|
||||
select @@global.read_only;
|
||||
@@global.read_only
|
||||
0
|
||||
unlock tables ;
|
||||
select @@global.read_only;
|
||||
@@global.read_only
|
||||
1
|
||||
connection con1;
|
||||
select @@global.read_only;
|
||||
@@global.read_only
|
||||
1
|
||||
unlock tables ;
|
||||
connection default;
|
||||
reap;
|
||||
connection default;
|
||||
set global read_only=0;
|
||||
BEGIN;
|
||||
|
|
|
|||
|
|
@ -43,27 +43,14 @@ a
|
|||
1
|
||||
COMMIT;
|
||||
UNLOCK TABLES;
|
||||
FLUSH STATUS;
|
||||
# Expected 0 at the beginning of the test
|
||||
show status like 'Opened_tables';
|
||||
Variable_name Value
|
||||
Opened_tables 0
|
||||
connection con1;
|
||||
lock table t1 write;
|
||||
lock table t1 read;
|
||||
connection default;
|
||||
set global read_only=1;
|
||||
# Expected 1 as the slow_log was reopened
|
||||
show status like 'Opened_tables';
|
||||
Variable_name Value
|
||||
Opened_tables 1
|
||||
connection con1;
|
||||
unlock tables;
|
||||
connection default;
|
||||
SET GLOBAL read_only=0;
|
||||
# Expected 2 as the slow_log was reopened
|
||||
show status like 'Opened_tables';
|
||||
Variable_name Value
|
||||
Opened_tables 2
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t1;
|
||||
DROP USER test@localhost;
|
||||
|
|
|
|||
|
|
@ -169,23 +169,19 @@ connection default;
|
|||
--error ER_LOCK_OR_ACTIVE_TRANSACTION
|
||||
set global read_only=1;
|
||||
unlock tables ;
|
||||
# The following call blocks until con1 releases the read lock.
|
||||
# Blocking is a limitation, and could be improved.
|
||||
--echo send set global read_only=1;
|
||||
send set global read_only=1;
|
||||
|
||||
# after unlock tables in current connection
|
||||
# the next command must be executed successfully
|
||||
set global read_only=1;
|
||||
select @@global.read_only;
|
||||
|
||||
--echo connection con1;
|
||||
connection con1;
|
||||
select @@global.read_only;
|
||||
unlock tables ;
|
||||
let $wait_condition= SELECT @@global.read_only= 1;
|
||||
--source include/wait_condition.inc
|
||||
select @@global.read_only;
|
||||
|
||||
--echo connection default;
|
||||
connection default;
|
||||
--echo reap;
|
||||
reap;
|
||||
|
||||
# pending transaction / READ_ONLY
|
||||
# - is an error in the same connection
|
||||
|
|
|
|||
|
|
@ -81,19 +81,14 @@ COMMIT;
|
|||
|
||||
connection default;
|
||||
UNLOCK TABLES;
|
||||
FLUSH STATUS;
|
||||
--echo # Expected 0 at the beginning of the test
|
||||
show status like 'Opened_tables';
|
||||
|
||||
--echo connection con1;
|
||||
connection con1;
|
||||
lock table t1 write;
|
||||
lock table t1 read;
|
||||
|
||||
--echo connection default;
|
||||
connection default;
|
||||
set global read_only=1;
|
||||
--echo # Expected 1 as the slow_log was reopened
|
||||
show status like 'Opened_tables';
|
||||
|
||||
--echo connection con1;
|
||||
connection con1;
|
||||
|
|
@ -102,8 +97,6 @@ unlock tables;
|
|||
--echo connection default;
|
||||
connection default;
|
||||
SET GLOBAL read_only=0;
|
||||
--echo # Expected 2 as the slow_log was reopened
|
||||
show status like 'Opened_tables';
|
||||
|
||||
UNLOCK TABLES;
|
||||
DROP TABLE t1;
|
||||
|
|
|
|||
|
|
@ -1596,14 +1596,15 @@ static bool fix_read_only(sys_var *self, THD *thd, enum_var_type type)
|
|||
}
|
||||
|
||||
/*
|
||||
Perform a 'FLUSH TABLES WITH READ LOCK'.
|
||||
This is a 3 step process:
|
||||
- [1] lock_global_read_lock()
|
||||
- [2] close_cached_tables()
|
||||
- [3] make_global_read_lock_block_commit()
|
||||
[1] prevents new connections from obtaining tables locked for write.
|
||||
[2] waits until all existing connections close their tables.
|
||||
[3] prevents transactions from being committed.
|
||||
READ_ONLY=1 prevents write locks from being taken on tables and
|
||||
blocks transactions from committing. We therefore should make sure
|
||||
that no such events occur while setting the read_only variable.
|
||||
This is a 2 step process:
|
||||
[1] lock_global_read_lock()
|
||||
Prevents connections from obtaining new write locks on
|
||||
tables. Note that we can still have active rw transactions.
|
||||
[2] make_global_read_lock_block_commit()
|
||||
Prevents transactions from committing.
|
||||
*/
|
||||
|
||||
read_only= opt_readonly;
|
||||
|
|
@ -1612,19 +1613,6 @@ static bool fix_read_only(sys_var *self, THD *thd, enum_var_type type)
|
|||
if (thd->global_read_lock.lock_global_read_lock(thd))
|
||||
goto end_with_mutex_unlock;
|
||||
|
||||
/*
|
||||
This call will be blocked by any connection holding a READ or WRITE lock.
|
||||
Ideally, we want to wait only for pending WRITE locks, but since:
|
||||
con 1> LOCK TABLE T FOR READ;
|
||||
con 2> LOCK TABLE T FOR WRITE; (blocked by con 1)
|
||||
con 3> SET GLOBAL READ ONLY=1; (blocked by con 2)
|
||||
can cause to wait on a read lock, it's required for the client application
|
||||
to unlock everything, and acceptable for the server to wait on all locks.
|
||||
*/
|
||||
if ((result= close_cached_tables(thd, NULL, TRUE,
|
||||
thd->variables.lock_wait_timeout)))
|
||||
goto end_with_read_lock;
|
||||
|
||||
if ((result= thd->global_read_lock.make_global_read_lock_block_commit(thd)))
|
||||
goto end_with_read_lock;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue