Commit 20e83474 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-11985 Make innodb_read_only shutdown more robust

If InnoDB is started in innodb_read_only mode such that
recovered incomplete transactions exist at startup
(but the redo logs are clean), an assertion will fail at shutdown,
because there would exist some non-prepared transactions.

logs_empty_and_mark_files_at_shutdown(): Do not wait for incomplete
transactions to finish if innodb_read_only or innodb_force_recovery>=3.
Wait for purge to finish in only one place.

trx_sys_close(): Relax the assertion that would fail first.

trx_free_prepared(): Also free recovered TRX_STATE_ACTIVE transactions
if innodb_read_only or innodb_force_recovery>=3.
parent 9f0dbb31
CREATE TABLE t(a INT PRIMARY KEY) ENGINE=InnoDB;
BEGIN;
INSERT INTO t VALUES(1),(2);
DELETE FROM t WHERE a=2;
# Normal MariaDB shutdown would roll back the above transaction.
# We want the transaction to remain open, so we will kill the server
# after ensuring that any non-transactional files are clean.
FLUSH TABLES;
# Ensure that the above incomplete transaction becomes durable.
SET GLOBAL innodb_flush_log_at_trx_commit=1;
BEGIN;
INSERT INTO t VALUES(0);
ROLLBACK;
# Kill and restart: --innodb-force-recovery=3
SELECT * FROM t;
a
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT * FROM t;
a
1
SELECT * FROM t;
a
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT * FROM t;
a
1
SELECT * FROM t;
a
DROP TABLE t;
--source include/have_innodb.inc
# need to restart server
--source include/not_embedded.inc
--connect(con1, localhost, root)
CREATE TABLE t(a INT PRIMARY KEY) ENGINE=InnoDB;
BEGIN;
# Generate insert_undo log.
INSERT INTO t VALUES(1),(2);
# Generate update_undo log.
DELETE FROM t WHERE a=2;
--connection default
--echo # Normal MariaDB shutdown would roll back the above transaction.
--echo # We want the transaction to remain open, so we will kill the server
--echo # after ensuring that any non-transactional files are clean.
FLUSH TABLES;
--echo # Ensure that the above incomplete transaction becomes durable.
SET GLOBAL innodb_flush_log_at_trx_commit=1;
BEGIN;
INSERT INTO t VALUES(0);
ROLLBACK;
--let $restart_parameters= --innodb-force-recovery=3
--source include/kill_and_restart_mysqld.inc
--disconnect con1
SELECT * FROM t;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT * FROM t;
--let $restart_parameters= --innodb-read-only
--source include/restart_mysqld.inc
SELECT * FROM t;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT * FROM t;
--let $restart_parameters=
--source include/restart_mysqld.inc
SELECT * FROM t;
DROP TABLE t;
......@@ -2,6 +2,7 @@
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2009, Google Inc.
Copyright (c) 2017, MariaDB Corporation. All Rights Reserved.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
......@@ -3251,10 +3252,9 @@ logs_empty_and_mark_files_at_shutdown(void)
shutdown, because the InnoDB layer may have committed or
prepared transactions and we don't want to lose them. */
total_trx = trx_sys_any_active_transactions();
if (total_trx > 0) {
if (ulint total_trx = srv_was_started && !srv_read_only_mode
&& srv_force_recovery < SRV_FORCE_NO_TRX_UNDO
? trx_sys_any_active_transactions() : 0) {
if (srv_print_verbose_log && count > 600) {
ib_logf(IB_LOG_LEVEL_INFO,
"Waiting for %lu active transactions to finish",
......
......@@ -1190,7 +1190,9 @@ trx_sys_close(void)
ut_a(UT_LIST_GET_LEN(trx_sys->ro_trx_list) == 0);
/* Only prepared transactions may be left in the system. Free them. */
ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == trx_sys->n_prepared_trx);
ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == trx_sys->n_prepared_trx
|| srv_read_only_mode
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
while ((trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list)) != NULL) {
trx_free_prepared(trx);
......
......@@ -307,7 +307,11 @@ trx_free_prepared(
/*==============*/
trx_t* trx) /*!< in, own: trx object */
{
ut_a(trx_state_eq(trx, TRX_STATE_PREPARED));
ut_a(trx_state_eq(trx, TRX_STATE_PREPARED)
|| (trx_state_eq(trx, TRX_STATE_ACTIVE)
&& trx->is_recovered
&& (srv_read_only_mode
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO)));
ut_a(trx->magic_n == TRX_MAGIC_N);
lock_trx_release_locks(trx);
......
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2014, 2017, MariaDB Corporation. All Rights Reserved.
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
......@@ -2011,13 +2012,37 @@ trx_undo_free_prepared(
ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS);
if (trx->update_undo) {
ut_a(trx->update_undo->state == TRX_UNDO_PREPARED);
switch (trx->update_undo->state) {
case TRX_UNDO_PREPARED:
break;
case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns
trx->is_recovered=false */
ut_a(srv_read_only_mode
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
break;
default:
ut_error;
}
UT_LIST_REMOVE(undo_list, trx->rseg->update_undo_list,
trx->update_undo);
trx_undo_mem_free(trx->update_undo);
}
if (trx->insert_undo) {
ut_a(trx->insert_undo->state == TRX_UNDO_PREPARED);
switch (trx->insert_undo->state) {
case TRX_UNDO_PREPARED:
break;
case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns
trx->is_recovered=false */
ut_a(srv_read_only_mode
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
break;
default:
ut_error;
}
UT_LIST_REMOVE(undo_list, trx->rseg->insert_undo_list,
trx->insert_undo);
trx_undo_mem_free(trx->insert_undo);
......
......@@ -2,6 +2,7 @@
Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2009, Google Inc.
Copyright (c) 2017, MariaDB Corporation. All Rights Reserved.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
......@@ -3556,10 +3557,9 @@ logs_empty_and_mark_files_at_shutdown(void)
shutdown, because the InnoDB layer may have committed or
prepared transactions and we don't want to lose them. */
total_trx = trx_sys_any_active_transactions();
if (total_trx > 0) {
if (ulint total_trx = srv_was_started && !srv_read_only_mode
&& srv_force_recovery < SRV_FORCE_NO_TRX_UNDO
? trx_sys_any_active_transactions() : 0) {
if (srv_print_verbose_log && count > 600) {
ib_logf(IB_LOG_LEVEL_INFO,
"Waiting for %lu active transactions to finish",
......
......@@ -1199,7 +1199,9 @@ trx_sys_close(void)
ut_a(UT_LIST_GET_LEN(trx_sys->ro_trx_list) == 0);
/* Only prepared transactions may be left in the system. Free them. */
ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == trx_sys->n_prepared_trx);
ut_a(UT_LIST_GET_LEN(trx_sys->rw_trx_list) == trx_sys->n_prepared_trx
|| srv_read_only_mode
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
while ((trx = UT_LIST_GET_FIRST(trx_sys->rw_trx_list)) != NULL) {
trx_free_prepared(trx);
......
......@@ -473,7 +473,11 @@ trx_free_prepared(
/*==============*/
trx_t* trx) /*!< in, own: trx object */
{
ut_a(trx_state_eq(trx, TRX_STATE_PREPARED));
ut_a(trx_state_eq(trx, TRX_STATE_PREPARED)
|| (trx_state_eq(trx, TRX_STATE_ACTIVE)
&& trx->is_recovered
&& (srv_read_only_mode
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO)));
ut_a(trx->magic_n == TRX_MAGIC_N);
lock_trx_release_locks(trx);
......
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2014, 2017, MariaDB Corporation. All Rights Reserved.
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
......@@ -2011,13 +2012,37 @@ trx_undo_free_prepared(
ut_ad(srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS);
if (trx->update_undo) {
ut_a(trx->update_undo->state == TRX_UNDO_PREPARED);
switch (trx->update_undo->state) {
case TRX_UNDO_PREPARED:
break;
case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns
trx->is_recovered=false */
ut_a(srv_read_only_mode
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
break;
default:
ut_error;
}
UT_LIST_REMOVE(undo_list, trx->rseg->update_undo_list,
trx->update_undo);
trx_undo_mem_free(trx->update_undo);
}
if (trx->insert_undo) {
ut_a(trx->insert_undo->state == TRX_UNDO_PREPARED);
switch (trx->insert_undo->state) {
case TRX_UNDO_PREPARED:
break;
case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns
trx->is_recovered=false */
ut_a(srv_read_only_mode
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
break;
default:
ut_error;
}
UT_LIST_REMOVE(undo_list, trx->rseg->insert_undo_list,
trx->insert_undo);
trx_undo_mem_free(trx->insert_undo);
......
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