Skip to content

Commit 02112f5

Browse files
author
Sven Sandberg
committed
WL#7083 step 6.3. GTID-violations: Introduce global counters of GTID-violating transactions.
@rpl_gtid.h - Introduce global counters, and wrappers to set and get them. - Update comment to account changes in rpl_gtid_state.cc @sql_class.h - Introduce per-thread flag to keep track of whether the current transaction violates GTID consistency. @sql_class.cc - Initialize per-thread flag in THD constructor. @binlog.cc - When starting to execute a GTID-violating transaction, increase global counter and set per-thread flag. @rpl_gtid_state.cc - update_gtids_impl: When ending a GTID-violating transaction, decrease global counter and clear per-thread flag. - update_on_rollback: Ensure counters are decreased on rollback even if nothing is owned.
1 parent c7463f7 commit 02112f5

File tree

7 files changed

+289
-21
lines changed

7 files changed

+289
-21
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
call mtr.add_suppression("Statement violates GTID consistency:");
2+
call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
3+
CREATE TABLE t1 (c1 INT) Engine=InnoDB;
4+
CREATE TABLE t2 (c1 INT) Engine=MyISAM;
5+
SET GLOBAL enforce_gtid_consistency=WARN;
6+
BEGIN;
7+
CREATE TEMPORARY TABLE temp1 (a INT);
8+
Warnings:
9+
Warning 1787 Statement violates GTID consistency: CREATE TEMPORARY TABLE and DROP TEMPORARY TABLE can only be executed outside transactional context.
10+
DROP TEMPORARY TABLE temp1;
11+
Warnings:
12+
Warning 1787 Statement violates GTID consistency: CREATE TEMPORARY TABLE and DROP TEMPORARY TABLE can only be executed outside transactional context.
13+
INSERT INTO t1 VALUES (1);
14+
INSERT INTO t2 VALUES (1);
15+
Warnings:
16+
Warning 1785 Statement violates GTID consistency: Updates to non-transactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables.
17+
COMMIT;
18+
BEGIN;
19+
INSERT INTO t1 VALUES (1);
20+
INSERT INTO t2 VALUES (1);
21+
Warnings:
22+
Warning 1785 Statement violates GTID consistency: Updates to non-transactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables.
23+
INSERT INTO t1 VALUES (1);
24+
COMMIT;
25+
BEGIN;
26+
UPDATE t1, t2 SET t1.c1=2, t2.c1=1000;
27+
Warnings:
28+
Warning 1785 Statement violates GTID consistency: Updates to non-transactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables.
29+
Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. Statement accesses nontransactional table as well as transactional or temporary table, and writes to any of them.
30+
INSERT INTO t1 VALUES (1);
31+
COMMIT;
32+
DROP TABLE t1;
33+
DROP TABLE t2;
34+
SET GLOBAL enforce_gtid_consistency=SAVED_LEVEL;
35+
RESET MASTER;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#
2+
# BUG#20414559: WL7083: ER1787, ER1785 NOT GENERATED ALWAYS WHEN ENFORCE_GTID_CONSISTENCY=WARN
3+
# WL#7083: GTIDs: set gtid_mode=ON online
4+
#
5+
# Verify that when enforce_gtid_consistency=WARN, and multiple
6+
# statements in the same transaction violate GTID-consistency, there
7+
# is one warning for each such statement.
8+
#
9+
--source include/have_binlog_format_statement.inc
10+
--source include/not_gtid_enabled.inc
11+
--source include/have_myisam.inc
12+
--source include/have_innodb.inc
13+
14+
#
15+
# BUG#20414559
16+
#
17+
18+
call mtr.add_suppression("Statement violates GTID consistency:");
19+
call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
20+
21+
--let $saved_enforce_gtid_consistency=`SELECT @@global.enforce_gtid_consistency`
22+
23+
CREATE TABLE t1 (c1 INT) Engine=InnoDB;
24+
CREATE TABLE t2 (c1 INT) Engine=MyISAM;
25+
26+
SET GLOBAL enforce_gtid_consistency=WARN;
27+
BEGIN;
28+
# warning is emitted
29+
CREATE TEMPORARY TABLE temp1 (a INT);
30+
# warning is emitted
31+
DROP TEMPORARY TABLE temp1;
32+
INSERT INTO t1 VALUES (1);
33+
# warning is emitted
34+
INSERT INTO t2 VALUES (1);
35+
COMMIT;
36+
37+
BEGIN;
38+
INSERT INTO t1 VALUES (1);
39+
# warning is emitted
40+
INSERT INTO t2 VALUES (1);
41+
INSERT INTO t1 VALUES (1);
42+
COMMIT;
43+
44+
BEGIN;
45+
# warning is emitted
46+
UPDATE t1, t2 SET t1.c1=2, t2.c1=1000;
47+
INSERT INTO t1 VALUES (1);
48+
COMMIT;
49+
50+
DROP TABLE t1;
51+
DROP TABLE t2;
52+
--replace_result $saved_enforce_gtid_consistency SAVED_LEVEL
53+
--eval SET GLOBAL enforce_gtid_consistency=$saved_enforce_gtid_consistency
54+
RESET MASTER;

sql/binlog.cc

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9491,7 +9491,6 @@ static bool handle_gtid_consistency_violation(THD *thd, int error_code)
94919491
enum_gtid_consistency_mode gtid_consistency_mode=
94929492
get_gtid_consistency_mode();
94939493
enum_gtid_mode gtid_mode= get_gtid_mode(GTID_MODE_LOCK_SID);
9494-
global_sid_lock->unlock();
94959494

94969495
DBUG_PRINT("info", ("gtid_next.type=%d gtid_mode=%s "
94979496
"gtid_consistency_mode=%d error=%d query=%s",
@@ -9514,18 +9513,59 @@ static bool handle_gtid_consistency_violation(THD *thd, int error_code)
95149513
gtid_next_type == GTID_GROUP ||
95159514
gtid_consistency_mode == GTID_CONSISTENCY_MODE_ON)
95169515
{
9516+
global_sid_lock->unlock();
95179517
my_error(error_code, MYF(0));
95189518
DBUG_RETURN(false);
95199519
}
9520-
else if (gtid_consistency_mode == GTID_CONSISTENCY_MODE_WARN)
9520+
else
95219521
{
9522-
// Need to print to log so that replication admin knows when users
9523-
// have adjusted their workloads.
9524-
sql_print_warning("%s", ER(error_code));
9525-
// Need to print to client so that users can adjust their workload.
9526-
push_warning(thd, Sql_condition::SL_WARNING, error_code, ER(error_code));
9522+
/*
9523+
If we are not generating an error, we must increase the counter
9524+
of GTID-violating transactions. This will prevent a concurrent
9525+
client from executing a SET GTID_MODE or SET
9526+
ENFORCE_GTID_CONSISTENCY statement that would be incompatible
9527+
with this transaction.
9528+
9529+
If the transaction had already been accounted as a gtid violating
9530+
transaction, then don't increment the counters, just issue the
9531+
warning below. This prevents calling
9532+
begin_automatic_gtid_violating_transaction or
9533+
begin_anonymous_gtid_violating_transaction multiple times for the
9534+
same transaction, which would make the counter go out of sync.
9535+
*/
9536+
if (!thd->has_gtid_consistency_violation)
9537+
{
9538+
if (gtid_next_type == AUTOMATIC_GROUP)
9539+
gtid_state->begin_automatic_gtid_violating_transaction();
9540+
else
9541+
{
9542+
DBUG_ASSERT(gtid_next_type == ANONYMOUS_GROUP);
9543+
gtid_state->begin_anonymous_gtid_violating_transaction();
9544+
}
9545+
9546+
/*
9547+
If a transaction generates multiple GTID violation conditions,
9548+
it must still only update the counters once. Hence we use
9549+
this per-thread flag to keep track of whether the thread has a
9550+
consistency or not. This function must only be called if the
9551+
transaction does not already have a GTID violation.
9552+
*/
9553+
thd->has_gtid_consistency_violation= true;
9554+
}
9555+
9556+
global_sid_lock->unlock();
9557+
9558+
// Generate warning if ENFORCE_GTID_CONSISTENCY = WARN.
9559+
if (gtid_consistency_mode == GTID_CONSISTENCY_MODE_WARN)
9560+
{
9561+
// Need to print to log so that replication admin knows when users
9562+
// have adjusted their workloads.
9563+
sql_print_warning("%s", ER(error_code));
9564+
// Need to print to client so that users can adjust their workload.
9565+
push_warning(thd, Sql_condition::SL_WARNING, error_code, ER(error_code));
9566+
}
9567+
DBUG_RETURN(true);
95279568
}
9528-
DBUG_RETURN(true);
95299569
}
95309570

95319571

sql/rpl_gtid.h

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,6 +2460,96 @@ class Gtid_state
24602460
return anonymous_gtid_count.atomic_get();
24612461
}
24622462

2463+
/**
2464+
Increase the global counter when starting a GTID-violating
2465+
transaction having GTID_NEXT=AUTOMATIC.
2466+
*/
2467+
void begin_automatic_gtid_violating_transaction()
2468+
{
2469+
DBUG_ENTER("Gtid_state::begin_automatic_gtid_violating_transaction");
2470+
DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) <= GTID_MODE_OFF_PERMISSIVE);
2471+
DBUG_ASSERT(get_gtid_consistency_mode() != GTID_CONSISTENCY_MODE_ON);
2472+
#ifndef DBUG_OFF
2473+
int32 old_value=
2474+
#endif
2475+
automatic_gtid_violation_count.atomic_add(1);
2476+
DBUG_PRINT("info", ("ongoing_automatic_gtid_violating_transaction_count increased to %d", old_value + 1));
2477+
DBUG_ASSERT(old_value >= 0);
2478+
DBUG_VOID_RETURN;
2479+
}
2480+
2481+
/**
2482+
Decrease the global counter when ending a GTID-violating
2483+
transaction having GTID_NEXT=AUTOMATIC.
2484+
*/
2485+
void end_automatic_gtid_violating_transaction()
2486+
{
2487+
DBUG_ENTER("Gtid_state::end_automatic_gtid_violating_transaction");
2488+
DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) <= GTID_MODE_OFF_PERMISSIVE);
2489+
DBUG_ASSERT(get_gtid_consistency_mode() != GTID_CONSISTENCY_MODE_ON);
2490+
#ifndef DBUG_OFF
2491+
int32 old_value=
2492+
#endif
2493+
automatic_gtid_violation_count.atomic_add(-1);
2494+
DBUG_PRINT("info", ("ongoing_automatic_gtid_violating_transaction_count decreased to %d", old_value - 1));
2495+
DBUG_ASSERT(old_value >= 1);
2496+
DBUG_VOID_RETURN;
2497+
}
2498+
2499+
/**
2500+
Return the number of ongoing GTID-violating transactions having
2501+
GTID_NEXT=AUTOMATIC.
2502+
*/
2503+
int32 get_automatic_gtid_violating_transaction_count()
2504+
{
2505+
return automatic_gtid_violation_count.atomic_get();
2506+
}
2507+
2508+
/**
2509+
Increase the global counter when starting a GTID-violating
2510+
transaction having GTID_NEXT=ANONYMOUS.
2511+
*/
2512+
void begin_anonymous_gtid_violating_transaction()
2513+
{
2514+
DBUG_ENTER("Gtid_state::begin_anonymous_gtid_violating_transaction");
2515+
DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) != GTID_MODE_ON);
2516+
DBUG_ASSERT(get_gtid_consistency_mode() != GTID_CONSISTENCY_MODE_ON);
2517+
#ifndef DBUG_OFF
2518+
int32 old_value=
2519+
#endif
2520+
anonymous_gtid_violation_count.atomic_add(1);
2521+
DBUG_PRINT("info", ("ongoing_anonymous_gtid_violating_transaction_count increased to %d", old_value + 1));
2522+
DBUG_ASSERT(old_value >= 0);
2523+
DBUG_VOID_RETURN;
2524+
}
2525+
2526+
/**
2527+
Decrease the global counter when ending a GTID-violating
2528+
transaction having GTID_NEXT=ANONYMOUS.
2529+
*/
2530+
void end_anonymous_gtid_violating_transaction()
2531+
{
2532+
DBUG_ENTER("Gtid_state::end_anonymous_gtid_violating_transaction");
2533+
DBUG_ASSERT(get_gtid_mode(GTID_MODE_LOCK_SID) != GTID_MODE_ON);
2534+
DBUG_ASSERT(get_gtid_consistency_mode() != GTID_CONSISTENCY_MODE_ON);
2535+
#ifndef DBUG_OFF
2536+
int32 old_value=
2537+
#endif
2538+
anonymous_gtid_violation_count.atomic_add(-1);
2539+
DBUG_PRINT("info", ("ongoing_anonymous_gtid_violating_transaction_count decreased to %d", old_value - 1));
2540+
DBUG_ASSERT(old_value >= 1);
2541+
DBUG_VOID_RETURN;
2542+
}
2543+
2544+
/**
2545+
Return the number of ongoing GTID-violating transactions having
2546+
GTID_NEXT=AUTOMATIC.
2547+
*/
2548+
int32 get_anonymous_gtid_violating_transaction_count()
2549+
{
2550+
return anonymous_gtid_violation_count.atomic_get();
2551+
}
2552+
24632553
#endif // ifndef MYSQL_CLIENT
24642554
private:
24652555
/**
@@ -2754,10 +2844,13 @@ class Gtid_state
27542844
27552845
This will:
27562846
2757-
- release ownership of all GTIDs owned by the THD;
2758-
- add all GTIDs in the Group_cache to executed_gtids is only done if the
2759-
is_commit flag is set.
2760-
- send a broadcast on the condition variable for every sidno for
2847+
- Release ownership of all GTIDs owned by the THD. This removes
2848+
the GTID from Owned_gtids and clears the ownership status in the
2849+
THD object.
2850+
- Add the owned GTID to executed_gtids if the is_commit flag is
2851+
set.
2852+
- Decrease counters of GTID-violating transactions.
2853+
- Send a broadcast on the condition variable for every sidno for
27612854
which we released ownership.
27622855
27632856
@param[in] thd - Thread for which owned groups are updated.
@@ -2795,6 +2888,10 @@ class Gtid_state
27952888

27962889
/// The number of anonymous transactions owned by any client.
27972890
Atomic_int32 anonymous_gtid_count;
2891+
/// The number of GTID-violating transactions that use GTID_NEXT=AUTOMATIC.
2892+
Atomic_int32 automatic_gtid_violation_count;
2893+
/// The number of GTID-violating transactions that use GTID_NEXT=AUTOMATIC.
2894+
Atomic_int32 anonymous_gtid_violation_count;
27982895

27992896
/// Used by unit tests that need to access private members.
28002897
#ifdef FRIEND_OF_GTID_STATE

sql/rpl_gtid_state.cc

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,42 @@ void Gtid_state::update_on_rollback(THD *thd)
158158
{
159159
DBUG_ENTER("Gtid_state::update_on_rollback");
160160

161-
if (!thd->owned_gtid.is_empty())
161+
/*
162+
If we don't own anything, there is nothing to do, so we do an
163+
early return. Except if there is a GTID consistency violation;
164+
then we need to decrement the counter, so then we go ahead and
165+
call update_gtids_impl.
166+
*/
167+
if (thd->owned_gtid.is_empty() && !thd->has_gtid_consistency_violation)
162168
{
163-
if (thd->skip_gtid_rollback)
164-
{
165-
DBUG_PRINT("info",("skipping the gtid_rollback"));
166-
DBUG_VOID_RETURN;
167-
}
169+
DBUG_PRINT("info", ("skipping gtid rollback because "
170+
"thread does not own anything and does not violate "
171+
"gtid consistency"));
172+
DBUG_VOID_RETURN;
173+
}
168174

169-
global_sid_lock->rdlock();
170-
update_gtids_impl(thd, false);
171-
global_sid_lock->unlock();
175+
/*
176+
The administrative commands [CHECK|REPAIR|OPTIMIZE|ANALYZE] TABLE
177+
are written to the binary log even when they fail. When the
178+
commands fail, they will call update_on_rollback; later they will
179+
write the binary log. But we must not do any of the things in
180+
update_gtids_impl if we are going to write the binary log. So
181+
these statements set the skip_gtid_rollback flag, which tells
182+
update_on_rollback to return early. When the statements are
183+
written to the binary log they will call update_on_commit as
184+
usual.
185+
*/
186+
if (thd->skip_gtid_rollback)
187+
{
188+
DBUG_PRINT("info", ("skipping gtid rollback because "
189+
"thd->skip_gtid_rollback is set"));
190+
DBUG_VOID_RETURN;
172191
}
173192

193+
global_sid_lock->rdlock();
194+
update_gtids_impl(thd, false);
195+
global_sid_lock->unlock();
196+
174197
DBUG_VOID_RETURN;
175198
}
176199

@@ -346,6 +369,19 @@ void Gtid_state::update_gtids_impl(THD *thd, bool is_commit)
346369
DBUG_ASSERT(thd->variables.gtid_next.type == AUTOMATIC_GROUP);
347370
}
348371

372+
if (thd->has_gtid_consistency_violation &&
373+
!more_transactions_with_same_gtid_next)
374+
{
375+
if (thd->variables.gtid_next.type == AUTOMATIC_GROUP)
376+
gtid_state->end_automatic_gtid_violating_transaction();
377+
else
378+
{
379+
DBUG_ASSERT(thd->variables.gtid_next.type == ANONYMOUS_GROUP);
380+
gtid_state->end_anonymous_gtid_violating_transaction();
381+
}
382+
thd->has_gtid_consistency_violation= false;
383+
}
384+
349385
thd->owned_gtid.dbug_print(NULL,
350386
"set owned_gtid (clear) in update_gtids_impl");
351387

sql/sql_class.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,7 @@ THD::THD(bool enable_plugins)
11971197
#endif
11981198
skip_gtid_rollback(false),
11991199
is_commit_in_middle_of_statement(false),
1200+
has_gtid_consistency_violation(false),
12001201
main_da(false),
12011202
m_parser_da(false),
12021203
m_query_rewrite_plugin_da(false),

sql/sql_class.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3617,6 +3617,11 @@ class THD :public MDL_context_owner,
36173617
flag.
36183618
*/
36193619
bool is_commit_in_middle_of_statement;
3620+
/*
3621+
True while the transaction is executing, if one of
3622+
is_ddl_gtid_consistent or is_dml_gtid_consistent returned false.
3623+
*/
3624+
bool has_gtid_consistency_violation;
36203625

36213626
const LEX_CSTRING &db() const
36223627
{ return m_db; }

0 commit comments

Comments
 (0)