diff options
29 files changed, 319 insertions, 89 deletions
diff --git a/client/mysqlslap.c b/client/mysqlslap.c index 5ca0b1cd207..9904eeae9d0 100644 --- a/client/mysqlslap.c +++ b/client/mysqlslap.c @@ -1886,10 +1886,17 @@ limit_not_met: { if (mysql_field_count(mysql)) { - result= mysql_store_result(mysql); - while ((row = mysql_fetch_row(result))) - counter++; - mysql_free_result(result); + if ((result= mysql_store_result(mysql))) + { + while ((row = mysql_fetch_row(result))) + counter++; + mysql_free_result(result); + } + else + { + fprintf(stderr,"%s: Error in mysql_store_result(): %d %s\n", + my_progname, mysql_errno(mysql), mysql_error(mysql)); + } } } while(mysql_next_result(mysql) == 0); queries++; diff --git a/dbug/dbug.c b/dbug/dbug.c index 9a4a27e9f43..fc2faf867e4 100644 --- a/dbug/dbug.c +++ b/dbug/dbug.c @@ -177,7 +177,11 @@ static void perror(); /* Fake system/library error print routine */ #endif +#ifdef SAFEMALLOC IMPORT int _sanity(const char *file,uint line); /* safemalloc sanity checker */ +#else +#define _sanity(X,Y) (1) +#endif /* * The user may specify a list of functions to trace or @@ -287,7 +291,7 @@ static void PushState(CODE_STATE *cs); /* Free memory associated with debug state. */ static void FreeState (CODE_STATE *cs, struct settings *state, int free_state); /* Test for tracing enabled */ -static int DoTrace(CODE_STATE *cs, int tracing); +static int DoTrace(CODE_STATE *cs); /* return values of DoTrace. Can also be used as bitmask: ret & DO_TRACE @@ -726,7 +730,7 @@ void FixTraceFlags_helper(CODE_STATE *cs, const char *func, It's ok, because cs->framep may only affect DO_TRACE/DONT_TRACE return values, but we ignore them here anyway */ - switch(DoTrace(cs, 1)) { + switch(DoTrace(cs)) { case ENABLE_TRACE: framep->level|= TRACE_ON; break; @@ -1153,18 +1157,23 @@ void _db_enter_(const char *_func_, const char *_file_, (void) fflush(cs->stack->prof_file); } #endif - switch (DoTrace(cs, TRACING)) { + switch (DoTrace(cs)) { case ENABLE_TRACE: cs->framep->level|= TRACE_ON; if (!TRACING) break; /* fall through */ case DO_TRACE: - if (!cs->locked) - pthread_mutex_lock(&THR_LOCK_dbug); - DoPrefix(cs, _line_); - Indent(cs, cs->level); - (void) fprintf(cs->stack->out_file, ">%s\n", cs->func); - DbugFlush(cs); /* This does a unlock */ + if ((cs->stack->flags & SANITY_CHECK_ON) && _sanity(_file_,_line_)) + cs->stack->flags &= ~SANITY_CHECK_ON; + if (TRACING) + { + if (!cs->locked) + pthread_mutex_lock(&THR_LOCK_dbug); + DoPrefix(cs, _line_); + Indent(cs, cs->level); + (void) fprintf(cs->stack->out_file, ">%s\n", cs->func); + DbugFlush(cs); /* This does a unlock */ + } break; case DISABLE_TRACE: cs->framep->level&= ~TRACE_ON; @@ -1172,11 +1181,6 @@ void _db_enter_(const char *_func_, const char *_file_, case DONT_TRACE: break; } -#ifdef SAFEMALLOC - if (cs->stack->flags & SANITY_CHECK_ON) - if (_sanity(_file_,_line_)) /* Check of safemalloc */ - cs->stack->flags &= ~SANITY_CHECK_ON; -#endif errno=save_errno; } @@ -1218,25 +1222,24 @@ void _db_return_(uint _line_, struct _db_stack_frame_ *_stack_frame_) } else { -#ifdef SAFEMALLOC - if (cs->stack->flags & SANITY_CHECK_ON) - { - if (_sanity(_stack_frame_->file,_line_)) - cs->stack->flags &= ~SANITY_CHECK_ON; - } -#endif #ifndef THREAD if (DoProfile(cs)) (void) fprintf(cs->stack->prof_file, PROF_XFMT, Clock(), cs->func); #endif - if (TRACING && DoTrace(cs, 1) & DO_TRACE) + if (DoTrace(cs) & DO_TRACE) { - if (!cs->locked) - pthread_mutex_lock(&THR_LOCK_dbug); - DoPrefix(cs, _line_); - Indent(cs, cs->level); - (void) fprintf(cs->stack->out_file, "<%s\n", cs->func); - DbugFlush(cs); + if ((cs->stack->flags & SANITY_CHECK_ON) && + _sanity(_stack_frame_->file,_line_)) + cs->stack->flags &= ~SANITY_CHECK_ON; + if (TRACING) + { + if (!cs->locked) + pthread_mutex_lock(&THR_LOCK_dbug); + DoPrefix(cs, _line_); + Indent(cs, cs->level); + (void) fprintf(cs->stack->out_file, "<%s\n", cs->func); + DbugFlush(cs); + } } } /* @@ -1694,28 +1697,24 @@ void _db_end_() * * DoTrace check to see if tracing is current enabled * - * tracing is the value of TRACING to check if the tracing is enabled - * or 1 to check if the function is enabled (in _db_keyword_) - * * DESCRIPTION * - * Checks to see if tracing is enabled based on whether the - * user has specified tracing, the maximum trace depth has - * not yet been reached, the current function is selected, - * and the current process is selected. + * Checks to see if dbug in this function is enabled based on + * whether the maximum trace depth has been reached, the current + * function is selected, and the current process is selected. * */ -static int DoTrace(CODE_STATE *cs, int tracing) +static int DoTrace(CODE_STATE *cs) { if ((cs->stack->maxdepth == 0 || cs->level <= cs->stack->maxdepth) && InList(cs->stack->processes, cs->process) & (MATCHED|INCLUDE)) switch(InList(cs->stack->functions, cs->func)) { case INCLUDE|SUBDIR: return ENABLE_TRACE; - case INCLUDE: return tracing ? DO_TRACE : DONT_TRACE; + case INCLUDE: return DO_TRACE; case MATCHED|SUBDIR: case NOT_MATCHED|SUBDIR: - case MATCHED: return tracing && framep_trace_flag(cs, cs->framep) ? + case MATCHED: return framep_trace_flag(cs, cs->framep) ? DO_TRACE : DONT_TRACE; case EXCLUDE: case NOT_MATCHED: return DONT_TRACE; @@ -1786,7 +1785,7 @@ BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict) get_code_state_if_not_set_or_return FALSE; strict=strict ? INCLUDE : INCLUDE|MATCHED; - return DEBUGGING && DoTrace(cs, 1) & DO_TRACE && + return DEBUGGING && DoTrace(cs) & DO_TRACE && InList(cs->stack->keywords, keyword) & strict; } diff --git a/mysql-test/include/maria_empty_logs.inc b/mysql-test/include/maria_empty_logs.inc index 7e3f2181897..0783020c443 100644 --- a/mysql-test/include/maria_empty_logs.inc +++ b/mysql-test/include/maria_empty_logs.inc @@ -13,7 +13,7 @@ append_file $MYSQLTEST_VARDIR/tmp/master0.expect; wait-maria_empty_logs.inc EOF ---exec $MYSQLADMIN --no-defaults -S $MASTER_MYSOCK -P $MASTER_MYPORT -u root --password= shutdown 2>&1; +--source include/mysqladmin_shutdown.inc if (!$mel_keep_control_file) { diff --git a/mysql-test/include/mysqladmin_shutdown.inc b/mysql-test/include/mysqladmin_shutdown.inc new file mode 100644 index 00000000000..16b33c2baf8 --- /dev/null +++ b/mysql-test/include/mysqladmin_shutdown.inc @@ -0,0 +1,7 @@ +# Initiates a clean shutdown of the server and waits for its completion + +--exec $MYSQLADMIN --no-defaults -S $MASTER_MYSOCK -P $MASTER_MYPORT -u root --password= shutdown 2>&1; + +# On Windows mysqladmin does not wait for shutdown to be finished, +# so we have to monitor this with our connection: +--source include/wait_until_disconnected.inc diff --git a/mysql-test/include/wait_until_connected_again.inc b/mysql-test/include/wait_until_connected_again.inc index 02d544cb966..4379f4d3145 100644 --- a/mysql-test/include/wait_until_connected_again.inc +++ b/mysql-test/include/wait_until_connected_again.inc @@ -2,6 +2,9 @@ # Include this script to wait until the connection to the # server has been restored or timeout occurs. # You should have done --enable_reconnect first +# When you change this file you may have to chance its cousin +# wait_until_disconnected.inc + --disable_result_log --disable_query_log let $counter= 5000; diff --git a/mysql-test/include/wait_until_disconnected.inc b/mysql-test/include/wait_until_disconnected.inc new file mode 100644 index 00000000000..cf4a574573f --- /dev/null +++ b/mysql-test/include/wait_until_disconnected.inc @@ -0,0 +1,24 @@ +# +# Include this script after a shutdown to wait until the connection +# to the server has been lost or timeout occurs. +# When you change this file you may have to chance its cousin +# wait_until_connected_again.inc + +--disable_result_log +--disable_query_log +let $counter= 5000; +let $mysql_errno= 0; +while (!$mysql_errno) +{ + --error 0,2002,2003,2006,1053 + show status; + + dec $counter; + if (!$counter) + { + --die Server failed to disconnect me + } + --sleep 0.1 +} +--enable_query_log +--enable_result_log diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 5dbef8d0957..bb7c2f053a8 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -4006,11 +4006,13 @@ sub mysqld_arguments ($$$$) { } my $log_base_path= "$opt_vardir/log/$mysqld->{'type'}$sidx"; - mtr_add_arg($args, "%s--general-log-file=%s.log --general-log", + mtr_add_arg($args, "%s--general-log-file=%s.log", $prefix, $log_base_path); + mtr_add_arg($args, "--general-log"); mtr_add_arg($args, - "%s--slow-query-log-file=%s-slow.log --slow-query-log", + "%s--slow-query-log-file=%s-slow.log", $prefix, $log_base_path); + mtr_add_arg($args, "--slow-query-log"); # Check if "extra_opt" contains --skip-log-bin my $skip_binlog= grep(/^--skip-log-bin/, @$extra_opt, @opt_extra_mysqld_opt); @@ -5113,8 +5115,9 @@ sub gdb_arguments { else { # write init file for mysqld - mtr_tofile($gdb_init_file, - "set args $str\n"); + mtr_tofile($gdb_init_file, <<EOGDB ); +set args $str +EOGDB } if ( $opt_manual_gdb ) diff --git a/mysql-test/suite/maria/r/maria-recovery3.result b/mysql-test/suite/maria/r/maria-recovery3.result new file mode 100644 index 00000000000..c1acef57a5a --- /dev/null +++ b/mysql-test/suite/maria/r/maria-recovery3.result @@ -0,0 +1,29 @@ +set global maria_log_file_size=4294967295; +drop database if exists mysqltest; +create database mysqltest; +use mysqltest; +* shut down mysqld, removed logs, restarted it +* TEST of Checkpoint between writing the commit log record and committing in trnman +create table t1(a int primary key) engine=maria; +insert into t1 values(1); +flush table t1; +* copied t1 for comparison +set session debug="+d,maria_sleep_in_commit"; +insert into t1 values(2); +set global maria_checkpoint_interval=1000; +delete from t1 where a=2; +SET SESSION debug="+d,maria_flush_whole_log,maria_crash"; +* crashing mysqld intentionally +set global maria_checkpoint_interval=1; +ERROR HY000: Lost connection to MySQL server during query +* recovery happens +check table t1 extended; +Table Op Msg_type Msg_text +mysqltest.t1 check status OK +* testing that checksum after recovery is as expected +Checksum-check +ok +use mysqltest; +drop table t1; +drop database mysqltest_for_comparison; +drop database mysqltest; diff --git a/mysql-test/suite/maria/t/maria-recovery3-master.opt b/mysql-test/suite/maria/t/maria-recovery3-master.opt new file mode 100644 index 00000000000..9023fb74e8b --- /dev/null +++ b/mysql-test/suite/maria/t/maria-recovery3-master.opt @@ -0,0 +1 @@ +--skip-stack-trace --skip-core-file --loose-maria-log-dir-path=$MYSQLTEST_VARDIR/tmp diff --git a/mysql-test/suite/maria/t/maria-recovery3.test b/mysql-test/suite/maria/t/maria-recovery3.test new file mode 100644 index 00000000000..68cc67fd328 --- /dev/null +++ b/mysql-test/suite/maria/t/maria-recovery3.test @@ -0,0 +1,71 @@ +--source include/not_embedded.inc +# Don't test this under valgrind, memory leaks will occur as we crash +--source include/not_valgrind.inc +# Binary must be compiled with debug for crash to occur +--source include/have_debug.inc +--source include/have_maria.inc + +set global maria_log_file_size=4294967295; +let $MARIA_LOG=../tmp; + +--disable_warnings +drop database if exists mysqltest; +--enable_warnings +create database mysqltest; +let $mms_tname=t; + +# Include scripts can perform SQL. For it to not influence the main test +# they use a separate connection. This way if they use a DDL it would +# not autocommit in the main test. +connect (admin, 127.0.0.1, root,,mysqltest,,); +--enable_reconnect + +connection default; +use mysqltest; +--enable_reconnect + +let $mms_tables=1; +let $mvr_restore_old_snapshot=0; +let $mms_compare_physically=0; +let $mvr_debug_option="+d,maria_flush_whole_log,maria_crash"; +let $mvr_crash_statement= set global maria_checkpoint_interval=1; + +-- source include/maria_empty_logs.inc + +# Test for BUG#41037 (recovery failure) +--echo * TEST of Checkpoint between writing the commit log record and committing in trnman +# we want recovery to use the tables as they were at time of crash +let $mvr_restore_old_snapshot=0; +# UNDO phase prevents physical comparison, normally, +# so we'll only use checksums to compare. +let $mms_compare_physically=0; +let $mvr_crash_statement= set global maria_checkpoint_interval=1; +create table t1(a int primary key) engine=maria; +insert into t1 values(1); +-- source include/maria_make_snapshot_for_comparison.inc +set session debug="+d,maria_sleep_in_commit"; +send insert into t1 values(2); +sleep 1; +# Now the INSERT of 2 has written a commit record +# but not yet called trnman_commit(), so for checkpoint it's not +# committed. +connection admin; +set global maria_checkpoint_interval=1000; # force a checkpoint +connection default; +reap; # end of INSERT +delete from t1 where a=2; +# Bug was that: Recovery starts REDO scanning from too far: from +# Checkpoint record which says INSERT is not committed, so +# Recovery executes the INSERT's UNDO and finds no key to delete +# (as DELETE already deleted it), fails. +-- source include/maria_verify_recovery.inc +drop table t1; + +# Note that even if machine is loaded and thus INSERT is committed +# before checkpoint happens, test should still pass (though it won't +# reproduce the conditions of the bug). + +# clean up everything +let $mms_purpose=comparison; +eval drop database mysqltest_for_$mms_purpose; +drop database mysqltest; diff --git a/mysql-test/suite/maria/t/maria_showlog_error.test b/mysql-test/suite/maria/t/maria_showlog_error.test index 4db78a35b38..d5eded903cd 100644 --- a/mysql-test/suite/maria/t/maria_showlog_error.test +++ b/mysql-test/suite/maria/t/maria_showlog_error.test @@ -1,4 +1,6 @@ -- source include/have_maria.inc +# can't restart server in embedded +--source include/not_embedded.inc # remove_file can't remove opened file under windows. So we can't reproduce # the problem there --source include/not_windows.inc diff --git a/mysql-test/t/events_logs_tests-master.opt b/mysql-test/t/events_logs_tests-master.opt index 35ff7911705..73d26b7260d 100644 --- a/mysql-test/t/events_logs_tests-master.opt +++ b/mysql-test/t/events_logs_tests-master.opt @@ -1 +1 @@ ---log-slow-queries +--slow-query-log --log-output=table --general-log --general-log-file="" --slow-query-log-file="" diff --git a/mysql-test/t/log_tables-big-master.opt b/mysql-test/t/log_tables-big-master.opt index 35ff7911705..b9bc885d0e4 100644 --- a/mysql-test/t/log_tables-big-master.opt +++ b/mysql-test/t/log_tables-big-master.opt @@ -1 +1 @@ ---log-slow-queries +--log-output=table,file --slow-query-log --general-log --general-log-file="" --slow-query-log-file="" diff --git a/mysql-test/t/log_tables-master.opt b/mysql-test/t/log_tables-master.opt index 35ff7911705..4c25518cefe 100644 --- a/mysql-test/t/log_tables-master.opt +++ b/mysql-test/t/log_tables-master.opt @@ -1 +1 @@ ---log-slow-queries +--log-output=table,file --slow-query-log --general-log diff --git a/mysql-test/t/multi_statement-master.opt b/mysql-test/t/multi_statement-master.opt index b30df037531..c735cf4c21b 100644 --- a/mysql-test/t/multi_statement-master.opt +++ b/mysql-test/t/multi_statement-master.opt @@ -1,2 +1,3 @@ ---log-slow-queries=slow.log +--slow-query-log +--slow-query-log-file=slow.log --log-queries-not-using-indexes diff --git a/mysql-test/t/ps-master.opt b/mysql-test/t/ps-master.opt index 3eb98fc3d6b..0e3f2e5092d 100644 --- a/mysql-test/t/ps-master.opt +++ b/mysql-test/t/ps-master.opt @@ -1 +1 @@ ---log-slow-queries --log-long-format --log-queries-not-using-indexes +--log-output=table,file --slow-query-log --log-long-format --log-queries-not-using-indexes --general-log diff --git a/mysql-test/t/show_check-master.opt b/mysql-test/t/show_check-master.opt index 7a438da06cc..6fd0fec0e08 100644 --- a/mysql-test/t/show_check-master.opt +++ b/mysql-test/t/show_check-master.opt @@ -1 +1 @@ ---log-slow-queries --log-long-format --log-queries-not-using-indexes --myisam-recover="" +--log-output=table --slow-query-log --log-long-format --log-queries-not-using-indexes --myisam-recover="" --general-log --general-log-file="" --slow-query-log-file="" diff --git a/mysql-test/t/slow_query_log_file_basic-master.opt b/mysql-test/t/slow_query_log_file_basic-master.opt index aca191f7fea..2b6ac309d6c 100644 --- a/mysql-test/t/slow_query_log_file_basic-master.opt +++ b/mysql-test/t/slow_query_log_file_basic-master.opt @@ -1 +1 @@ ---log-slow-queries=slowtest.log +--slow-query-log --slow-query-log-file=slowtest.log diff --git a/mysql-test/t/slow_query_log_file_func-master.opt b/mysql-test/t/slow_query_log_file_func-master.opt index e5b1c0948b0..e866f016022 100644 --- a/mysql-test/t/slow_query_log_file_func-master.opt +++ b/mysql-test/t/slow_query_log_file_func-master.opt @@ -1,2 +1,2 @@ ---log-slow-queries=my_slow_test.log +--slow-query-log --slow-query-log-file=my_slow_test.log diff --git a/mysql-test/t/union-master.opt b/mysql-test/t/union-master.opt index 3eb98fc3d6b..4ddf5c3734a 100644 --- a/mysql-test/t/union-master.opt +++ b/mysql-test/t/union-master.opt @@ -1 +1 @@ ---log-slow-queries --log-long-format --log-queries-not-using-indexes +--slow-query-log --log-long-format --log-queries-not-using-indexes diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 11838089432..dbf919959e2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3449,23 +3449,23 @@ static int init_common_variables(const char *conf_file_name, int argc, sys_init_slave.value=my_strdup("",MYF(0)); /* check log options and issue warnings if needed */ - if (opt_log && opt_logname && !(log_output_options & LOG_FILE) && - !(log_output_options & LOG_NONE)) + if (opt_log && opt_logname && *opt_logname && + !(log_output_options & (LOG_FILE | LOG_NONE))) sql_print_warning("Although a path was specified for the " "--log option, log tables are used. " "To enable logging to files use the --log-output option."); - if (opt_slow_log && opt_slow_logname && !(log_output_options & LOG_FILE) - && !(log_output_options & LOG_NONE)) + if (opt_slow_log && opt_slow_logname && *opt_slow_logname && + !(log_output_options & (LOG_FILE | LOG_NONE))) sql_print_warning("Although a path was specified for the " "--log_slow_queries option, log tables are used. " "To enable logging to files use the --log-output=file option."); - s= opt_logname ? opt_logname : make_default_log_name(buff, ".log"); + s= opt_logname && *opt_logname ? opt_logname : make_default_log_name(buff, ".log"); sys_var_general_log_path.value= my_strdup(s, MYF(0)); sys_var_general_log_path.value_length= strlen(s); - s= opt_slow_logname ? opt_slow_logname : make_default_log_name(buff, "-slow.log"); + s= opt_slow_logname && *opt_slow_logname ? opt_slow_logname : make_default_log_name(buff, "-slow.log"); sys_var_slow_log_path.value= my_strdup(s, MYF(0)); sys_var_slow_log_path.value_length= strlen(s); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 9c2b71094b6..396844f265b 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1405,6 +1405,9 @@ innobase_init( int err; bool ret; char *default_path; +#ifdef SAFE_MUTEX + my_bool old_safe_mutex_deadlock_detector; +#endif DBUG_ENTER("innobase_init"); handlerton *innobase_hton= (handlerton *)p; @@ -1660,13 +1663,13 @@ innobase_init( srv_sizeof_trx_t_in_ha_innodb_cc = sizeof(trx_t); #ifdef SAFE_MUTEX - /* Disable deadlock detection as it's very slow for the buffer pool */ - my_bool old_safe_mutex_deadlock_detector; - safe_mutex_deadlock_detector= 0; + /* Disable deadlock detection as it's very slow for the buffer pool */ + old_safe_mutex_deadlock_detector= safe_mutex_deadlock_detector; + safe_mutex_deadlock_detector= 0; #endif err = innobase_start_or_create_for_mysql(); #ifdef SAFE_MUTEX - safe_mutex_deadlock_detector= old_safe_mutex_deadlock_detector; + safe_mutex_deadlock_detector= old_safe_mutex_deadlock_detector; #endif if (err != DB_SUCCESS) { my_free(internal_innobase_data_file_path, diff --git a/storage/maria/ma_blockrec.c b/storage/maria/ma_blockrec.c index ed4d98015b1..2709a2bba5c 100644 --- a/storage/maria/ma_blockrec.c +++ b/storage/maria/ma_blockrec.c @@ -5914,7 +5914,7 @@ my_bool write_hook_for_file_id(enum translog_record_type type TRN *trn __attribute__ ((unused)), MARIA_HA *tbl_info, - LSN *lsn __attribute__ ((unused)), + LSN *lsn, void *hook_arg __attribute__ ((unused))) { @@ -5923,6 +5923,44 @@ my_bool write_hook_for_file_id(enum translog_record_type type return 0; } + +/** + Updates transaction's rec_lsn when committing. + + A transaction writes its commit record before being committed in trnman, so + if Checkpoint happens just between the COMMIT record log write and the + commit in trnman, it will record that transaction is not committed. Assume + the transaction (trn1) did an INSERT; after the checkpoint, a second + transaction (trn2) does a DELETE of what trn1 has inserted. Then crash, + Checkpoint record says that trn1 was not committed, and REDO phase starts + from Checkpoint record's LSN. So it will not find the COMMIT record of + trn1, will want to roll back trn1, which will fail because the row/key + which it wants to delete does not exist anymore. + To avoid this, Checkpoint needs to know that the REDO phase must start + before this COMMIT record, so transaction sets its rec_lsn to the COMMIT's + record LSN, and as Checkpoint reads the transaction's rec_lsn, Checkpoint + will know. + + @note so after commit trn->rec_lsn is a "commit LSN", which could be of + use later. + + @return Operation status, always 0 (success) +*/ + +my_bool write_hook_for_commit(enum translog_record_type type + __attribute__ ((unused)), + TRN *trn, + MARIA_HA *tbl_info + __attribute__ ((unused)), + LSN *lsn, + void *hook_arg + __attribute__ ((unused))) +{ + trn->rec_lsn= *lsn; + return 0; +} + + /*************************************************************************** Applying of REDO log records ***************************************************************************/ diff --git a/storage/maria/ma_blockrec.h b/storage/maria/ma_blockrec.h index 34f6a3e3008..9b373d1d0ea 100644 --- a/storage/maria/ma_blockrec.h +++ b/storage/maria/ma_blockrec.h @@ -271,6 +271,9 @@ my_bool write_hook_for_undo_bulk_insert(enum translog_record_type type, my_bool write_hook_for_file_id(enum translog_record_type type, TRN *trn, MARIA_HA *tbl_info, LSN *lsn, void *hook_arg); +my_bool write_hook_for_commit(enum translog_record_type type, + TRN *trn, MARIA_HA *tbl_info, LSN *lsn, + void *hook_arg); void _ma_block_get_status(void* param, my_bool concurrent_insert); void _ma_block_update_status(void *param); void _ma_block_restore_status(void *param); diff --git a/storage/maria/ma_commit.c b/storage/maria/ma_commit.c index 1066eb15e11..951de1beeff 100644 --- a/storage/maria/ma_commit.c +++ b/storage/maria/ma_commit.c @@ -33,6 +33,7 @@ int ma_commit(TRN *trn) LEX_CUSTRING log_array[TRANSLOG_INTERNAL_PARTS]; DBUG_ENTER("ma_commit"); + DBUG_ASSERT(trn->rec_lsn == LSN_IMPOSSIBLE); if (trn->undo_lsn == 0) /* no work done, rollback (cheaper than commit) */ DBUG_RETURN(trnman_rollback_trn(trn)); /* @@ -61,8 +62,14 @@ int ma_commit(TRN *trn) trn, NULL, 0, sizeof(log_array)/sizeof(log_array[0]), log_array, NULL, NULL) | - translog_flush(commit_lsn) | - trnman_commit_trn(trn)); + translog_flush(commit_lsn)); + + DBUG_EXECUTE_IF("maria_sleep_in_commit", + { + DBUG_PRINT("info", ("maria_sleep_in_commit")); + sleep(3); + }); + res|= trnman_commit_trn(trn); /* diff --git a/storage/maria/ma_loghandler.c b/storage/maria/ma_loghandler.c index cc8c7898d84..a0c8d0e63a3 100644 --- a/storage/maria/ma_loghandler.c +++ b/storage/maria/ma_loghandler.c @@ -618,11 +618,11 @@ static LOG_DESC INIT_LOGREC_PREPARE_WITH_UNDO_PURGE= static LOG_DESC INIT_LOGREC_COMMIT= {LOGRECTYPE_FIXEDLENGTH, 0, 0, NULL, - NULL, NULL, 0, "commit", LOGREC_IS_GROUP_ITSELF, NULL, + write_hook_for_commit, NULL, 0, "commit", LOGREC_IS_GROUP_ITSELF, NULL, NULL}; static LOG_DESC INIT_LOGREC_COMMIT_WITH_UNDO_PURGE= -{LOGRECTYPE_PSEUDOFIXEDLENGTH, 5, 5, NULL, NULL, NULL, 1, +{LOGRECTYPE_PSEUDOFIXEDLENGTH, 5, 5, NULL, write_hook_for_commit, NULL, 1, "commit_with_undo_purge", LOGREC_IS_GROUP_ITSELF, NULL, NULL}; static LOG_DESC INIT_LOGREC_CHECKPOINT= @@ -1404,18 +1404,21 @@ LSN translog_get_file_max_lsn_stored(uint32 file) SYNOPSIS translog_buffer_init() buffer The buffer to initialize + num Number of this buffer RETURN 0 OK 1 Error */ -static my_bool translog_buffer_init(struct st_translog_buffer *buffer) +static my_bool translog_buffer_init(struct st_translog_buffer *buffer, int num) { DBUG_ENTER("translog_buffer_init"); buffer->prev_last_lsn= buffer->last_lsn= LSN_IMPOSSIBLE; DBUG_PRINT("info", ("last_lsn and prev_last_lsn set to 0 buffer: 0x%lx", (ulong) buffer)); + + buffer->buffer_no= (uint8) num; /* This Buffer File */ buffer->file= NULL; buffer->overlay= 0; @@ -1430,10 +1433,23 @@ static my_bool translog_buffer_init(struct st_translog_buffer *buffer) buffer->copy_to_buffer_in_progress= 0; /* list of waiting buffer ready threads */ buffer->waiting_flush= 0; - /* lock for the buffer. Current buffer also lock the handler */ + /* + Buffers locked by fallowing mutex. As far as buffers create logical + circle (after last buffer goes first) it trigger false alarm of deadlock + detect system, so we remove check of deadlock for this buffers. In deed + all mutex locks concentrated around current buffer except flushing + thread (but it is only one thread). One thread can't take more then + 2 buffer locks at once. So deadlock is impossible here. + + To prevent false alarm of dead lock detection we switch dead lock + detection for one buffer in the middle of the buffers chain. Excluding + only one of eight buffers from deadlock detection hardly can hide other + possible problems which include this mutexes. + */ if (my_pthread_mutex_init(&buffer->mutex, MY_MUTEX_INIT_FAST, "translog_buffer->mutex", - MYF_NO_DEADLOCK_DETECTION) || + (num == TRANSLOG_BUFFERS_NO - 2 ? + MYF_NO_DEADLOCK_DETECTION : 0)) || pthread_cond_init(&buffer->prev_sent_to_disk_cond, 0)) DBUG_RETURN(1); buffer->is_closing_buffer= 0; @@ -3555,9 +3571,8 @@ my_bool translog_init_with_table(const char *directory, /* Buffers for log writing */ for (i= 0; i < TRANSLOG_BUFFERS_NO; i++) { - if (translog_buffer_init(log_descriptor.buffers + i)) + if (translog_buffer_init(log_descriptor.buffers + i, i)) goto err; - log_descriptor.buffers[i].buffer_no= (uint8) i; DBUG_PRINT("info", ("translog_buffer buffer #%u: 0x%lx", i, (ulong) log_descriptor.buffers + i)); } diff --git a/storage/maria/ma_open.c b/storage/maria/ma_open.c index 992061fcda2..309ccf78ea1 100644 --- a/storage/maria/ma_open.c +++ b/storage/maria/ma_open.c @@ -809,7 +809,7 @@ MARIA_HA *maria_open(const char *name, int mode, uint open_flags) if (!(share->state_history= (MARIA_STATE_HISTORY *) my_malloc(sizeof(*share->state_history), MYF(MY_WME)))) goto err; - share->state_history->trid= 0; /* Visibly by all */ + share->state_history->trid= 0; /* Visible by all */ share->state_history->state= share->state.state; share->state_history->next= 0; } diff --git a/storage/maria/ma_recovery.c b/storage/maria/ma_recovery.c index 51eedf157c5..baebdcf2eb4 100644 --- a/storage/maria/ma_recovery.c +++ b/storage/maria/ma_recovery.c @@ -555,8 +555,9 @@ static void new_transaction(uint16 sid, TrID long_id, LSN undo_lsn, char llbuf[22]; all_active_trans[sid].long_trid= long_id; llstr(long_id, llbuf); - tprint(tracef, "Transaction long_trid %s short_trid %u starts\n", - llbuf, sid); + tprint(tracef, "Transaction long_trid %s short_trid %u starts," + " undo_lsn (%lu,0x%lx) first_undo_lsn (%lu,0x%lx)\n", + llbuf, sid, LSN_IN_PARTS(undo_lsn), LSN_IN_PARTS(first_undo_lsn)); all_active_trans[sid].undo_lsn= undo_lsn; all_active_trans[sid].first_undo_lsn= first_undo_lsn; set_if_bigger(max_long_trid, long_id); @@ -2968,6 +2969,8 @@ static LSN parse_checkpoint_record(LSN lsn) ptr= log_record_buffer.str; start_address= lsn_korr(ptr); ptr+= LSN_STORE_SIZE; + tprint(tracef, "Checkpoint record has start_horizon at (%lu,0x%lx)\n", + LSN_IN_PARTS(start_address)); /* transactions */ nb_active_transactions= uint2korr(ptr); @@ -2983,6 +2986,9 @@ static LSN parse_checkpoint_record(LSN lsn) line. It may make start_address slightly decrease (only by the time it takes to write one or a few rows, roughly). */ + tprint(tracef, "Checkpoint record has min_rec_lsn of active transactions" + " at (%lu,0x%lx)\n", + LSN_IN_PARTS(minimum_rec_lsn_of_active_transactions)); set_if_smaller(start_address, minimum_rec_lsn_of_active_transactions); for (i= 0; i < nb_active_transactions; i++) @@ -3086,6 +3092,8 @@ static LSN parse_checkpoint_record(LSN lsn) */ start_address= checkpoint_start= translog_next_LSN(start_address, LSN_IMPOSSIBLE); + tprint(tracef, "Checkpoint record start_horizon now adjusted to" + " LSN (%lu,0x%lx)\n", LSN_IN_PARTS(start_address)); if (checkpoint_start == LSN_IMPOSSIBLE) { /* @@ -3095,6 +3103,8 @@ static LSN parse_checkpoint_record(LSN lsn) return LSN_ERROR; } /* now, where the REDO phase should start reading log: */ + tprint(tracef, "Checkpoint has min_rec_lsn of dirty pages at" + " LSN (%lu,0x%lx)\n", LSN_IN_PARTS(minimum_rec_lsn_of_dirty_pages)); set_if_smaller(start_address, minimum_rec_lsn_of_dirty_pages); DBUG_PRINT("info", ("checkpoint_start: (%lu,0x%lx) start_address: (%lu,0x%lx)", diff --git a/storage/maria/trnman.c b/storage/maria/trnman.c index 09c8f9a264f..f523da22291 100644 --- a/storage/maria/trnman.c +++ b/storage/maria/trnman.c @@ -261,6 +261,7 @@ TRN *trnman_new_trn(WT_THD *wt) { int res; TRN *trn; + union { TRN *trn; void *v; } tmp; DBUG_ENTER("trnman_new_trn"); /* @@ -276,19 +277,19 @@ TRN *trnman_new_trn(WT_THD *wt) pthread_mutex_lock(&LOCK_trn_list); /* Allocating a new TRN structure */ - trn= pool; + tmp.trn= pool; /* Popping an unused TRN from the pool (ABA isn't possible, we're behind a mutex */ my_atomic_rwlock_wrlock(&LOCK_pool); - while (trn && !my_atomic_casptr((void **)&pool, (void **)&trn, - (void *)trn->next)) + while (tmp.trn && !my_atomic_casptr((void **)&pool, &tmp.v, + (void *)tmp.trn->next)) /* no-op */; my_atomic_rwlock_wrunlock(&LOCK_pool); /* Nothing in the pool ? Allocate a new one */ - if (!trn) + if (!(trn= tmp.trn)) { /* trn should be completely initalized at create time to allow @@ -359,7 +360,7 @@ TRN *trnman_new_trn(WT_THD *wt) return 0; } - DBUG_PRINT("exit", ("trn: x%lx trid: 0x%lu", + DBUG_PRINT("exit", ("trn: 0x%lx trid: 0x%lu", (ulong) trn, (ulong) trn->trid)); DBUG_RETURN(trn); @@ -385,11 +386,12 @@ TRN *trnman_new_trn(WT_THD *wt) my_bool trnman_end_trn(TRN *trn, my_bool commit) { int res= 1; + uint16 cached_short_id= trn->short_id; /* we have to cache it, see below */ TRN *free_me= 0; LF_PINS *pins= trn->pins; DBUG_ENTER("trnman_end_trn"); + DBUG_PRINT("enter", ("trn=0x%lx commit=%d", (ulong) trn, commit)); - DBUG_ASSERT(trn->rec_lsn == 0); /* if a rollback, all UNDO records should have been executed */ DBUG_ASSERT(commit || trn->undo_lsn == 0); DBUG_PRINT("info", ("pthread_mutex_lock LOCK_trn_list")); @@ -454,11 +456,17 @@ my_bool trnman_end_trn(TRN *trn, my_bool commit) res= -1; trnman_active_transactions--; + DBUG_PRINT("info", ("pthread_mutex_unlock LOCK_trn_list")); pthread_mutex_unlock(&LOCK_trn_list); - /* the rest is done outside of a critical section */ + /* + the rest is done outside of a critical section + + note that we don't own trn anymore, it may be in a shared list now. + Thus, we cannot dereference it, and must use cached_short_id below. + */ my_atomic_rwlock_rdlock(&LOCK_short_trid_to_trn); - my_atomic_storeptr((void **)&short_trid_to_active_trn[trn->short_id], 0); + my_atomic_storeptr((void **)&short_trid_to_active_trn[cached_short_id], 0); my_atomic_rwlock_rdunlock(&LOCK_short_trid_to_trn); /* @@ -503,7 +511,6 @@ void trnman_free_trn(TRN *trn) */ union { TRN *trn; void *v; } tmp; - pthread_mutex_lock(&trn->state_lock); trn->short_id= 0; pthread_mutex_unlock(&trn->state_lock); |