mirror of
https://github.com/MariaDB/server.git
synced 2026-05-16 20:07:13 +02:00
Merge 10.0 into 10.1
This commit is contained in:
commit
46305b006b
10 changed files with 345 additions and 158 deletions
56
mysql-test/suite/innodb/r/recovery_shutdown.result
Normal file
56
mysql-test/suite/innodb/r/recovery_shutdown.result
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#
|
||||
# MDEV-13797 InnoDB may hang if shutdown is initiated soon after startup
|
||||
# while rolling back recovered incomplete transactions
|
||||
#
|
||||
CREATE TABLE t (a INT) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
COMMIT;
|
||||
CREATE TABLE t8 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
INSERT INTO t8 (a) SELECT NULL FROM t;
|
||||
UPDATE t8 SET a=a+100, b=a;
|
||||
DELETE FROM t8;
|
||||
CREATE TABLE t7 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
INSERT INTO t7 (a) SELECT NULL FROM t;
|
||||
UPDATE t7 SET a=a+100, b=a;
|
||||
DELETE FROM t7;
|
||||
CREATE TABLE t6 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
INSERT INTO t6 (a) SELECT NULL FROM t;
|
||||
UPDATE t6 SET a=a+100, b=a;
|
||||
DELETE FROM t6;
|
||||
CREATE TABLE t5 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
INSERT INTO t5 (a) SELECT NULL FROM t;
|
||||
UPDATE t5 SET a=a+100, b=a;
|
||||
DELETE FROM t5;
|
||||
CREATE TABLE t4 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
INSERT INTO t4 (a) SELECT NULL FROM t;
|
||||
UPDATE t4 SET a=a+100, b=a;
|
||||
DELETE FROM t4;
|
||||
CREATE TABLE t3 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
INSERT INTO t3 (a) SELECT NULL FROM t;
|
||||
UPDATE t3 SET a=a+100, b=a;
|
||||
DELETE FROM t3;
|
||||
CREATE TABLE t2 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
INSERT INTO t2 (a) SELECT NULL FROM t;
|
||||
UPDATE t2 SET a=a+100, b=a;
|
||||
DELETE FROM t2;
|
||||
CREATE TABLE t1 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
INSERT INTO t1 (a) SELECT NULL FROM t;
|
||||
UPDATE t1 SET a=a+100, b=a;
|
||||
DELETE FROM t1;
|
||||
INSERT INTO t1(a) SELECT NULL FROM t;
|
||||
INSERT INTO t1(a) SELECT NULL FROM t1;
|
||||
INSERT INTO t1(a) SELECT NULL FROM t1;
|
||||
INSERT INTO t1(a) SELECT NULL FROM t1;
|
||||
INSERT INTO t1(a) SELECT NULL FROM t1;
|
||||
SET GLOBAL innodb_flush_log_at_trx_commit=1;
|
||||
CREATE TABLE u(a SERIAL) ENGINE=INNODB;
|
||||
# Kill and restart
|
||||
DROP TABLE t,u;
|
||||
57
mysql-test/suite/innodb/t/recovery_shutdown.test
Normal file
57
mysql-test/suite/innodb/t/recovery_shutdown.test
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
--source include/have_innodb.inc
|
||||
--source include/not_embedded.inc
|
||||
|
||||
--echo #
|
||||
--echo # MDEV-13797 InnoDB may hang if shutdown is initiated soon after startup
|
||||
--echo # while rolling back recovered incomplete transactions
|
||||
--echo #
|
||||
|
||||
CREATE TABLE t (a INT) ENGINE=InnoDB;
|
||||
let $size = 100;
|
||||
let $trx = 8;
|
||||
let $c = $size;
|
||||
BEGIN;
|
||||
--disable_query_log
|
||||
while ($c) {
|
||||
INSERT INTO t VALUES();
|
||||
dec $c;
|
||||
}
|
||||
--enable_query_log
|
||||
COMMIT;
|
||||
|
||||
let $c = $trx;
|
||||
while ($c)
|
||||
{
|
||||
connect (con$c,localhost,root,,);
|
||||
eval CREATE TABLE t$c (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB;
|
||||
BEGIN;
|
||||
eval INSERT INTO t$c (a) SELECT NULL FROM t;
|
||||
eval UPDATE t$c SET a=a+$size, b=a;
|
||||
eval DELETE FROM t$c;
|
||||
dec $c;
|
||||
}
|
||||
|
||||
INSERT INTO t1(a) SELECT NULL FROM t;
|
||||
INSERT INTO t1(a) SELECT NULL FROM t1;
|
||||
INSERT INTO t1(a) SELECT NULL FROM t1;
|
||||
INSERT INTO t1(a) SELECT NULL FROM t1;
|
||||
INSERT INTO t1(a) SELECT NULL FROM t1;
|
||||
|
||||
--connection default
|
||||
SET GLOBAL innodb_flush_log_at_trx_commit=1;
|
||||
CREATE TABLE u(a SERIAL) ENGINE=INNODB;
|
||||
|
||||
--source include/kill_and_restart_mysqld.inc
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
--disable_query_log
|
||||
let $c = $trx;
|
||||
while ($c)
|
||||
{
|
||||
disconnect con$c;
|
||||
eval DROP TABLE t$c;
|
||||
dec $c;
|
||||
}
|
||||
--enable_query_log
|
||||
|
||||
DROP TABLE t,u;
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
Copyright (c) 2017, 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
|
||||
|
|
@ -33,6 +34,7 @@ Created 3/26/1996 Heikki Tuuri
|
|||
#include "trx0sys.h"
|
||||
|
||||
extern bool trx_rollback_or_clean_is_active;
|
||||
extern const trx_t* trx_roll_crash_recv_trx;
|
||||
|
||||
/*******************************************************************//**
|
||||
Determines if this transaction is rolling back an incomplete transaction
|
||||
|
|
@ -103,6 +105,11 @@ trx_undo_rec_release(
|
|||
/*=================*/
|
||||
trx_t* trx, /*!< in/out: transaction */
|
||||
undo_no_t undo_no);/*!< in: undo number */
|
||||
/** Report progress when rolling back a row of a recovered transaction.
|
||||
@return whether the rollback should be aborted due to pending shutdown */
|
||||
UNIV_INTERN
|
||||
bool
|
||||
trx_roll_must_shutdown();
|
||||
/*******************************************************************//**
|
||||
Rollback or clean up any incomplete transactions which were
|
||||
encountered in crash recovery. If the transaction already was
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
Copyright (c) 2017, 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
|
||||
|
|
@ -348,6 +349,13 @@ row_undo_step(
|
|||
|
||||
ut_ad(que_node_get_type(node) == QUE_NODE_UNDO);
|
||||
|
||||
if (UNIV_UNLIKELY(trx == trx_roll_crash_recv_trx)
|
||||
&& trx_roll_must_shutdown()) {
|
||||
/* Shutdown has been initiated. */
|
||||
trx->error_state = DB_INTERRUPTED;
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
err = row_undo(node, thr);
|
||||
|
||||
trx->error_state = err;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ Transaction rollback
|
|||
Created 3/26/1996 Heikki Tuuri
|
||||
*******************************************************/
|
||||
|
||||
#include "my_config.h"
|
||||
#include <my_systemd.h>
|
||||
|
||||
#include "trx0roll.h"
|
||||
|
||||
#ifdef UNIV_NONINL
|
||||
|
|
@ -55,14 +58,7 @@ rollback */
|
|||
bool trx_rollback_or_clean_is_active;
|
||||
|
||||
/** In crash recovery, the current trx to be rolled back; NULL otherwise */
|
||||
static const trx_t* trx_roll_crash_recv_trx = NULL;
|
||||
|
||||
/** In crash recovery we set this to the undo n:o of the current trx to be
|
||||
rolled back. Then we can print how many % the rollback has progressed. */
|
||||
static undo_no_t trx_roll_max_undo_no;
|
||||
|
||||
/** Auxiliary variable which tells the previous progress % we printed */
|
||||
static ulint trx_roll_progress_printed_pct;
|
||||
const trx_t* trx_roll_crash_recv_trx;
|
||||
|
||||
/****************************************************************//**
|
||||
Finishes a transaction rollback. */
|
||||
|
|
@ -552,8 +548,6 @@ trx_rollback_active(
|
|||
que_thr_t* thr;
|
||||
roll_node_t* roll_node;
|
||||
dict_table_t* table;
|
||||
ib_int64_t rows_to_undo;
|
||||
const char* unit = "";
|
||||
ibool dictionary_locked = FALSE;
|
||||
|
||||
heap = mem_heap_create(512);
|
||||
|
|
@ -572,30 +566,8 @@ trx_rollback_active(
|
|||
|
||||
ut_a(thr == que_fork_start_command(fork));
|
||||
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
|
||||
trx_roll_crash_recv_trx = trx;
|
||||
|
||||
trx_roll_max_undo_no = trx->undo_no;
|
||||
|
||||
trx_roll_progress_printed_pct = 0;
|
||||
|
||||
rows_to_undo = trx_roll_max_undo_no;
|
||||
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
|
||||
if (rows_to_undo > 1000000000) {
|
||||
rows_to_undo = rows_to_undo / 1000000;
|
||||
unit = "M";
|
||||
}
|
||||
|
||||
ut_print_timestamp(stderr);
|
||||
fprintf(stderr,
|
||||
" InnoDB: Rolling back trx with id " TRX_ID_FMT ", %lu%s"
|
||||
" rows to undo\n",
|
||||
trx->id,
|
||||
(ulong) rows_to_undo, unit);
|
||||
|
||||
if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
|
||||
row_mysql_lock_data_dictionary(trx);
|
||||
dictionary_locked = TRUE;
|
||||
|
|
@ -606,6 +578,14 @@ trx_rollback_active(
|
|||
|
||||
que_run_threads(roll_node->undo_thr);
|
||||
|
||||
if (trx->error_state != DB_SUCCESS) {
|
||||
ut_ad(trx->error_state == DB_INTERRUPTED);
|
||||
ut_ad(!srv_undo_sources);
|
||||
ut_ad(srv_fast_shutdown);
|
||||
ut_ad(!dictionary_locked);
|
||||
goto func_exit;
|
||||
}
|
||||
|
||||
trx_rollback_finish(thr_get_trx(roll_node->undo_thr));
|
||||
|
||||
/* Free the memory reserved by the undo graph */
|
||||
|
|
@ -650,13 +630,14 @@ trx_rollback_active(
|
|||
}
|
||||
}
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_INFO,
|
||||
"Rollback of trx with id " TRX_ID_FMT " completed", trx->id);
|
||||
|
||||
func_exit:
|
||||
if (dictionary_locked) {
|
||||
row_mysql_unlock_data_dictionary(trx);
|
||||
}
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_INFO,
|
||||
"Rollback of trx with id " TRX_ID_FMT " completed", trx->id);
|
||||
|
||||
mem_heap_free(heap);
|
||||
|
||||
trx_roll_crash_recv_trx = NULL;
|
||||
|
|
@ -673,7 +654,7 @@ ibool
|
|||
trx_rollback_resurrected(
|
||||
/*=====================*/
|
||||
trx_t* trx, /*!< in: transaction to rollback or clean */
|
||||
ibool all) /*!< in: FALSE=roll back dictionary transactions;
|
||||
ibool* all) /*!< in/out: FALSE=roll back dictionary transactions;
|
||||
TRUE=roll back all non-PREPARED transactions */
|
||||
{
|
||||
ut_ad(mutex_own(&trx_sys->mutex));
|
||||
|
|
@ -684,16 +665,15 @@ trx_rollback_resurrected(
|
|||
to accidentally clean up a non-recovered transaction here. */
|
||||
|
||||
trx_mutex_enter(trx);
|
||||
bool is_recovered = trx->is_recovered;
|
||||
trx_state_t state = trx->state;
|
||||
if (!trx->is_recovered) {
|
||||
func_exit:
|
||||
trx_mutex_exit(trx);
|
||||
|
||||
if (!is_recovered) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
switch (trx->state) {
|
||||
case TRX_STATE_COMMITTED_IN_MEMORY:
|
||||
trx_mutex_exit(trx);
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
fprintf(stderr,
|
||||
"InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n",
|
||||
|
|
@ -702,21 +682,83 @@ trx_rollback_resurrected(
|
|||
trx_free_for_background(trx);
|
||||
return(TRUE);
|
||||
case TRX_STATE_ACTIVE:
|
||||
if (all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
|
||||
if (!srv_undo_sources && srv_fast_shutdown) {
|
||||
fake_prepared:
|
||||
trx->state = TRX_STATE_PREPARED;
|
||||
trx_sys->n_prepared_trx++;
|
||||
trx_sys->n_prepared_recovered_trx++;
|
||||
*all = FALSE;
|
||||
goto func_exit;
|
||||
}
|
||||
trx_mutex_exit(trx);
|
||||
|
||||
if (*all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
trx_rollback_active(trx);
|
||||
if (trx->error_state != DB_SUCCESS) {
|
||||
ut_ad(trx->error_state == DB_INTERRUPTED);
|
||||
ut_ad(!srv_undo_sources);
|
||||
ut_ad(srv_fast_shutdown);
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
trx_mutex_enter(trx);
|
||||
goto fake_prepared;
|
||||
}
|
||||
trx_free_for_background(trx);
|
||||
return(TRUE);
|
||||
}
|
||||
return(FALSE);
|
||||
case TRX_STATE_PREPARED:
|
||||
return(FALSE);
|
||||
goto func_exit;
|
||||
case TRX_STATE_NOT_STARTED:
|
||||
break;
|
||||
}
|
||||
|
||||
ut_error;
|
||||
return(FALSE);
|
||||
goto func_exit;
|
||||
}
|
||||
|
||||
/** Report progress when rolling back a row of a recovered transaction.
|
||||
@return whether the rollback should be aborted due to pending shutdown */
|
||||
UNIV_INTERN
|
||||
bool
|
||||
trx_roll_must_shutdown()
|
||||
{
|
||||
const trx_t* trx = trx_roll_crash_recv_trx;
|
||||
ut_ad(trx);
|
||||
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
|
||||
|
||||
if (trx_get_dict_operation(trx) == TRX_DICT_OP_NONE
|
||||
&& !srv_undo_sources && srv_fast_shutdown) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ib_time_t time = ut_time();
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
mutex_enter(&recv_sys->mutex);
|
||||
|
||||
if (recv_sys->report(time)) {
|
||||
ulint n_trx = 0, n_rows = 0;
|
||||
for (const trx_t* t = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
|
||||
t != NULL;
|
||||
t = UT_LIST_GET_NEXT(trx_list, t)) {
|
||||
|
||||
assert_trx_in_rw_list(t);
|
||||
if (t->is_recovered
|
||||
&& trx_state_eq(t, TRX_STATE_ACTIVE)) {
|
||||
n_trx++;
|
||||
n_rows += t->undo_no;
|
||||
}
|
||||
}
|
||||
ib_logf(IB_LOG_LEVEL_INFO,
|
||||
"To roll back: " ULINTPF " transactions, "
|
||||
ULINTPF " rows", n_trx, n_rows);
|
||||
sd_notifyf(0, "STATUS=To roll back: " ULINTPF " transactions, "
|
||||
ULINTPF " rows", n_trx, n_rows);
|
||||
}
|
||||
|
||||
mutex_exit(&recv_sys->mutex);
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*******************************************************************//**
|
||||
|
|
@ -763,17 +805,11 @@ trx_rollback_or_clean_recovered(
|
|||
|
||||
assert_trx_in_rw_list(trx);
|
||||
|
||||
if (srv_shutdown_state != SRV_SHUTDOWN_NONE
|
||||
&& srv_fast_shutdown != 0) {
|
||||
all = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If this function does a cleanup or rollback
|
||||
then it will release the trx_sys->mutex, therefore
|
||||
we need to reacquire it before retrying the loop. */
|
||||
|
||||
if (trx_rollback_resurrected(trx, all)) {
|
||||
if (trx_rollback_resurrected(trx, &all)) {
|
||||
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
|
||||
|
|
@ -1100,7 +1136,6 @@ trx_roll_pop_top_rec_of_trx(
|
|||
undo_no_t undo_no;
|
||||
ibool is_insert;
|
||||
trx_rseg_t* rseg;
|
||||
ulint progress_pct;
|
||||
mtr_t mtr;
|
||||
|
||||
rseg = trx->rseg;
|
||||
|
|
@ -1158,27 +1193,6 @@ try_again:
|
|||
|
||||
ut_ad(undo_no + 1 == trx->undo_no);
|
||||
|
||||
/* We print rollback progress info if we are in a crash recovery
|
||||
and the transaction has at least 1000 row operations to undo. */
|
||||
|
||||
if (trx == trx_roll_crash_recv_trx && trx_roll_max_undo_no > 1000) {
|
||||
|
||||
progress_pct = 100 - (ulint)
|
||||
((undo_no * 100) / trx_roll_max_undo_no);
|
||||
if (progress_pct != trx_roll_progress_printed_pct) {
|
||||
if (trx_roll_progress_printed_pct == 0) {
|
||||
fprintf(stderr,
|
||||
"\nInnoDB: Progress in percents:"
|
||||
" %lu", (ulong) progress_pct);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
" %lu", (ulong) progress_pct);
|
||||
}
|
||||
fflush(stderr);
|
||||
trx_roll_progress_printed_pct = progress_pct;
|
||||
}
|
||||
}
|
||||
|
||||
trx->undo_no = undo_no;
|
||||
|
||||
if (!trx_undo_arr_store_info(trx, undo_no)) {
|
||||
|
|
|
|||
|
|
@ -2023,9 +2023,13 @@ trx_undo_free_prepared(
|
|||
/* fall through */
|
||||
case TRX_UNDO_ACTIVE:
|
||||
/* lock_trx_release_locks() assigns
|
||||
trx->is_recovered=false */
|
||||
trx->is_recovered=false and
|
||||
trx->state = TRX_STATE_COMMITTED_IN_MEMORY,
|
||||
also for transactions that we faked
|
||||
to TRX_STATE_PREPARED in trx_rollback_resurrected(). */
|
||||
ut_a(srv_read_only_mode
|
||||
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
|
||||
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|
||||
|| srv_fast_shutdown);
|
||||
break;
|
||||
default:
|
||||
ut_error;
|
||||
|
|
@ -2047,9 +2051,13 @@ trx_undo_free_prepared(
|
|||
/* fall through */
|
||||
case TRX_UNDO_ACTIVE:
|
||||
/* lock_trx_release_locks() assigns
|
||||
trx->is_recovered=false */
|
||||
trx->is_recovered=false and
|
||||
trx->state = TRX_STATE_COMMITTED_IN_MEMORY,
|
||||
also for transactions that we faked
|
||||
to TRX_STATE_PREPARED in trx_rollback_resurrected(). */
|
||||
ut_a(srv_read_only_mode
|
||||
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
|
||||
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|
||||
|| srv_fast_shutdown);
|
||||
break;
|
||||
default:
|
||||
ut_error;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
Copyright (c) 2017, 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
|
||||
|
|
@ -34,6 +35,7 @@ Created 3/26/1996 Heikki Tuuri
|
|||
#include "trx0sys.h"
|
||||
|
||||
extern bool trx_rollback_or_clean_is_active;
|
||||
extern const trx_t* trx_roll_crash_recv_trx;
|
||||
|
||||
/*******************************************************************//**
|
||||
Determines if this transaction is rolling back an incomplete transaction
|
||||
|
|
@ -104,6 +106,11 @@ trx_undo_rec_release(
|
|||
/*=================*/
|
||||
trx_t* trx, /*!< in/out: transaction */
|
||||
undo_no_t undo_no);/*!< in: undo number */
|
||||
/** Report progress when rolling back a row of a recovered transaction.
|
||||
@return whether the rollback should be aborted due to pending shutdown */
|
||||
UNIV_INTERN
|
||||
bool
|
||||
trx_roll_must_shutdown();
|
||||
/*******************************************************************//**
|
||||
Rollback or clean up any incomplete transactions which were
|
||||
encountered in crash recovery. If the transaction already was
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/*****************************************************************************
|
||||
|
||||
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved.
|
||||
Copyright (c) 2017, 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
|
||||
|
|
@ -348,6 +349,13 @@ row_undo_step(
|
|||
|
||||
ut_ad(que_node_get_type(node) == QUE_NODE_UNDO);
|
||||
|
||||
if (UNIV_UNLIKELY(trx == trx_roll_crash_recv_trx)
|
||||
&& trx_roll_must_shutdown()) {
|
||||
/* Shutdown has been initiated. */
|
||||
trx->error_state = DB_INTERRUPTED;
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
err = row_undo(node, thr);
|
||||
|
||||
trx->error_state = err;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ Transaction rollback
|
|||
Created 3/26/1996 Heikki Tuuri
|
||||
*******************************************************/
|
||||
|
||||
#include "my_config.h"
|
||||
#include <my_systemd.h>
|
||||
|
||||
#include "trx0roll.h"
|
||||
|
||||
#ifdef UNIV_NONINL
|
||||
|
|
@ -60,14 +63,7 @@ rollback */
|
|||
bool trx_rollback_or_clean_is_active;
|
||||
|
||||
/** In crash recovery, the current trx to be rolled back; NULL otherwise */
|
||||
static const trx_t* trx_roll_crash_recv_trx = NULL;
|
||||
|
||||
/** In crash recovery we set this to the undo n:o of the current trx to be
|
||||
rolled back. Then we can print how many % the rollback has progressed. */
|
||||
static undo_no_t trx_roll_max_undo_no;
|
||||
|
||||
/** Auxiliary variable which tells the previous progress % we printed */
|
||||
static ulint trx_roll_progress_printed_pct;
|
||||
const trx_t* trx_roll_crash_recv_trx;
|
||||
|
||||
/****************************************************************//**
|
||||
Finishes a transaction rollback. */
|
||||
|
|
@ -564,8 +560,6 @@ trx_rollback_active(
|
|||
que_thr_t* thr;
|
||||
roll_node_t* roll_node;
|
||||
dict_table_t* table;
|
||||
ib_int64_t rows_to_undo;
|
||||
const char* unit = "";
|
||||
ibool dictionary_locked = FALSE;
|
||||
|
||||
heap = mem_heap_create(512);
|
||||
|
|
@ -584,30 +578,8 @@ trx_rollback_active(
|
|||
|
||||
ut_a(thr == que_fork_start_command(fork));
|
||||
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
|
||||
trx_roll_crash_recv_trx = trx;
|
||||
|
||||
trx_roll_max_undo_no = trx->undo_no;
|
||||
|
||||
trx_roll_progress_printed_pct = 0;
|
||||
|
||||
rows_to_undo = trx_roll_max_undo_no;
|
||||
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
|
||||
if (rows_to_undo > 1000000000) {
|
||||
rows_to_undo = rows_to_undo / 1000000;
|
||||
unit = "M";
|
||||
}
|
||||
|
||||
ut_print_timestamp(stderr);
|
||||
fprintf(stderr,
|
||||
" InnoDB: Rolling back trx with id " TRX_ID_FMT ", %lu%s"
|
||||
" rows to undo\n",
|
||||
trx->id,
|
||||
(ulong) rows_to_undo, unit);
|
||||
|
||||
if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
|
||||
row_mysql_lock_data_dictionary(trx);
|
||||
dictionary_locked = TRUE;
|
||||
|
|
@ -618,6 +590,14 @@ trx_rollback_active(
|
|||
|
||||
que_run_threads(roll_node->undo_thr);
|
||||
|
||||
if (trx->error_state != DB_SUCCESS) {
|
||||
ut_ad(trx->error_state == DB_INTERRUPTED);
|
||||
ut_ad(!srv_undo_sources);
|
||||
ut_ad(srv_fast_shutdown);
|
||||
ut_ad(!dictionary_locked);
|
||||
goto func_exit;
|
||||
}
|
||||
|
||||
trx_rollback_finish(thr_get_trx(roll_node->undo_thr));
|
||||
|
||||
/* Free the memory reserved by the undo graph */
|
||||
|
|
@ -662,13 +642,14 @@ trx_rollback_active(
|
|||
}
|
||||
}
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_INFO,
|
||||
"Rollback of trx with id " TRX_ID_FMT " completed", trx->id);
|
||||
|
||||
func_exit:
|
||||
if (dictionary_locked) {
|
||||
row_mysql_unlock_data_dictionary(trx);
|
||||
}
|
||||
|
||||
ib_logf(IB_LOG_LEVEL_INFO,
|
||||
"Rollback of trx with id " TRX_ID_FMT " completed", trx->id);
|
||||
|
||||
mem_heap_free(heap);
|
||||
|
||||
trx_roll_crash_recv_trx = NULL;
|
||||
|
|
@ -685,7 +666,7 @@ ibool
|
|||
trx_rollback_resurrected(
|
||||
/*=====================*/
|
||||
trx_t* trx, /*!< in: transaction to rollback or clean */
|
||||
ibool all) /*!< in: FALSE=roll back dictionary transactions;
|
||||
ibool* all) /*!< in/out: FALSE=roll back dictionary transactions;
|
||||
TRUE=roll back all non-PREPARED transactions */
|
||||
{
|
||||
ut_ad(mutex_own(&trx_sys->mutex));
|
||||
|
|
@ -696,16 +677,15 @@ trx_rollback_resurrected(
|
|||
to accidentally clean up a non-recovered transaction here. */
|
||||
|
||||
trx_mutex_enter(trx);
|
||||
bool is_recovered = trx->is_recovered;
|
||||
trx_state_t state = trx->state;
|
||||
if (!trx->is_recovered) {
|
||||
func_exit:
|
||||
trx_mutex_exit(trx);
|
||||
|
||||
if (!is_recovered) {
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
switch (trx->state) {
|
||||
case TRX_STATE_COMMITTED_IN_MEMORY:
|
||||
trx_mutex_exit(trx);
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
fprintf(stderr,
|
||||
"InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n",
|
||||
|
|
@ -714,21 +694,83 @@ trx_rollback_resurrected(
|
|||
trx_free_for_background(trx);
|
||||
return(TRUE);
|
||||
case TRX_STATE_ACTIVE:
|
||||
if (all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
|
||||
if (!srv_undo_sources && srv_fast_shutdown) {
|
||||
fake_prepared:
|
||||
trx->state = TRX_STATE_PREPARED;
|
||||
trx_sys->n_prepared_trx++;
|
||||
trx_sys->n_prepared_recovered_trx++;
|
||||
*all = FALSE;
|
||||
goto func_exit;
|
||||
}
|
||||
trx_mutex_exit(trx);
|
||||
|
||||
if (*all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
trx_rollback_active(trx);
|
||||
if (trx->error_state != DB_SUCCESS) {
|
||||
ut_ad(trx->error_state == DB_INTERRUPTED);
|
||||
ut_ad(!srv_undo_sources);
|
||||
ut_ad(srv_fast_shutdown);
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
trx_mutex_enter(trx);
|
||||
goto fake_prepared;
|
||||
}
|
||||
trx_free_for_background(trx);
|
||||
return(TRUE);
|
||||
}
|
||||
return(FALSE);
|
||||
case TRX_STATE_PREPARED:
|
||||
return(FALSE);
|
||||
goto func_exit;
|
||||
case TRX_STATE_NOT_STARTED:
|
||||
break;
|
||||
}
|
||||
|
||||
ut_error;
|
||||
return(FALSE);
|
||||
goto func_exit;
|
||||
}
|
||||
|
||||
/** Report progress when rolling back a row of a recovered transaction.
|
||||
@return whether the rollback should be aborted due to pending shutdown */
|
||||
UNIV_INTERN
|
||||
bool
|
||||
trx_roll_must_shutdown()
|
||||
{
|
||||
const trx_t* trx = trx_roll_crash_recv_trx;
|
||||
ut_ad(trx);
|
||||
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE));
|
||||
|
||||
if (trx_get_dict_operation(trx) == TRX_DICT_OP_NONE
|
||||
&& !srv_undo_sources && srv_fast_shutdown) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ib_time_t time = ut_time();
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
mutex_enter(&recv_sys->mutex);
|
||||
|
||||
if (recv_sys->report(time)) {
|
||||
ulint n_trx = 0, n_rows = 0;
|
||||
for (const trx_t* t = UT_LIST_GET_FIRST(trx_sys->rw_trx_list);
|
||||
t != NULL;
|
||||
t = UT_LIST_GET_NEXT(trx_list, t)) {
|
||||
|
||||
assert_trx_in_rw_list(t);
|
||||
if (t->is_recovered
|
||||
&& trx_state_eq(t, TRX_STATE_ACTIVE)) {
|
||||
n_trx++;
|
||||
n_rows += t->undo_no;
|
||||
}
|
||||
}
|
||||
ib_logf(IB_LOG_LEVEL_INFO,
|
||||
"To roll back: " ULINTPF " transactions, "
|
||||
ULINTPF " rows", n_trx, n_rows);
|
||||
sd_notifyf(0, "STATUS=To roll back: " ULINTPF " transactions, "
|
||||
ULINTPF " rows", n_trx, n_rows);
|
||||
}
|
||||
|
||||
mutex_exit(&recv_sys->mutex);
|
||||
mutex_exit(&trx_sys->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*******************************************************************//**
|
||||
|
|
@ -775,17 +817,11 @@ trx_rollback_or_clean_recovered(
|
|||
|
||||
assert_trx_in_rw_list(trx);
|
||||
|
||||
if (srv_shutdown_state != SRV_SHUTDOWN_NONE
|
||||
&& srv_fast_shutdown != 0) {
|
||||
all = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If this function does a cleanup or rollback
|
||||
then it will release the trx_sys->mutex, therefore
|
||||
we need to reacquire it before retrying the loop. */
|
||||
|
||||
if (trx_rollback_resurrected(trx, all)) {
|
||||
if (trx_rollback_resurrected(trx, &all)) {
|
||||
|
||||
mutex_enter(&trx_sys->mutex);
|
||||
|
||||
|
|
@ -1118,7 +1154,6 @@ trx_roll_pop_top_rec_of_trx(
|
|||
undo_no_t undo_no;
|
||||
ibool is_insert;
|
||||
trx_rseg_t* rseg;
|
||||
ulint progress_pct;
|
||||
mtr_t mtr;
|
||||
|
||||
rseg = trx->rseg;
|
||||
|
|
@ -1176,27 +1211,6 @@ try_again:
|
|||
|
||||
ut_ad(undo_no + 1 == trx->undo_no);
|
||||
|
||||
/* We print rollback progress info if we are in a crash recovery
|
||||
and the transaction has at least 1000 row operations to undo. */
|
||||
|
||||
if (trx == trx_roll_crash_recv_trx && trx_roll_max_undo_no > 1000) {
|
||||
|
||||
progress_pct = 100 - (ulint)
|
||||
((undo_no * 100) / trx_roll_max_undo_no);
|
||||
if (progress_pct != trx_roll_progress_printed_pct) {
|
||||
if (trx_roll_progress_printed_pct == 0) {
|
||||
fprintf(stderr,
|
||||
"\nInnoDB: Progress in percents:"
|
||||
" %lu", (ulong) progress_pct);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
" %lu", (ulong) progress_pct);
|
||||
}
|
||||
fflush(stderr);
|
||||
trx_roll_progress_printed_pct = progress_pct;
|
||||
}
|
||||
}
|
||||
|
||||
trx->undo_no = undo_no;
|
||||
|
||||
if (!trx_undo_arr_store_info(trx, undo_no)) {
|
||||
|
|
|
|||
|
|
@ -2023,10 +2023,14 @@ trx_undo_free_prepared(
|
|||
/* fall through */
|
||||
case TRX_UNDO_ACTIVE:
|
||||
/* lock_trx_release_locks() assigns
|
||||
trx->is_recovered=false */
|
||||
trx->is_recovered=false and
|
||||
trx->state = TRX_STATE_COMMITTED_IN_MEMORY,
|
||||
also for transactions that we faked
|
||||
to TRX_STATE_PREPARED in trx_rollback_resurrected(). */
|
||||
ut_a(srv_read_only_mode
|
||||
|| srv_apply_log_only
|
||||
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
|
||||
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|
||||
|| srv_fast_shutdown);
|
||||
break;
|
||||
default:
|
||||
ut_error;
|
||||
|
|
@ -2048,10 +2052,14 @@ trx_undo_free_prepared(
|
|||
/* fall through */
|
||||
case TRX_UNDO_ACTIVE:
|
||||
/* lock_trx_release_locks() assigns
|
||||
trx->is_recovered=false */
|
||||
trx->is_recovered=false and
|
||||
trx->state = TRX_STATE_COMMITTED_IN_MEMORY,
|
||||
also for transactions that we faked
|
||||
to TRX_STATE_PREPARED in trx_rollback_resurrected(). */
|
||||
ut_a(srv_read_only_mode
|
||||
|| srv_apply_log_only
|
||||
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
|
||||
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|
||||
|| srv_fast_shutdown);
|
||||
break;
|
||||
default:
|
||||
ut_error;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue