mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 20:42:30 +01:00
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.
This commit is contained in:
parent
4fe5be1171
commit
76e4d6acf0
1 changed files with 46 additions and 23 deletions
|
@ -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 @@ try_again:
|
|||
|
||||
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 @@ try_again:
|
|||
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 @@ try_again:
|
|||
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 @@ try_again:
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue