Commit aa240f8b authored by Inaam Rana's avatar Inaam Rana

Bug#14594600 ASSERT FROM DROP TABLE CONCURRENT WITH IBUF MERGES

rb://1293
approved by: Marko Makela

There is race when dropping a single table tablespace where a reader
thread can initiate a read request before the delete flag is set and
before it is finished the deleting thread can attempt to free the
fil_node.

This patch checks the status in fil_io() to make sure that the
tablespace is not being deleted. If it is being deleted then
an error is returned instead of attempting IO.
parent 1b38b7a6
......@@ -187,14 +187,16 @@ struct fil_space_struct {
requests on the file */
ibool stop_new_ops;
/*!< we set this TRUE when we start
deleting a single-table tablespace */
ibool is_being_deleted;
/*!< this is set to TRUE when we start
deleting a single-table tablespace and its
file; when this flag is set no further i/o
or flush requests can be placed on this space,
though there may be such requests still being
processed on this space */
deleting a single-table tablespace.
When this is set following new ops
are not allowed:
* read IO request
* ibuf merge
* file flush
Note that we can still possibly have
new write operations because we don't
check this flag when doing flush
batches. */
ulint purpose;/*!< FIL_TABLESPACE, FIL_LOG, or
FIL_ARCH_LOG */
UT_LIST_BASE_NODE_T(fil_node_t) chain;
......@@ -1286,7 +1288,6 @@ fil_space_create(
space->stop_ios = FALSE;
space->stop_new_ops = FALSE;
space->is_being_deleted = FALSE;
space->purpose = purpose;
space->size = 0;
space->flags = flags;
......@@ -2301,11 +2302,9 @@ fil_delete_tablespace(
return(FALSE);
}
ut_a(space);
ut_a(space->stop_new_ops);
ut_a(space->n_pending_ops == 0);
space->is_being_deleted = TRUE;
ut_a(UT_LIST_GET_LEN(space->chain) == 1);
node = UT_LIST_GET_FIRST(space->chain);
......@@ -2348,13 +2347,26 @@ fil_delete_tablespace(
rw_lock_x_lock(&space->latch);
#ifndef UNIV_HOTBACKUP
/* Invalidate in the buffer pool all pages belonging to the
tablespace. Since we have set space->is_being_deleted = TRUE, readahead
or ibuf merge can no longer read more pages of this tablespace to the
buffer pool. Thus we can clean the tablespace out of the buffer pool
completely and permanently. The flag is_being_deleted also prevents
fil_flush() from being applied to this tablespace. */
/* IMPORTANT: Because we have set space::stop_new_ops there
can't be any new ibuf merges, reads or flushes. We are here
because node::n_pending was zero above. However, it is still
possible to have pending read and write requests:
A read request can happen because the reader thread has
gone through the ::stop_new_ops check in buf_page_init_for_read()
before the flag was set and has not yet incremented ::n_pending
when we checked it above.
A write request can be issued any time because we don't check
the ::stop_new_ops flag when queueing a block for write.
We deal with pending write requests in the following function
where we'd minimally evict all dirty pages belonging to this
space from the flush_list. Not that if a block is IO-fixed
we'll wait for IO to complete.
To deal with potential read requests by checking the
::stop_new_ops flag in fil_io() */
buf_LRU_flush_or_remove_pages(
id, evict_all
? BUF_REMOVE_ALL_NO_WRITE
......@@ -2364,6 +2376,15 @@ fil_delete_tablespace(
mutex_enter(&fil_system->mutex);
/* Double check the sanity of pending ops after reacquiring
the fil_system::mutex. */
if (fil_space_get_by_id(id)) {
ut_a(space->n_pending_ops == 0);
ut_a(UT_LIST_GET_LEN(space->chain) == 1);
node = UT_LIST_GET_FIRST(space->chain);
ut_a(node->n_pending == 0);
}
success = fil_space_free(id, TRUE);
mutex_exit(&fil_system->mutex);
......@@ -2421,7 +2442,7 @@ fil_tablespace_is_being_deleted(
ut_a(space != NULL);
is_being_deleted = space->is_being_deleted;
is_being_deleted = space->stop_new_ops;
mutex_exit(&fil_system->mutex);
......@@ -3695,7 +3716,7 @@ fil_tablespace_deleted_or_being_deleted_in_mem(
space = fil_space_get_by_id(id);
if (space == NULL || space->is_being_deleted) {
if (space == NULL || space->stop_new_ops) {
mutex_exit(&fil_system->mutex);
return(TRUE);
......@@ -4408,7 +4429,9 @@ fil_io(
space = fil_space_get_by_id(space_id);
if (!space) {
/* If we are deleting a tablespace we don't allow any read
operations on that. However, we do allow write operations. */
if (!space || (type == OS_FILE_READ && space->stop_new_ops)) {
mutex_exit(&fil_system->mutex);
ut_print_timestamp(stderr);
......@@ -4624,7 +4647,7 @@ fil_flush(
space = fil_space_get_by_id(space_id);
if (!space || space->is_being_deleted) {
if (!space || space->stop_new_ops) {
mutex_exit(&fil_system->mutex);
return;
......@@ -4755,7 +4778,7 @@ fil_flush_file_spaces(
space;
space = UT_LIST_GET_NEXT(unflushed_spaces, space)) {
if (space->purpose == purpose && !space->is_being_deleted) {
if (space->purpose == purpose && !space->stop_new_ops) {
space_ids[n_space_ids++] = space->id;
}
......
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