Skip to content

Commit d4c4eb7

Browse files
FooBarriormidenok
authored andcommitted
MDEV-15990 Refactor write_record and fix idempotent replication
See also MDEV-30046. Idempotent write_row works same as REPLACE: if there is a duplicating record in the table, then it will be deleted and re-inserted, with the same update optimization. The code in Rows:log_event::write_row was basically copy-pasted from write_record. What's done: REPLACE operation was unified across replication and sql. It is now representred as a Write_record class, that holds the whole state, and allows re-using some resources in between the row writes. Replace, IODKU and single insert implementations are split across different methods, reluting in a much cleaner code. The entry point is preserved as a single Write_record::write_record() call. The implementation to call is chosen on the constructor stage. This allowed several optimizations to be done: 1. The table key list is not iterated for every row. We find last unique key in the order of checking once and preserve it across the rows. See last_uniq_key(). 2. ib_handler::referenced_by_foreign_key acquires a global lock. This call was done per row as well. Not all the table config that allows optimized replace is folded into a single boolean field can_optimize. All the fields to check are even stored in a single register on a 64-bit platform. 3. DUP_REPLACE and DUP_UPDATE cases now have one less level of indirection 4. modified_non_trans_tables is checked and set only when it's really needed. 5. Obsolete bitmap manipulations are removed. Also: * Unify replace initialization step across implementations: add prepare_for_replace and finalize_replace * alloca is removed in favor of mem_root allocation. This memory is reused across the rows. * An rpl-related callback is added to the replace branch, meaning that an extra check is made per row replace even for the common case. It can be avoided with templates if considered a problem.
1 parent 7e386c5 commit d4c4eb7

File tree

12 files changed

+857
-735
lines changed

12 files changed

+857
-735
lines changed

mysql-test/main/long_unique_bugs_replication.result

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,64 @@ insert into t1 values (2,2);
99
update t1 set a1 = 'd' limit 1;
1010
update t1 set a1 = 'd2' where i1= 2;
1111
connection slave;
12+
connection slave;
13+
select * from t1;
14+
i1 a1
15+
1 d
16+
2 d2
1217
connection master;
1318
drop table t1;
19+
connection slave;
20+
connection master;
1421
#
1522
# MDEV-32093 long uniques break old->new replication
1623
#
17-
connection slave;
1824
create table t1 (id int not null, b1 varchar(255) not null, b2 varchar(2550) not null, unique (id), unique key (b1,b2) using hash) default charset utf8mb3;
1925
set global slave_exec_mode=idempotent;
2026
binlog 'aRf2ZA8BAAAA/AAAAAABAAAAAAQAMTAuNS4xNS1NYXJpYURCLWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABpF/ZkEzgNAAgAEgAEBAQEEgAA5AAEGggAAAAICAgCAAAACgoKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEwQADQgICAoKCgFRmTlk';
2127
binlog 'bBf2ZBMBAAAANAAAAJgHAAAAAHEAAAAAAAEABHRlc3QAAnQxAAQDDw8IBP0C4h0AeqMD4A==bBf2ZBcBAAAANAAAAMwHAAAAAHEAAAAAAAEABP/wj6QAAAEAYgEAZa6/VU0JAAAANteqUw==';
2228
binlog 'bBf2ZBMBAAAANAAAAJgHAAAAAHEAAAAAAAEABHRlc3QAAnQxAAQDDw8IBP0C4h0AeqMD4A==bBf2ZBcBAAAANAAAAMwHAAAAAHEAAAAAAAEABP/wj6QAAAEAYgEAZa6/VU0JAAAANteqUw==';
2329
binlog 'bBf2ZBMBAAAANAAAAHUkAAAAAHEAAAAAAAEABHRlc3QAAnQxAAQDDw8IBP0C4h0AaTGFIg==bBf2ZBgBAAAASAAAAL0kAAAAAHEAAAAAAAEABP//8I+kAAABAGIBAGWuv1VNCQAAAPBuWwAAAQBiAQBlrr9VTQkAAADxS9Lu';
24-
drop table t1;
30+
connection slave;
31+
select * from t1;
32+
id b1 b2
33+
23406 b e
34+
connection master;
2535
set global slave_exec_mode=default;
36+
drop table t1;
37+
#
38+
# End of 10.4 tests
39+
#
40+
# Idempotent scenario, which triggers REPLACE code to be used in the
41+
# event, i.e. duplicated record will be deleted and then re-inserted.
42+
create table t1 (i1 int, a1 text, unique key i1 (a1)) engine=myisam;
43+
connection slave;
44+
connection slave;
45+
set @save_slave_exec_mode= @@slave_exec_mode;
46+
set global slave_exec_mode = idempotent;
47+
insert into t1 values (1,1);
48+
connection master;
49+
insert into t1 values (2,1);
50+
connection slave;
51+
connection slave;
52+
select * from t1;
53+
i1 a1
54+
2 1
55+
connection master;
56+
insert into t1 values (3,3);
57+
update t1 set a1 = 'd' limit 1;
58+
update t1 set a1 = 'd2' where i1= 3;
59+
connection slave;
60+
connection slave;
61+
select * from t1;
62+
i1 a1
63+
2 d
64+
3 d2
65+
set global slave_exec_mode = @save_slave_exec_mode;
66+
connection master;
67+
drop table t1;
68+
connection slave;
69+
connection master;
2670
#
2771
# End of 10.4 tests
2872
#

mysql-test/main/long_unique_bugs_replication.test

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,21 @@ update t1 set a1 = 'd' limit 1;
1616
update t1 set a1 = 'd2' where i1= 2;
1717

1818
sync_slave_with_master;
19-
19+
connection slave;
20+
select * from t1;
2021
connection master;
2122
drop table t1;
2223

24+
sync_slave_with_master;
25+
connection master;
26+
27+
2328
--echo #
2429
--echo # MDEV-32093 long uniques break old->new replication
2530
--echo #
2631

2732
# this is techically a bug in replication, but it needs an old master
2833
# so we'll run it as a non-replicated test with BINLOG command
29-
sync_slave_with_master;
3034
create table t1 (id int not null, b1 varchar(255) not null, b2 varchar(2550) not null, unique (id), unique key (b1,b2) using hash) default charset utf8mb3;
3135
set global slave_exec_mode=idempotent;
3236

@@ -40,10 +44,46 @@ binlog 'bBf2ZBMBAAAANAAAAJgHAAAAAHEAAAAAAAEABHRlc3QAAnQxAAQDDw8IBP0C4h0AeqMD4A==
4044
### UPDATE t1 WHERE (42127, 'b', 'e', 39952170926) SET (23406, 'b', 'e', 39952170926)
4145
binlog 'bBf2ZBMBAAAANAAAAHUkAAAAAHEAAAAAAAEABHRlc3QAAnQxAAQDDw8IBP0C4h0AaTGFIg==bBf2ZBgBAAAASAAAAL0kAAAAAHEAAAAAAAEABP//8I+kAAABAGIBAGWuv1VNCQAAAPBuWwAAAQBiAQBlrr9VTQkAAADxS9Lu';
4246

43-
drop table t1;
47+
sync_slave_with_master;
48+
select * from t1;
49+
connection master;
4450
set global slave_exec_mode=default;
51+
drop table t1;
52+
53+
--echo #
54+
--echo # End of 10.4 tests
55+
--echo #
56+
57+
--echo # Idempotent scenario, which triggers REPLACE code to be used in the
58+
--echo # event, i.e. duplicated record will be deleted and then re-inserted.
59+
create table t1 (i1 int, a1 text, unique key i1 (a1)) engine=myisam;
60+
61+
sync_slave_with_master;
62+
connection slave;
63+
set @save_slave_exec_mode= @@slave_exec_mode;
64+
set global slave_exec_mode = idempotent;
65+
insert into t1 values (1,1);
66+
connection master;
67+
insert into t1 values (2,1);
68+
sync_slave_with_master;
69+
connection slave;
70+
select * from t1;
71+
connection master;
72+
insert into t1 values (3,3);
73+
update t1 set a1 = 'd' limit 1;
74+
update t1 set a1 = 'd2' where i1= 3;
75+
sync_slave_with_master;
76+
77+
connection slave;
78+
select * from t1;
79+
set global slave_exec_mode = @save_slave_exec_mode;
80+
connection master;
81+
drop table t1;
82+
sync_slave_with_master;
83+
connection master;
4584

4685
--echo #
4786
--echo # End of 10.4 tests
4887
--echo #
88+
4989
--source include/rpl_end.inc

sql/handler.cc

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5297,12 +5297,6 @@ uint handler::get_dup_key(int error)
52975297
DBUG_RETURN(errkey);
52985298
}
52995299

5300-
bool handler::has_dup_ref() const
5301-
{
5302-
DBUG_ASSERT(lookup_errkey != (uint)-1 || errkey != (uint)-1);
5303-
return ha_table_flags() & HA_DUPLICATE_POS || lookup_errkey != (uint)-1;
5304-
}
5305-
53065300

53075301
/**
53085302
Delete all files with extension from bas_ext().

sql/handler.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3694,7 +3694,6 @@ class handler :public Sql_alloc
36943694
virtual void print_error(int error, myf errflag);
36953695
virtual bool get_error_message(int error, String *buf);
36963696
uint get_dup_key(int error);
3697-
bool has_dup_ref() const;
36983697
/**
36993698
Retrieves the names of the table and the key for which there was a
37003699
duplicate entry in the case of HA_ERR_FOREIGN_DUPLICATE_KEY.

sql/log_event.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "rpl_record.h"
5151
#include "rpl_reporting.h"
5252
#include "sql_class.h" /* THD */
53+
#include "sql_insert.h"
5354
#else
5455
typedef ulong enum_slave_exec_mode;
5556
#endif
@@ -4501,7 +4502,7 @@ class Table_map_log_event : public Log_event
45014502
enum_logged_status logged_status() override { return LOGGED_TABLE_MAP; }
45024503
bool is_valid() const override { return m_memory != NULL; /* we check malloc */ }
45034504

4504-
int get_data_size() override { return (uint) m_data_size; }
4505+
int get_data_size() override { return (uint) m_data_size; }
45054506
#ifdef MYSQL_SERVER
45064507
#ifdef HAVE_REPLICATION
45074508
bool is_part_of_group() override { return 1; }
@@ -4926,7 +4927,6 @@ class Rows_log_event : public Log_event
49264927
uint find_key_parts(const KEY *key) const;
49274928
bool use_pk_position() const;
49284929
int find_row(rpl_group_info *);
4929-
int write_row(rpl_group_info *, const bool);
49304930
int update_sequence();
49314931

49324932
// Unpack the current row into m_table->record[0], but with
@@ -4990,7 +4990,8 @@ class Rows_log_event : public Log_event
49904990
error code otherwise.
49914991
*/
49924992
virtual
4993-
int do_before_row_operations(const rpl_group_info *) = 0;
4993+
int do_before_row_operations(const rpl_group_info *log,
4994+
COPY_INFO*, Write_record*) = 0;
49944995

49954996
/**
49964997
@brief Primitive to clean up after a sequence of row executions.
@@ -5070,6 +5071,7 @@ class Write_rows_log_event : public Rows_log_event
50705071
{
50715072
*rows += m_row_count;
50725073
}
5074+
int incomplete_record_callback(rpl_group_info *rgi);
50735075
#endif
50745076

50755077
private:
@@ -5080,7 +5082,10 @@ class Write_rows_log_event : public Rows_log_event
50805082
#endif
50815083

50825084
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
5083-
int do_before_row_operations(const rpl_group_info *) override;
5085+
Write_record *m_write_record;
5086+
int write_row(rpl_group_info *, bool);
5087+
int do_before_row_operations(const rpl_group_info *rgi,
5088+
COPY_INFO*, Write_record*) override;
50845089
int do_after_row_operations(int) override;
50855090
int do_exec_row(rpl_group_info *) override;
50865091
#endif
@@ -5169,7 +5174,8 @@ class Update_rows_log_event : public Rows_log_event
51695174
#endif
51705175

51715176
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
5172-
int do_before_row_operations(const rpl_group_info *) override;
5177+
int do_before_row_operations(const rpl_group_info *rgi,
5178+
COPY_INFO*, Write_record*) override;
51735179
int do_after_row_operations(int) override;
51745180
int do_exec_row(rpl_group_info *) override;
51755181
#endif /* defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) */
@@ -5260,7 +5266,8 @@ class Delete_rows_log_event : public Rows_log_event
52605266
#endif
52615267

52625268
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
5263-
int do_before_row_operations(const rpl_group_info *const) override;
5269+
int do_before_row_operations(const rpl_group_info *rgi,
5270+
COPY_INFO*, Write_record*) override;
52645271
int do_after_row_operations(int) override;
52655272
int do_exec_row(rpl_group_info *) override;
52665273
#endif

0 commit comments

Comments
 (0)