Skip to content

Commit 39f4674

Browse files
denis-protivenskyJan Lindström
authored andcommitted
MDEV-24623 Replicate bulk insert as table-level exclusive key
- introduce table key construction function in wsrep service interface - don't add row keys when replicating bulk insert - don't start bulk insert on applier or when transaction is not active - don't start bulk insert on system versioned tables - implement actual bulk insert table-level key replication Reviewed-by: Jan Lindström <jan.lindstrom@mariadb.com>
1 parent a10003b commit 39f4674

File tree

8 files changed

+208
-1
lines changed

8 files changed

+208
-1
lines changed

include/mysql/service_wsrep.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ extern struct wsrep_service_st {
6969
void (*wsrep_thd_self_abort_func)(MYSQL_THD thd);
7070
int (*wsrep_thd_append_key_func)(MYSQL_THD thd, const struct wsrep_key* key,
7171
int n_keys, enum Wsrep_service_key_type);
72+
int (*wsrep_thd_append_table_key_func)(MYSQL_THD thd, const char* db,
73+
const char* table, enum Wsrep_service_key_type);
74+
my_bool (*wsrep_thd_is_local_transaction)(const MYSQL_THD thd);
7275
const char* (*wsrep_thd_client_state_str_func)(const MYSQL_THD thd);
7376
const char* (*wsrep_thd_client_mode_str_func)(const MYSQL_THD thd);
7477
const char* (*wsrep_thd_transaction_state_str_func)(const MYSQL_THD thd);
@@ -121,6 +124,8 @@ extern struct wsrep_service_st {
121124
#define wsrep_thd_is_local(T) wsrep_service->wsrep_thd_is_local_func(T)
122125
#define wsrep_thd_self_abort(T) wsrep_service->wsrep_thd_self_abort_func(T)
123126
#define wsrep_thd_append_key(T,W,N,K) wsrep_service->wsrep_thd_append_key_func(T,W,N,K)
127+
#define wsrep_thd_append_table_key(T,D,B,K) wsrep_service->wsrep_thd_append_table_key_func(T,D,B,K)
128+
#define wsrep_thd_is_local_transaction(T) wsrep_service->wsrep_thd_is_local_transaction_func(T)
124129
#define wsrep_thd_client_state_str(T) wsrep_service->wsrep_thd_client_state_str_func(T)
125130
#define wsrep_thd_client_mode_str(T) wsrep_service->wsrep_thd_client_mode_str_func(T)
126131
#define wsrep_thd_transaction_state_str(T) wsrep_service->wsrep_thd_transaction_state_str_func(T)
@@ -226,6 +231,13 @@ extern "C" int wsrep_thd_append_key(MYSQL_THD thd,
226231
int n_keys,
227232
enum Wsrep_service_key_type);
228233

234+
extern "C" int wsrep_thd_append_table_key(MYSQL_THD thd,
235+
const char* db,
236+
const char* table,
237+
enum Wsrep_service_key_type);
238+
239+
extern "C" my_bool wsrep_thd_is_local_transaction(const MYSQL_THD thd);
240+
229241
extern const char* wsrep_sr_table_name_full;
230242

231243
extern "C" const char* wsrep_get_sr_table_name();
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
connection node_2;
2+
connection node_1;
3+
connection node_1;
4+
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
5+
SET foreign_key_checks = 0;
6+
SET unique_checks = 0;
7+
START TRANSACTION;
8+
connection node_2;
9+
SET foreign_key_checks = 1;
10+
SET unique_checks = 1;
11+
INSERT INTO t1 VALUES (1001);
12+
connection node_1;
13+
COMMIT;
14+
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
15+
DROP TABLE t1;
16+
connection node_1;
17+
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
18+
START TRANSACTION;
19+
connection node_2;
20+
SET foreign_key_checks = 1;
21+
SET unique_checks = 1;
22+
START TRANSACTION;
23+
INSERT INTO t1 VALUES (1001);
24+
connection node_1;
25+
COMMIT;
26+
2
27+
connection node_2;
28+
COMMIT;
29+
ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
30+
DROP TABLE t1;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#
2+
# Test that bulk insert replicates as table-level exclusive key and
3+
# rolls back properly if needed.
4+
#
5+
6+
--source include/galera_cluster.inc
7+
--source include/have_innodb.inc
8+
9+
#
10+
# Make bulk insert BF-abort, but regular insert succeed.
11+
#
12+
13+
--connection node_1
14+
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
15+
16+
# Disable foreign and unique key checks to allow bulk insert.
17+
SET foreign_key_checks = 0;
18+
SET unique_checks = 0;
19+
20+
START TRANSACTION;
21+
22+
--let $count=0
23+
--disable_query_log
24+
while ($count < 1000)
25+
{
26+
--eval INSERT INTO t1 VALUES ($count)
27+
--inc $count
28+
}
29+
--enable_query_log
30+
31+
--connection node_2
32+
33+
# Disable bulk insert.
34+
SET foreign_key_checks = 1;
35+
SET unique_checks = 1;
36+
37+
# Insert a value out of the bulk insert range.
38+
INSERT INTO t1 VALUES (1001);
39+
40+
--connection node_1
41+
--error ER_LOCK_DEADLOCK
42+
COMMIT;
43+
44+
DROP TABLE t1;
45+
46+
#
47+
# Make bulk insert succeed, but regular insert BF-abort.
48+
#
49+
50+
--connection node_1
51+
CREATE TABLE t1 (f1 INTEGER PRIMARY KEY) ENGINE=InnoDB;
52+
53+
--let $before_bulk_keys = `SELECT VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_repl_keys'`
54+
55+
START TRANSACTION;
56+
57+
--let $count=0
58+
--disable_query_log
59+
while ($count < 1000)
60+
{
61+
--eval INSERT INTO t1 VALUES ($count)
62+
--inc $count
63+
}
64+
--enable_query_log
65+
66+
--connection node_2
67+
68+
# Disable bulk insert.
69+
SET foreign_key_checks = 1;
70+
SET unique_checks = 1;
71+
72+
START TRANSACTION;
73+
74+
# Insert a value out of the bulk insert range.
75+
INSERT INTO t1 VALUES (1001);
76+
77+
--connection node_1
78+
COMMIT;
79+
80+
# Expect two keys to be added for bulk insert: DB-level shared key and table-level exclusive key.
81+
--let $bulk_keys_count = `SELECT VARIABLE_VALUE - $before_bulk_keys FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_repl_keys'`
82+
--echo $bulk_keys_count
83+
84+
--connection node_2
85+
--error ER_LOCK_DEADLOCK
86+
COMMIT;
87+
88+
DROP TABLE t1;

sql/service_wsrep.cc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,3 +417,23 @@ extern "C" void wsrep_thd_set_PA_unsafe(THD *thd)
417417
WSREP_DEBUG("session does not have active transaction, can not mark as PA unsafe");
418418
}
419419
}
420+
421+
extern "C" int wsrep_thd_append_table_key(MYSQL_THD thd,
422+
const char* db,
423+
const char* table,
424+
enum Wsrep_service_key_type key_type)
425+
{
426+
wsrep_key_arr_t key_arr = {0, 0};
427+
int ret = wsrep_prepare_keys_for_isolation(thd, db, table, NULL, &key_arr);
428+
ret = ret || wsrep_thd_append_key(thd, key_arr.keys,
429+
(int)key_arr.keys_len, key_type);
430+
wsrep_keys_free(&key_arr);
431+
return ret;
432+
}
433+
434+
extern "C" my_bool wsrep_thd_is_local_transaction(const THD *thd)
435+
{
436+
return (wsrep_thd_is_local(thd) &&
437+
thd->wsrep_cs().transaction().active());
438+
}
439+

sql/sql_plugin_services.inl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ static struct wsrep_service_st wsrep_handler = {
162162
wsrep_thd_is_local,
163163
wsrep_thd_self_abort,
164164
wsrep_thd_append_key,
165+
wsrep_thd_append_table_key,
166+
wsrep_thd_is_local_transaction,
165167
wsrep_thd_client_state_str,
166168
wsrep_thd_client_mode_str,
167169
wsrep_thd_transaction_state_str,

sql/wsrep_dummy.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ void wsrep_thd_self_abort(THD *)
101101
int wsrep_thd_append_key(THD *, const struct wsrep_key*, int, enum Wsrep_service_key_type)
102102
{ return 0; }
103103

104+
int wsrep_thd_append_table_key(THD *, const char*, const char*, enum Wsrep_service_key_type)
105+
{ return 0; }
106+
107+
my_bool wsrep_thd_is_local_transaction(const THD*)
108+
{ return 0; }
109+
104110
const char* wsrep_thd_client_state_str(const THD*)
105111
{ return 0; }
106112

storage/innobase/handler/ha_innodb.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8039,6 +8039,7 @@ ha_innobase::write_row(
80398039

80408040
#ifdef WITH_WSREP
80418041
if (!error_result && trx->is_wsrep()
8042+
&& !trx->is_bulk_insert()
80428043
&& wsrep_thd_is_local(m_user_thd)
80438044
&& !wsrep_thd_ignore_table(m_user_thd)
80448045
&& !wsrep_consistency_check(m_user_thd)
@@ -10142,6 +10143,8 @@ wsrep_append_key(
1014210143
(shared, exclusive, semi...) */
1014310144
)
1014410145
{
10146+
ut_ad(!trx->is_bulk_insert());
10147+
1014510148
DBUG_ENTER("wsrep_append_key");
1014610149
DBUG_PRINT("enter",
1014710150
("thd: %lu trx: %lld", thd_get_thread_id(thd),

storage/innobase/row/row0ins.cc

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2573,6 +2573,42 @@ but GCC 4.8.5 does not support pop_options. */
25732573
# pragma GCC optimize ("O0")
25742574
#endif
25752575

2576+
#ifdef WITH_WSREP
2577+
/** Start bulk insert operation for Galera by appending
2578+
table-level exclusive key for bulk insert.
2579+
@param trx transaction
2580+
@param index index
2581+
@retval false on success
2582+
@retval true on failure */
2583+
ATTRIBUTE_COLD static bool row_ins_wsrep_start_bulk(trx_t *trx, const dict_index_t &index)
2584+
{
2585+
char db_buf[NAME_LEN + 1];
2586+
char tbl_buf[NAME_LEN + 1];
2587+
ulint db_buf_len, tbl_buf_len;
2588+
2589+
if (!index.table->parse_name(db_buf, tbl_buf, &db_buf_len, &tbl_buf_len))
2590+
{
2591+
WSREP_ERROR("Parse_name for bulk insert failed: %s",
2592+
wsrep_thd_query(trx->mysql_thd));
2593+
trx->error_state = DB_ROLLBACK;
2594+
return true;
2595+
}
2596+
2597+
/* Append table-level exclusive key for bulk insert. */
2598+
const int rcode = wsrep_thd_append_table_key(trx->mysql_thd, db_buf,
2599+
tbl_buf, WSREP_SERVICE_KEY_EXCLUSIVE);
2600+
if (rcode)
2601+
{
2602+
WSREP_ERROR("Appending table key for bulk insert failed: %s, %d",
2603+
wsrep_thd_query(trx->mysql_thd), rcode);
2604+
trx->error_state = DB_ROLLBACK;
2605+
return true;
2606+
}
2607+
2608+
return false;
2609+
}
2610+
#endif
2611+
25762612
/***************************************************************//**
25772613
Tries to insert an entry into a clustered index, ignoring foreign key
25782614
constraints. If a record with the same unique key is found, the other
@@ -2702,7 +2738,7 @@ row_ins_clust_index_entry_low(
27022738
&& !index->table->skip_alter_undo
27032739
&& !index->table->n_rec_locks
27042740
&& !index->table->is_active_ddl()
2705-
&& !trx->is_wsrep() /* FIXME: MDEV-24623 */
2741+
&& !index->table->versioned()
27062742
&& !thd_is_slave(trx->mysql_thd) /* FIXME: MDEV-24622 */) {
27072743
DEBUG_SYNC_C("empty_root_page_insert");
27082744

@@ -2718,6 +2754,16 @@ row_ins_clust_index_entry_low(
27182754
goto skip_bulk_insert;
27192755
}
27202756

2757+
#ifdef WITH_WSREP
2758+
if (trx->is_wsrep())
2759+
{
2760+
if (!wsrep_thd_is_local_transaction(trx->mysql_thd))
2761+
goto skip_bulk_insert;
2762+
if (row_ins_wsrep_start_bulk(trx, *index))
2763+
goto err_exit;
2764+
}
2765+
#endif /* WITH_WSREP */
2766+
27212767
#ifdef BTR_CUR_HASH_ADAPT
27222768
if (btr_search_enabled) {
27232769
btr_search_x_lock_all();

0 commit comments

Comments
 (0)