Skip to content

Commit 213e38d

Browse files
Martin SköldMartin Sköld
Martin Sköld
authored and
Martin Sköld
committed
Bug#17812505 MULTIPLE ALTER AND SELECT STATEMENTS CAUSING THE SQL NODE TO HANG
For copying ALTER TABLE the server sets table lock TL_READ_NO_INSERT on the originating table. Use that instead of increasing lock. Ensure that we lock tuples exclusively for the copying scan table. Adding test case that verifies there is no more lock conflict. Approved by : Frazer Clement <frazer.clement@oracle.com>
1 parent f1bb35c commit 213e38d

File tree

3 files changed

+131
-15
lines changed

3 files changed

+131
-15
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
CREATE TABLE t1 (
2+
counter1 integer,
3+
counter2 integer,
4+
counter3 integer,
5+
counter4 integer
6+
7+
) ENGINE=engine;
8+
call query_select_repeat(5);
9+
INSERT INTO t1 (counter1, counter2, counter3, counter4)
10+
VALUES (1,1,1,1);
11+
INSERT INTO t1 (counter1, counter2, counter3, counter4)
12+
VALUES (2,2,2,2);
13+
ALTER TABLE t1 DROP COLUMN counter4;
14+
INSERT INTO t1 (counter1, counter2, counter3)
15+
VALUES (3,3,3);
16+
ALTER TABLE t1 ADD COLUMN counter4 integer;
17+
INSERT INTO t1 (counter1, counter2, counter3, counter4)
18+
VALUES (4,4,4,4);
19+
drop procedure query_select_repeat;
20+
DROP TABLE t1;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
--source include/have_ndb.inc
2+
3+
# Select which engine to use(for easy comparison)
4+
let $engine = NDB;
5+
#let $engine = InnoDB;
6+
7+
# Choose if table have primary key or not
8+
# NOTE! Without pk, the table get a hidden primary key
9+
# which is a "bigint auto increment" when using engine=ndb
10+
let $table_pk = ;
11+
#let $table_pk = ,PRIMARY KEY(counter1);
12+
13+
--replace_result $engine engine
14+
eval CREATE TABLE t1 (
15+
counter1 integer,
16+
counter2 integer,
17+
counter3 integer,
18+
counter4 integer
19+
$table_pk
20+
) ENGINE=$engine;
21+
22+
#
23+
# Create a second connection to the same mysqld
24+
# to use for running the concurrent select queries
25+
#
26+
connect (j1,localhost,root,,test);
27+
connection j1;
28+
29+
#
30+
# Create procedure performing select queries
31+
# repeatedly for specified number of seconds
32+
#
33+
--disable_query_log
34+
delimiter %;
35+
create procedure query_select_repeat (seconds int)
36+
begin
37+
set @x=time_to_sec(current_time()) + seconds;
38+
repeat
39+
select counter1,counter2,counter3 from t1;
40+
until @x <= time_to_sec(current_time())
41+
end repeat;
42+
end%
43+
44+
delimiter ;%
45+
--enable_query_log
46+
47+
#
48+
# Start the select queries to run repeatedly for a couple of seconds
49+
#
50+
send call query_select_repeat(5);
51+
52+
#
53+
# Switch back to default connection and run the ALTERs
54+
# and INSERTs which locks up with the repeated SELECTs
55+
#
56+
connection default;
57+
INSERT INTO t1 (counter1, counter2, counter3, counter4)
58+
VALUES (1,1,1,1);
59+
INSERT INTO t1 (counter1, counter2, counter3, counter4)
60+
VALUES (2,2,2,2);
61+
ALTER TABLE t1 DROP COLUMN counter4;
62+
INSERT INTO t1 (counter1, counter2, counter3)
63+
VALUES (3,3,3);
64+
ALTER TABLE t1 ADD COLUMN counter4 integer;
65+
INSERT INTO t1 (counter1, counter2, counter3, counter4)
66+
VALUES (4,4,4,4);
67+
68+
#
69+
# Wait for the read queries to complete
70+
#
71+
connection j1;
72+
--disable_query_log
73+
--disable_result_log
74+
reap;
75+
--enable_result_log
76+
--enable_query_log
77+
78+
drop procedure query_select_repeat;
79+
80+
#
81+
# Switch back to default connection and clean up
82+
#
83+
connection default;
84+
DROP TABLE t1;

sql/ha_ndbcluster.cc

+27-15
Original file line numberDiff line numberDiff line change
@@ -3556,6 +3556,18 @@ ha_ndbcluster::scan_handle_lock_tuple(NdbScanOperation *scanOp,
35563556
DBUG_RETURN(0);
35573557
}
35583558

3559+
/*
3560+
Some MySQL table locks are mapped to Ndb internal exclusive
3561+
row locks to achieve part of the table locking semantics. If rows are
3562+
not exclusively locked a new batch of rows need to be fetched.
3563+
*/
3564+
static bool table_lock_not_mapped_to_row_lock(enum thr_lock_type lock_type)
3565+
{
3566+
return
3567+
(lock_type < TL_READ_NO_INSERT &&
3568+
lock_type != TL_READ_WITH_SHARED_LOCKS);
3569+
}
3570+
35593571
inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
35603572
{
35613573
DBUG_ENTER("fetch_next");
@@ -3567,8 +3579,7 @@ inline int ha_ndbcluster::fetch_next(NdbScanOperation* cursor)
35673579
if ((error= scan_handle_lock_tuple(cursor, trans)) != 0)
35683580
DBUG_RETURN(error);
35693581

3570-
bool contact_ndb= m_lock.type < TL_WRITE_ALLOW_WRITE &&
3571-
m_lock.type != TL_READ_WITH_SHARED_LOCKS;
3582+
bool contact_ndb= table_lock_not_mapped_to_row_lock(m_lock.type);
35723583
do {
35733584
DBUG_PRINT("info", ("Call nextResult, contact_ndb: %d", contact_ndb));
35743585
/*
@@ -4352,6 +4363,7 @@ int ha_ndbcluster::full_table_scan(const KEY* key_info,
43524363
const key_range *end_key,
43534364
uchar *buf)
43544365
{
4366+
THD *thd= table->in_use;
43554367
int error;
43564368
NdbTransaction *trans= m_thd_ndb->trans;
43574369
part_id_range part_spec;
@@ -4403,7 +4415,14 @@ int ha_ndbcluster::full_table_scan(const KEY* key_info,
44034415
if (unlikely(!(trans= start_transaction(error))))
44044416
DBUG_RETURN(error);
44054417

4406-
const NdbOperation::LockMode lm = get_ndb_lock_mode(m_lock.type);
4418+
/*
4419+
If the scan is part of an ALTER TABLE we need exclusive locks on rows
4420+
to block parallel updates from other connections to Ndb.
4421+
*/
4422+
const NdbOperation::LockMode lm =
4423+
(thd_sql_command(thd) == SQLCOM_ALTER_TABLE) ?
4424+
NdbOperation::LM_Exclusive
4425+
: get_ndb_lock_mode(m_lock.type);
44074426
NdbScanOperation::ScanOptions options;
44084427
options.optionsPresent = (NdbScanOperation::ScanOptions::SO_SCANFLAGS |
44094428
NdbScanOperation::ScanOptions::SO_PARALLEL);
@@ -8228,22 +8247,15 @@ THR_LOCK_DATA **ha_ndbcluster::store_lock(THD *thd,
82288247
/* In queries of type INSERT INTO t1 SELECT ... FROM t2 ...
82298248
MySQL would use the lock TL_READ_NO_INSERT on t2, and that
82308249
would conflict with TL_WRITE_ALLOW_WRITE, blocking all inserts
8231-
to t2. Convert the lock to a normal read lock to allow
8250+
to t2. If table is not explicitly locked or part of an ALTER TABLE
8251+
then convert the lock to a normal read lock to allow
82328252
concurrent inserts to t2. */
82338253

8234-
if (lock_type == TL_READ_NO_INSERT && !thd->in_lock_tables)
8254+
if (lock_type == TL_READ_NO_INSERT &&
8255+
!thd->in_lock_tables &&
8256+
sql_command != SQLCOM_ALTER_TABLE)
82358257
lock_type= TL_READ;
82368258

8237-
/**
8238-
* We need locks on source table when
8239-
* doing offline alter...
8240-
* In 5.1 this worked due to TL_WRITE_ALLOW_READ...
8241-
* but that has been removed in 5.5
8242-
* I simply add this to get it...
8243-
*/
8244-
if (sql_command == SQLCOM_ALTER_TABLE)
8245-
lock_type = TL_WRITE;
8246-
82478259
m_lock.type=lock_type;
82488260
}
82498261
*to++= &m_lock;

0 commit comments

Comments
 (0)