diff options
Diffstat (limited to 'sql/log.cc')
-rw-r--r-- | sql/log.cc | 368 |
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) |