Skip to content

Commit 1dfd030

Browse files
author
Anibal Pinto
committed
BUG#25232042: ASSERT "!CONTAINS_GTID(GTID)" RPL_GTID_OWNED.CC:94 OWNED_GTIDS::ADD_GTID_OWNER
When a existing GTID_NEXT transaction gets a conflicting GTID generated by server, the Group Replication plugin will generate a assert due having two transactions with same gtid. On Group Replication the gtid is generated after conflict detection, later than normal replication. Some conditions were relaxed to allow only be called when commit is done and add message to alert when gtid is already used. The check for macro HAVE_REPLICATION was removed due to being now correctly defined on cmake.
1 parent ee1fcca commit 1dfd030

File tree

9 files changed

+240
-60
lines changed

9 files changed

+240
-60
lines changed

rapid/plugin/group_replication/src/certifier.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,27 @@ rpl_gno Certifier::certify(Gtid_set *snapshot_version,
776776
}
777777
else
778778
{
779+
/*
780+
Check if it is an already used GTID
781+
*/
782+
rpl_sidno sidno_for_group_gtid_sid_map= gle->get_sidno(group_gtid_sid_map);
783+
if (sidno_for_group_gtid_sid_map < 1)
784+
{
785+
log_message(MY_ERROR_LEVEL,
786+
"Error fetching transaction sidno after transaction"
787+
" being positively certified"); /* purecov: inspected */
788+
goto end; /* purecov: inspected */
789+
}
790+
if (group_gtid_executed->contains_gtid(sidno_for_group_gtid_sid_map, gle->get_gno()))
791+
{
792+
char buf[rpl_sid::TEXT_LENGTH + 1];
793+
gle->get_sid()->to_string(buf);
794+
795+
log_message(MY_ERROR_LEVEL,
796+
"The requested GTID '%s:%lld' was already used, the transaction will rollback"
797+
, buf, gle->get_gno());
798+
goto end;
799+
}
779800
/*
780801
Add received transaction GTID to transaction snapshot version.
781802
*/
@@ -787,6 +808,7 @@ rpl_gno Certifier::certify(Gtid_set *snapshot_version,
787808
" being positively certified"); /* purecov: inspected */
788809
goto end; /* purecov: inspected */
789810
}
811+
790812
if (snapshot_version->ensure_sidno(sidno) != RETURN_STATUS_OK)
791813
{
792814
log_message(MY_ERROR_LEVEL,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
include/group_replication.inc
2+
Warnings:
3+
Note #### Sending passwords in plain text without SSL/TLS is extremely insecure.
4+
Note #### Storing MySQL user name or password information in the master info repository is not secure and is therefore not recommended. Please consider using the USER and PASSWORD connection options for START SLAVE; see the 'START SLAVE Syntax' in the MySQL Manual for more information.
5+
[connection server1]
6+
[connection server2]
7+
set sql_log_bin=0;
8+
call mtr.add_suppression("The requested GTID .* was already used, the transaction will rollback");
9+
set sql_log_bin=1;
10+
11+
#############################################################
12+
# 1. Establish another connection to server, to execute
13+
# transactions in parallel.
14+
[connection server1]
15+
set sql_log_bin=0;
16+
call mtr.add_suppression("The requested GTID .* was already used, the transaction will rollback");
17+
set sql_log_bin=1;
18+
CREATE TABLE t1(a INT PRIMARY KEY);
19+
include/rpl_sync.inc
20+
21+
#############################################################
22+
# 2. Create a table and set a manual gtid_next equal to next
23+
# automatic gtid. Start a transaction.
24+
include/assert.inc [GTID_EXECUTED should contain three GTIDs.]
25+
set GTID_NEXT='c3b5927f-2a51-11e7-94b3-0010e0734796:4';
26+
BEGIN;
27+
INSERT INTO t1 VALUES (1);
28+
29+
#############################################################
30+
# 3. Using the other connection execute a transaction.
31+
[connection server_1]
32+
INSERT INTO t1 VALUES (2);
33+
34+
#############################################################
35+
# 4. Connect to first session and commit pending transaction.
36+
# Server will rollback it due to already used gtid.
37+
[connection server1]
38+
COMMIT;
39+
ERROR HY000: Plugin instructed the server to rollback the current transaction.
40+
set GTID_NEXT='automatic';
41+
include/assert_grep.inc [The GR plug-in reported an error stating that a transaction will rollback]
42+
43+
#############################################################
44+
# 5. Clean up.
45+
DROP TABLE t1;
46+
include/group_replication_end.inc
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--log_error=$MYSQLTEST_VARDIR/tmp/gr_gtid_next_conflict.1.err
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
###############################################################################
2+
#
3+
# This test validate that if we have two sessions and one set gtid_next to the
4+
# next automatic gtid, if both session execute a transaction in parallel one
5+
# of the transactions will rollback.
6+
#
7+
# Test:
8+
# 0. Test requires two servers.
9+
# 1. Establish another connection to server, to execute transactions in
10+
# parallel.
11+
# 2. Create a table and set a manual gtid_next equal to next automatic gtid.
12+
# Start a transaction.
13+
# 3. Using the other connection execute a transaction.
14+
# 4. Connect to first session and commit pending transaction. Server will
15+
# rollback it due to already used gtid.
16+
# 5. Clean up.
17+
#
18+
###############################################################################
19+
20+
--let $group_replication_group_name= c3b5927f-2a51-11e7-94b3-0010e0734796
21+
--source ../inc/have_group_replication_plugin.inc
22+
--source ../inc/group_replication.inc
23+
24+
--let $rpl_connection_name= server2
25+
--source include/rpl_connection.inc
26+
set sql_log_bin=0;
27+
call mtr.add_suppression("The requested GTID .* was already used, the transaction will rollback");
28+
set sql_log_bin=1;
29+
30+
--echo
31+
--echo #############################################################
32+
--echo # 1. Establish another connection to server, to execute
33+
--echo # transactions in parallel.
34+
35+
--let $rpl_connection_name= server1
36+
--source include/rpl_connection.inc
37+
38+
set sql_log_bin=0;
39+
call mtr.add_suppression("The requested GTID .* was already used, the transaction will rollback");
40+
set sql_log_bin=1;
41+
42+
CREATE TABLE t1(a INT PRIMARY KEY);
43+
--source include/rpl_sync.inc
44+
45+
--echo
46+
--echo #############################################################
47+
--echo # 2. Create a table and set a manual gtid_next equal to next
48+
--echo # automatic gtid. Start a transaction.
49+
50+
--let $assert_cond= "[SELECT @@GLOBAL.GTID_EXECUTED]" = "c3b5927f-2a51-11e7-94b3-0010e0734796:1-3"
51+
--let $assert_text= GTID_EXECUTED should contain three GTIDs.
52+
--source include/assert.inc
53+
54+
set GTID_NEXT='c3b5927f-2a51-11e7-94b3-0010e0734796:4';
55+
BEGIN;
56+
INSERT INTO t1 VALUES (1);
57+
58+
--echo
59+
--echo #############################################################
60+
--echo # 3. Using the other connection execute a transaction.
61+
62+
--let $rpl_connection_name= server_1
63+
--source include/rpl_connection.inc
64+
INSERT INTO t1 VALUES (2);
65+
66+
--echo
67+
--echo #############################################################
68+
--echo # 4. Connect to first session and commit pending transaction.
69+
--echo # Server will rollback it due to already used gtid.
70+
71+
--let $rpl_connection_name= server1
72+
--source include/rpl_connection.inc
73+
74+
--let $wait_condition= SELECT @@GLOBAL.GTID_EXECUTED = 'c3b5927f-2a51-11e7-94b3-0010e0734796:1-4'
75+
--source include/wait_condition.inc
76+
77+
--error ER_TRANSACTION_ROLLBACK_DURING_COMMIT
78+
COMMIT;
79+
set GTID_NEXT='automatic';
80+
81+
--let $assert_file= $MYSQLTEST_VARDIR/tmp/gr_gtid_next_conflict.1.err
82+
--let $assert_count= 1
83+
--let $assert_select= .* was already used, the transaction will rollback
84+
--let $assert_text= The GR plug-in reported an error stating that a transaction will rollback
85+
--source include/assert_grep.inc
86+
87+
--echo
88+
--echo #############################################################
89+
--echo # 5. Clean up.
90+
DROP TABLE t1;
91+
--source ../inc/group_replication_end.inc

sql/rpl_group_replication.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@
1313
along with this program; if not, write to the Free Software Foundation,
1414
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
1515

16-
#ifndef HAVE_REPLICATION
17-
#define HAVE_REPLICATION
18-
#endif
19-
2016
#include "rpl_group_replication.h"
2117
#include "rpl_channel_service_interface.h"
2218
#include "rpl_info_factory.h"
@@ -142,6 +138,7 @@ Group_replication_handler* group_replication_handler= NULL;
142138
/*
143139
Group Replication plugin handler function accessors.
144140
*/
141+
#ifdef HAVE_REPLICATION
145142
int group_replication_init(const char* plugin_name)
146143
{
147144
if (initialize_channel_service_interface())
@@ -165,6 +162,7 @@ int group_replication_init(const char* plugin_name)
165162

166163
return 1;
167164
}
165+
#endif
168166

169167
int group_replication_cleanup()
170168
{
@@ -323,6 +321,7 @@ unsigned int get_group_replication_members_number_info()
323321
Server methods exported to plugin through
324322
include/mysql/group_replication_priv.h
325323
*/
324+
#ifdef HAVE_REPLICATION
326325
void get_server_parameters(char **hostname, uint *port, char** uuid,
327326
unsigned int *out_server_version,
328327
st_server_ssl_variables* server_ssl_variables)
@@ -380,6 +379,7 @@ void get_server_parameters(char **hostname, uint *port, char** uuid,
380379

381380
return;
382381
}
382+
#endif
383383

384384
ulong get_server_id()
385385
{

sql/rpl_gtid.h

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,8 +2026,11 @@ struct Gtid_set_or_null
20262026
lock and then degrades it to a read lock again; there will be a
20272027
short period when the lock is not held at all.
20282028
2029-
The internal representation is a dynamic array that maps SIDNO to
2030-
HASH, where each HASH maps GNO to my_thread_id.
2029+
The internal representation is a multi-valued map from GTIDs to
2030+
threads, mapping GTIDs to one or more threads that owns it.
2031+
2032+
In Group Replication multiple threads can own a GTID whereas if GR
2033+
is disabeld there is at most one owner per GTID.
20312034
*/
20322035
class Owned_gtids
20332036
{
@@ -2049,14 +2052,6 @@ class Owned_gtids
20492052
@return RETURN_STATUS_OK or RETURN_STATUS_REPORTED_ERROR.
20502053
*/
20512054
enum_return_status add_gtid_owner(const Gtid &gtid, my_thread_id owner);
2052-
/**
2053-
Returns the owner of the given GTID, or 0 if the GTID is not owned.
2054-
2055-
@param Gtid The Gtid to query.
2056-
@return my_thread_id of the thread that owns the group, or
2057-
0 if the group is not owned.
2058-
*/
2059-
my_thread_id get_owner(const Gtid &gtid) const;
20602055

20612056
/*
20622057
Fill all gtids into the given Gtid_set object. It doesn't clear the given
@@ -2070,8 +2065,9 @@ class Owned_gtids
20702065
nothing.
20712066
20722067
@param gtid The Gtid.
2068+
@param owner thread_id of the owner thread
20732069
*/
2074-
void remove_gtid(const Gtid &gtid);
2070+
void remove_gtid(const Gtid &gtid, const my_thread_id owner);
20752071
/**
20762072
Ensures that this Owned_gtids object can accomodate SIDNOs up to
20772073
the given SIDNO.
@@ -2205,6 +2201,13 @@ class Owned_gtids
22052201
my_free(str);
22062202
#endif
22072203
}
2204+
2205+
/**
2206+
If thd_id==0, returns true when gtid is not owned by any thread.
2207+
If thd_id!=0, returns true when gtid is owned by that thread.
2208+
*/
2209+
bool is_owned_by(const Gtid &gtid, const my_thread_id thd_id) const;
2210+
22082211
private:
22092212
/// Represents one owned group.
22102213
struct Node
@@ -2223,23 +2226,9 @@ class Owned_gtids
22232226
sid_lock->assert_some_lock();
22242227
return sidno_to_hash[sidno - 1];
22252228
}
2226-
/**
2227-
Returns the Node for the given HASH and GNO, or NULL if the GNO
2228-
does not exist in the HASH.
2229-
*/
2230-
Node *get_node(const HASH *hash, rpl_gno gno) const
2231-
{
2232-
sid_lock->assert_some_lock();
2233-
return (Node *)my_hash_search(hash, (const uchar *)&gno, sizeof(rpl_gno));
2234-
}
2235-
/**
2236-
Returns the Node for the given group, or NULL if the group does
2237-
not exist in this Owned_gtids object.
2238-
*/
2239-
Node *get_node(const Gtid &gtid) const
2240-
{ return get_node(get_hash(gtid.sidno), gtid.gno); };
22412229
/// Return true iff this Owned_gtids object contains the given group.
2242-
bool contains_gtid(const Gtid &gtid) const { return get_node(gtid) != NULL; }
2230+
bool contains_gtid(const Gtid &gtid) const;
2231+
22432232
/// Growable array of hashes.
22442233
Prealloced_array<HASH*, 8, true> sidno_to_hash;
22452234

@@ -2412,14 +2401,14 @@ class Gtid_state
24122401
DBUG_RETURN(ret);
24132402
}
24142403
/**
2415-
Returns the owner of the given GTID, or 0 if the group is not owned.
2404+
Returns true if GTID is owned, otherwise returns 0.
24162405
24172406
@param gtid The Gtid to check.
2418-
@return my_thread_id of the thread that owns the group, or
2419-
0 if the group is not owned.
2407+
@return true if some thread owns the group, false if the group is
2408+
not owned
24202409
*/
2421-
my_thread_id get_owner(const Gtid &gtid) const
2422-
{ return owned_gtids.get_owner(gtid); }
2410+
bool is_owned(const Gtid &gtid) const
2411+
{ return !owned_gtids.is_owned_by(gtid, 0); }
24232412
#ifndef MYSQL_CLIENT
24242413
/**
24252414
Acquires ownership of the given GTID, on behalf of the given thread.

sql/rpl_gtid_execution.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,9 @@ bool set_gtid_next(THD *thd, const Gtid_specification &spec)
113113
*/
114114
break;
115115
}
116-
my_thread_id owner= gtid_state->get_owner(spec.gtid);
116+
117117
// GTID not owned by anyone: acquire ownership
118-
if (owner == 0)
118+
if (!gtid_state->is_owned(spec.gtid))
119119
{
120120
// acquire_ownership can't fail
121121
gtid_state->acquire_ownership(thd, spec.gtid);

0 commit comments

Comments
 (0)