Skip to content

Commit 2e2b2a0

Browse files
committed
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 f8ac3a7 commit 2e2b2a0

File tree

12 files changed

+853
-727
lines changed

12 files changed

+853
-727
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
@@ -5003,12 +5003,6 @@ uint handler::get_dup_key(int error)
50035003
DBUG_RETURN(errkey);
50045004
}
50055005

5006-
bool handler::has_dup_ref() const
5007-
{
5008-
DBUG_ASSERT(lookup_errkey != (uint)-1 || errkey != (uint)-1);
5009-
return ha_table_flags() & HA_DUPLICATE_POS || lookup_errkey != (uint)-1;
5010-
}
5011-
50125006

50135007
/**
50145008
Delete all files with extension from bas_ext().

sql/handler.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3138,7 +3138,6 @@ class handler :public Sql_alloc
31383138
Table_flags cached_table_flags; /* Set on init() and open() */
31393139

31403140
ha_rows estimation_rows_to_insert;
3141-
handler *lookup_handler;
31423141
/* Statistics for the query. Updated if handler_stats.active is set */
31433142
ha_handler_stats active_handler_stats;
31443143
void set_handler_stats();
@@ -3147,6 +3146,7 @@ class handler :public Sql_alloc
31473146
uchar *ref; /* Pointer to current row */
31483147
uchar *dup_ref; /* Pointer to duplicate row */
31493148
uchar *lookup_buffer;
3149+
handler *lookup_handler;
31503150

31513151
/* General statistics for the table like number of row, file sizes etc */
31523152
ha_statistics stats;
@@ -3348,9 +3348,8 @@ class handler :public Sql_alloc
33483348
handler(handlerton *ht_arg, TABLE_SHARE *share_arg)
33493349
:table_share(share_arg), table(0),
33503350
estimation_rows_to_insert(0),
3351-
lookup_handler(this),
3352-
ht(ht_arg), ref(0), lookup_buffer(NULL), handler_stats(NULL),
3353-
end_range(NULL), implicit_emptied(0),
3351+
ht(ht_arg), ref(0), lookup_buffer(NULL), lookup_handler(this),
3352+
handler_stats(NULL), end_range(NULL), implicit_emptied(0),
33543353
mark_trx_read_write_done(0),
33553354
check_table_binlog_row_based_done(0),
33563355
check_table_binlog_row_based_result(0),
@@ -3543,7 +3542,6 @@ class handler :public Sql_alloc
35433542
virtual void print_error(int error, myf errflag);
35443543
virtual bool get_error_message(int error, String *buf);
35453544
uint get_dup_key(int error);
3546-
bool has_dup_ref() const;
35473545
/**
35483546
Retrieves the names of the table and the key for which there was a
35493547
duplicate entry in the case of HA_ERR_FOREIGN_DUPLICATE_KEY.

sql/log_event.h

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include "rpl_record.h"
5454
#include "rpl_reporting.h"
5555
#include "sql_class.h" /* THD */
56+
#include "sql_insert.h"
5657
#endif
5758

5859
#include "rpl_gtid.h"
@@ -4930,7 +4931,7 @@ class Table_map_log_event : public Log_event
49304931
enum_logged_status logged_status() override { return LOGGED_TABLE_MAP; }
49314932
bool is_valid() const override { return m_memory != NULL; /* we check malloc */ }
49324933

4933-
int get_data_size() override { return (uint) m_data_size; }
4934+
int get_data_size() override { return (uint) m_data_size; }
49344935
#ifdef MYSQL_SERVER
49354936
#ifdef HAVE_REPLICATION
49364937
bool is_part_of_group() override { return 1; }
@@ -5345,7 +5346,6 @@ class Rows_log_event : public Log_event
53455346

53465347
int find_key(); // Find a best key to use in find_row()
53475348
int find_row(rpl_group_info *);
5348-
int write_row(rpl_group_info *, const bool);
53495349
int update_sequence();
53505350

53515351
// Unpack the current row into m_table->record[0], but with
@@ -5410,8 +5410,9 @@ class Rows_log_event : public Log_event
54105410
The member function will return 0 if all went OK, or a non-zero
54115411
error code otherwise.
54125412
*/
5413-
virtual
5414-
int do_before_row_operations(const Slave_reporting_capability *const log) = 0;
5413+
virtual
5414+
int do_before_row_operations(rpl_group_info *log,
5415+
COPY_INFO*, Write_record*) = 0;
54155416

54165417
/*
54175418
Primitive to clean up after a sequence of row executions.
@@ -5489,6 +5490,7 @@ class Write_rows_log_event : public Rows_log_event
54895490

54905491
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
54915492
uint8 get_trg_event_map() override;
5493+
int incomplete_record_callback(rpl_group_info *rgi);
54925494
#endif
54935495

54945496
private:
@@ -5499,7 +5501,10 @@ class Write_rows_log_event : public Rows_log_event
54995501
#endif
55005502

55015503
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
5502-
int do_before_row_operations(const Slave_reporting_capability *const) override;
5504+
Write_record *m_write_record;
5505+
int write_row(rpl_group_info *, bool);
5506+
int do_before_row_operations(rpl_group_info *rgi,
5507+
COPY_INFO*, Write_record*) override;
55035508
int do_after_row_operations(const Slave_reporting_capability *const,int) override;
55045509
int do_exec_row(rpl_group_info *) override;
55055510
#endif
@@ -5587,7 +5592,8 @@ class Update_rows_log_event : public Rows_log_event
55875592
#endif
55885593

55895594
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
5590-
int do_before_row_operations(const Slave_reporting_capability *const) override;
5595+
int do_before_row_operations(rpl_group_info *rgi,
5596+
COPY_INFO*, Write_record*) override;
55915597
int do_after_row_operations(const Slave_reporting_capability *const,int) override;
55925598
int do_exec_row(rpl_group_info *) override;
55935599
#endif /* defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) */
@@ -5672,7 +5678,8 @@ class Delete_rows_log_event : public Rows_log_event
56725678
#endif
56735679

56745680
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
5675-
int do_before_row_operations(const Slave_reporting_capability *const) override;
5681+
int do_before_row_operations(rpl_group_info *rgi,
5682+
COPY_INFO*, Write_record*) override;
56765683
int do_after_row_operations(const Slave_reporting_capability *const,int) override;
56775684
int do_exec_row(rpl_group_info *) override;
56785685
#endif

0 commit comments

Comments
 (0)