Skip to content

Commit efc0025

Browse files
author
Venkatesh Duggirala
committed
Bug#19704825 TEMPORARY SLAVE TYPE CONVERSION TABLES RELEASED TO EARLY
Problem: The memory used in preparing slave type conversion temporary table is getting released early and causing unexpected results Analysis: As part of bug#18770469 fix, We introduced an event m_event_mem_root (a special mem root), added to Log_event class. While server is creating the temporary table, the memory needed is allocated from this special mem root which will be freed in ~Log_event() i.e., the scope of this memroot is one event. But it could happen that in some cases, server might need to access this conversion temporary table for next following event. For eg: A nested row event (insert is causing insert in a trigger) In this situation, the memory is getting delayed too early and causing issues when the server is trying to access the temporary table inside the trigger. Fix: We cannot use a mem_root whose scope is limited to an event execution in this situation. With some further analysis, found out that clearing a thd->mem_root at the end of statement (upon applying an event which has STMT_END_F flag) will solve out of memory problem while running a long transactions (bug#18770469) and will also make this reported problem (memory is getting released early) to go away.
1 parent 3b798d3 commit efc0025

File tree

10 files changed

+122
-113
lines changed

10 files changed

+122
-113
lines changed

mysql-test/suite/rpl/r/rpl_colSize.result

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,22 @@ t1 CREATE TABLE `t1` (
185185
`d` bit(25) DEFAULT NULL,
186186
`e` bit(13) DEFAULT NULL
187187
) ENGINE=MyISAM DEFAULT CHARSET=latin1
188+
CREATE TABLE t2(i INTEGER);
189+
CREATE TRIGGER t1_tr BEFORE INSERT ON t1 FOR EACH ROW
190+
BEGIN
191+
INSERT INTO t2 VALUES(1);
192+
END $$
193+
INSERT INTO t1 VALUES (
194+
b'1010101',
195+
b'10101011',
196+
b'101010110101010101111',
197+
b'10101010101',
198+
b'10101011111'
199+
);
200+
include/sync_slave_sql_with_master.inc
188201
*** Cleanup ***
189202
DROP TABLE t1;
203+
DROP TABLE t2;
190204
include/sync_slave_sql_with_master.inc
191205
SET GLOBAL SLAVE_TYPE_CONVERSIONS = @saved_slave_type_conversions;
192206
include/rpl_end.inc

mysql-test/suite/rpl/t/rpl_colSize.test

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,16 +215,63 @@ SHOW CREATE TABLE t1;
215215
--let $sync_slave_connection= slave
216216
--source include/sync_slave_sql_with_master.inc
217217

218-
219-
220218
--echo Insert some values and select them on master
221219
SELECT BIN(a), BIN(b), BIN(c), BIN(d), BIN(e) FROM t1;
222220
--replace_result default DEFAULT
223221
SHOW CREATE TABLE t1;
224222

223+
###############################################################################
224+
# Bug#19704825 TEMPORARY SLAVE TYPE CONVERSION TABLES RELEASED TO EARLY
225+
# Problem: Memory used in preparing slave type conversion
226+
# temporary table is getting released early and causing unexpected results
227+
# in case of nested events.
228+
# Eg: If a insert statement is calling a trigger which contains another
229+
# insert statement.
230+
# Steps to reproduce:
231+
# 1) Create a table t1
232+
# 2) Change it's definition on slave, so that insert will create conversion
233+
# temporary table on slave (set SLAVE_TYPE_CONVERSIONS appropriately to
234+
# allow the scenario)
235+
# 3) Create a table t2
236+
# 4) Create a trigger on top of table t1. Inside the trigger, insert a tuple
237+
# into table t2
238+
# 5) Insert a tuple into table t1 on Master
239+
# 6) Make sure this insert is replicated without any issues.
240+
###############################################################################
241+
242+
# Step 1, 2 are already done by above test code.
243+
244+
# Step 3: Create a table t2
245+
connection master;
246+
CREATE TABLE t2(i INTEGER);
247+
248+
# Step 4: Create a trigger on top of table t1. Inside the trigger, insert a
249+
# tuple into table t2
250+
--delimiter $$
251+
CREATE TRIGGER t1_tr BEFORE INSERT ON t1 FOR EACH ROW
252+
BEGIN
253+
INSERT INTO t2 VALUES(1);
254+
END $$
255+
--delimiter ;
256+
257+
# Step 5: Insert a tuple into table t1 on Master
258+
INSERT INTO t1 VALUES (
259+
b'1010101',
260+
b'10101011',
261+
b'101010110101010101111',
262+
b'10101010101',
263+
b'10101011111'
264+
);
265+
266+
# Step 6) Make sure this insert is replicated without any issues.
267+
--source include/sync_slave_sql_with_master.inc
268+
269+
# End of test for Bug#19704825
270+
225271
--echo *** Cleanup ***
226272
connection master;
227273
DROP TABLE t1;
274+
DROP TABLE t2;
228275
--source include/sync_slave_sql_with_master.inc
229276

230277
SET GLOBAL SLAVE_TYPE_CONVERSIONS = @saved_slave_type_conversions;

sql/field.cc

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10167,8 +10167,7 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
1016710167
Field::geometry_type geom_type,
1016810168
Field::utype unireg_check,
1016910169
TYPELIB *interval,
10170-
const char *field_name,
10171-
MEM_ROOT *mem_root /* default= NULL */)
10170+
const char *field_name)
1017210171
{
1017310172
uchar *UNINIT_VAR(bit_ptr);
1017410173
uchar UNINIT_VAR(bit_offset);
@@ -10203,8 +10202,6 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
1020310202
FLAGSTR(pack_flag, FIELDFLAG_NUMBER),
1020410203
FLAGSTR(pack_flag, FIELDFLAG_PACK),
1020510204
FLAGSTR(pack_flag, FIELDFLAG_BLOB)));
10206-
if (mem_root == NULL)
10207-
mem_root= current_thd->mem_root;
1020810205

1020910206
if (f_is_alpha(pack_flag))
1021010207
{
@@ -10213,11 +10210,11 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
1021310210
if (field_type == MYSQL_TYPE_STRING ||
1021410211
field_type == MYSQL_TYPE_DECIMAL || // 3.23 or 4.0 string
1021510212
field_type == MYSQL_TYPE_VAR_STRING)
10216-
return new (mem_root) Field_string(ptr,field_length,null_pos,null_bit,
10213+
return new Field_string(ptr,field_length,null_pos,null_bit,
1021710214
unireg_check, field_name,
1021810215
field_charset);
1021910216
if (field_type == MYSQL_TYPE_VARCHAR)
10220-
return new (mem_root) Field_varstring(ptr,field_length,
10217+
return new Field_varstring(ptr,field_length,
1022110218
HA_VARCHAR_PACKLENGTH(field_length),
1022210219
null_pos,null_bit,
1022310220
unireg_check, field_name,
@@ -10232,115 +10229,115 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
1023210229

1023310230
#ifdef HAVE_SPATIAL
1023410231
if (f_is_geom(pack_flag))
10235-
return new (mem_root) Field_geom(ptr,null_pos,null_bit,
10232+
return new Field_geom(ptr,null_pos,null_bit,
1023610233
unireg_check, field_name, share,
1023710234
pack_length, geom_type);
1023810235
#endif
1023910236
if (f_is_blob(pack_flag))
10240-
return new (mem_root) Field_blob(ptr,null_pos,null_bit,
10237+
return new Field_blob(ptr,null_pos,null_bit,
1024110238
unireg_check, field_name, share,
1024210239
pack_length, field_charset);
1024310240
if (interval)
1024410241
{
1024510242
if (f_is_enum(pack_flag))
10246-
return new (mem_root) Field_enum(ptr,field_length,null_pos,null_bit,
10243+
return new Field_enum(ptr,field_length,null_pos,null_bit,
1024710244
unireg_check, field_name,
1024810245
pack_length, interval, field_charset);
1024910246
else
10250-
return new (mem_root) Field_set(ptr,field_length,null_pos,null_bit,
10247+
return new Field_set(ptr,field_length,null_pos,null_bit,
1025110248
unireg_check, field_name,
1025210249
pack_length, interval, field_charset);
1025310250
}
1025410251
}
1025510252

1025610253
switch (field_type) {
1025710254
case MYSQL_TYPE_DECIMAL:
10258-
return new (mem_root) Field_decimal(ptr,field_length,null_pos,null_bit,
10255+
return new Field_decimal(ptr,field_length,null_pos,null_bit,
1025910256
unireg_check, field_name,
1026010257
f_decimals(pack_flag),
1026110258
f_is_zerofill(pack_flag) != 0,
1026210259
f_is_dec(pack_flag) == 0);
1026310260
case MYSQL_TYPE_NEWDECIMAL:
10264-
return new (mem_root) Field_new_decimal(ptr,field_length,null_pos,null_bit,
10261+
return new Field_new_decimal(ptr,field_length,null_pos,null_bit,
1026510262
unireg_check, field_name,
1026610263
f_decimals(pack_flag),
1026710264
f_is_zerofill(pack_flag) != 0,
1026810265
f_is_dec(pack_flag) == 0);
1026910266
case MYSQL_TYPE_FLOAT:
10270-
return new (mem_root) Field_float(ptr,field_length,null_pos,null_bit,
10267+
return new Field_float(ptr,field_length,null_pos,null_bit,
1027110268
unireg_check, field_name,
1027210269
f_decimals(pack_flag),
1027310270
f_is_zerofill(pack_flag) != 0,
1027410271
f_is_dec(pack_flag)== 0);
1027510272
case MYSQL_TYPE_DOUBLE:
10276-
return new (mem_root) Field_double(ptr,field_length,null_pos,null_bit,
10273+
return new Field_double(ptr,field_length,null_pos,null_bit,
1027710274
unireg_check, field_name,
1027810275
f_decimals(pack_flag),
1027910276
f_is_zerofill(pack_flag) != 0,
1028010277
f_is_dec(pack_flag)== 0);
1028110278
case MYSQL_TYPE_TINY:
10282-
return new (mem_root) Field_tiny(ptr,field_length,null_pos,null_bit,
10279+
return new Field_tiny(ptr,field_length,null_pos,null_bit,
1028310280
unireg_check, field_name,
1028410281
f_is_zerofill(pack_flag) != 0,
1028510282
f_is_dec(pack_flag) == 0);
1028610283
case MYSQL_TYPE_SHORT:
10287-
return new (mem_root) Field_short(ptr,field_length,null_pos,null_bit,
10284+
return new Field_short(ptr,field_length,null_pos,null_bit,
1028810285
unireg_check, field_name,
1028910286
f_is_zerofill(pack_flag) != 0,
1029010287
f_is_dec(pack_flag) == 0);
1029110288
case MYSQL_TYPE_INT24:
10292-
return new (mem_root) Field_medium(ptr,field_length,null_pos,null_bit,
10289+
return new Field_medium(ptr,field_length,null_pos,null_bit,
1029310290
unireg_check, field_name,
1029410291
f_is_zerofill(pack_flag) != 0,
1029510292
f_is_dec(pack_flag) == 0);
1029610293
case MYSQL_TYPE_LONG:
10297-
return new (mem_root) Field_long(ptr,field_length,null_pos,null_bit,
10294+
return new Field_long(ptr,field_length,null_pos,null_bit,
1029810295
unireg_check, field_name,
1029910296
f_is_zerofill(pack_flag) != 0,
1030010297
f_is_dec(pack_flag) == 0);
1030110298
case MYSQL_TYPE_LONGLONG:
10302-
return new (mem_root) Field_longlong(ptr,field_length,null_pos,null_bit,
10299+
return new Field_longlong(ptr,field_length,null_pos,null_bit,
1030310300
unireg_check, field_name,
1030410301
f_is_zerofill(pack_flag) != 0,
1030510302
f_is_dec(pack_flag) == 0);
1030610303
case MYSQL_TYPE_TIMESTAMP:
10307-
return new (mem_root) Field_timestamp(ptr, field_length, null_pos, null_bit,
10304+
return new Field_timestamp(ptr, field_length, null_pos, null_bit,
1030810305
unireg_check, field_name);
1030910306
case MYSQL_TYPE_TIMESTAMP2:
10310-
return new (mem_root) Field_timestampf(ptr, null_pos, null_bit,
10307+
return new Field_timestampf(ptr, null_pos, null_bit,
1031110308
unireg_check, field_name,
1031210309
field_length > MAX_DATETIME_WIDTH ?
1031310310
field_length - 1 - MAX_DATETIME_WIDTH : 0);
1031410311
case MYSQL_TYPE_YEAR:
10315-
return new (mem_root) Field_year(ptr,field_length,null_pos,null_bit,
10312+
return new Field_year(ptr,field_length,null_pos,null_bit,
1031610313
unireg_check, field_name);
1031710314
case MYSQL_TYPE_NEWDATE:
10318-
return new (mem_root) Field_newdate(ptr, null_pos, null_bit, unireg_check, field_name);
10315+
return new Field_newdate(ptr, null_pos, null_bit, unireg_check, field_name);
1031910316

1032010317
case MYSQL_TYPE_TIME:
10321-
return new (mem_root) Field_time(ptr, null_pos, null_bit,
10318+
return new Field_time(ptr, null_pos, null_bit,
1032210319
unireg_check, field_name);
1032310320
case MYSQL_TYPE_TIME2:
10324-
return new (mem_root) Field_timef(ptr, null_pos, null_bit,
10321+
return new Field_timef(ptr, null_pos, null_bit,
1032510322
unireg_check, field_name,
1032610323
(field_length > MAX_TIME_WIDTH) ?
1032710324
field_length - 1 - MAX_TIME_WIDTH : 0);
1032810325
case MYSQL_TYPE_DATETIME:
10329-
return new (mem_root) Field_datetime(ptr, null_pos, null_bit,
10326+
return new Field_datetime(ptr, null_pos, null_bit,
1033010327
unireg_check, field_name);
1033110328
case MYSQL_TYPE_DATETIME2:
10332-
return new (mem_root) Field_datetimef(ptr, null_pos, null_bit,
10329+
return new Field_datetimef(ptr, null_pos, null_bit,
1033310330
unireg_check, field_name,
1033410331
(field_length > MAX_DATETIME_WIDTH) ?
1033510332
field_length - 1 - MAX_DATETIME_WIDTH : 0);
1033610333
case MYSQL_TYPE_NULL:
10337-
return new (mem_root) Field_null(ptr, field_length, unireg_check, field_name,
10334+
return new Field_null(ptr, field_length, unireg_check, field_name,
1033810335
field_charset);
1033910336
case MYSQL_TYPE_BIT:
1034010337
return f_bit_as_char(pack_flag) ?
10341-
new (mem_root) Field_bit_as_char(ptr, field_length, null_pos, null_bit,
10338+
new Field_bit_as_char(ptr, field_length, null_pos, null_bit,
1034210339
unireg_check, field_name) :
10343-
new (mem_root) Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr,
10340+
new Field_bit(ptr, field_length, null_pos, null_bit, bit_ptr,
1034410341
bit_offset, unireg_check, field_name);
1034510342

1034610343
default: // Impossible (Wrong version)

sql/field.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3893,8 +3893,7 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length,
38933893
const CHARSET_INFO *cs,
38943894
Field::geometry_type geom_type,
38953895
Field::utype unireg_check,
3896-
TYPELIB *interval, const char *field_name,
3897-
MEM_ROOT *mem_root= NULL);
3896+
TYPELIB *interval, const char *field_name);
38983897
uint pack_length_to_packflag(uint type);
38993898
enum_field_types get_blob_type_from_length(ulong length);
39003899
uint32 calc_pack_length(enum_field_types type,uint32 length);

sql/log_event.cc

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -727,9 +727,6 @@ Log_event::Log_event(THD* thd_arg, uint16 flags_arg,
727727
server_id= thd->server_id;
728728
unmasked_server_id= server_id;
729729
when= thd->start_time;
730-
#ifdef HAVE_REPLICATION
731-
init_sql_alloc(&m_event_mem_root, 4096, 0);
732-
#endif //HAVE_REPLICATION
733730
}
734731

735732
/**
@@ -754,9 +751,6 @@ Log_event::Log_event(enum_event_cache_type cache_type_arg,
754751
when.tv_sec= 0;
755752
when.tv_usec= 0;
756753
log_pos= 0;
757-
#ifdef HAVE_REPLICATION
758-
init_sql_alloc(&m_event_mem_root, 4096, 0);
759-
#endif //HAVE_REPLICATION
760754
}
761755
#endif /* !MYSQL_CLIENT */
762756

@@ -774,11 +768,7 @@ Log_event::Log_event(const char* buf,
774768
{
775769
#ifndef MYSQL_CLIENT
776770
thd = 0;
777-
#ifdef HAVE_REPLICATION
778-
init_sql_alloc(&m_event_mem_root, 4096, 0);
779-
#endif //HAVE_REPLICATION
780771
#endif
781-
782772
when.tv_sec= uint4korr(buf);
783773
when.tv_usec= 0;
784774
server_id = uint4korr(buf + SERVER_ID_OFFSET);
@@ -9910,8 +9900,7 @@ Rows_log_event::decide_row_lookup_algorithm_and_key()
99109900
this->m_key_index= search_key_in_table(table, cols, (PRI_KEY_FLAG | UNIQUE_KEY_FLAG | MULTIPLE_KEY_FLAG));
99119901
this->m_rows_lookup_algorithm= ROW_LOOKUP_HASH_SCAN;
99129902
if (m_key_index < MAX_KEY)
9913-
m_distinct_key_spare_buf= (uchar*) alloc_root(&m_event_mem_root,
9914-
table->key_info[m_key_index].key_length);
9903+
m_distinct_key_spare_buf= (uchar*) thd->alloc(table->key_info[m_key_index].key_length);
99159904
DBUG_PRINT("info", ("decide_row_lookup_algorithm_and_key: decided - HASH_SCAN"));
99169905
goto end;
99179906

@@ -10442,8 +10431,7 @@ Rows_log_event::add_key_to_distinct_keyset()
1044210431
if (ret.second)
1044310432
{
1044410433
/* Insert is successful, so allocate a new buffer for next key */
10445-
m_distinct_key_spare_buf= (uchar*) alloc_root(&m_event_mem_root,
10446-
m_key_info->key_length);
10434+
m_distinct_key_spare_buf= (uchar*) thd->alloc(m_key_info->key_length);
1044710435
if (!m_distinct_key_spare_buf)
1044810436
{
1044910437
error= HA_ERR_OUT_OF_MEM;
@@ -11153,13 +11141,8 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
1115311141
{
1115411142
DBUG_ASSERT(ptr->m_tabledef_valid);
1115511143
TABLE *conv_table;
11156-
/*
11157-
Use special mem_root 'Log_event::m_event_mem_root' while doing
11158-
compatiblity check (i.e., while creating temporary table)
11159-
*/
1116011144
if (!ptr->m_tabledef.compatible_with(thd, const_cast<Relay_log_info*>(rli),
11161-
ptr->table,
11162-
&conv_table,&m_event_mem_root))
11145+
ptr->table, &conv_table))
1116311146
{
1116411147
DBUG_PRINT("debug", ("Table: %s.%s is not compatible with master",
1116511148
ptr->table->s->db.str,
@@ -11438,13 +11421,26 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli)
1143811421
DBUG_RETURN(error);
1143911422
}
1144011423

11441-
if (get_flags(STMT_END_F) && (error= rows_event_stmt_cleanup(rli, thd)))
11424+
if (get_flags(STMT_END_F))
11425+
{
11426+
if((error= rows_event_stmt_cleanup(rli, thd)))
1144211427
slave_rows_error_report(ERROR_LEVEL,
1144311428
thd->is_error() ? 0 : error,
1144411429
rli, thd, table,
1144511430
get_type_str(),
1144611431
const_cast<Relay_log_info*>(rli)->get_rpl_log_name(),
1144711432
(ulong) log_pos);
11433+
/* We are at end of the statement (STMT_END_F flag), lets clean
11434+
the memory which was used from thd's mem_root now.
11435+
This needs to be done only if we are here in SQL thread context.
11436+
In other flow ( in case of a regular thread which can happen
11437+
when the thread is applying BINLOG'...' row event) we should
11438+
*not* try to free the memory here. It will be done latter
11439+
in dispatch_command() after command execution is completed.
11440+
*/
11441+
if (thd->slave_thread)
11442+
free_root(thd->mem_root, MYF(MY_KEEP_PREALLOC));
11443+
}
1144811444
DBUG_RETURN(error);
1144911445
}
1145011446

sql/log_event.h

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,13 +1347,7 @@ class Log_event
13471347
}
13481348
Log_event(const char* buf, const Format_description_log_event
13491349
*description_event);
1350-
virtual ~Log_event()
1351-
{
1352-
free_temp_buf();
1353-
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
1354-
free_root(&m_event_mem_root, MYF(MY_KEEP_PREALLOC));
1355-
#endif //!MYSQL_CLIENT && HAVE_REPLICATION
1356-
}
1350+
virtual ~Log_event() { free_temp_buf();}
13571351
void register_temp_buf(char* buf) { temp_buf = buf; }
13581352
void free_temp_buf()
13591353
{
@@ -1609,13 +1603,6 @@ class Log_event
16091603

16101604
virtual int do_apply_event_worker(Slave_worker *w);
16111605

1612-
/*
1613-
Mem root whose scope is equalent to event's scope.
1614-
This mem_root will be initialized in constructor
1615-
Log_event() and freed in destructor ~Log_event().
1616-
*/
1617-
MEM_ROOT m_event_mem_root;
1618-
16191606
protected:
16201607

16211608
/**

0 commit comments

Comments
 (0)