Commit 46305b00 authored by Marko Mäkelä's avatar Marko Mäkelä

Merge 10.0 into 10.1

parents b93a87f1 b1977a39
#
# 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;
--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;
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. 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 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 the terms of the GNU General Public License as published by the Free Software
...@@ -32,7 +33,8 @@ Created 3/26/1996 Heikki Tuuri ...@@ -32,7 +33,8 @@ Created 3/26/1996 Heikki Tuuri
#include "mtr0mtr.h" #include "mtr0mtr.h"
#include "trx0sys.h" #include "trx0sys.h"
extern bool trx_rollback_or_clean_is_active; 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 Determines if this transaction is rolling back an incomplete transaction
...@@ -103,6 +105,11 @@ trx_undo_rec_release( ...@@ -103,6 +105,11 @@ trx_undo_rec_release(
/*=================*/ /*=================*/
trx_t* trx, /*!< in/out: transaction */ trx_t* trx, /*!< in/out: transaction */
undo_no_t undo_no);/*!< in: undo number */ 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 Rollback or clean up any incomplete transactions which were
encountered in crash recovery. If the transaction already was encountered in crash recovery. If the transaction already was
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. 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 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 the terms of the GNU General Public License as published by the Free Software
...@@ -348,6 +349,13 @@ row_undo_step( ...@@ -348,6 +349,13 @@ row_undo_step(
ut_ad(que_node_get_type(node) == QUE_NODE_UNDO); 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); err = row_undo(node, thr);
trx->error_state = err; trx->error_state = err;
......
...@@ -24,6 +24,9 @@ Transaction rollback ...@@ -24,6 +24,9 @@ Transaction rollback
Created 3/26/1996 Heikki Tuuri Created 3/26/1996 Heikki Tuuri
*******************************************************/ *******************************************************/
#include "my_config.h"
#include <my_systemd.h>
#include "trx0roll.h" #include "trx0roll.h"
#ifdef UNIV_NONINL #ifdef UNIV_NONINL
...@@ -55,14 +58,7 @@ rollback */ ...@@ -55,14 +58,7 @@ rollback */
bool trx_rollback_or_clean_is_active; bool trx_rollback_or_clean_is_active;
/** In crash recovery, the current trx to be rolled back; NULL otherwise */ /** In crash recovery, the current trx to be rolled back; NULL otherwise */
static const trx_t* trx_roll_crash_recv_trx = NULL; const trx_t* trx_roll_crash_recv_trx;
/** 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;
/****************************************************************//** /****************************************************************//**
Finishes a transaction rollback. */ Finishes a transaction rollback. */
...@@ -552,8 +548,6 @@ trx_rollback_active( ...@@ -552,8 +548,6 @@ trx_rollback_active(
que_thr_t* thr; que_thr_t* thr;
roll_node_t* roll_node; roll_node_t* roll_node;
dict_table_t* table; dict_table_t* table;
ib_int64_t rows_to_undo;
const char* unit = "";
ibool dictionary_locked = FALSE; ibool dictionary_locked = FALSE;
heap = mem_heap_create(512); heap = mem_heap_create(512);
...@@ -572,30 +566,8 @@ trx_rollback_active( ...@@ -572,30 +566,8 @@ trx_rollback_active(
ut_a(thr == que_fork_start_command(fork)); ut_a(thr == que_fork_start_command(fork));
mutex_enter(&trx_sys->mutex);
trx_roll_crash_recv_trx = trx; 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) { if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
row_mysql_lock_data_dictionary(trx); row_mysql_lock_data_dictionary(trx);
dictionary_locked = TRUE; dictionary_locked = TRUE;
...@@ -606,6 +578,14 @@ trx_rollback_active( ...@@ -606,6 +578,14 @@ trx_rollback_active(
que_run_threads(roll_node->undo_thr); 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)); trx_rollback_finish(thr_get_trx(roll_node->undo_thr));
/* Free the memory reserved by the undo graph */ /* Free the memory reserved by the undo graph */
...@@ -650,13 +630,14 @@ trx_rollback_active( ...@@ -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) { if (dictionary_locked) {
row_mysql_unlock_data_dictionary(trx); 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); mem_heap_free(heap);
trx_roll_crash_recv_trx = NULL; trx_roll_crash_recv_trx = NULL;
...@@ -673,7 +654,7 @@ ibool ...@@ -673,7 +654,7 @@ ibool
trx_rollback_resurrected( trx_rollback_resurrected(
/*=====================*/ /*=====================*/
trx_t* trx, /*!< in: transaction to rollback or clean */ 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 */ TRUE=roll back all non-PREPARED transactions */
{ {
ut_ad(mutex_own(&trx_sys->mutex)); ut_ad(mutex_own(&trx_sys->mutex));
...@@ -684,16 +665,15 @@ trx_rollback_resurrected( ...@@ -684,16 +665,15 @@ trx_rollback_resurrected(
to accidentally clean up a non-recovered transaction here. */ to accidentally clean up a non-recovered transaction here. */
trx_mutex_enter(trx); trx_mutex_enter(trx);
bool is_recovered = trx->is_recovered; if (!trx->is_recovered) {
trx_state_t state = trx->state; func_exit:
trx_mutex_exit(trx); trx_mutex_exit(trx);
if (!is_recovered) {
return(FALSE); return(FALSE);
} }
switch (state) { switch (trx->state) {
case TRX_STATE_COMMITTED_IN_MEMORY: case TRX_STATE_COMMITTED_IN_MEMORY:
trx_mutex_exit(trx);
mutex_exit(&trx_sys->mutex); mutex_exit(&trx_sys->mutex);
fprintf(stderr, fprintf(stderr,
"InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n", "InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n",
...@@ -702,21 +682,83 @@ trx_rollback_resurrected( ...@@ -702,21 +682,83 @@ trx_rollback_resurrected(
trx_free_for_background(trx); trx_free_for_background(trx);
return(TRUE); return(TRUE);
case TRX_STATE_ACTIVE: 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); mutex_exit(&trx_sys->mutex);
trx_rollback_active(trx); 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); trx_free_for_background(trx);
return(TRUE); return(TRUE);
} }
return(FALSE); return(FALSE);
case TRX_STATE_PREPARED: case TRX_STATE_PREPARED:
return(FALSE); goto func_exit;
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:
break; break;
} }
ut_error; 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( ...@@ -763,17 +805,11 @@ trx_rollback_or_clean_recovered(
assert_trx_in_rw_list(trx); 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 /* If this function does a cleanup or rollback
then it will release the trx_sys->mutex, therefore then it will release the trx_sys->mutex, therefore
we need to reacquire it before retrying the loop. */ 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); mutex_enter(&trx_sys->mutex);
...@@ -1100,7 +1136,6 @@ trx_roll_pop_top_rec_of_trx( ...@@ -1100,7 +1136,6 @@ trx_roll_pop_top_rec_of_trx(
undo_no_t undo_no; undo_no_t undo_no;
ibool is_insert; ibool is_insert;
trx_rseg_t* rseg; trx_rseg_t* rseg;
ulint progress_pct;
mtr_t mtr; mtr_t mtr;
rseg = trx->rseg; rseg = trx->rseg;
...@@ -1158,27 +1193,6 @@ trx_roll_pop_top_rec_of_trx( ...@@ -1158,27 +1193,6 @@ trx_roll_pop_top_rec_of_trx(
ut_ad(undo_no + 1 == trx->undo_no); 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; trx->undo_no = undo_no;
if (!trx_undo_arr_store_info(trx, undo_no)) { if (!trx_undo_arr_store_info(trx, undo_no)) {
......
...@@ -2023,9 +2023,13 @@ trx_undo_free_prepared( ...@@ -2023,9 +2023,13 @@ trx_undo_free_prepared(
/* fall through */ /* fall through */
case TRX_UNDO_ACTIVE: case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns /* 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 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; break;
default: default:
ut_error; ut_error;
...@@ -2047,9 +2051,13 @@ trx_undo_free_prepared( ...@@ -2047,9 +2051,13 @@ trx_undo_free_prepared(
/* fall through */ /* fall through */
case TRX_UNDO_ACTIVE: case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns /* 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 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; break;
default: default:
ut_error; ut_error;
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. 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 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 the terms of the GNU General Public License as published by the Free Software
...@@ -33,7 +34,8 @@ Created 3/26/1996 Heikki Tuuri ...@@ -33,7 +34,8 @@ Created 3/26/1996 Heikki Tuuri
#include "mtr0mtr.h" #include "mtr0mtr.h"
#include "trx0sys.h" #include "trx0sys.h"
extern bool trx_rollback_or_clean_is_active; 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 Determines if this transaction is rolling back an incomplete transaction
...@@ -104,6 +106,11 @@ trx_undo_rec_release( ...@@ -104,6 +106,11 @@ trx_undo_rec_release(
/*=================*/ /*=================*/
trx_t* trx, /*!< in/out: transaction */ trx_t* trx, /*!< in/out: transaction */
undo_no_t undo_no);/*!< in: undo number */ 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 Rollback or clean up any incomplete transactions which were
encountered in crash recovery. If the transaction already was encountered in crash recovery. If the transaction already was
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. 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 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 the terms of the GNU General Public License as published by the Free Software
...@@ -348,6 +349,13 @@ row_undo_step( ...@@ -348,6 +349,13 @@ row_undo_step(
ut_ad(que_node_get_type(node) == QUE_NODE_UNDO); 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); err = row_undo(node, thr);
trx->error_state = err; trx->error_state = err;
......
...@@ -24,6 +24,9 @@ Transaction rollback ...@@ -24,6 +24,9 @@ Transaction rollback
Created 3/26/1996 Heikki Tuuri Created 3/26/1996 Heikki Tuuri
*******************************************************/ *******************************************************/
#include "my_config.h"
#include <my_systemd.h>
#include "trx0roll.h" #include "trx0roll.h"
#ifdef UNIV_NONINL #ifdef UNIV_NONINL
...@@ -60,14 +63,7 @@ rollback */ ...@@ -60,14 +63,7 @@ rollback */
bool trx_rollback_or_clean_is_active; bool trx_rollback_or_clean_is_active;
/** In crash recovery, the current trx to be rolled back; NULL otherwise */ /** In crash recovery, the current trx to be rolled back; NULL otherwise */
static const trx_t* trx_roll_crash_recv_trx = NULL; const trx_t* trx_roll_crash_recv_trx;
/** 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;
/****************************************************************//** /****************************************************************//**
Finishes a transaction rollback. */ Finishes a transaction rollback. */
...@@ -564,8 +560,6 @@ trx_rollback_active( ...@@ -564,8 +560,6 @@ trx_rollback_active(
que_thr_t* thr; que_thr_t* thr;
roll_node_t* roll_node; roll_node_t* roll_node;
dict_table_t* table; dict_table_t* table;
ib_int64_t rows_to_undo;
const char* unit = "";
ibool dictionary_locked = FALSE; ibool dictionary_locked = FALSE;
heap = mem_heap_create(512); heap = mem_heap_create(512);
...@@ -584,30 +578,8 @@ trx_rollback_active( ...@@ -584,30 +578,8 @@ trx_rollback_active(
ut_a(thr == que_fork_start_command(fork)); ut_a(thr == que_fork_start_command(fork));
mutex_enter(&trx_sys->mutex);
trx_roll_crash_recv_trx = trx; 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) { if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
row_mysql_lock_data_dictionary(trx); row_mysql_lock_data_dictionary(trx);
dictionary_locked = TRUE; dictionary_locked = TRUE;
...@@ -618,6 +590,14 @@ trx_rollback_active( ...@@ -618,6 +590,14 @@ trx_rollback_active(
que_run_threads(roll_node->undo_thr); 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)); trx_rollback_finish(thr_get_trx(roll_node->undo_thr));
/* Free the memory reserved by the undo graph */ /* Free the memory reserved by the undo graph */
...@@ -662,13 +642,14 @@ trx_rollback_active( ...@@ -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) { if (dictionary_locked) {
row_mysql_unlock_data_dictionary(trx); 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); mem_heap_free(heap);
trx_roll_crash_recv_trx = NULL; trx_roll_crash_recv_trx = NULL;
...@@ -685,7 +666,7 @@ ibool ...@@ -685,7 +666,7 @@ ibool
trx_rollback_resurrected( trx_rollback_resurrected(
/*=====================*/ /*=====================*/
trx_t* trx, /*!< in: transaction to rollback or clean */ 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 */ TRUE=roll back all non-PREPARED transactions */
{ {
ut_ad(mutex_own(&trx_sys->mutex)); ut_ad(mutex_own(&trx_sys->mutex));
...@@ -696,16 +677,15 @@ trx_rollback_resurrected( ...@@ -696,16 +677,15 @@ trx_rollback_resurrected(
to accidentally clean up a non-recovered transaction here. */ to accidentally clean up a non-recovered transaction here. */
trx_mutex_enter(trx); trx_mutex_enter(trx);
bool is_recovered = trx->is_recovered; if (!trx->is_recovered) {
trx_state_t state = trx->state; func_exit:
trx_mutex_exit(trx); trx_mutex_exit(trx);
if (!is_recovered) {
return(FALSE); return(FALSE);
} }
switch (state) { switch (trx->state) {
case TRX_STATE_COMMITTED_IN_MEMORY: case TRX_STATE_COMMITTED_IN_MEMORY:
trx_mutex_exit(trx);
mutex_exit(&trx_sys->mutex); mutex_exit(&trx_sys->mutex);
fprintf(stderr, fprintf(stderr,
"InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n", "InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n",
...@@ -714,21 +694,83 @@ trx_rollback_resurrected( ...@@ -714,21 +694,83 @@ trx_rollback_resurrected(
trx_free_for_background(trx); trx_free_for_background(trx);
return(TRUE); return(TRUE);
case TRX_STATE_ACTIVE: 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); mutex_exit(&trx_sys->mutex);
trx_rollback_active(trx); 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); trx_free_for_background(trx);
return(TRUE); return(TRUE);
} }
return(FALSE); return(FALSE);
case TRX_STATE_PREPARED: case TRX_STATE_PREPARED:
return(FALSE); goto func_exit;
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:
break; break;
} }
ut_error; 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( ...@@ -775,17 +817,11 @@ trx_rollback_or_clean_recovered(
assert_trx_in_rw_list(trx); 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 /* If this function does a cleanup or rollback
then it will release the trx_sys->mutex, therefore then it will release the trx_sys->mutex, therefore
we need to reacquire it before retrying the loop. */ 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); mutex_enter(&trx_sys->mutex);
...@@ -1118,7 +1154,6 @@ trx_roll_pop_top_rec_of_trx( ...@@ -1118,7 +1154,6 @@ trx_roll_pop_top_rec_of_trx(
undo_no_t undo_no; undo_no_t undo_no;
ibool is_insert; ibool is_insert;
trx_rseg_t* rseg; trx_rseg_t* rseg;
ulint progress_pct;
mtr_t mtr; mtr_t mtr;
rseg = trx->rseg; rseg = trx->rseg;
...@@ -1176,27 +1211,6 @@ trx_roll_pop_top_rec_of_trx( ...@@ -1176,27 +1211,6 @@ trx_roll_pop_top_rec_of_trx(
ut_ad(undo_no + 1 == trx->undo_no); 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; trx->undo_no = undo_no;
if (!trx_undo_arr_store_info(trx, undo_no)) { if (!trx_undo_arr_store_info(trx, undo_no)) {
......
...@@ -2023,10 +2023,14 @@ trx_undo_free_prepared( ...@@ -2023,10 +2023,14 @@ trx_undo_free_prepared(
/* fall through */ /* fall through */
case TRX_UNDO_ACTIVE: case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns /* 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 ut_a(srv_read_only_mode
|| srv_apply_log_only || srv_apply_log_only
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|| srv_fast_shutdown);
break; break;
default: default:
ut_error; ut_error;
...@@ -2048,10 +2052,14 @@ trx_undo_free_prepared( ...@@ -2048,10 +2052,14 @@ trx_undo_free_prepared(
/* fall through */ /* fall through */
case TRX_UNDO_ACTIVE: case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns /* 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 ut_a(srv_read_only_mode
|| srv_apply_log_only || srv_apply_log_only
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|| srv_fast_shutdown);
break; break;
default: default:
ut_error; ut_error;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment