Possible source of the leak is in following lines:
slave.cc, exec_relay_log_event:
if (ev->get_type_code() != FORMAT_DESCRIPTION_EVENT && !rli->is_deferred_event(ev))
{
DBUG_PRINT("info", ("Deleting the event after it has been executed"));
delete ev;
}
Deferred events are Int-, Rand- and User- var events. However above code *COULD* skip deletion of another types of events *WITHOUT* adding them to deferred_events container, i.e. these events eventually will not be deleted and memory will leak. The reason is implementation of is_deferred_event:
/*
Returns true if the argument event resides in the containter;
more specifically, the checking is done against the last added event.
*/
bool is_deferred_event(Log_event * ev)
{
return deferred_events_collecting ? deferred_events->is_last(ev) : false;
};
and
bool is_last(Log_event *ev) { return ev == last_added; };
It just checks whether last added event pointer is equal to pointer to event which was added to container by previous call of Deferred_log_events::add:
However deferred_events usage pattern is following:
handle_slave_sql()
{
rli->deferred_events= new Deferred_log_events(rli);
while(!killed)
{
rli->deferred_events= new Deferred_log_events(rli);
if (received deferred event ev) {
rli->deferred_events->add(ev);
}
else {
execute statement
rli->deferred_events->rewind();
}
}
delete rli->deferred_events;
}
Container constantly filled with events and cleared, filled and cleared again innumerable times. When container cleared in following code:
void Deferred_log_events::rewind()
{
/*
Reset preceeding Query log event events which execution was
deferred because of slave side filtering.
*/
if (!is_empty())
{
for (uint i= 0; i < array.elements; i++)
{
Log_event *ev= *(Log_event **) dynamic_array_ptr(&array, i);
delete ev;
}
if (array.elements > array.max_element)
freeze_size(&array);
reset_dynamic(&array);
}
}
last_added still points to already deleted event. Which mean nothing blocks the allocator from allocating memory region with same address (last_added) for the next (unnecessary deferred) event. Which *SURELY* happens innumerable times and leads to memory leaks of pseudo random replication log events.
Possible source of the leak is in following lines: log_event:
slave.cc, exec_relay_
if (ev->get_ type_code( ) != FORMAT_ DESCRIPTION_ EVENT &&
!rli-> is_deferred_ event(ev) ) PRINT(" info", ("Deleting the event after it has been executed"));
{
DBUG_
delete ev;
}
Deferred events are Int-, Rand- and User- var events. However above code *COULD* skip deletion of another types of events *WITHOUT* adding them to deferred_events container, i.e. these events eventually will not be deleted and memory will leak. The reason is implementation of is_deferred_event: event(Log_ event * ev) events_ collecting ? deferred_ events- >is_last( ev) : false;
/*
Returns true if the argument event resides in the containter;
more specifically, the checking is done against the last added event.
*/
bool is_deferred_
{
return deferred_
};
and
bool is_last(Log_event *ev) { return ev == last_added; };
It just checks whether last added event pointer is equal to pointer to event which was added to container by previous call of Deferred_ log_events: :add:
int Deferred_ log_events: :add(Log_ event *ev) dynamic( &array, (uchar*) &ev);
{
last_added= ev;
insert_
return 0;
}
as we can see last_added updated correctly.
However deferred_events usage pattern is following:
handle_slave_sql() events= new Deferred_ log_events( rli); deferred_ events= new Deferred_ log_events( rli); >deferred_ events- >add(ev) ; >deferred_ events- >rewind( ); events;
{
rli->deferred_
while(!killed)
{
rli->
if (received deferred event ev) {
rli-
}
else {
execute statement
rli-
}
}
delete rli->deferred_
}
Container constantly filled with events and cleared, filled and cleared again innumerable times. When container cleared in following code: log_events: :rewind( ) array_ptr( &array, i); size(&array) ; dynamic( &array) ;
void Deferred_
{
/*
Reset preceeding Query log event events which execution was
deferred because of slave side filtering.
*/
if (!is_empty())
{
for (uint i= 0; i < array.elements; i++)
{
Log_event *ev= *(Log_event **) dynamic_
delete ev;
}
if (array.elements > array.max_element)
freeze_
reset_
}
}
last_added still points to already deleted event. Which mean nothing blocks the allocator from allocating memory region with same address (last_added) for the next (unnecessary deferred) event. Which *SURELY* happens innumerable times and leads to memory leaks of pseudo random replication log events.
Suggested patch to fix this is:
=== modified file 'Percona- Server/ sql/rpl_ utility. cc' Server/ sql/rpl_ utility. cc 2012-04-21 10:24:39 +0000 Server/ sql/rpl_ utility. cc 2012-12-19 08:58:02 +0000
freeze_ size(&array) ; dynamic( &array) ;
--- Percona-
+++ Percona-
@@ -1115,6 +1115,7 @@
if (array.elements > array.max_element)
reset_
+ last_added= NULL;
}
}