diff options
Diffstat (limited to 'sql/log.cc')
-rw-r--r-- | sql/log.cc | 311 |
1 files changed, 172 insertions, 139 deletions
diff --git a/sql/log.cc b/sql/log.cc index 6f08a924116..90305ec227e 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2953,7 +2953,8 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period) bytes_written(0), file_id(1), open_count(1), group_commit_queue(0), group_commit_queue_busy(FALSE), num_commits(0), num_group_commits(0), - sync_period_ptr(sync_period), sync_counter(0), state_read(false), + sync_period_ptr(sync_period), sync_counter(0), + state_file_deleted(false), binlog_state_recover_done(false), is_relay_log(0), signal_cnt(0), checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF), relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF), @@ -3155,12 +3156,19 @@ bool MYSQL_BIN_LOG::open(const char *log_name, DBUG_ENTER("MYSQL_BIN_LOG::open"); DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg)); - if (!is_relay_log && read_state_from_file()) - DBUG_RETURN(1); + if (!is_relay_log) + { + if (!binlog_state_recover_done) + { + binlog_state_recover_done= true; + if (do_binlog_recovery(opt_bin_logname, false)) + DBUG_RETURN(1); + } - if (!is_relay_log && !binlog_background_thread_started && - start_binlog_background_thread()) - DBUG_RETURN(1); + if (!binlog_background_thread_started && + start_binlog_background_thread()) + DBUG_RETURN(1); + } if (init_and_set_log_file_name(log_name, new_name, log_type_arg, io_cache_type_arg)) @@ -3447,6 +3455,25 @@ bool MYSQL_BIN_LOG::open(const char *log_name, my_free(binlog_xid_count_list.get()); binlog_xid_count_list.push_back(new_xid_list_entry); mysql_mutex_unlock(&LOCK_xid_list); + + /* + Now that we have synced a new binlog file with an initial Gtid_list + event, it is safe to delete the binlog state file. We will write out + a new, updated file at shutdown, and if we crash before we can recover + the state from the newly written binlog file. + + Since the state file will contain out-of-date data as soon as the first + new GTID is binlogged, it is better to remove it, to avoid any risk of + accidentally reading incorrect data later. + */ + if (!state_file_deleted) + { + char buf[FN_REFLEN]; + fn_format(buf, opt_bin_logname, mysql_data_home, ".state", + MY_UNPACK_FILENAME); + my_delete(buf, MY_SYNC_DIR); + state_file_deleted= true; + } } log_state= LOG_OPENED; @@ -5422,19 +5449,15 @@ MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone, gtid.domain_id= domain_id; gtid.server_id= server_id; gtid.seq_no= seq_no; - mysql_mutex_lock(&LOCK_rpl_gtid_state); err= rpl_global_gtid_binlog_state.update(>id, opt_gtid_strict_mode); - mysql_mutex_unlock(&LOCK_rpl_gtid_state); if (err && thd->get_stmt_da()->sql_errno()==ER_GTID_STRICT_OUT_OF_ORDER) errno= ER_GTID_STRICT_OUT_OF_ORDER; } else { /* Allocate the next sequence number for the GTID. */ - mysql_mutex_lock(&LOCK_rpl_gtid_state); err= rpl_global_gtid_binlog_state.update_with_next_gtid(domain_id, server_id, >id); - mysql_mutex_unlock(&LOCK_rpl_gtid_state); seq_no= gtid.seq_no; } if (err) @@ -5508,10 +5531,6 @@ MYSQL_BIN_LOG::read_state_from_file() bool opened= false; bool inited= false; - if (state_read) - return 0; - state_read= true; - fn_format(buf, opt_bin_logname, mysql_data_home, ".state", MY_UNPACK_FILENAME); if ((file_no= mysql_file_open(key_file_binlog_state, buf, @@ -5564,36 +5583,21 @@ MYSQL_BIN_LOG::get_most_recent_gtid_list(rpl_gtid **list, uint32 *size) bool MYSQL_BIN_LOG::append_state_pos(String *str) { - bool err; - - mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); - err= rpl_global_gtid_binlog_state.append_pos(str); - mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); - return err; + return rpl_global_gtid_binlog_state.append_pos(str); } bool MYSQL_BIN_LOG::append_state(String *str) { - bool err; - - mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); - err= rpl_global_gtid_binlog_state.append_state(str); - mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); - return err; + return rpl_global_gtid_binlog_state.append_state(str); } bool MYSQL_BIN_LOG::is_empty_state() { - bool res; - - mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); - res= (rpl_global_gtid_binlog_state.count() == 0); - mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); - return res; + return (rpl_global_gtid_binlog_state.count() == 0); } @@ -5602,10 +5606,8 @@ MYSQL_BIN_LOG::find_in_binlog_state(uint32 domain_id, uint32 server_id, rpl_gtid *out_gtid) { rpl_gtid *gtid; - mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); if ((gtid= rpl_global_gtid_binlog_state.find(domain_id, server_id))) *out_gtid= *gtid; - mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); return gtid != NULL; } @@ -5615,29 +5617,21 @@ MYSQL_BIN_LOG::lookup_domain_in_binlog_state(uint32 domain_id, rpl_gtid *out_gtid) { rpl_gtid *found_gtid; - bool res= false; - mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); if ((found_gtid= rpl_global_gtid_binlog_state.find_most_recent(domain_id))) { *out_gtid= *found_gtid; - res= true; + return true; } - mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); - return res; + return false; } int MYSQL_BIN_LOG::bump_seq_no_counter_if_needed(uint32 domain_id, uint64 seq_no) { - int err; - - mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); - err= rpl_global_gtid_binlog_state.bump_seq_no_if_needed(domain_id, seq_no); - mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); - return err; + return rpl_global_gtid_binlog_state.bump_seq_no_if_needed(domain_id, seq_no); } @@ -5645,13 +5639,8 @@ bool MYSQL_BIN_LOG::check_strict_gtid_sequence(uint32 domain_id, uint32 server_id, uint64 seq_no) { - bool err; - - mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); - err= rpl_global_gtid_binlog_state.check_strict_sequence(domain_id, server_id, - seq_no); - mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state); - return err; + return rpl_global_gtid_binlog_state.check_strict_sequence(domain_id, + server_id, seq_no); } @@ -8608,7 +8597,6 @@ int TC_LOG::using_heuristic_recover() int TC_LOG_BINLOG::open(const char *opt_name) { - LOG_INFO log_info; int error= 1; DBUG_ASSERT(total_ha_2pc > 1); @@ -8629,65 +8617,8 @@ int TC_LOG_BINLOG::open(const char *opt_name) return 1; } - if ((error= find_log_pos(&log_info, NullS, 1))) - { - if (error != LOG_INFO_EOF) - sql_print_error("find_log_pos() failed (error: %d)", error); - else - error= 0; - goto err; - } - - { - const char *errmsg; - IO_CACHE log; - File file; - Log_event *ev=0; - Format_description_log_event fdle(BINLOG_VERSION); - char log_name[FN_REFLEN]; - - if (! fdle.is_valid()) - goto err; - - do - { - strmake_buf(log_name, log_info.log_file_name); - } while (!(error= find_next_log(&log_info, 1))); - - if (error != LOG_INFO_EOF) - { - sql_print_error("find_log_pos() failed (error: %d)", error); - goto err; - } - - if ((file= open_binlog(&log, log_name, &errmsg)) < 0) - { - sql_print_error("%s", errmsg); - goto err; - } - - if ((ev= Log_event::read_log_event(&log, 0, &fdle, - opt_master_verify_checksum)) && - ev->get_type_code() == FORMAT_DESCRIPTION_EVENT && - ev->flags & LOG_EVENT_BINLOG_IN_USE_F) - { - sql_print_information("Recovering after a crash using %s", opt_name); - error= recover(&log_info, log_name, &log, - (Format_description_log_event *)ev); - state_read= true; - } - else - error= read_state_from_file(); - - delete ev; - end_io_cache(&log); - mysql_file_close(file, MYF(MY_WME)); - - if (error) - goto err; - } - -err: + error= do_binlog_recovery(opt_name, true); + binlog_state_recover_done= true; return error; } @@ -9064,9 +8995,9 @@ start_binlog_background_thread() int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE *first_log, - Format_description_log_event *fdle) + Format_description_log_event *fdle, bool do_xa) { - Log_event *ev; + Log_event *ev= NULL; HASH xids; MEM_ROOT mem_root; char binlog_checkpoint_name[FN_REFLEN]; @@ -9075,13 +9006,19 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE log; File file= -1; const char *errmsg; +#ifdef HAVE_REPLICATION + rpl_gtid last_gtid; + bool last_gtid_standalone= false; + bool last_gtid_valid= false; +#endif if (! fdle->is_valid() || - my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0, - sizeof(my_xid), 0, 0, MYF(0))) + (do_xa && my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0, + sizeof(my_xid), 0, 0, MYF(0)))) goto err1; - init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE, MYF(0)); + if (do_xa) + init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE, MYF(0)); fdle->flags&= ~LOG_EVENT_BINLOG_IN_USE_F; // abort on the first error @@ -9101,22 +9038,23 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, 0, fdle, opt_master_verify_checksum)) && ev->is_valid()) { - switch (ev->get_type_code()) + enum Log_event_type typ= ev->get_type_code(); + switch (typ) { case XID_EVENT: { - Xid_log_event *xev=(Xid_log_event *)ev; - uchar *x= (uchar *) memdup_root(&mem_root, (uchar*) &xev->xid, - sizeof(xev->xid)); - if (!x || my_hash_insert(&xids, x)) + if (do_xa) { - delete ev; - goto err2; + Xid_log_event *xev=(Xid_log_event *)ev; + uchar *x= (uchar *) memdup_root(&mem_root, (uchar*) &xev->xid, + sizeof(xev->xid)); + if (!x || my_hash_insert(&xids, x)) + goto err2; + break; } - break; } case BINLOG_CHECKPOINT_EVENT: - if (first_round) + if (first_round && do_xa) { uint dir_len; Binlog_checkpoint_log_event *cev= (Binlog_checkpoint_log_event *)ev; @@ -9134,8 +9072,8 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, cev->binlog_file_name, FN_REFLEN - 1 - dir_len); binlog_checkpoint_found= true; } - break; } + break; case GTID_LIST_EVENT: if (first_round) { @@ -9147,28 +9085,49 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, } break; +#ifdef HAVE_REPLICATION case GTID_EVENT: if (first_round) { Gtid_log_event *gev= (Gtid_log_event *)ev; - rpl_gtid gtid; /* Update the binlog state with any GTID logged after Gtid_list. */ - gtid.domain_id= gev->domain_id; - gtid.server_id= gev->server_id; - gtid.seq_no= gev->seq_no; - if (rpl_global_gtid_binlog_state.update(>id, false)) - goto err2; + last_gtid.domain_id= gev->domain_id; + last_gtid.server_id= gev->server_id; + last_gtid.seq_no= gev->seq_no; + last_gtid_standalone= + ((gev->flags2 & Gtid_log_event::FL_STANDALONE) ? true : false); + last_gtid_valid= true; } break; +#endif default: /* Nothing. */ break; } + +#ifdef HAVE_REPLICATION + if (last_gtid_valid && + ((last_gtid_standalone && !ev->is_part_of_group(typ)) || + (!last_gtid_standalone && + (typ == XID_EVENT || + (typ == QUERY_EVENT && + (((Query_log_event *)ev)->is_commit() || + ((Query_log_event *)ev)->is_rollback())))))) + { + if (rpl_global_gtid_binlog_state.update_nolock(&last_gtid, false)) + goto err2; + last_gtid_valid= false; + } +#endif + delete ev; + ev= NULL; } + if (!do_xa) + break; /* If the last binlog checkpoint event points to an older log, we have to scan all logs from there also, to get all possible XIDs to recover. @@ -9221,21 +9180,28 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name, } } - if (ha_recover(&xids)) - goto err2; + if (do_xa) + { + if (ha_recover(&xids)) + goto err2; - free_root(&mem_root, MYF(0)); - my_hash_free(&xids); + free_root(&mem_root, MYF(0)); + my_hash_free(&xids); + } return 0; err2: + delete ev; if (file >= 0) { end_io_cache(&log); mysql_file_close(file, MYF(MY_WME)); } - free_root(&mem_root, MYF(0)); - my_hash_free(&xids); + if (do_xa) + { + free_root(&mem_root, MYF(0)); + my_hash_free(&xids); + } err1: sql_print_error("Crash recovery failed. Either correct the problem " "(if it's, for example, out of memory error) and restart, " @@ -9245,6 +9211,73 @@ err1: } +int +MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery) +{ + LOG_INFO log_info; + const char *errmsg; + IO_CACHE log; + File file; + Log_event *ev= 0; + Format_description_log_event fdle(BINLOG_VERSION); + char log_name[FN_REFLEN]; + int error; + + if ((error= find_log_pos(&log_info, NullS, 1))) + { + /* + If there are no binlog files (LOG_INFO_EOF), then we still try to read + the .state file to restore the binlog state. This allows to copy a server + to provision a new one without copying the binlog files (except the + master-bin.state file) and still preserve the correct binlog state. + */ + if (error != LOG_INFO_EOF) + sql_print_error("find_log_pos() failed (error: %d)", error); + else + error= read_state_from_file(); + return error; + } + + if (! fdle.is_valid()) + return 1; + + do + { + strmake_buf(log_name, log_info.log_file_name); + } while (!(error= find_next_log(&log_info, 1))); + + if (error != LOG_INFO_EOF) + { + sql_print_error("find_log_pos() failed (error: %d)", error); + return error; + } + + if ((file= open_binlog(&log, log_name, &errmsg)) < 0) + { + sql_print_error("%s", errmsg); + return 1; + } + + if ((ev= Log_event::read_log_event(&log, 0, &fdle, + opt_master_verify_checksum)) && + ev->get_type_code() == FORMAT_DESCRIPTION_EVENT && + ev->flags & LOG_EVENT_BINLOG_IN_USE_F) + { + sql_print_information("Recovering after a crash using %s", opt_name); + error= recover(&log_info, log_name, &log, + (Format_description_log_event *)ev, do_xa_recovery); + } + else + error= read_state_from_file(); + + delete ev; + end_io_cache(&log); + mysql_file_close(file, MYF(MY_WME)); + + return error; +} + + #ifdef INNODB_COMPATIBILITY_HOOKS /** Get the file name of the MySQL binlog. |