summaryrefslogtreecommitdiff
path: root/sql/log.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/log.cc')
-rw-r--r--sql/log.cc368
1 files changed, 243 insertions, 125 deletions
diff --git a/sql/log.cc b/sql/log.cc
index 73e26ca2641..4873cfd49c3 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -209,11 +209,11 @@ class binlog_cache_data
{
public:
binlog_cache_data(): m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF),
- incident(FALSE), changes_to_non_trans_temp_table_flag(FALSE)
- {
- cache_log.end_of_file= max_binlog_cache_size;
- }
-
+ incident(FALSE), changes_to_non_trans_temp_table_flag(FALSE),
+ saved_max_binlog_cache_size(0), ptr_binlog_cache_use(0),
+ ptr_binlog_cache_disk_use(0)
+ { }
+
~binlog_cache_data()
{
DBUG_ASSERT(empty());
@@ -257,11 +257,19 @@ public:
void reset()
{
+ compute_statistics();
truncate(0);
changes_to_non_trans_temp_table_flag= FALSE;
incident= FALSE;
before_stmt_pos= MY_OFF_T_UNDEF;
- cache_log.end_of_file= max_binlog_cache_size;
+ /*
+ The truncate function calls reinit_io_cache that calls my_b_flush_io_cache
+ which may increase disk_writes. This breaks the disk_writes use by the
+ binary log which aims to compute the ratio between in-memory cache usage
+ and disk cache usage. To avoid this undesirable behavior, we reset the
+ variable after truncating the cache.
+ */
+ cache_log.disk_writes= 0;
DBUG_ASSERT(empty());
}
@@ -292,6 +300,36 @@ public:
before_stmt_pos= MY_OFF_T_UNDEF;
}
+ void set_binlog_cache_info(ulong param_max_binlog_cache_size,
+ ulong *param_ptr_binlog_cache_use,
+ ulong *param_ptr_binlog_cache_disk_use)
+ {
+ /*
+ The assertions guarantee that the set_binlog_cache_info is
+ called just once and information passed as parameters are
+ never zero.
+
+ This is done while calling the constructor binlog_cache_mngr.
+ We cannot set informaton in the constructor binlog_cache_data
+ because the space for binlog_cache_mngr is allocated through
+ a placement new.
+
+ In the future, we can refactor this and change it to avoid
+ the set_binlog_info.
+ */
+ DBUG_ASSERT(saved_max_binlog_cache_size == 0 &&
+ param_max_binlog_cache_size != 0 &&
+ ptr_binlog_cache_use == 0 &&
+ param_ptr_binlog_cache_use != 0 &&
+ ptr_binlog_cache_disk_use == 0 &&
+ param_ptr_binlog_cache_disk_use != 0);
+
+ saved_max_binlog_cache_size= param_max_binlog_cache_size;
+ ptr_binlog_cache_use= param_ptr_binlog_cache_use;
+ ptr_binlog_cache_disk_use= param_ptr_binlog_cache_disk_use;
+ cache_log.end_of_file= saved_max_binlog_cache_size;
+ }
+
/*
Cache to store data before copying it to the binary log.
*/
@@ -321,6 +359,40 @@ private:
*/
bool changes_to_non_trans_temp_table_flag;
+ /**
+ This function computes binlog cache and disk usage.
+ */
+ void compute_statistics()
+ {
+ if (!empty())
+ {
+ statistic_increment(*ptr_binlog_cache_use, &LOCK_status);
+ if (cache_log.disk_writes != 0)
+ statistic_increment(*ptr_binlog_cache_disk_use, &LOCK_status);
+ }
+ }
+
+ /*
+ Stores the values of maximum size of the cache allowed when this cache
+ is configured. This corresponds to either
+ . max_binlog_cache_size or max_binlog_stmt_cache_size.
+ */
+ ulong saved_max_binlog_cache_size;
+
+ /*
+ Stores a pointer to the status variable that keeps track of the in-memory
+ cache usage. This corresponds to either
+ . binlog_cache_use or binlog_stmt_cache_use.
+ */
+ ulong *ptr_binlog_cache_use;
+
+ /*
+ Stores a pointer to the status variable that keeps track of the disk
+ cache usage. This corresponds to either
+ . binlog_cache_disk_use or binlog_stmt_cache_disk_use.
+ */
+ ulong *ptr_binlog_cache_disk_use;
+
/*
It truncates the cache to a certain position. This includes deleting the
pending event.
@@ -334,7 +406,7 @@ private:
set_pending(0);
}
reinit_io_cache(&cache_log, WRITE_CACHE, pos, 0, 0);
- cache_log.end_of_file= max_binlog_cache_size;
+ cache_log.end_of_file= saved_max_binlog_cache_size;
}
binlog_cache_data& operator=(const binlog_cache_data& info);
@@ -343,7 +415,20 @@ private:
class binlog_cache_mngr {
public:
- binlog_cache_mngr() {}
+ binlog_cache_mngr(ulong param_max_binlog_stmt_cache_size,
+ ulong param_max_binlog_cache_size,
+ ulong *param_ptr_binlog_stmt_cache_use,
+ ulong *param_ptr_binlog_stmt_cache_disk_use,
+ ulong *param_ptr_binlog_cache_use,
+ ulong *param_ptr_binlog_cache_disk_use)
+ {
+ stmt_cache.set_binlog_cache_info(param_max_binlog_stmt_cache_size,
+ param_ptr_binlog_stmt_cache_use,
+ param_ptr_binlog_stmt_cache_disk_use);
+ trx_cache.set_binlog_cache_info(param_max_binlog_cache_size,
+ param_ptr_binlog_cache_use,
+ param_ptr_binlog_cache_disk_use);
+ }
void reset_cache(binlog_cache_data* cache_data)
{
@@ -1506,57 +1591,122 @@ static int binlog_close_connection(handlerton *hton, THD *thd)
}
/**
- This function flushes a transactional cache upon commit/rollback.
+ This function flushes a cache upon commit/rollback.
- @param thd The thread whose transaction should be flushed
- @param cache_mngr Pointer to the cache data to be flushed
- @param end_ev The end event either commit/rollback.
+ @param thd The thread whose transaction should be flushed
+ @param cache_data Pointer to the cache
+ @param end_ev The end event either commit/rollback
+ @param is_transactional The type of the cache: transactional or
+ non-transactional
@return
- nonzero if an error pops up when flushing the transactional cache.
+ nonzero if an error pops up when flushing the cache.
*/
-static int
-binlog_flush_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr,
- Log_event *end_ev)
+static inline int
+binlog_flush_cache(THD *thd, binlog_cache_data* cache_data, Log_event *end_evt,
+ bool is_transactional)
{
- DBUG_ENTER("binlog_flush_trx_cache");
- int error=0;
- IO_CACHE *cache_log= &cache_mngr->trx_cache.cache_log;
-
- /*
- This function handles transactional changes and as such
- this flag equals to true.
- */
- bool const is_transactional= TRUE;
-
- if (thd->binlog_flush_pending_rows_event(TRUE, is_transactional))
- DBUG_RETURN(1);
- /*
- Doing a commit or a rollback including non-transactional tables,
- i.e., ending a transaction where we might write the transaction
- cache to the binary log.
-
- We can always end the statement when ending a transaction since
- transactions are not allowed inside stored functions. If they
- were, we would have to ensure that we're not ending a statement
- inside a stored function.
- */
- error= mysql_bin_log.write(thd, &cache_mngr->trx_cache.cache_log, end_ev,
- cache_mngr->trx_cache.has_incident());
- cache_mngr->reset_cache(&cache_mngr->trx_cache);
+ DBUG_ENTER("binlog_flush_cache");
+ int error= 0;
- statistic_increment(binlog_cache_use, &LOCK_status);
- if (cache_log->disk_writes != 0)
+ if (!cache_data->empty())
{
- statistic_increment(binlog_cache_disk_use, &LOCK_status);
- cache_log->disk_writes= 0;
+ if (thd->binlog_flush_pending_rows_event(TRUE, is_transactional))
+ DBUG_RETURN(1);
+ /*
+ Doing a commit or a rollback including non-transactional tables,
+ i.e., ending a transaction where we might write the transaction
+ cache to the binary log.
+
+ We can always end the statement when ending a transaction since
+ transactions are not allowed inside stored functions. If they
+ were, we would have to ensure that we're not ending a statement
+ inside a stored function.
+ */
+ error= mysql_bin_log.write(thd, &cache_data->cache_log, end_evt,
+ cache_data->has_incident());
}
+ cache_data->reset();
- DBUG_ASSERT(cache_mngr->trx_cache.empty());
+ DBUG_ASSERT(cache_data->empty());
DBUG_RETURN(error);
}
/**
+ This function flushes the stmt-cache upon commit.
+
+ @param thd The thread whose transaction should be flushed
+ @param cache_mngr Pointer to the cache manager
+
+ @return
+ nonzero if an error pops up when flushing the cache.
+*/
+static inline int
+binlog_commit_flush_stmt_cache(THD *thd,
+ binlog_cache_mngr *cache_mngr)
+{
+ Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
+ FALSE, FALSE, TRUE, 0);
+ return (binlog_flush_cache(thd, &cache_mngr->stmt_cache, &end_evt,
+ FALSE));
+}
+
+/**
+ This function flushes the trx-cache upon commit.
+
+ @param thd The thread whose transaction should be flushed
+ @param cache_mngr Pointer to the cache manager
+
+ @return
+ nonzero if an error pops up when flushing the cache.
+*/
+static inline int
+binlog_commit_flush_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr)
+{
+ Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
+ TRUE, FALSE, TRUE, 0);
+ return (binlog_flush_cache(thd, &cache_mngr->trx_cache, &end_evt,
+ TRUE));
+}
+
+/**
+ This function flushes the trx-cache upon rollback.
+
+ @param thd The thread whose transaction should be flushed
+ @param cache_mngr Pointer to the cache manager
+
+ @return
+ nonzero if an error pops up when flushing the cache.
+*/
+static inline int
+binlog_rollback_flush_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr)
+{
+ Query_log_event end_evt(thd, STRING_WITH_LEN("ROLLBACK"),
+ TRUE, FALSE, TRUE, 0);
+ return (binlog_flush_cache(thd, &cache_mngr->trx_cache, &end_evt,
+ TRUE));
+}
+
+/**
+ This function flushes the trx-cache upon commit.
+
+ @param thd The thread whose transaction should be flushed
+ @param cache_mngr Pointer to the cache manager
+ @param xid Transaction Id
+
+ @return
+ nonzero if an error pops up when flushing the cache.
+*/
+static inline int
+binlog_commit_flush_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr,
+ my_xid xid)
+{
+ Xid_log_event end_evt(thd, xid);
+ return (binlog_flush_cache(thd, &cache_mngr->trx_cache, &end_evt,
+ TRUE));
+}
+
+/**
This function truncates the transactional cache upon committing or rolling
back either a transaction or a statement.
@@ -1579,23 +1729,24 @@ binlog_truncate_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, bool all)
*/
bool const is_transactional= TRUE;
- DBUG_PRINT("info", ("thd->options={ %s%s}, transaction: %s",
+ DBUG_PRINT("info", ("thd->options={ %s %s}, transaction: %s",
FLAGSTR(thd->variables.option_bits, OPTION_NOT_AUTOCOMMIT),
FLAGSTR(thd->variables.option_bits, OPTION_BEGIN),
all ? "all" : "stmt"));
+
+ thd->binlog_remove_pending_rows_event(TRUE, is_transactional);
/*
If rolling back an entire transaction or a single statement not
inside a transaction, we reset the transaction cache.
*/
- thd->binlog_remove_pending_rows_event(TRUE, is_transactional);
if (ending_trans(thd, all))
{
if (cache_mngr->trx_cache.has_incident())
error= mysql_bin_log.write_incident(thd, TRUE);
- cache_mngr->reset_cache(&cache_mngr->trx_cache);
-
thd->clear_binlog_table_maps();
+
+ cache_mngr->reset_cache(&cache_mngr->trx_cache);
}
/*
If rolling back a statement in a transaction, we truncate the
@@ -1620,51 +1771,6 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
}
/**
- This function flushes the non-transactional to the binary log upon
- committing or rolling back a statement.
-
- @param thd The thread whose transaction should be flushed
- @param cache_mngr Pointer to the cache data to be flushed
-
- @return
- nonzero if an error pops up when flushing the non-transactional cache.
-*/
-static int
-binlog_flush_stmt_cache(THD *thd, binlog_cache_mngr *cache_mngr)
-{
- int error= 0;
- DBUG_ENTER("binlog_flush_stmt_cache");
- /*
- If we are flushing the statement cache, it means that the changes get
- through otherwise the cache is empty and this routine should not be called.
- */
- DBUG_ASSERT(cache_mngr->stmt_cache.has_incident() == FALSE);
- /*
- This function handles non-transactional changes and as such this flag equals
- to false.
- */
- bool const is_transactional= FALSE;
- IO_CACHE *cache_log= &cache_mngr->stmt_cache.cache_log;
-
- if (thd->binlog_flush_pending_rows_event(TRUE, is_transactional))
- DBUG_RETURN(1);
-
- Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE, TRUE, 0);
- if ((error= mysql_bin_log.write(thd, cache_log, &qev,
- cache_mngr->stmt_cache.has_incident())))
- DBUG_RETURN(error);
- cache_mngr->reset_cache(&cache_mngr->stmt_cache);
-
- statistic_increment(binlog_cache_use, &LOCK_status);
- if (cache_log->disk_writes != 0)
- {
- statistic_increment(binlog_cache_disk_use, &LOCK_status);
- cache_log->disk_writes= 0;
- }
- DBUG_RETURN(error);
-}
-
-/**
This function is called once after each statement.
It has the responsibility to flush the caches to the binary log on commits.
@@ -1692,7 +1798,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
if (!cache_mngr->stmt_cache.empty())
{
- binlog_flush_stmt_cache(thd, cache_mngr);
+ error= binlog_commit_flush_stmt_cache(thd, cache_mngr);
}
if (cache_mngr->trx_cache.empty())
@@ -1701,7 +1807,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
*/
cache_mngr->reset_cache(&cache_mngr->trx_cache);
- DBUG_RETURN(0);
+ DBUG_RETURN(error);
}
/*
@@ -1710,17 +1816,15 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
- We are in a transaction and a full transaction is committed.
Otherwise, we accumulate the changes.
*/
- if (ending_trans(thd, all))
- {
- Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE, TRUE, 0);
- error= binlog_flush_trx_cache(thd, cache_mngr, &qev);
- }
+ if (!error && ending_trans(thd, all))
+ error= binlog_commit_flush_trx_cache(thd, cache_mngr);
/*
This is part of the stmt rollback.
*/
if (!all)
cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
+
DBUG_RETURN(error);
}
@@ -1737,7 +1841,7 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
static int binlog_rollback(handlerton *hton, THD *thd, bool all)
{
DBUG_ENTER("binlog_rollback");
- int error=0;
+ int error= 0;
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
@@ -1757,16 +1861,16 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
}
else if (!cache_mngr->stmt_cache.empty())
{
- binlog_flush_stmt_cache(thd, cache_mngr);
+ error= binlog_commit_flush_stmt_cache(thd, cache_mngr);
}
if (cache_mngr->trx_cache.empty())
{
- /*
+ /*
we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
*/
cache_mngr->reset_cache(&cache_mngr->trx_cache);
- DBUG_RETURN(0);
+ DBUG_RETURN(error);
}
if (mysql_bin_log.check_write_error(thd))
@@ -1782,9 +1886,9 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
We reach this point if the effect of a statement did not properly get into
a cache and need to be rolled back.
*/
- error= binlog_truncate_trx_cache(thd, cache_mngr, all);
+ error |= binlog_truncate_trx_cache(thd, cache_mngr, all);
}
- else
+ else if (!error)
{
/*
We flush the cache wrapped in a beging/rollback if:
@@ -1796,7 +1900,6 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
. the format is MIXED, non-trans table was updated and
aborting a single statement transaction;
*/
-
if (ending_trans(thd, all) &&
((thd->variables.option_bits & OPTION_KEEP_LOG) ||
(trans_has_updated_non_trans_table(thd) &&
@@ -1806,10 +1909,7 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
(trans_has_updated_non_trans_table(thd) &&
ending_single_stmt_trans(thd,all) &&
thd->variables.binlog_format == BINLOG_FORMAT_MIXED)))
- {
- Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE, TRUE, 0);
- error= binlog_flush_trx_cache(thd, cache_mngr, &qev);
- }
+ error= binlog_rollback_flush_trx_cache(thd, cache_mngr);
/*
Truncate the cache if:
. aborting a single or multi-statement transaction or;
@@ -1832,11 +1932,12 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
This is part of the stmt rollback.
*/
if (!all)
- cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
+ cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
+
DBUG_RETURN(error);
}
-void MYSQL_BIN_LOG::set_write_error(THD *thd)
+void MYSQL_BIN_LOG::set_write_error(THD *thd, bool is_transactional)
{
DBUG_ENTER("MYSQL_BIN_LOG::set_write_error");
@@ -1846,9 +1947,20 @@ void MYSQL_BIN_LOG::set_write_error(THD *thd)
DBUG_VOID_RETURN;
if (my_errno == EFBIG)
- my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(MY_WME));
+ {
+ if (is_transactional)
+ {
+ my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(MY_WME));
+ }
+ else
+ {
+ my_message(ER_STMT_CACHE_FULL, ER(ER_STMT_CACHE_FULL), MYF(MY_WME));
+ }
+ }
else
+ {
my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), name, errno);
+ }
DBUG_VOID_RETURN;
}
@@ -1865,6 +1977,7 @@ bool MYSQL_BIN_LOG::check_write_error(THD *thd)
switch (thd->stmt_da->sql_errno())
{
case ER_TRANS_CACHE_FULL:
+ case ER_STMT_CACHE_FULL:
case ER_ERROR_ON_WRITE:
case ER_BINLOG_LOGGING_IMPOSSIBLE:
checked= TRUE;
@@ -4370,7 +4483,7 @@ int THD::binlog_setup_trx_data()
cache_mngr= (binlog_cache_mngr*) my_malloc(sizeof(binlog_cache_mngr), MYF(MY_ZEROFILL));
if (!cache_mngr ||
open_cached_file(&cache_mngr->stmt_cache.cache_log, mysql_tmpdir,
- LOG_PREFIX, binlog_cache_size, MYF(MY_WME)) ||
+ LOG_PREFIX, binlog_stmt_cache_size, MYF(MY_WME)) ||
open_cached_file(&cache_mngr->trx_cache.cache_log, mysql_tmpdir,
LOG_PREFIX, binlog_cache_size, MYF(MY_WME)))
{
@@ -4379,8 +4492,13 @@ int THD::binlog_setup_trx_data()
}
thd_set_ha_data(this, binlog_hton, cache_mngr);
- cache_mngr= new (thd_get_ha_data(this, binlog_hton)) binlog_cache_mngr;
-
+ cache_mngr= new (thd_get_ha_data(this, binlog_hton))
+ binlog_cache_mngr(max_binlog_stmt_cache_size,
+ max_binlog_cache_size,
+ &binlog_stmt_cache_use,
+ &binlog_stmt_cache_disk_use,
+ &binlog_cache_use,
+ &binlog_cache_disk_use);
DBUG_RETURN(0);
}
@@ -4632,7 +4750,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
*/
if (pending->write(file))
{
- set_write_error(thd);
+ set_write_error(thd, is_transactional);
if (check_write_error(thd) && cache_data &&
stmt_has_updated_non_trans_table(thd))
cache_data->set_incident();
@@ -4657,6 +4775,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
bool error= 1;
DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)");
binlog_cache_data *cache_data= 0;
+ bool is_trans_cache= FALSE;
if (thd->binlog_evt_union.do_union)
{
@@ -4717,7 +4836,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info)
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
- bool is_trans_cache= use_trans_cache(thd, event_info->use_trans_cache());
+ is_trans_cache= use_trans_cache(thd, event_info->use_trans_cache());
file= cache_mngr->get_binlog_cache_log(is_trans_cache);
cache_data= cache_mngr->get_binlog_cache_data(is_trans_cache);
@@ -4823,7 +4942,7 @@ unlock:
if (error)
{
- set_write_error(thd);
+ set_write_error(thd, is_trans_cache);
if (check_write_error(thd) && cache_data &&
stmt_has_updated_non_trans_table(thd))
cache_data->set_incident();
@@ -6322,15 +6441,14 @@ void TC_LOG_BINLOG::close()
int TC_LOG_BINLOG::log_xid(THD *thd, my_xid xid)
{
DBUG_ENTER("TC_LOG_BINLOG::log");
- Xid_log_event xle(thd, xid);
binlog_cache_mngr *cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
/*
We always commit the entire transaction when writing an XID. Also
note that the return value is inverted.
*/
- DBUG_RETURN(!binlog_flush_stmt_cache(thd, cache_mngr) &&
- !binlog_flush_trx_cache(thd, cache_mngr, &xle));
+ DBUG_RETURN(!binlog_commit_flush_stmt_cache(thd, cache_mngr) &&
+ !binlog_commit_flush_trx_cache(thd, cache_mngr, xid));
}
void TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)