Browse Source

MDEV-13935 INSERT stuck at state Unlocking tables

Revert the dead code for MySQL 5.7 multi-master replication (GCS),
also known as
WL#6835: InnoDB: GCS Replication: Deterministic Deadlock Handling
(High Prio Transactions in InnoDB).

Also, make innodb_lock_schedule_algorithm=vats skip SPATIAL INDEX,
because the code does not seem to be compatible with them.

Add FIXME comments to some SPATIAL INDEX locking code. It looks
like Galera write-set replication might not work with SPATIAL INDEX.
pull/665/head
Marko Mäkelä 8 years ago
parent
commit
bd7ed1b923
  1. 1
      extra/mariabackup/xtrabackup.cc
  2. 2
      storage/innobase/gis/gis0rtree.cc
  3. 1
      storage/innobase/gis/gis0sea.cc
  4. 331
      storage/innobase/handler/ha_innodb.cc
  5. 3
      storage/innobase/handler/handler0alter.cc
  6. 23
      storage/innobase/include/ha_prototypes.h
  7. 3
      storage/innobase/include/hash0hash.h
  8. 79
      storage/innobase/include/lock0lock.h
  9. 46
      storage/innobase/include/lock0lock.ic
  10. 401
      storage/innobase/include/lock0priv.h
  11. 2
      storage/innobase/include/lock0priv.ic
  12. 330
      storage/innobase/include/trx0trx.h
  13. 69
      storage/innobase/include/trx0trx.ic
  14. 22
      storage/innobase/include/trx0types.h
  15. 5
      storage/innobase/include/trx0undo.h
  16. 1065
      storage/innobase/lock/lock0lock.cc
  17. 62
      storage/innobase/lock/lock0prdt.cc
  18. 6
      storage/innobase/lock/lock0wait.cc
  19. 3
      storage/innobase/page/page0page.cc
  20. 1
      storage/innobase/page/page0zip.cc
  21. 2
      storage/innobase/row/row0ins.cc
  22. 4
      storage/innobase/row/row0merge.cc
  23. 3
      storage/innobase/row/row0mysql.cc
  24. 18
      storage/innobase/row/row0sel.cc
  25. 5
      storage/innobase/row/row0trunc.cc
  26. 33
      storage/innobase/trx/trx0roll.cc
  27. 284
      storage/innobase/trx/trx0trx.cc

1
extra/mariabackup/xtrabackup.cc

@ -69,6 +69,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <row0mysql.h>
#include <row0quiesce.h>
#include <srv0start.h>
#include "trx0sys.h"
#include <buf0dblwr.h>
#include <list>

2
storage/innobase/gis/gis0rtree.cc

@ -34,7 +34,7 @@ Created 2013/03/27 Allen Lai and Jimmy Yang
#include "rem0cmp.h"
#include "lock0lock.h"
#include "ibuf0ibuf.h"
#include "trx0trx.h"
#include "trx0undo.h"
#include "srv0mon.h"
#include "gis0geo.h"

1
storage/innobase/gis/gis0sea.cc

@ -37,6 +37,7 @@ Created 2014/01/16 Jimmy Yang
#include "ibuf0ibuf.h"
#include "trx0trx.h"
#include "srv0mon.h"
#include "que0que.h"
#include "gis0geo.h"
/** Restore the stored position of a persistent cursor bufferfixing the page */

331
storage/innobase/handler/ha_innodb.cc

@ -1721,41 +1721,6 @@ innobase_reset_background_thd(MYSQL_THD thd)
}
#if 0
/**
Check if the transaction can be rolled back
@param[in] requestor Session requesting the lock
@param[in] holder Session that holds the lock
@return the session that will be rolled back, null don't care */
THD*
thd_trx_arbitrate(THD* requestor, THD* holder)
{
/* Non-user (thd==0) transactions by default can't rollback, in
practice DDL transactions should never rollback and that's because
they should never wait on table/record locks either */
ut_a(holder != NULL);
ut_a(holder != requestor);
THD* victim = thd_tx_arbitrate(requestor, holder);
ut_a(victim == NULL || victim == requestor || victim == holder);
return(victim);
}
/**
@param[in] thd Session to check
@return the priority */
int
thd_trx_priority(THD* thd)
{
return(thd == NULL ? 0 : thd_tx_priority(thd));
}
#endif
/******************************************************************//**
Check if the transaction is an auto-commit transaction. TRUE also
implies that it is a SELECT (read-only) transaction.
@ -2057,7 +2022,6 @@ convert_error_code_to_mysql(
case DB_RECORD_NOT_FOUND:
return(HA_ERR_NO_ACTIVE_RECORD);
case DB_FORCED_ABORT:
case DB_DEADLOCK:
/* Since we rolled back the whole transaction, we must
tell it also to MySQL so that MySQL knows to empty the
@ -2879,10 +2843,6 @@ check_trx_exists(
return trx;
} else {
trx = innobase_trx_allocate(thd);
/* User trx can be forced to rollback,
so we unset the disable flag. */
ut_ad(trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE);
trx->in_innodb &= TRX_FORCE_ROLLBACK_MASK;
thd_set_ha_data(thd, innodb_hton_ptr, trx);
return trx;
}
@ -3094,11 +3054,8 @@ ha_innobase::update_thd(
trx_t* trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(trx);
ut_ad(trx_in_innodb.is_aborted()
|| (trx->dict_operation_lock_mode == 0
&& trx->dict_operation == TRX_DICT_OP_NONE));
ut_ad(trx->dict_operation_lock_mode == 0);
ut_ad(trx->dict_operation == TRX_DICT_OP_NONE);
if (m_prebuilt->trx != trx) {
@ -3564,8 +3521,6 @@ ha_innobase::init_table_handle_for_HANDLER(void)
trx_start_if_not_started_xa(m_prebuilt->trx, false);
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
/* Assign a read view if the transaction does not have it yet */
trx_assign_read_view(m_prebuilt->trx);
@ -4549,8 +4504,6 @@ innobase_start_trx_and_assign_read_view(
trx_t* trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(trx);
innobase_srv_conc_force_exit_innodb(trx);
/* The transaction should not be active yet, start it */
@ -4687,7 +4640,6 @@ innobase_commit_ordered(
DBUG_ASSERT(hton == innodb_hton_ptr);
trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(trx);
if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
/* We cannot throw error here; instead we will catch this error
@ -4731,16 +4683,6 @@ innobase_commit(
trx_t* trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(trx);
if (trx_in_innodb.is_aborted()) {
innobase_rollback(hton, thd, commit_trx);
DBUG_RETURN(convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, thd));
}
ut_ad(trx->dict_operation_lock_mode == 0);
ut_ad(trx->dict_operation == TRX_DICT_OP_NONE);
@ -4832,11 +4774,8 @@ innobase_rollback(
trx_t* trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(trx);
ut_ad(trx_in_innodb.is_aborted()
|| (trx->dict_operation_lock_mode == 0
&& trx->dict_operation == TRX_DICT_OP_NONE));
ut_ad(trx->dict_operation_lock_mode == 0);
ut_ad(trx->dict_operation == TRX_DICT_OP_NONE);
innobase_srv_conc_force_exit_innodb(trx);
@ -4847,10 +4786,7 @@ innobase_rollback(
/* If we had reserved the auto-inc lock for some table (if
we come here to roll back the latest SQL statement) we
release it now before a possibly lengthy rollback */
if (!trx_in_innodb.is_aborted()) {
lock_unlock_table_autoinc(trx);
}
lock_unlock_table_autoinc(trx);
/* This is a statement level variable. */
@ -4863,18 +4799,6 @@ innobase_rollback(
error = trx_rollback_for_mysql(trx);
if (trx->state == TRX_STATE_FORCED_ROLLBACK) {
#ifndef DBUG_OFF
char buffer[1024];
DBUG_LOG("trx", "Forced rollback: "
<< thd_get_error_context_description(
thd, buffer, sizeof buffer, 512));
#endif /* !DBUG_OFF */
trx->state = TRX_STATE_NOT_STARTED;
}
trx_deregister_from_2pc(trx);
} else {
@ -4901,9 +4825,7 @@ innobase_rollback_trx(
/* If we had reserved the auto-inc lock for some table (if
we come here to roll back the latest SQL statement) we
release it now before a possibly lengthy rollback */
if (!TrxInInnoDB::is_aborted(trx)) {
lock_unlock_table_autoinc(trx);
}
lock_unlock_table_autoinc(trx);
if (!trx->has_logged()) {
trx->will_lock = 0;
@ -5075,8 +4997,6 @@ innobase_rollback_to_savepoint(
trx_t* trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(trx);
innobase_srv_conc_force_exit_innodb(trx);
/* TODO: use provided savepoint data area to store savepoint data */
@ -5117,8 +5037,6 @@ innobase_rollback_to_savepoint_can_release_mdl(
trx_t* trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(trx);
/* If transaction has not acquired any locks then it is safe
to release MDL after rollback to savepoint */
if (UT_LIST_GET_LEN(trx->lock.trx_locks) == 0) {
@ -5152,8 +5070,6 @@ innobase_release_savepoint(
trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(trx);
/* TODO: use provided savepoint data area to store savepoint data */
longlong2str((ulint) savepoint, name, 36);
@ -5187,8 +5103,6 @@ innobase_savepoint(
trx_t* trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(trx);
innobase_srv_conc_force_exit_innodb(trx);
/* Cannot happen outside of transaction */
@ -5224,7 +5138,6 @@ innobase_close_connection(
DBUG_ASSERT(hton == innodb_hton_ptr);
trx_t* trx = thd_to_trx(thd);
bool free_trx = false;
/* During server initialization MySQL layer will try to open
some of the master-slave tables those residing in InnoDB.
@ -5241,16 +5154,6 @@ innobase_close_connection(
if (trx) {
TrxInInnoDB trx_in_innodb(trx);
if (trx_in_innodb.is_aborted()) {
while (trx_is_started(trx)) {
os_thread_sleep(20);
}
}
if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
sql_print_error("Transaction not registered for MariaDB 2PC, "
@ -5268,9 +5171,8 @@ innobase_close_connection(
if (trx->has_logged_persistent()) {
trx_disconnect_prepared(trx);
} else {
trx_rollback_for_mysql(trx);
trx_deregister_from_2pc(trx);
free_trx = true;
goto rollback_and_free;
}
} else {
sql_print_warning(
@ -5278,24 +5180,15 @@ innobase_close_connection(
"InnoDB transaction. " TRX_ID_FMT " row modifications "
"will roll back.",
trx->undo_no);
ut_d(ib::warn()
<< "trx: " << trx << " started on: "
<< innobase_basename(trx->start_file)
<< ":" << trx->start_line);
innobase_rollback_trx(trx);
free_trx = true;
goto rollback_and_free;
}
} else {
rollback_and_free:
innobase_rollback_trx(trx);
free_trx = true;
trx_free_for_mysql(trx);
}
}
/* Free trx only after TrxInInnoDB is deleted. */
if (free_trx) {
trx_free_for_mysql(trx);
}
DBUG_RETURN(0);
}
@ -8320,15 +8213,6 @@ ha_innobase::write_row(
DBUG_ENTER("ha_innobase::write_row");
trx_t* trx = thd_to_trx(m_user_thd);
TrxInInnoDB trx_in_innodb(trx);
if (trx_in_innodb.is_aborted()) {
innobase_rollback(ht, m_user_thd, false);
DBUG_RETURN(convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, m_user_thd));
}
/* Validation checks before we commence write_row operation. */
if (high_level_read_only) {
@ -9161,14 +9045,6 @@ ha_innobase::update_row(
goto func_exit;
}
if (TrxInInnoDB::is_aborted(trx)) {
innobase_rollback(ht, m_user_thd, false);
DBUG_RETURN(convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, m_user_thd));
}
/* This is not a delete */
m_prebuilt->upd_node->is_delete = FALSE;
@ -9258,18 +9134,9 @@ ha_innobase::delete_row(
{
dberr_t error;
trx_t* trx = thd_to_trx(m_user_thd);
TrxInInnoDB trx_in_innodb(trx);
DBUG_ENTER("ha_innobase::delete_row");
if (trx_in_innodb.is_aborted()) {
innobase_rollback(ht, m_user_thd, false);
DBUG_RETURN(convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, m_user_thd));
}
ut_a(m_prebuilt->trx == trx);
if (high_level_read_only) {
@ -9340,19 +9207,7 @@ ha_innobase::unlock_row(void)
DBUG_VOID_RETURN;
}
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
if (trx_in_innodb.is_aborted()) {
DBUG_VOID_RETURN;
}
/* Ideally, this assert must be in the beginning of the function.
But there are some calls to this function from the SQL layer when the
transaction is in state TRX_STATE_NOT_STARTED. The check on
m_prebuilt->select_lock_type above gets around this issue. */
ut_ad(trx_state_eq(m_prebuilt->trx, TRX_STATE_ACTIVE, true)
|| trx_state_eq(m_prebuilt->trx, TRX_STATE_FORCED_ROLLBACK, true));
ut_ad(trx_state_eq(m_prebuilt->trx, TRX_STATE_ACTIVE, true));
switch (m_prebuilt->row_read_type) {
case ROW_READ_WITH_LOCKS:
@ -9634,14 +9489,6 @@ ha_innobase::index_read(
innobase_srv_conc_enter_innodb(m_prebuilt);
if (TrxInInnoDB::is_aborted(m_prebuilt->trx)) {
innobase_rollback(ht, m_user_thd, false);
DBUG_RETURN(convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, m_user_thd));
}
ret = row_search_mvcc(
buf, mode, m_prebuilt, match_mode, 0);
@ -9822,16 +9669,6 @@ ha_innobase::change_active_index(
ut_ad(m_user_thd == ha_thd());
ut_a(m_prebuilt->trx == thd_to_trx(m_user_thd));
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
if (trx_in_innodb.is_aborted()) {
innobase_rollback(ht, m_user_thd, false);
DBUG_RETURN(convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, m_user_thd));
}
active_index = keynr;
m_prebuilt->index = innobase_get_index(keynr);
@ -9950,14 +9787,6 @@ ha_innobase::general_fetch(
ut_ad(trx == thd_to_trx(m_user_thd));
if (TrxInInnoDB::is_aborted(trx)) {
innobase_rollback(ht, m_user_thd, false);
DBUG_RETURN(convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, m_user_thd));
}
if (m_prebuilt->table->is_readable()) {
} else if (m_prebuilt->table->corrupted) {
DBUG_RETURN(HA_ERR_CRASHED);
@ -10124,7 +9953,6 @@ ha_innobase::rnd_init(
/*==================*/
bool scan) /*!< in: true if table/index scan FALSE otherwise */
{
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
int err;
/* Store the active index value so that we can restore the original
@ -10174,8 +10002,6 @@ ha_innobase::rnd_next(
DBUG_ENTER("rnd_next");
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
if (m_start_of_scan) {
error = index_first(buf);
@ -10299,21 +10125,6 @@ ha_innobase::ft_init_ext(
trx_t* trx = m_prebuilt->trx;
TrxInInnoDB trx_in_innodb(trx);
if (trx_in_innodb.is_aborted()) {
innobase_rollback(ht, m_user_thd, false);
int err;
err = convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, m_user_thd);
my_error(err, MYF(0));
return(NULL);
}
/* FTS queries are not treated as autocommit non-locking selects.
This is because the FTS implementation can acquire locks behind
the scenes. This has not been verified but it is safer to treat
@ -10455,16 +10266,6 @@ ha_innobase::ft_read(
/*=================*/
uchar* buf) /*!< in/out: buf contain result row */
{
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
if (trx_in_innodb.is_aborted()) {
innobase_rollback(ht, m_user_thd, false);
return(convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, m_user_thd));
}
row_prebuilt_t* ft_prebuilt;
ft_prebuilt = reinterpret_cast<NEW_FT_INFO*>(ft_handler)->ft_prebuilt;
@ -13268,15 +13069,6 @@ ha_innobase::discard_or_import_tablespace(
DBUG_RETURN(HA_ERR_TABLE_NEEDS_UPGRADE);
}
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
if (trx_in_innodb.is_aborted()) {
innobase_rollback(ht, m_user_thd, false);
DBUG_RETURN(convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, m_user_thd));
}
trx_start_if_not_started(m_prebuilt->trx, true);
/* Obtain an exclusive lock on the table. */
@ -13376,8 +13168,6 @@ ha_innobase::truncate()
update_thd(ha_thd());
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
if (!trx_is_started(m_prebuilt->trx)) {
++m_prebuilt->trx->will_lock;
}
@ -13452,8 +13242,6 @@ ha_innobase::delete_table(
trx_t* parent_trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(parent_trx);
/* Remove the to-be-dropped table from the list of modified tables
by parent_trx. Otherwise we may end up with an orphaned pointer to
the table object from parent_trx::mod_tables. This could happen in:
@ -13697,8 +13485,6 @@ innobase_rename_table(
DEBUG_SYNC_C("innodb_rename_table_ready");
TrxInInnoDB trx_in_innodb(trx);
trx_start_if_not_started(trx, true);
/* Serialize data dictionary operations with dictionary mutex:
@ -13790,13 +13576,6 @@ ha_innobase::rename_table(
DBUG_RETURN(HA_ERR_TABLE_READONLY);
}
/* Get the transaction associated with the current thd, or create one
if not yet created */
trx_t* parent_trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(parent_trx);
trx_t* trx = innobase_trx_allocate(thd);
/* We are doing a DDL operation. */
@ -13880,8 +13659,6 @@ ha_innobase::records_in_range(
m_prebuilt->trx->op_info = "estimating records in index range";
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
active_index = keynr;
key = table->key_info + active_index;
@ -14013,8 +13790,6 @@ ha_innobase::estimate_rows_upper_bound()
update_thd(ha_thd());
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
m_prebuilt->trx->op_info = "calculating upper bound for table rows";
index = dict_table_get_first_index(m_prebuilt->table);
@ -14856,8 +14631,6 @@ ha_innobase::optimize(
HA_CHECK_OPT* check_opt) /*!< in: currently ignored */
{
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
/* FTS-FIXME: Since MySQL doesn't support engine-specific commands,
we have to hijack some existing command in order to be able to test
the new admin commands added in InnoDB's FTS support. For now, we
@ -14926,8 +14699,6 @@ ha_innobase::check(
ut_a(m_prebuilt->trx->magic_n == TRX_MAGIC_N);
ut_a(m_prebuilt->trx == thd_to_trx(thd));
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
if (m_prebuilt->mysql_template == NULL) {
/* Build the template; we will use a dummy template
in index scans done in checking */
@ -15418,8 +15189,6 @@ ha_innobase::get_foreign_key_list(
{
update_thd(ha_thd());
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
m_prebuilt->trx->op_info = "getting list of foreign keys";
mutex_enter(&dict_sys->mutex);
@ -15458,8 +15227,6 @@ ha_innobase::get_parent_foreign_key_list(
{
update_thd(ha_thd());
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
m_prebuilt->trx->op_info = "getting list of referencing foreign keys";
mutex_enter(&dict_sys->mutex);
@ -15556,8 +15323,6 @@ ha_innobase::get_cascade_foreign_key_table_list(
THD* thd,
List<st_handler_tablename>* fk_table_list)
{
TrxInInnoDB trx_in_innodb(m_prebuilt->trx);
m_prebuilt->trx->op_info = "getting cascading foreign keys";
std::list<table_list_item, ut_allocator<table_list_item> > table_list;
@ -15807,15 +15572,6 @@ ha_innobase::end_stmt()
/* This is a statement level counter. */
m_prebuilt->autoinc_last_value = 0;
/* This transaction had called ha_innobase::start_stmt() */
trx_t* trx = m_prebuilt->trx;
if (trx->lock.start_stmt) {
TrxInInnoDB::end_stmt(trx);
trx->lock.start_stmt = false;
}
return(0);
}
@ -15855,8 +15611,6 @@ ha_innobase::start_stmt(
ut_ad(m_prebuilt->table != NULL);
TrxInInnoDB trx_in_innodb(trx);
trx = m_prebuilt->trx;
innobase_srv_conc_force_exit_innodb(trx);
@ -15929,14 +15683,6 @@ ha_innobase::start_stmt(
++trx->will_lock;
}
/* Only do it once per transaction. */
if (!trx->lock.start_stmt && lock_type != TL_UNLOCK) {
TrxInInnoDB::begin_stmt(trx);
trx->lock.start_stmt = true;
}
DBUG_RETURN(0);
}
@ -16167,13 +15913,8 @@ ha_innobase::external_lock(
++trx->will_lock;
}
TrxInInnoDB::begin_stmt(trx);
DBUG_RETURN(0);
} else {
TrxInInnoDB::end_stmt(trx);
DEBUG_SYNC_C("ha_innobase_end_statement");
}
@ -16268,8 +16009,6 @@ innodb_show_status(
innobase_srv_conc_force_exit_innodb(trx);
TrxInInnoDB trx_in_innodb(trx);
/* We let the InnoDB Monitor to output at most MAX_STATUS_SIZE
bytes of text. */
@ -16854,8 +16593,6 @@ ha_innobase::store_lock(
trx_t* trx = check_trx_exists(thd);
TrxInInnoDB trx_in_innodb(trx);
/* NOTE: MySQL can call this function with lock 'type' TL_IGNORE!
Be careful to ignore TL_IGNORE if we are going to do something with
only 'real' locks! */
@ -17176,8 +16913,6 @@ ha_innobase::get_auto_increment(
trx = m_prebuilt->trx;
TrxInInnoDB trx_in_innodb(trx);
/* Note: We can't rely on *first_value since some MySQL engines,
in particular the partition engine, don't initialize it to 0 when
invoking this method. So we are not sure if it's guaranteed to
@ -17578,16 +17313,6 @@ innobase_xa_prepare(
innobase_srv_conc_force_exit_innodb(trx);
TrxInInnoDB trx_in_innodb(trx);
if (trx_in_innodb.is_aborted()) {
innobase_rollback(hton, thd, prepare_trx);
return(convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, thd));
}
if (!trx_is_registered_for_2pc(trx) && trx_is_started(trx)) {
sql_print_error("Transaction not registered for MariaDB 2PC,"
@ -17602,18 +17327,7 @@ innobase_xa_prepare(
ut_ad(trx_is_registered_for_2pc(trx));
dberr_t err = trx_prepare_for_mysql(trx);
ut_ad(err == DB_SUCCESS || err == DB_FORCED_ABORT);
if (err == DB_FORCED_ABORT) {
innobase_rollback(hton, thd, prepare_trx);
return(convert_error_code_to_mysql(
DB_FORCED_ABORT, 0, thd));
}
trx_prepare_for_mysql(trx);
} else {
/* We just mark the SQL statement ended and do not do a
transaction prepare */
@ -17690,14 +17404,10 @@ innobase_commit_by_xid(
}
if (trx_t* trx = trx_get_trx_by_xid(xid)) {
ut_ad(trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE);
/* use cases are: disconnected xa, slave xa, recovery */
{
TrxInInnoDB trx_in_innodb(trx);
innobase_commit_low(trx);
ut_ad(trx->mysql_thd == NULL);
trx_deregister_from_2pc(trx);
}
innobase_commit_low(trx);
ut_ad(trx->mysql_thd == NULL);
trx_deregister_from_2pc(trx);
ut_ad(!trx->will_lock); /* trx cache requirement */
trx_free_for_background(trx);
@ -17726,14 +17436,9 @@ innobase_rollback_by_xid(
}
if (trx_t* trx = trx_get_trx_by_xid(xid)) {
int ret;
ut_ad(trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE);
{
TrxInInnoDB trx_in_innodb(trx);
ret = innobase_rollback_trx(trx);
trx_deregister_from_2pc(trx);
ut_ad(!trx->will_lock);
}
int ret = innobase_rollback_trx(trx);
trx_deregister_from_2pc(trx);
ut_ad(!trx->will_lock);
trx_free_for_background(trx);
return(ret);

3
storage/innobase/handler/handler0alter.cc

@ -2804,8 +2804,7 @@ online_retry_drop_indexes_with_trx(
dict_table_t* table, /*!< in/out: table */
trx_t* trx) /*!< in/out: transaction */
{
ut_ad(trx_state_eq(trx, TRX_STATE_NOT_STARTED)
|| trx_state_eq(trx, TRX_STATE_FORCED_ROLLBACK));
ut_ad(trx_state_eq(trx, TRX_STATE_NOT_STARTED));
ut_ad(trx->dict_operation_lock_mode == RW_X_LATCH);

23
storage/innobase/include/ha_prototypes.h

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2006, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation. All Rights Reserved.
Copyright (c) 2017, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -361,27 +361,6 @@ thd_trx_is_read_only(
/*=================*/
THD* thd); /*!< in/out: thread handle */
#if 0
/**
Check if the transaction can be rolled back
@param[in] requestor Session requesting the lock
@param[in] holder Session that holds the lock
@return the session that will be rolled back, null don't care */
THD*
thd_trx_arbitrate(THD* requestor, THD* holder);
/**
@param[in] thd Session to check
@return the priority */
int
thd_trx_priority(THD* thd);
#else
static inline THD* thd_trx_arbitrate(THD*, THD*) { return NULL; }
static inline int thd_trx_priority(THD*) { return 0; }
#endif
/******************************************************************//**
Check if the transaction is an auto-commit transaction. TRUE also
implies that it is a SELECT (read-only) transaction.

3
storage/innobase/include/hash0hash.h

@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -122,7 +123,6 @@ do {\
}\
} while (0)
#ifdef WITH_WSREP
/*******************************************************************//**
Inserts a struct to the head of hash table. */
@ -148,7 +148,6 @@ do { \
cell3333->node = DATA; \
} \
} while (0)
#endif /*WITH_WSREP */
#ifdef UNIV_HASH_DEBUG
# define HASH_ASSERT_VALID(DATA) ut_a((void*) (DATA) != (void*) -1)
# define HASH_INVALIDATE(DATA, NAME) *(void**) (&DATA->NAME) = (void*) -1

79
storage/innobase/include/lock0lock.h

@ -954,6 +954,30 @@ struct lock_sys_t{
is running */
};
/*********************************************************************//**
Creates a new record lock and inserts it to the lock queue. Does NOT check
for deadlocks or lock compatibility!
@return created lock */
UNIV_INLINE
lock_t*
lock_rec_create(
/*============*/
#ifdef WITH_WSREP
lock_t* c_lock, /*!< conflicting lock */
que_thr_t* thr, /*!< thread owning trx */
#endif
ulint type_mode,/*!< in: lock mode and wait
flag, type is ignored and
replaced by LOCK_REC */
const buf_block_t* block, /*!< in: buffer block containing
the record */
ulint heap_no,/*!< in: heap number of the record */
dict_index_t* index, /*!< in: index of record */
trx_t* trx, /*!< in,out: transaction */
bool caller_owns_trx_mutex);
/*!< in: true if caller owns
trx mutex */
/*************************************************************//**
Removes a record lock request, waiting or granted, from the queue. */
void
@ -963,6 +987,61 @@ lock_rec_discard(
record locks which are contained
in this lock object are removed */
/** Create a new record lock and inserts it to the lock queue,
without checking for deadlocks or conflicts.
@param[in] type_mode lock mode and wait flag; type will be replaced
with LOCK_REC
@param[in] space tablespace id
@param[in] page_no index page number
@param[in] page R-tree index page, or NULL
@param[in] heap_no record heap number in the index page
@param[in] index the index tree
@param[in,out] trx transaction
@param[in] holds_trx_mutex whether the caller holds trx->mutex
@return created lock */
lock_t*
lock_rec_create_low(
#ifdef WITH_WSREP
lock_t* c_lock, /*!< conflicting lock */
que_thr_t* thr, /*!< thread owning trx */
#endif
ulint type_mode,
ulint space,
ulint page_no,
const page_t* page,
ulint heap_no,
dict_index_t* index,
trx_t* trx,
bool holds_trx_mutex);
/** Enqueue a waiting request for a lock which cannot be granted immediately.
Check for deadlocks.
@param[in] type_mode the requested lock mode (LOCK_S or LOCK_X)
possibly ORed with LOCK_GAP or
LOCK_REC_NOT_GAP, ORed with
LOCK_INSERT_INTENTION if this
waiting lock request is set
when performing an insert of
an index record
@param[in] block leaf page in the index
@param[in] heap_no record heap number in the block
@param[in] index index tree
@param[in,out] thr query thread
@param[in] prdt minimum bounding box (spatial index)
@retval DB_LOCK_WAIT if the waiting lock was enqueued
@retval DB_DEADLOCK if this transaction was chosen as the victim
@retval DB_SUCCESS_LOCKED_REC if the other transaction was chosen as a victim
(or it happened to commit) */
dberr_t
lock_rec_enqueue_waiting(
#ifdef WITH_WSREP
lock_t* c_lock, /*!< conflicting lock */
#endif
ulint type_mode,
const buf_block_t* block,
ulint heap_no,
dict_index_t* index,
que_thr_t* thr,
lock_prdt_t* prdt);
/*************************************************************//**
Moves the explicit locks on user records to another page if a record
list start is moved to another page. */

46
storage/innobase/include/lock0lock.ic

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
Copyright (c) 2017, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -24,19 +24,9 @@ The transaction lock system
Created 5/7/1996 Heikki Tuuri
*******************************************************/
#include "srv0srv.h"
#include "dict0dict.h"
#include "row0row.h"
#include "trx0sys.h"
#include "trx0trx.h"
#include "buf0buf.h"
#include "page0page.h"
#include "page0cur.h"
#include "row0vers.h"
#include "que0que.h"
#include "btr0cur.h"
#include "read0read.h"
#include "log0recv.h"
/*********************************************************************//**
Calculates the fold value of a page file address: used in inserting or
@ -108,3 +98,37 @@ lock_hash_get(
}
}
/*********************************************************************//**
Creates a new record lock and inserts it to the lock queue. Does NOT check
for deadlocks or lock compatibility!
@return created lock */
UNIV_INLINE
lock_t*
lock_rec_create(
/*============*/
#ifdef WITH_WSREP
lock_t* c_lock, /*!< conflicting lock */
que_thr_t* thr, /*!< thread owning trx */
#endif
ulint type_mode,/*!< in: lock mode and wait
flag, type is ignored and
replaced by LOCK_REC */
const buf_block_t* block, /*!< in: buffer block containing
the record */
ulint heap_no,/*!< in: heap number of the record */
dict_index_t* index, /*!< in: index of record */
trx_t* trx, /*!< in,out: transaction */
bool caller_owns_trx_mutex)
/*!< in: TRUE if caller owns
trx mutex */
{
btr_assert_not_corrupted(block, index);
return lock_rec_create_low(
#ifdef WITH_WSREP
c_lock, thr,
#endif
type_mode,
block->page.id.space(), block->page.id.page_no(),
block->frame, heap_no,
index, trx, caller_owns_trx_mutex);
}

401
storage/innobase/include/lock0priv.h

@ -562,407 +562,6 @@ enum lock_rec_req_status {
LOCK_REC_SUCCESS_CREATED
};
/**
Record lock ID */
struct RecID {
RecID(ulint space_id, ulint page_no, ulint heap_no)
:
m_space_id(static_cast<uint32_t>(space_id)),
m_page_no(static_cast<uint32_t>(page_no)),
m_heap_no(static_cast<uint32_t>(heap_no)),
m_fold(lock_rec_fold(m_space_id, m_page_no))
{
ut_ad(space_id < UINT32_MAX);
ut_ad(page_no < UINT32_MAX);
ut_ad(heap_no < UINT32_MAX);
}
RecID(const buf_block_t* block, ulint heap_no)
:
m_space_id(block->page.id.space()),
m_page_no(block->page.id.page_no()),
m_heap_no(static_cast<uint32_t>(heap_no)),
m_fold(lock_rec_fold(m_space_id, m_page_no))
{
ut_ad(heap_no < UINT32_MAX);
}
/**
@return the "folded" value of {space, page_no} */
ulint fold() const
{
return(m_fold);
}
/**
Tablespace ID */
uint32_t m_space_id;
/**
Page number within the space ID */
uint32_t m_page_no;
/**
Heap number within the page */
uint32_t m_heap_no;
/**
Hashed key value */
ulint m_fold;
};
/**
Create record locks */
class RecLock {
public:
/**
@param[in,out] thr Transaction query thread requesting the record
lock
@param[in] index Index on which record lock requested
@param[in] rec_id Record lock tuple {space, page_no, heap_no}
@param[in] mode The lock mode */
RecLock(que_thr_t* thr,
dict_index_t* index,
const RecID& rec_id,
ulint mode)
:
m_thr(thr),
m_trx(thr_get_trx(thr)),
m_mode(mode),
m_index(index),
m_rec_id(rec_id)
{
ut_ad(is_predicate_lock(m_mode));
init(NULL);
}
/**
@param[in,out] thr Transaction query thread requesting the record
lock
@param[in] index Index on which record lock requested
@param[in] block Buffer page containing record
@param[in] heap_no Heap number within the block
@param[in] mode The lock mode
@param[in] prdt The predicate for the rtree lock */
RecLock(que_thr_t* thr,
dict_index_t* index,
const buf_block_t*
block,
ulint heap_no,
ulint mode,
lock_prdt_t* prdt = NULL)
:
m_thr(thr),
m_trx(thr_get_trx(thr)),
m_mode(mode),
m_index(index),
m_rec_id(block, heap_no)
{
btr_assert_not_corrupted(block, index);
init(block->frame);
}
/**
@param[in] index Index on which record lock requested
@param[in] rec_id Record lock tuple {space, page_no, heap_no}
@param[in] mode The lock mode */
RecLock(dict_index_t* index,
const RecID& rec_id,
ulint mode)
:
m_thr(),
m_trx(),
m_mode(mode),
m_index(index),
m_rec_id(rec_id)
{
ut_ad(is_predicate_lock(m_mode));
init(NULL);
}
/**
@param[in] index Index on which record lock requested
@param[in] block Buffer page containing record
@param[in] heap_no Heap number withing block
@param[in] mode The lock mode */
RecLock(dict_index_t* index,
const buf_block_t*
block,
ulint heap_no,
ulint mode)
:
m_thr(),
m_trx(),
m_mode(mode),
m_index(index),
m_rec_id(block, heap_no)
{
btr_assert_not_corrupted(block, index);
init(block->frame);
}
/**
Enqueue a lock wait for a transaction. If it is a high priority
transaction (cannot rollback) then jump ahead in the record lock wait
queue and if the transaction at the head of the queue is itself waiting
roll it back.
@param[in, out] wait_for The lock that the the joining
transaction is waiting for
@param[in] prdt Predicate [optional]
@return DB_LOCK_WAIT, DB_DEADLOCK, or
DB_SUCCESS_LOCKED_REC; DB_SUCCESS_LOCKED_REC means that
there was a deadlock, but another transaction was chosen
as a victim, and we got the lock immediately: no need to
wait then */
dberr_t add_to_waitq(
lock_t* wait_for,
const lock_prdt_t*
prdt = NULL);
/**
Create a lock for a transaction and initialise it.
@param[in, out] trx Transaction requesting the new lock
@param[in] owns_trx_mutex true if caller owns the trx_t::mutex
@param[in] add_to_hash add the lock to hash table
@param[in] prdt Predicate lock (optional)
@param[in,out] c_lock Conflicting lock request or NULL
in Galera conflicting lock is selected
as deadlock victim if requester
is BF transaction.
@return new lock instance */
lock_t* create(
trx_t* trx,
bool owns_trx_mutex,
bool add_to_hash,
const lock_prdt_t*
prdt = NULL
#ifdef WITH_WSREP
,lock_t* c_lock = NULL
#endif /* WITH_WSREP */
);
/**
Check of the lock is on m_rec_id.
@param[in] lock Lock to compare with
@return true if the record lock is on m_rec_id*/
bool is_on_row(const lock_t* lock) const;
/**
Create the lock instance
@param[in, out] trx The transaction requesting the lock
@param[in, out] index Index on which record lock is required
@param[in] mode The lock mode desired
@param[in] rec_id The record id
@param[in] size Size of the lock + bitmap requested
@return a record lock instance */
static lock_t* lock_alloc(
trx_t* trx,
dict_index_t* index,
ulint mode,
const RecID& rec_id,
ulint size);
private:
/*
@return the record lock size in bytes */
size_t lock_size() const
{
return(m_size);
}
/**
Do some checks and prepare for creating a new record lock */
void prepare() const;
/**
Collect the transactions that will need to be rolled back asynchronously
@param[in, out] trx Transaction to be rolled back */
void mark_trx_for_rollback(trx_t* trx);
/**
Jump the queue for the record over all low priority transactions and
add the lock. If all current granted locks are compatible, grant the
lock. Otherwise, mark all granted transaction for asynchronous
rollback and add to hit list.
@param[in, out] lock Lock being requested
@param[in] conflict_lock First conflicting lock from the head
@return true if the lock is granted */
bool jump_queue(lock_t* lock, const lock_t* conflict_lock);
/** Find position in lock queue and add the high priority transaction
lock. Intention and GAP only locks can be granted even if there are
waiting locks in front of the queue. To add the High priority
transaction in a safe position we keep the following rule.
1. If the lock can be granted, add it before the first waiting lock
in the queue so that all currently waiting locks need to do conflict
check before getting granted.
2. If the lock has to wait, add it after the last granted lock or the
last waiting high priority transaction in the queue whichever is later.
This ensures that the transaction is granted only after doing conflict
check with all granted transactions.
@param[in] lock Lock being requested
@param[in] conflict_lock First conflicting lock from the head
@param[out] high_priority high priority transaction ahead in queue
@return true if the lock can be granted */
bool
lock_add_priority(
lock_t* lock,
const lock_t* conflict_lock,
bool* high_priority);
/** Iterate over the granted locks and prepare the hit list for ASYNC Rollback.
If the transaction is waiting for some other lock then wake up with deadlock error.
Currently we don't mark following transactions for ASYNC Rollback.
1. Read only transactions
2. Background transactions
3. Other High priority transactions
@param[in] lock Lock being requested
@param[in] conflict_lock First conflicting lock from the head */
void make_trx_hit_list(lock_t* lock, const lock_t* conflict_lock);
/**
Setup the requesting transaction state for lock grant
@param[in,out] lock Lock for which to change state */
void set_wait_state(lock_t* lock);
/**
Add the lock to the record lock hash and the transaction's lock list
@param[in,out] lock Newly created record lock to add to the
rec hash and the transaction lock list
@param[in] add_to_hash If the lock should be added to the hash table */
void lock_add(lock_t* lock, bool add_to_hash);
/**
Check and resolve any deadlocks
@param[in, out] lock The lock being acquired
@return DB_LOCK_WAIT, DB_DEADLOCK, or
DB_SUCCESS_LOCKED_REC; DB_SUCCESS_LOCKED_REC means that
there was a deadlock, but another transaction was chosen
as a victim, and we got the lock immediately: no need to
wait then */
dberr_t deadlock_check(lock_t* lock);
/**
Check the outcome of the deadlock check
@param[in,out] victim_trx Transaction selected for rollback
@param[in,out] lock Lock being requested
@return DB_LOCK_WAIT, DB_DEADLOCK or DB_SUCCESS_LOCKED_REC */
dberr_t check_deadlock_result(const trx_t* victim_trx, lock_t* lock);
/**
Setup the context from the requirements */
void init(const page_t* page)
{
ut_ad(lock_mutex_own());
ut_ad(!srv_read_only_mode);
ut_ad(dict_index_is_clust(m_index)
|| !dict_index_is_online_ddl(m_index));
ut_ad(m_thr == NULL || m_trx == thr_get_trx(m_thr));
m_size = is_predicate_lock(m_mode)
? lock_size(m_mode) : lock_size(page);
/** If rec is the supremum record, then we reset the
gap and LOCK_REC_NOT_GAP bits, as all locks on the
supremum are automatically of the gap type */
if (m_rec_id.m_heap_no == PAGE_HEAP_NO_SUPREMUM) {
ut_ad(!(m_mode & LOCK_REC_NOT_GAP));
m_mode &= ~(LOCK_GAP | LOCK_REC_NOT_GAP);
}
}
/**
Calculate the record lock physical size required for a predicate lock.
@param[in] mode For predicate locks the lock mode
@return the size of the lock data structure required in bytes */
static size_t lock_size(ulint mode)
{
ut_ad(is_predicate_lock(mode));
/* The lock is always on PAGE_HEAP_NO_INFIMUM(0),
so we only need 1 bit (which is rounded up to 1
byte) for lock bit setting */
size_t n_bytes;
if (mode & LOCK_PREDICATE) {
const ulint align = UNIV_WORD_SIZE - 1;
/* We will attach the predicate structure
after lock. Make sure the memory is
aligned on 8 bytes, the mem_heap_alloc
will align it with MEM_SPACE_NEEDED
anyway. */
n_bytes = (1 + sizeof(lock_prdt_t) + align) & ~align;
/* This should hold now */
ut_ad(n_bytes == sizeof(lock_prdt_t) + UNIV_WORD_SIZE);
} else {
n_bytes = 1;
}
return(n_bytes);
}
/**
Calculate the record lock physical size required, non-predicate lock.
@param[in] page For non-predicate locks the buffer page
@return the size of the lock data structure required in bytes */
static size_t lock_size(const page_t* page)
{
ulint n_recs = page_dir_get_n_heap(page);
/* Make lock bitmap bigger by a safety margin */
return(1 + ((n_recs + LOCK_PAGE_BITMAP_MARGIN) / 8));
}
/**
@return true if the requested lock mode is for a predicate
or page lock */
static bool is_predicate_lock(ulint mode)
{
return(mode & (LOCK_PREDICATE | LOCK_PRDT_PAGE));
}
private:
/** The query thread of the transaction */
que_thr_t* m_thr;
/**
Transaction requesting the record lock */
trx_t* m_trx;
/**
Lock mode requested */
ulint m_mode;
/**
Size of the record lock in bytes */
size_t m_size;
/**
Index on which the record lock is required */
dict_index_t* m_index;
/**
The record lock tuple {space, page_no, heap_no} */
RecID m_rec_id;
};
#ifdef UNIV_DEBUG
/** The count of the types of locks. */
static const ulint lock_types = UT_ARR_SIZE(lock_compatibility_matrix);

2
storage/innobase/include/lock0priv.ic

@ -32,6 +32,8 @@ methods but they are used only in that file. */
#error Do not include lock0priv.ic outside of the lock/ module
#endif
#include "row0row.h"
/*********************************************************************//**
Gets the type of a lock.
@return LOCK_TABLE or LOCK_REC */

330
storage/innobase/include/trx0trx.h

@ -28,13 +28,11 @@ Created 3/26/1996 Heikki Tuuri
#define trx0trx_h
#include <set>
#include <list>
#include "ha_prototypes.h"
#include "dict0types.h"
#include "trx0types.h"
#include "ut0new.h"
#include "lock0types.h"
#include "log0log.h"
@ -43,7 +41,6 @@ Created 3/26/1996 Heikki Tuuri
#include "trx0xa.h"
#include "ut0vec.h"
#include "fts0fts.h"
#include "srv0srv.h"
// Forward declaration
struct mtr_t;
@ -255,14 +252,9 @@ dberr_t
trx_commit_for_mysql(
/*=================*/
trx_t* trx); /*!< in/out: transaction */
/**
Does the transaction prepare for MySQL.
@param[in, out] trx Transaction instance to prepare */
dberr_t
trx_prepare_for_mysql(trx_t* trx);
/** XA PREPARE a transaction.
@param[in,out] trx transaction to prepare */
void trx_prepare_for_mysql(trx_t* trx);
/**********************************************************************//**
This function is used to find number of prepared transactions and
their transaction objects for a recovery.
@ -551,29 +543,6 @@ trx_release_reference(
Check if the transaction is being referenced. */
#define trx_is_referenced(t) ((t)->n_ref > 0)
/**
@param[in] requestor Transaction requesting the lock
@param[in] holder Transaction holding the lock
@return the transaction that will be rolled back, null don't care */
UNIV_INLINE
const trx_t*
trx_arbitrate(const trx_t* requestor, const trx_t* holder);
/**
@param[in] trx Transaction to check
@return true if the transaction is a high priority transaction.*/
UNIV_INLINE
bool
trx_is_high_priority(const trx_t* trx);
/**
Kill all transactions that are blocking this transaction from acquiring locks.
@param[in,out] trx High priority transaction */
void
trx_kill_blocking(trx_t* trx);
/**
Transactions that aren't started by the MySQL server don't set
the trx_t::mysql_thd field. For such transactions we set the lock
@ -622,7 +591,6 @@ Check transaction state */
case TRX_STATE_COMMITTED_IN_MEMORY: \
continue; \
case TRX_STATE_NOT_STARTED: \
case TRX_STATE_FORCED_ROLLBACK: \
break; \
} \
ut_error; \
@ -631,8 +599,7 @@ Check transaction state */
/** Check if transaction is free so that it can be re-initialized.
@param t transaction handle */
#define assert_trx_is_free(t) do { \
ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED) \
|| trx_state_eq((t), TRX_STATE_FORCED_ROLLBACK)); \
ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED)); \
ut_ad(!trx->has_logged()); \
ut_ad(!MVCC::is_view_active((t)->read_view)); \
ut_ad((t)->lock.wait_thr == NULL); \
@ -662,7 +629,6 @@ The tranasction must be in the mysql_trx_list. */
ut_ad(!(t)->in_rw_trx_list); \
ut_ad((t)->in_mysql_trx_list); \
ut_ad(t_state == TRX_STATE_NOT_STARTED \
|| t_state == TRX_STATE_FORCED_ROLLBACK \
|| t_state == TRX_STATE_ACTIVE); \
} else { \
check_trx_state(t); \
@ -769,10 +735,6 @@ struct trx_lock_t {
Protected by both the lock sys mutex
and the trx_t::mutex. */
ulint n_rec_locks; /*!< number of rec locks in this trx */
/** The transaction called ha_innobase::start_stmt() to
lock a table. Most likely a temporary table. */
bool start_stmt;
};
/** Type used to store the list of tables that are modified by a given
@ -858,47 +820,12 @@ struct trx_rsegs_t {
trx_temp_undo_t m_noredo;
};
struct TrxVersion {
TrxVersion(trx_t* trx);
/**
@return true if the trx_t instance is the same */
bool operator==(const TrxVersion& rhs) const
{
return(rhs.m_trx == m_trx);
}
trx_t* m_trx;
ulint m_version;
};
typedef std::list<TrxVersion, ut_allocator<TrxVersion> > hit_list_t;
struct trx_t {
TrxMutex mutex; /*!< Mutex protecting the fields
state and lock (except some fields
of lock, which are protected by
lock_sys->mutex) */
/* Note: in_depth was split from in_innodb for fixing a RO
performance issue. Acquiring the trx_t::mutex for each row
costs ~3% in performance. It is not required for correctness.
Therefore we increment/decrement in_depth without holding any
mutex. The assumption is that the Server will only ever call
the handler from one thread. This is not true for kill_connection.
Therefore in innobase_kill_connection. We don't increment this
counter via TrxInInnoDB. */
ib_uint32_t in_depth; /*!< Track nested TrxInInnoDB
count */
ib_uint32_t in_innodb; /*!< if the thread is executing
in the InnoDB context count > 0. */
bool abort; /*!< if this flag is set then
this transaction must abort when
it can */
trx_id_t id; /*!< transaction id */
trx_id_t no; /*!< transaction serialization number:
@ -915,7 +842,6 @@ struct trx_t {
Possible states:
TRX_STATE_NOT_STARTED
TRX_STATE_FORCED_ROLLBACK
TRX_STATE_ACTIVE
TRX_STATE_PREPARED
TRX_STATE_COMMITTED_IN_MEMORY (alias below COMMITTED)
@ -995,22 +921,6 @@ struct trx_t {
protected by trx_sys->mutex when
trx->in_rw_trx_list holds */
hit_list_t hit_list; /*!< List of transactions to kill,
when a high priority transaction
is blocked on a lock wait. */
os_thread_id_t killed_by; /*!< The thread ID that wants to
kill this transaction asynchronously.
This is required because we recursively
enter the handlerton methods and need
to distinguish between the kill thread
and the transaction thread.
Note: We need to be careful w.r.t the
Thread Pool. The thread doing the kill
should not leave InnoDB between the
mark and the actual async kill because
the running thread can change. */
/* These fields are not protected by any mutex. */
const char* op_info; /*!< English text describing the
@ -1223,12 +1133,6 @@ struct trx_t {
signify that it is no longer
"active". */
/** Version of this instance. It is incremented each time the
instance is re-used in trx_start_low(). It is used to track
whether a transaction has been restarted since it was tagged
for asynchronous rollback. */
ulint version;
XID* xid; /*!< X/Open XA transaction
identification to identify a
transaction branch */
@ -1292,13 +1196,9 @@ private:
Check if transaction is started.
@param[in] trx Transaction whose state we need to check
@reutrn true if transaction is in state started */
inline
bool
trx_is_started(
const trx_t* trx)
inline bool trx_is_started(const trx_t* trx)
{
return(trx->state != TRX_STATE_NOT_STARTED
&& trx->state != TRX_STATE_FORCED_ROLLBACK);
return trx->state != TRX_STATE_NOT_STARTED;
}
/* Transaction isolation levels (trx->isolation_level) */
@ -1371,224 +1271,6 @@ struct commit_node_t{
mutex_exit(&t->mutex); \
} while (0)
/** Track if a transaction is executing inside InnoDB code. It acts
like a gate between the Server and InnoDB. */
class TrxInInnoDB {
public:
/**
@param[in,out] trx Transaction entering InnoDB via the handler
@param[in] disable true if called from COMMIT/ROLLBACK method */
TrxInInnoDB(trx_t* trx, bool disable = false)
:
m_trx(trx)
{
enter(trx, disable);
}
/**
Destructor */
~TrxInInnoDB()
{
exit(m_trx);
}
/**
@return true if the transaction has been marked for asynchronous
rollback */
bool is_aborted() const
{
return(is_aborted(m_trx));
}
/**
@return true if the transaction can't be rolled back asynchronously */
bool is_rollback_disabled() const
{
return((m_trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE) > 0);
}
/**
@return true if the transaction has been marked for asynchronous
rollback */
static bool is_aborted(const trx_t* trx)
{
if (trx->state == TRX_STATE_NOT_STARTED) {
return(false);
}
ut_ad(srv_read_only_mode || trx->in_depth > 0);
ut_ad(srv_read_only_mode || trx->in_innodb > 0);
return(trx->abort
|| trx->state == TRX_STATE_FORCED_ROLLBACK);
}
/**
Start statement requested for transaction.
@param[in, out] trx Transaction at the start of a SQL statement */
static void begin_stmt(trx_t* trx)
{
enter(trx, false);
}
/**
Note an end statement for transaction
@param[in, out] trx Transaction at end of a SQL statement */
static void end_stmt(trx_t* trx)
{
exit(trx);
}
/**
@return true if the rollback is being initiated by the thread that
marked the transaction for asynchronous rollback */
static bool is_async_rollback(const trx_t* trx)
{
return(trx->killed_by == os_thread_get_curr_id());
}
private:
/**
Note that we have crossed into InnoDB code.
@param[in] disable true if called from COMMIT/ROLLBACK method */
static void enter(trx_t* trx, bool disable)
{
if (srv_read_only_mode) {
return;
}
ut_ad(!is_async_rollback(trx));
/* If it hasn't already been marked for async rollback.
and it will be committed/rolled back. */
if (disable) {
trx_mutex_enter(trx);
if (!is_forced_rollback(trx)
&& is_started(trx)
&& !trx_is_autocommit_non_locking(trx)) {
ut_ad(trx->killed_by == 0);
/* This transaction has crossed the point of
no return and cannot be rolled back
asynchronously now. It must commit or rollback
synhronously. */
trx->in_innodb |= TRX_FORCE_ROLLBACK_DISABLE;
}
trx_mutex_exit(trx);
}
/* Avoid excessive mutex acquire/release */
++trx->in_depth;
/* If trx->in_depth is greater than 1 then
transaction is already in InnoDB. */
if (trx->in_depth > 1) {
return;
}
trx_mutex_enter(trx);
wait(trx);
ut_ad((trx->in_innodb & TRX_FORCE_ROLLBACK_MASK) == 0);
++trx->in_innodb;
trx_mutex_exit(trx);
}
/**
Note that we are exiting InnoDB code */
static void exit(trx_t* trx)
{
if (srv_read_only_mode) {
return;
}
/* Avoid excessive mutex acquire/release */
ut_ad(trx->in_depth > 0);
--trx->in_depth;
if (trx->in_depth > 0) {
return;
}
trx_mutex_enter(trx);
ut_ad((trx->in_innodb & TRX_FORCE_ROLLBACK_MASK) > 0);
--trx->in_innodb;
trx_mutex_exit(trx);
}
/*
@return true if it is a forced rollback, asynchronously */
static bool is_forced_rollback(const trx_t* trx)
{
ut_ad(trx_mutex_own(trx));
return((trx->in_innodb & TRX_FORCE_ROLLBACK)) > 0;
}
/**
Wait for the asynchronous rollback to complete, if it is in progress */
static void wait(trx_t* trx)
{
ut_ad(trx_mutex_own(trx));
ulint loop_count = 0;
/* start with optimistic sleep time - 20 micro seconds. */
ulint sleep_time = 20;
while (is_forced_rollback(trx)) {
/* Wait for the async rollback to complete */
trx_mutex_exit(trx);
loop_count++;
/* If the wait is long, don't hog the cpu. */
if (loop_count < 100) {
/* 20 microseconds */
sleep_time = 20;
} else if (loop_count < 1000) {
/* 1 millisecond */
sleep_time = 1000;
} else {
/* 100 milliseconds */
sleep_time = 100000;
}
os_thread_sleep(sleep_time);
trx_mutex_enter(trx);
}
}
/**
@return true if transaction is started */
static bool is_started(const trx_t* trx)
{
ut_ad(trx_mutex_own(trx));
return(trx_is_started(trx));
}
private:
/**
Transaction instance crossing the handler boundary from the Server. */
trx_t* m_trx;
};
#include "trx0trx.ic"
#endif

69
storage/innobase/include/trx0trx.ic

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2016, MariaDB Corporation. All Rights Reserved.
Copyright (c) 2016, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -64,11 +64,8 @@ trx_state_eq(
return(state == trx->state);
case TRX_STATE_NOT_STARTED:
case TRX_STATE_FORCED_ROLLBACK:
/* These states are not allowed for running transactions. */
ut_a(state == TRX_STATE_NOT_STARTED
|| state == TRX_STATE_FORCED_ROLLBACK
|| (relaxed
&& thd_get_error_number(trx->mysql_thd)));
@ -280,67 +277,3 @@ trx_get_read_view(
{
return(!MVCC::is_view_active(trx->read_view) ? NULL : trx->read_view);
}
/**
@param[in] trx Transaction to check
@return true if the transaction is a high priority transaction.*/
UNIV_INLINE
bool
trx_is_high_priority(const trx_t* trx)
{
if (trx->mysql_thd == NULL) {
return(false);
}
return(thd_trx_priority(trx->mysql_thd) > 0);
}
/**
@param[in] requestor Transaction requesting the lock
@param[in] holder Transaction holding the lock
@return the transaction that will be rolled back, null don't care */
UNIV_INLINE
const trx_t*
trx_arbitrate(const trx_t* requestor, const trx_t* holder)
{
ut_ad(!trx_is_autocommit_non_locking(holder));
ut_ad(!trx_is_autocommit_non_locking(requestor));
/* Note: Background stats collection transactions also acquire
locks on user tables. They don't have an associated MySQL session
instance. */
if (requestor->mysql_thd == NULL) {
ut_ad(!trx_is_high_priority(requestor));
if (trx_is_high_priority(holder)) {
return(requestor);
} else {
return(NULL);
}
} else if (holder->mysql_thd == NULL) {
ut_ad(!trx_is_high_priority(holder));
if (trx_is_high_priority(requestor)) {
return(holder);
}
return(NULL);
}
const THD* victim = thd_trx_arbitrate(
requestor->mysql_thd, holder->mysql_thd);
ut_ad(victim == NULL
|| victim == requestor->mysql_thd
|| victim == holder->mysql_thd);
if (victim != NULL) {
return(victim == requestor->mysql_thd ? requestor : holder);
}
return(NULL);
}

22
storage/innobase/include/trx0types.h

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2014, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
Copyright (c) 2017, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -53,21 +53,6 @@ static const ulint TRX_SYS_SPACE = 0;
/** Random value to check for corruption of trx_t */
static const ulint TRX_MAGIC_N = 91118598;
/** If this flag is set then the transaction cannot be rolled back
asynchronously. */
static const ib_uint32_t TRX_FORCE_ROLLBACK_DISABLE = 1 << 29;
/** Was the transaction rolled back asynchronously or by the
owning thread. This flag is relevant only if TRX_FORCE_ROLLBACK
is set. */
static const ib_uint32_t TRX_FORCE_ROLLBACK_ASYNC = 1 << 30;
/** Mark the transaction for forced rollback */
static const ib_uint32_t TRX_FORCE_ROLLBACK = 1U << 31;
/** For masking out the above four flags */
static const ib_uint32_t TRX_FORCE_ROLLBACK_MASK = 0x1FFFFFFF;
/** Transaction execution states when trx->state == TRX_STATE_ACTIVE */
enum trx_que_t {
TRX_QUE_RUNNING, /*!< transaction is running */
@ -79,13 +64,8 @@ enum trx_que_t {
/** Transaction states (trx_t::state) */
enum trx_state_t {
TRX_STATE_NOT_STARTED,
/** Same as not started but with additional semantics that it
was rolled back asynchronously the last time it was active. */
TRX_STATE_FORCED_ROLLBACK,
TRX_STATE_ACTIVE,
/** Support for 2PC/XA */

5
storage/innobase/include/trx0undo.h

@ -28,12 +28,7 @@ Created 3/26/1996 Heikki Tuuri
#define trx0undo_h
#ifndef UNIV_INNOCHECKSUM
#include "univ.i"
#include "trx0types.h"
#include "mtr0mtr.h"
#include "trx0sys.h"
#include "page0types.h"
#include "trx0xa.h"
/** The LSB of the "is insert" flag in DB_ROLL_PTR */
#define ROLL_PTR_INSERT_FLAG_POS 55

1065
storage/innobase/lock/lock0lock.cc
File diff suppressed because it is too large
View File

62
storage/innobase/lock/lock0prdt.cc

@ -38,6 +38,7 @@ Created 9/7/2013 Jimmy Yang
#include "ut0vec.h"
#include "btr0btr.h"
#include "dict0boot.h"
#include "que0que.h"
#include <set>
/*********************************************************************//**
@ -495,9 +496,18 @@ lock_prdt_add_to_queue(
}
}
RecLock rec_lock(index, block, PRDT_HEAPNO, type_mode);
lock = lock_rec_create(
#ifdef WITH_WSREP
NULL, NULL, /* FIXME: replicate SPATIAL INDEX locks */
#endif
type_mode, block, PRDT_HEAPNO, index, trx,
caller_owns_trx_mutex);
return(rec_lock.create(trx, caller_owns_trx_mutex, true, prdt));
if (lock->type_mode & LOCK_PREDICATE) {
lock_prdt_set_prdt(lock, prdt);
}
return lock;
}
/*********************************************************************//**
@ -565,7 +575,7 @@ lock_prdt_insert_check_and_lock(
const ulint mode = LOCK_X | LOCK_PREDICATE | LOCK_INSERT_INTENTION;
lock_t* wait_for = lock_prdt_other_has_conflicting(
const lock_t* wait_for = lock_prdt_other_has_conflicting(
mode, block, prdt, trx);
if (wait_for != NULL) {
@ -574,16 +584,17 @@ lock_prdt_insert_check_and_lock(
/* Allocate MBR on the lock heap */
lock_init_prdt_from_mbr(prdt, mbr, 0, trx->lock.lock_heap);
RecLock rec_lock(thr, index, block, PRDT_HEAPNO, mode);
/* Note that we may get DB_SUCCESS also here! */
trx_mutex_enter(trx);
err = rec_lock.add_to_waitq(wait_for, prdt);
err = lock_rec_enqueue_waiting(
#ifdef WITH_WSREP
NULL, /* FIXME: replicate SPATIAL INDEX locks */
#endif
LOCK_X | LOCK_PREDICATE | LOCK_INSERT_INTENTION,
block, PRDT_HEAPNO, index, thr, prdt);
trx_mutex_exit(trx);
} else {
err = DB_SUCCESS;
}
@ -831,13 +842,14 @@ lock_prdt_lock(
lock_t* lock = lock_rec_get_first_on_page(hash, block);
if (lock == NULL) {
RecLock rec_lock(index, block, PRDT_HEAPNO, prdt_mode);
lock = rec_lock.create(trx, false, true);
lock = lock_rec_create(
#ifdef WITH_WSREP
NULL, NULL, /* FIXME: replicate SPATIAL INDEX locks */
#endif
mode | type_mode, block, PRDT_HEAPNO,
index, trx, FALSE);
status = LOCK_REC_SUCCESS_CREATED;
} else {
trx_mutex_enter(trx);
@ -861,12 +873,14 @@ lock_prdt_lock(
if (wait_for != NULL) {
RecLock rec_lock(
thr, index, block, PRDT_HEAPNO,
prdt_mode, prdt);
err = rec_lock.add_to_waitq(wait_for);
err = lock_rec_enqueue_waiting(
#ifdef WITH_WSREP
NULL, /* FIXME: replicate
SPATIAL INDEX locks */
#endif
mode | type_mode,
block, PRDT_HEAPNO,
index, thr, prdt);
} else {
lock_prdt_add_to_queue(
@ -947,10 +961,12 @@ lock_place_prdt_page_lock(
}
if (lock == NULL) {
RecID rec_id(space, page_no, PRDT_HEAPNO);
RecLock rec_lock(index, rec_id, mode);
rec_lock.create(trx, false, true);
lock = lock_rec_create_low(
#ifdef WITH_WSREP
NULL, NULL, /* FIXME: replicate SPATIAL INDEX locks */
#endif
mode, space, page_no, NULL, PRDT_HEAPNO,
index, trx, FALSE);
#ifdef PRDT_DIAG
printf("GIS_DIAGNOSTIC: page lock %d\n", (int) page_no);

6
storage/innobase/lock/lock0wait.cc

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2014, 2017, MariaDB Corporation.
Copyright (c) 2014, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -419,7 +419,7 @@ lock_wait_suspend_thread(
&& (!wsrep_on_trx(trx) ||
(!wsrep_is_BF_lock_timeout(trx, false) && trx->error_state != DB_DEADLOCK))
#endif /* WITH_WSREP */
&& !trx_is_high_priority(trx)) {
) {
trx->error_state = DB_LOCK_WAIT_TIMEOUT;
@ -502,7 +502,7 @@ lock_wait_check_and_cancel(
trx_mutex_enter(trx);
if (trx->lock.wait_lock != NULL && !trx_is_high_priority(trx)) {
if (trx->lock.wait_lock != NULL) {
ut_a(trx->lock.que_state == TRX_QUE_LOCK_WAIT);

3
storage/innobase/page/page0page.cc

@ -2,7 +2,7 @@
Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2012, Facebook Inc.
Copyright (c) 2017, MariaDB Corporation.
Copyright (c) 2017, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -35,6 +35,7 @@ Created 2/2/1994 Heikki Tuuri
#include "lock0lock.h"
#include "fut0lst.h"
#include "btr0sea.h"
#include "trx0sys.h"
/* THE INDEX PAGE
==============

1
storage/innobase/page/page0zip.cc

@ -45,6 +45,7 @@ const byte field_ref_zero[FIELD_REF_SIZE] = {
#include "btr0cur.h"
#include "page0types.h"
#include "log0recv.h"
#include "row0row.h"
#include "row0trunc.h"
#include "zlib.h"
#include "buf0buf.h"

2
storage/innobase/row/row0ins.cc

@ -1852,8 +1852,6 @@ do_possible_lock_wait:
my_atomic_addlint(
&check_table->n_foreign_key_checks_running, 1);
trx_kill_blocking(trx);
lock_wait_suspend_thread(thr);
thr->lock_state = QUE_THR_LOCK_NOLOCK;

4
storage/innobase/row/row0merge.cc

@ -36,6 +36,7 @@ Completed by Sunny Bains and Marko Makela
#include "row0ext.h"
#include "row0log.h"
#include "row0ins.h"
#include "row0row.h"
#include "row0sel.h"
#include "log0crypt.h"
#include "dict0crea.h"
@ -45,6 +46,7 @@ Completed by Sunny Bains and Marko Makela
#include "ut0sort.h"
#include "row0ftsort.h"
#include "row0import.h"
#include "row0vers.h"
#include "handler0alter.h"
#include "btr0bulk.h"
#include "fsp0sysspace.h"
@ -3586,8 +3588,6 @@ row_merge_lock_table(
trx->op_info = "setting table lock for creating or dropping index";
trx->ddl = true;
/* Trx for DDL should not be forced to rollback for now */
trx->in_innodb |= TRX_FORCE_ROLLBACK_DISABLE;
return(lock_table_for_trx(table, trx, mode));
}

3
storage/innobase/row/row0mysql.cc

@ -729,9 +729,6 @@ handle_new_error:
/* MySQL will roll back the latest SQL statement */
break;
case DB_LOCK_WAIT:
trx_kill_blocking(trx);
lock_wait_suspend_thread(thr);
if (trx->error_state != DB_SUCCESS) {

18
storage/innobase/row/row0sel.cc

@ -4437,17 +4437,12 @@ row_search_mvcc(
naturally moves upward (in fetch next) in alphabetical order,
otherwise downward */
if (direction == 0) {
if (mode == PAGE_CUR_GE
|| mode == PAGE_CUR_G
if (UNIV_UNLIKELY(direction == 0)) {
if (mode == PAGE_CUR_GE || mode == PAGE_CUR_G
|| mode >= PAGE_CUR_CONTAIN) {
moves_up = TRUE;
}
} else if (direction == ROW_SEL_NEXT) {
moves_up = TRUE;
}
@ -5682,15 +5677,6 @@ normal_return:
mtr.commit();
/* Rollback blocking transactions from hit list for high priority
transaction, if any. We should not be holding latches here as
we are going to rollback the blocking transactions. */
if (!trx->hit_list.empty()) {
ut_ad(trx_is_high_priority(trx));
trx_kill_blocking(trx);
}
DEBUG_SYNC_C("row_search_for_mysql_before_return");
if (prebuilt->idx_cond != 0) {

5
storage/innobase/row/row0trunc.cc

@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2013, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, MariaDB Corporation.
Copyright (c) 2017, 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@ -36,7 +36,8 @@ Created 2013-04-12 Sunny Bains
#include "srv0start.h"
#include "row0trunc.h"
#include "os0file.h"
#include <vector>
#include "que0que.h"
#include "trx0undo.h"
/* FIXME: For temporary tables, use a simple approach of btr_free()
and btr_create() of each index tree. */

33
storage/innobase/trx/trx0roll.cc

@ -183,10 +183,7 @@ trx_rollback_for_mysql_low(
/** Rollback a transaction used in MySQL
@param[in, out] trx transaction
@return error code or DB_SUCCESS */
static
dberr_t
trx_rollback_low(
trx_t* trx)
dberr_t trx_rollback_for_mysql(trx_t* trx)
{
/* We are reading trx->state without holding trx_sys->mutex
here, because the rollback should be invoked for a running
@ -194,7 +191,6 @@ trx_rollback_low(
that is associated with the current thread. */
switch (trx->state) {
case TRX_STATE_FORCED_ROLLBACK:
case TRX_STATE_NOT_STARTED:
trx->will_lock = 0;
ut_ad(trx->in_mysql_trx_list);
@ -261,28 +257,6 @@ trx_rollback_low(
return(DB_CORRUPTION);
}
/*******************************************************************//**
Rollback a transaction used in MySQL.
@return error code or DB_SUCCESS */
dberr_t
trx_rollback_for_mysql(
/*===================*/
trx_t* trx) /*!< in/out: transaction */
{
/* Avoid the tracking of async rollback killer
thread to enter into InnoDB. */
if (TrxInInnoDB::is_async_rollback(trx)) {
return(trx_rollback_low(trx));
} else {
TrxInInnoDB trx_in_innodb(trx, true);
return(trx_rollback_low(trx));
}
}
/*******************************************************************//**
Rollback the latest SQL statement for MySQL.
@return error code or DB_SUCCESS */
@ -300,7 +274,6 @@ trx_rollback_last_sql_stat_for_mysql(
ut_ad(trx->in_mysql_trx_list);
switch (trx->state) {
case TRX_STATE_FORCED_ROLLBACK:
case TRX_STATE_NOT_STARTED:
return(DB_SUCCESS);
@ -487,12 +460,9 @@ trx_rollback_to_savepoint_for_mysql(
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
case TRX_STATE_FORCED_ROLLBACK:
ib::error() << "Transaction has a savepoint "
<< savep->name
<< " though it is not started";
return(DB_ERROR);
case TRX_STATE_ACTIVE:
@ -780,7 +750,6 @@ fake_prepared:
case TRX_STATE_PREPARED:
goto func_exit;
case TRX_STATE_NOT_STARTED:
case TRX_STATE_FORCED_ROLLBACK:
break;
}

284
storage/innobase/trx/trx0trx.cc

@ -43,7 +43,6 @@ Created 3/26/1996 Heikki Tuuri
#include "srv0mon.h"
#include "srv0srv.h"
#include "fsp0sysspace.h"
#include "row0mysql.h"
#include "srv0start.h"
#include "trx0purge.h"
#include "trx0rec.h"
@ -69,15 +68,6 @@ typedef std::set<
std::less<table_id_t>,
ut_allocator<table_id_t> > table_id_set;
/** Constructor */
TrxVersion::TrxVersion(trx_t* trx)
:
m_trx(trx),
m_version(trx->version)
{
/* No op */
}
/** Set flush observer for the transaction
@param[in/out] trx transaction struct
@param[in] observer flush observer */
@ -121,14 +111,12 @@ trx_init(
/*=====*/
trx_t* trx)
{
/* This is called at the end of commit, do not reset the
trx_t::state here to NOT_STARTED. The FORCED_ROLLBACK
status is required for asynchronous handling. */
trx->id = 0;
trx->no = TRX_ID_MAX;
trx->state = TRX_STATE_NOT_STARTED;
trx->is_recovered = false;
trx->op_info = "";
@ -183,29 +171,7 @@ trx_init(
trx->lock.table_cached = 0;
/* During asynchronous rollback, we should reset forced rollback flag
only after rollback is complete to avoid race with the thread owning
the transaction. */
if (!TrxInInnoDB::is_async_rollback(trx)) {
my_atomic_storelong(&trx->killed_by, 0);
/* Note: Do not set to 0, the ref count is decremented inside
the TrxInInnoDB() destructor. We only need to clear the flags. */
trx->in_innodb &= TRX_FORCE_ROLLBACK_MASK;
}
/* Note: It's possible that this list is not empty if a transaction
was interrupted after it collected the victim transactions and before
it got a chance to roll them back asynchronously. */
trx->hit_list.clear();
trx->flush_observer = NULL;
++trx->version;
}
/** For managing the life-cycle of the trx_t instance that we get
@ -220,7 +186,7 @@ struct TrxFactory {
{
/* Explicitly call the constructor of the already
allocated object. trx_t objects are allocated by
ut_zalloc() in Pool::Pool() which would not call
ut_zalloc_nokey() in Pool::Pool() which would not call
the constructors of the trx_t members. */
new(&trx->mod_tables) trx_mod_tables_t();
@ -230,13 +196,8 @@ struct TrxFactory {
new(&trx->lock.table_locks) lock_pool_t();
new(&trx->hit_list) hit_list_t();
trx_init(trx);
DBUG_LOG("trx", "Init: " << trx);
trx->state = TRX_STATE_NOT_STARTED;
trx->dict_operation_lock_mode = 0;
trx->xid = UT_NEW_NOKEY(xid_t());
@ -309,8 +270,6 @@ struct TrxFactory {
trx->lock.table_pool.~lock_pool_t();
trx->lock.table_locks.~lock_pool_t();
trx->hit_list.~hit_list_t();
}
/** Enforce any invariants here, this is called before the transaction
@ -324,8 +283,7 @@ struct TrxFactory {
ut_ad(!trx->read_only);
ut_ad(trx->state == TRX_STATE_NOT_STARTED
|| trx->state == TRX_STATE_FORCED_ROLLBACK);
ut_ad(trx->state == TRX_STATE_NOT_STARTED);
ut_ad(trx->dict_operation == TRX_DICT_OP_NONE);
@ -344,12 +302,6 @@ struct TrxFactory {
ut_ad(trx->lock.table_locks.empty());
ut_ad(!trx->abort);
ut_ad(trx->hit_list.empty());
ut_ad(trx->killed_by == 0);
return(true);
}
};
@ -442,15 +394,9 @@ trx_create_low()
/* We just got trx from pool, it should be non locking */
ut_ad(trx->will_lock == 0);
ut_ad(trx->state == TRX_STATE_NOT_STARTED);
/* Background trx should not be forced to rollback,
we will unset the flag for user trx. */
trx->in_innodb |= TRX_FORCE_ROLLBACK_DISABLE;
/* Trx state can be TRX_STATE_FORCED_ROLLBACK if
the trx was forced to rollback before it's reused.*/
DBUG_LOG("trx", "Create: " << trx);
trx->state = TRX_STATE_NOT_STARTED;
heap = mem_heap_create(sizeof(ib_vector_t) + sizeof(void*) * 8);
@ -1204,7 +1150,6 @@ trx_start_low(
{
ut_ad(!trx->in_rollback);
ut_ad(!trx->is_recovered);
ut_ad(trx->hit_list.empty());
ut_ad(trx->start_line != 0);
ut_ad(trx->start_file != 0);
ut_ad(trx->roll_limit == 0);
@ -1213,10 +1158,6 @@ trx_start_low(
ut_ad(trx->rsegs.m_noredo.rseg == NULL);
ut_ad(trx_state_eq(trx, TRX_STATE_NOT_STARTED));
ut_ad(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
ut_ad(!(trx->in_innodb & TRX_FORCE_ROLLBACK));
ut_ad(!(trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC));
++trx->version;
/* Check whether it is an AUTOCOMMIT SELECT */
trx->auto_commit = thd_trx_is_auto_commit(trx->mysql_thd);
@ -1731,16 +1672,9 @@ trx_commit_in_memory(
MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT);
/* AC-NL-RO transactions can't be rolled back asynchronously. */
ut_ad(!trx->abort);
ut_ad(!(trx->in_innodb
& (TRX_FORCE_ROLLBACK | TRX_FORCE_ROLLBACK_ASYNC)));
DBUG_LOG("trx", "Autocommit in memory: " << trx);
trx->state = TRX_STATE_NOT_STARTED;
} else {
if (trx->id > 0) {
/* For consistent snapshot, we need to remove current
transaction from running transaction id list for mvcc
@ -1864,20 +1798,8 @@ trx_commit_in_memory(
}
#endif
/* Because we can rollback transactions asynchronously, we change
the state at the last step. trx_t::abort cannot change once commit
or rollback has started because we will have released the locks by
the time we get here. */
if (trx->abort) {
trx->abort = false;
DBUG_LOG("trx", "Abort: " << trx);
trx->state = TRX_STATE_FORCED_ROLLBACK;
} else {
DBUG_LOG("trx", "Commit in memory: " << trx);
trx->state = TRX_STATE_NOT_STARTED;
}
DBUG_LOG("trx", "Commit in memory: " << trx);
trx->state = TRX_STATE_NOT_STARTED;
/* trx->in_mysql_trx_list would hold between
trx_allocate_for_mysql() and trx_free_for_mysql(). It does not
@ -2089,8 +2011,6 @@ trx_commit_or_rollback_prepare(
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
case TRX_STATE_FORCED_ROLLBACK:
trx_start_low(trx, true);
/* fall through */
@ -2194,22 +2114,12 @@ trx_commit_for_mysql(
/*=================*/
trx_t* trx) /*!< in/out: transaction */
{
TrxInInnoDB trx_in_innodb(trx, true);
if (trx_in_innodb.is_aborted()
&& trx->killed_by != os_thread_get_curr_id()) {
return(DB_FORCED_ABORT);
}
/* Because we do not do the commit by sending an Innobase
sig to the transaction, we must here make sure that trx has been
started. */
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
case TRX_STATE_FORCED_ROLLBACK:
ut_d(trx->start_file = __FILE__);
ut_d(trx->start_line = __LINE__);
@ -2270,7 +2180,6 @@ trx_mark_sql_stat_end(
case TRX_STATE_COMMITTED_IN_MEMORY:
break;
case TRX_STATE_NOT_STARTED:
case TRX_STATE_FORCED_ROLLBACK:
trx->undo_no = 0;
trx->undo_rseg_space = 0;
/* fall through */
@ -2321,9 +2230,6 @@ trx_print_low(
case TRX_STATE_NOT_STARTED:
fputs(", not started", f);
goto state_ok;
case TRX_STATE_FORCED_ROLLBACK:
fputs(", forced rollback", f);
goto state_ok;
case TRX_STATE_ACTIVE:
fprintf(f, ", ACTIVE %lu sec",
(ulong) difftime(time(NULL), trx->start_time));
@ -2465,10 +2371,6 @@ wsrep_trx_print_locking(
fprintf(f, ", ACTIVE %lu sec",
(ulong) difftime(time(NULL), trx->start_time));
goto state_ok;
case TRX_STATE_FORCED_ROLLBACK:
fprintf(f, ", FORCED ROLLBACK, %lu sec",
(ulong) difftime(time(NULL), trx->start_time));
goto state_ok;
case TRX_STATE_PREPARED:
fprintf(f, ", ACTIVE (PREPARED) %lu sec",
(ulong) difftime(time(NULL), trx->start_time));
@ -2599,7 +2501,6 @@ trx_assert_started(
return(TRUE);
case TRX_STATE_NOT_STARTED:
case TRX_STATE_FORCED_ROLLBACK:
break;
}
@ -2714,10 +2615,6 @@ trx_prepare(
/*========*/
trx_t* trx) /*!< in/out: transaction */
{
/* This transaction has crossed the point of no return and cannot
be rolled back asynchronously now. It must commit or rollback
synhronously. */
/* Only fresh user transactions can be prepared.
Recovered transactions cannot. */
ut_a(!trx->is_recovered);
@ -2755,29 +2652,17 @@ trx_prepare(
}
}
/**
Does the transaction prepare for MySQL.
@param[in, out] trx Transaction instance to prepare */
dberr_t
trx_prepare_for_mysql(trx_t* trx)
/** XA PREPARE a transaction.
@param[in,out] trx transaction to prepare */
void trx_prepare_for_mysql(trx_t* trx)
{
trx_start_if_not_started_xa(trx, false);
TrxInInnoDB trx_in_innodb(trx, true);
if (trx_in_innodb.is_aborted()
&& trx->killed_by != os_thread_get_curr_id()) {
return(DB_FORCED_ABORT);
}
trx->op_info = "preparing";
trx_prepare(trx);
trx->op_info = "";
return(DB_SUCCESS);
}
/**********************************************************************//**
@ -2925,7 +2810,6 @@ trx_start_if_not_started_xa_low(
{
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
case TRX_STATE_FORCED_ROLLBACK:
trx_start_low(trx, read_write);
return;
@ -2958,13 +2842,10 @@ trx_start_if_not_started_low(
{
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
case TRX_STATE_FORCED_ROLLBACK:
trx_start_low(trx, read_write);
return;
case TRX_STATE_ACTIVE:
if (read_write && trx->id == 0 && !trx->read_only) {
trx_set_rw_mode(trx);
}
@ -3021,8 +2902,6 @@ trx_start_for_ddl_low(
{
switch (trx->state) {
case TRX_STATE_NOT_STARTED:
case TRX_STATE_FORCED_ROLLBACK:
/* Flag this transaction as a dictionary operation, so that
the data dictionary will be locked in crash recovery. */
@ -3114,146 +2993,3 @@ trx_set_rw_mode(
mutex_exit(&trx_sys->mutex);
}
/**
Kill all transactions that are blocking this transaction from acquiring locks.
@param[in,out] trx High priority transaction */
void
trx_kill_blocking(trx_t* trx)
{
if (trx->hit_list.empty()) {
return;
}
DEBUG_SYNC_C("trx_kill_blocking_enter");
ulint had_dict_lock = trx->dict_operation_lock_mode;
switch (had_dict_lock) {
case 0:
break;
case RW_S_LATCH:
/* Release foreign key check latch */
row_mysql_unfreeze_data_dictionary(trx);
break;
default:
/* There should never be a lock wait when the
dictionary latch is reserved in X mode. Dictionary
transactions should only acquire locks on dictionary
tables, not other tables. All access to dictionary
tables should be covered by dictionary
transactions. */
ut_error;
}
ut_a(trx->dict_operation_lock_mode == 0);
/** Kill the transactions in the lock acquisition order old -> new. */
hit_list_t::reverse_iterator end = trx->hit_list.rend();
for (hit_list_t::reverse_iterator it = trx->hit_list.rbegin();
it != end;
++it) {
trx_t* victim_trx = it->m_trx;
ulint version = it->m_version;
/* Shouldn't commit suicide. */
ut_ad(victim_trx != trx);
ut_ad(victim_trx->mysql_thd != trx->mysql_thd);
/* Check that the transaction isn't active inside
InnoDB code. We have to wait while it is executing
in the InnoDB context. This can potentially take a
long time */
trx_mutex_enter(victim_trx);
ut_ad(version <= victim_trx->version);
ulint loop_count = 0;
/* start with optimistic sleep time of 20 micro seconds. */
ulint sleep_time = 20;
while ((victim_trx->in_innodb & TRX_FORCE_ROLLBACK_MASK) > 0
&& victim_trx->version == version) {
trx_mutex_exit(victim_trx);
loop_count++;
/* If the wait is long, don't hog the cpu. */
if (loop_count < 100) {
/* 20 microseconds */
sleep_time = 20;
} else if (loop_count < 1000) {
/* 1 millisecond */
sleep_time = 1000;
} else {
/* 100 milliseconds */
sleep_time = 100000;
}
os_thread_sleep(sleep_time);
trx_mutex_enter(victim_trx);
}
/* Compare the version to check if the transaction has
already finished */
if (victim_trx->version != version) {
trx_mutex_exit(victim_trx);
continue;
}
/* We should never kill background transactions. */
ut_ad(victim_trx->mysql_thd != NULL);
ut_ad(!(trx->in_innodb & TRX_FORCE_ROLLBACK_DISABLE));
ut_ad(victim_trx->in_innodb & TRX_FORCE_ROLLBACK);
ut_ad(victim_trx->in_innodb & TRX_FORCE_ROLLBACK_ASYNC);
ut_ad(victim_trx->killed_by == os_thread_get_curr_id());
ut_ad(victim_trx->version == it->m_version);
/* We don't kill Read Only, Background or high priority
transactions. */
ut_a(!victim_trx->read_only);
ut_a(victim_trx->mysql_thd != NULL);
trx_mutex_exit(victim_trx);
#ifndef DBUG_OFF
char buffer[1024];
#endif /* !DBUG_OFF */
DBUG_LOG("trx",
"High Priority Transaction "
<< trx->id << " killed transaction "
<< victim_trx->id << " in hit list"
<< " - "
<< thd_get_error_context_description(
victim_trx->mysql_thd,
buffer, sizeof(buffer), 512));
trx_rollback_for_mysql(victim_trx);
trx_mutex_enter(victim_trx);
version++;
ut_ad(victim_trx->version == version);
my_atomic_storelong(&victim_trx->killed_by, 0);
victim_trx->in_innodb &= TRX_FORCE_ROLLBACK_MASK;
trx_mutex_exit(victim_trx);
}
trx->hit_list.clear();
if (had_dict_lock) {
row_mysql_freeze_data_dictionary(trx);
}
}
Loading…
Cancel
Save