Skip to content

Commit

Permalink
Adding high_priority syntax in DDL commands
Browse files Browse the repository at this point in the history
Summary:
This diff introduces `HIGH_PRIORITY` syntax in some DDL commands and optimize
command. The purpose is the same as the system variable `high_priority_ddl`
which is described in D4784076.

The list of commands that support `HIGH_PRIORITY` syntax is:
- create/drop/alter/truncate table
- create/drop index
- create/drop trigger
- optimize table

With this diff, there are two ways of issuing high priority DDLs:
  e.g. `CREATE HIGH_PRIORITY INDEX ...`

Recap of the high_priority ddl feature:

- Only the shared locks (read/write) will be affected, which are DML queries
  such as select, insert, update, etc. Any lock higher than upgradable MDL
  (i.e. from conflicting DDL) will not be affected.
- When killing the blocking connections, their transactions will automatically
  be rolled back. This is the same behavior as `kill processlist_id` command.
- The DDL will kill blocking connections at the end of the timeout instead of
  instantly. Admin can control the waiting period by setting the session
  lock_wait_timeout before issuing the DDL.

Reviewed By: abhinav04sharma

Differential Revision: D5635549

fbshipit-source-id: e51ed02
  • Loading branch information
tianx authored and facebook-github-bot committed Aug 18, 2017
1 parent 87784d3 commit 397fa65
Show file tree
Hide file tree
Showing 16 changed files with 2,456 additions and 257 deletions.
174 changes: 174 additions & 0 deletions mysql-test/include/ddl_high_priority.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
###############################################################################
# Common test file for high priority DDL
###############################################################################


create user test_user1@localhost;
grant all on test to test_user1@localhost;
create user test_user2@localhost;
grant all on test to test_user2@localhost;

# Default values
--let $con_block = con1
--let $con_kill = default
--let $should_kill = 1
--let $recreate_table = 1
--let $throw_error = 1

##
## killing conflicting shared locks by alter table
##

--let $blocking_sql = lock tables t1 read;
--let $cmd = alter table t1 modify i bigint;
--let $high_priority_cmd = alter high_priority table t1 modify i bigint;

--source include/ddl_high_priority_module.inc

##
## killing conflicting shared lock in a transaction
## transaction will rollback
##

--let $blocking_sql = begin; insert into t1 values (4); select i from t1;
--let $cmd = alter table t1 rename t1_new;
--let $high_priority_cmd = alter high_priority table t1 rename t1_new;

--source include/ddl_high_priority_module.inc

select * from t1_new;
drop table t1_new;

##
## simulate conflicting DDL which will not be killed
##

# Simulate conflicting DDL
# This will hold MDL_SHARED_NO_READ_WRITE, which may be upgraded to exclusive
# locks to run DDLs like ALTER TABLE
# the upgradable/exclusive lock should not be killed

--let $should_kill = 0

--let $blocking_sql = lock tables t1 write;
--let $cmd = drop table t1;
--let $high_priority_cmd = drop high_priority table t1;

--source include/ddl_high_priority_module.inc

# restore $should_kill
--let $should_kill = 1

##
## killing conflicting transaction by drop table DDL
##

--let $blocking_sql = lock tables t1 read; begin; insert into t1 values (4);
--let $cmd = drop table t1;
--let $high_priority_cmd = drop high_priority table t1;

--source include/ddl_high_priority_module.inc

##
## no effect for regular users
##

connect (con2,localhost,test_user2,,test,,);
# $con_kill is regular user
--let $con_kill = con2
--let $should_kill = 0

--let $blocking_sql = lock tables t1 read;
--let $cmd = alter table t1 modify i bigint;
--let $high_priority_cmd = alter high_priority table t1 modify i bigint;

--source include/ddl_high_priority_module.inc

disconnect con2;

# restore $con_kill
--let $con_kill = default
# restore $should_kill
--let $should_kill = 1

##
## create/drop index
##

# create index

--let $blocking_sql = lock tables t1 read;
--let $cmd = create index idx1 on t1 (i);
--let $high_priority_cmd = create high_priority index idx1 on t1 (i);

--source include/ddl_high_priority_module.inc

# drop index (use the previously created table)
--let $recreate_table = 0

--let $cmd = drop index idx1 on t1;
--let $high_priority_cmd = drop high_priority index idx1 on t1;

--source include/ddl_high_priority_module.inc

# restore $recreate_table
--let $recreate_table = 1

##
## high_priority truncate table
##

--let $blocking_sql = lock tables t1 read;
--let $cmd = truncate t1;
--let $high_priority_cmd = truncate high_priority t1;

--source include/ddl_high_priority_module.inc

##
## high_priority create/drop trigger
##

--let $blocking_sql = lock tables t1 read;
--let $cmd = create trigger ins_sum before insert on t1 for each row set @sum = @sum + new.i;
--let $high_priority_cmd = create high_priority trigger ins_sum before insert on t1 for each row set @sum = @sum + new.i;

--source include/ddl_high_priority_module.inc

# drop trigger (use the previously created table)
--let $recreate_table = 0

--let $cmd = drop trigger ins_sum;
--let $high_priority_cmd = drop high_priority trigger ins_sum;

--source include/ddl_high_priority_module.inc

# restore $recreate_table
--let $recreate_table = 1

##
## high_priority optimize table
##
## "optimize table" doesn't throw errors. It catches all errors, and
## returns a result set in a table
##

--let $throw_error = 0

--let $blocking_sql = lock tables t1 read;
--let $cmd = optimize table t1;
--let $high_priority_cmd = optimize high_priority table t1;

--source include/ddl_high_priority_module.inc

# restore throw_error
--let $throw_error = 1

##
## clean up
##

drop user test_user1@localhost;
drop user test_user2@localhost;
--disable_warnings
drop table if exists t1;
--enable_warnings
140 changes: 140 additions & 0 deletions mysql-test/include/ddl_high_priority_module.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
###############################################################################
# This file plays as a function/module for ddl_high_priority test
#
# Usage: set the following variables before including
#
# $use_sys_var: whether using sys_var or syntax to trigger high_priority
# value: 0/1
#
# $con_block: a blocking connection
# value: con1/con2/default
#
# $con_kill: a connection that will attempt to kill $con_blocking
# value: con1/con2/default
#
# $cmd: a regular command to evaluate (to use with sys var)
# value: sql command
#
# $high_priority_cmd: a high_priority command to evaluate
# value: sql command
#
# $should_kill: Expect the con_block to be killed or not
# value: 0/1
#
# $recreate_table: Should recreate the test table or not
# value: 0/1
#
# $throw_error: whether a command will throw lock_wait_timeout error.
# Note, optimize table catches all errors.
# value: 0/1
###############################################################################

##
## Print out the parameters of the test set
## (useful for debugging)
##
--echo
--echo ## Test parameters:
--echo ## use_sys_var = $use_sys_var
--echo ## con_block = $con_block
--echo ## con_kill = $con_kill
--echo ## cmd = $cmd
--echo ## high_priority_cmd = $high_priority_cmd
--echo ## should_kill = $should_kill
--echo ## recreate_table = $recreate_table
--echo ## throw_error = $throw_error
--echo


##
## Setup
##

connection default;

# create con1
connect (con1,localhost,test_user1,,test,,);

if ($recreate_table) {
# create t1
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1 (i int);
show create table t1;
insert into t1 values (1), (2), (3);
}

##
## Testing
##

--echo connection: $con_block
--connection $con_block
--eval $blocking_sql

--echo connection: $con_kill
--connection $con_kill
set lock_wait_timeout = 0.02;

describe t1;

--echo connection: default (for show processlist)
connection default;
--echo # both $con_block and $con_kill exist
--replace_column 1 <Id> 3 <Host> 5 <Command> 6 <Time> 7 <State> 8 <Info> 9 <RExam> 10 <RSent> 11 <TID>
show processlist;

--echo connection: $con_kill
--connection $con_kill

# command will fail without high_priority
if ($throw_error) {
--error ER_LOCK_WAIT_TIMEOUT
--eval $cmd
}

if (!$throw_error) {
--eval $cmd
}

if ($use_sys_var) {
set high_priority_ddl = 1;
select @@high_priority_ddl;

# non-supported command will timeout
--error ER_LOCK_WAIT_TIMEOUT
lock tables t1 write;

if (!$should_kill) {
# regular user ddl will fail regardless of high_priority_ddl being on
--error ER_LOCK_WAIT_TIMEOUT
--eval $cmd
}

if ($should_kill) {
--eval $cmd
}

# reset high_priority_ddl
set high_priority_ddl = 0;
}

if (!$use_sys_var) {
if (!$should_kill) {
# regular user ddl will fail regardless of high_priority being on
--error ER_LOCK_WAIT_TIMEOUT
--eval $high_priority_cmd
}

if ($should_kill) {
--eval $high_priority_cmd
}
}

--echo connection: default (for show processlist)
connection default;
--replace_column 1 <Id> 3 <Host> 5 <Command> 6 <Time> 7 <State> 8 <Info> 9 <RExam> 10 <RSent> 11 <TID>
show processlist;

disconnect con1;
71 changes: 71 additions & 0 deletions mysql-test/include/rpl_ddl_high_priority.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
###############################################################################
# Test replication of the high_priority syntax
###############################################################################


--source include/master-slave.inc

--let $rpl_connection_name= slave_block
--let $rpl_server_number= 2
--source include/rpl_connect.inc

connection master;

# create t1
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1 (i int);
show create table t1;
insert into t1 values (1), (2), (3);

sync_slave_with_master;

--echo connection slave
connection slave;
select * from t1;

let $sql_thread=`select id from information_schema.processlist where state
like 'Slave has read all relay log; waiting for the slave I/O thread%'`;
let $worker_thread=`select id from information_schema.processlist where state
like 'Waiting for%'`;

--echo # set slave thread's lock_wait_timeout to a small value

--replace_regex /session [0-9]*/session sql_thread/
--eval set session $sql_thread lock_wait_timeout = 0.2

--replace_regex /session [0-9]*/session sql_thread/
--eval show session $sql_thread variables like 'lock_wait_timeout'

--echo connection slave_block
connection slave_block;

# issue a bloking read from slave_block connection
lock tables t1 read;

--echo connection master;
connection master;

# issue a high_pri command from master
create high_priority index idx1 on t1 (i);
show create table t1;

# sync
sync_slave_with_master;

--echo connection slave;
connection slave;

# the high_pri ddl replication should go through
show create table t1;


##
## clean up
##

connection master;
drop table t1;

source include/rpl_end.inc;
Loading

0 comments on commit 397fa65

Please sign in to comment.