diff --git a/mysql-test/suite/innodb/r/temp_truncate.result b/mysql-test/suite/innodb/r/temp_truncate.result new file mode 100644 index 00000000000..65129927d7b --- /dev/null +++ b/mysql-test/suite/innodb/r/temp_truncate.result @@ -0,0 +1,38 @@ +# restart +CREATE TEMPORARY TABLE t1(f1 INT NOT NULL, +f2 INT NOT NULL)ENGINE=InnoDB; +INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536; +DROP TABLE t1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294; +NAME FILE_SIZE +innodb_temporary 72351744 +SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 0; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294; +NAME FILE_SIZE +innodb_temporary 72351744 +SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294; +NAME FILE_SIZE +innodb_temporary 5242880 +CREATE TEMPORARY TABLE t1(f1 INT NOT NULL, +f2 INT NOT NULL)ENGINE=InnoDB; +BEGIN; +INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536; +connect con1,localhost,root,,,; +CREATE TEMPORARY TABLE t2(f1 INT NOT NULL, +f2 INT NOT NULL)ENGINE=InnoDB; +INSERT INTO t2 SELECT seq, seq FROM seq_1_to_65536; +DROP TABLE t2; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294; +NAME FILE_SIZE +innodb_temporary 72351744 +SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294; +NAME FILE_SIZE +innodb_temporary 7340032 +connection default; +COMMIT; +SELECT COUNT(*) FROM t1; +COUNT(*) +65536 +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/temp_truncate_debug.result b/mysql-test/suite/innodb/r/temp_truncate_debug.result new file mode 100644 index 00000000000..37809c0a10d --- /dev/null +++ b/mysql-test/suite/innodb/r/temp_truncate_debug.result @@ -0,0 +1,22 @@ +call mtr.add_suppression("InnoDB: Cannot shrink the temporary tablespace"); +# restart +SET GLOBAL INNODB_LIMIT_OPTIMISTIC_INSERT_DEBUG=2; +CREATE TEMPORARY TABLE t1(f1 INT NOT NULL, +f2 INT NOT NULL)ENGINE=InnoDB; +INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536; +DROP TABLE t1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294; +NAME FILE_SIZE +innodb_temporary 1146093568 +SET @saved_debug_dbug = @@SESSION.debug_dbug; +SET DEBUG_DBUG="+d,fail_temp_truncate"; +SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294; +NAME FILE_SIZE +innodb_temporary 1146093568 +SET DEBUG_DBUG=@saved_debug_dbug; +SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294; +NAME FILE_SIZE +innodb_temporary 5242880 +SET GLOBAL INNODB_LIMIT_OPTIMISTIC_INSERT_DEBUG=default; diff --git a/mysql-test/suite/innodb/t/temp_truncate.opt b/mysql-test/suite/innodb/t/temp_truncate.opt new file mode 100644 index 00000000000..70b8eaf51eb --- /dev/null +++ b/mysql-test/suite/innodb/t/temp_truncate.opt @@ -0,0 +1,2 @@ +--innodb_temp_data_file_path=ibtmp1:5M:autoextend +--innodb_sys_tablespaces diff --git a/mysql-test/suite/innodb/t/temp_truncate.test b/mysql-test/suite/innodb/t/temp_truncate.test new file mode 100644 index 00000000000..1eb35c11a09 --- /dev/null +++ b/mysql-test/suite/innodb/t/temp_truncate.test @@ -0,0 +1,34 @@ +--source include/have_innodb.inc +--source include/have_sequence.inc + +--source include/restart_mysqld.inc +CREATE TEMPORARY TABLE t1(f1 INT NOT NULL, + f2 INT NOT NULL)ENGINE=InnoDB; +INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536; +DROP TABLE t1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294; +SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 0; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294; +SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294; + +# Concurrent session has open transaction for temporary tables +CREATE TEMPORARY TABLE t1(f1 INT NOT NULL, + f2 INT NOT NULL)ENGINE=InnoDB; +BEGIN; +INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536; + +# Concurrent session has open transaction for temporary tables +connect(con1,localhost,root,,,); +CREATE TEMPORARY TABLE t2(f1 INT NOT NULL, + f2 INT NOT NULL)ENGINE=InnoDB; +INSERT INTO t2 SELECT seq, seq FROM seq_1_to_65536; +DROP TABLE t2; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE=4294967294; +SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294; + +connection default; +COMMIT; +SELECT COUNT(*) FROM t1; +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/temp_truncate_debug.opt b/mysql-test/suite/innodb/t/temp_truncate_debug.opt new file mode 100644 index 00000000000..70b8eaf51eb --- /dev/null +++ b/mysql-test/suite/innodb/t/temp_truncate_debug.opt @@ -0,0 +1,2 @@ +--innodb_temp_data_file_path=ibtmp1:5M:autoextend +--innodb_sys_tablespaces diff --git a/mysql-test/suite/innodb/t/temp_truncate_debug.test b/mysql-test/suite/innodb/t/temp_truncate_debug.test new file mode 100644 index 00000000000..b33ad7134e9 --- /dev/null +++ b/mysql-test/suite/innodb/t/temp_truncate_debug.test @@ -0,0 +1,22 @@ +--source include/have_innodb.inc +--source include/have_sequence.inc +--source include/have_debug.inc + +call mtr.add_suppression("InnoDB: Cannot shrink the temporary tablespace"); +--source include/restart_mysqld.inc +SET GLOBAL INNODB_LIMIT_OPTIMISTIC_INSERT_DEBUG=2; +CREATE TEMPORARY TABLE t1(f1 INT NOT NULL, + f2 INT NOT NULL)ENGINE=InnoDB; +INSERT INTO t1 SELECT seq, seq FROM seq_1_to_65536; +DROP TABLE t1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294; + +SET @saved_debug_dbug = @@SESSION.debug_dbug; +SET DEBUG_DBUG="+d,fail_temp_truncate"; +SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294; +SET DEBUG_DBUG=@saved_debug_dbug; + +SET GLOBAL INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW= 1; +SELECT NAME, FILE_SIZE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES WHERE SPACE= 4294967294; +SET GLOBAL INNODB_LIMIT_OPTIMISTIC_INSERT_DEBUG=default; diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result index 019a5ccabd6..b5f88162daf 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result +++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result @@ -1531,6 +1531,18 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST NULL READ_ONLY NO COMMAND_LINE_ARGUMENT OPTIONAL +VARIABLE_NAME INNODB_TRUNCATE_TEMPORARY_TABLESPACE_NOW +SESSION_VALUE NULL +DEFAULT_VALUE OFF +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE BOOLEAN +VARIABLE_COMMENT Shrink the temporary tablespace +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST OFF,ON +READ_ONLY NO +COMMAND_LINE_ARGUMENT OPTIONAL VARIABLE_NAME INNODB_TRX_PURGE_VIEW_UPDATE_ONLY_DEBUG SESSION_VALUE NULL DEFAULT_VALUE OFF diff --git a/storage/innobase/buf/buf0lru.cc b/storage/innobase/buf/buf0lru.cc index 3e556c7df37..24b4e512aa0 100644 --- a/storage/innobase/buf/buf0lru.cc +++ b/storage/innobase/buf/buf0lru.cc @@ -1330,6 +1330,51 @@ bool buf_LRU_scan_and_free_block(ulint limit) buf_LRU_free_from_common_LRU_list(limit); } +void buf_LRU_truncate_temp(uint32_t threshold) +{ + /* Set the extent descriptor page state as FREED */ + for (uint32_t cur_xdes_page= xdes_calc_descriptor_page( + 0, fil_system.temp_space->free_limit); + cur_xdes_page >= threshold;) + { + mtr_t mtr; + mtr.start(); + if (buf_block_t* block= buf_page_get_gen( + page_id_t(SRV_TMP_SPACE_ID, cur_xdes_page), 0, RW_X_LATCH, + nullptr, BUF_PEEK_IF_IN_POOL, &mtr)) + { + uint32_t state= block->page.state(); + ut_ad(state > buf_page_t::UNFIXED); + ut_ad(state < buf_page_t::READ_FIX); + block->page.set_freed(state); + } + cur_xdes_page-= uint32_t(srv_page_size); + mtr.commit(); + } + + const page_id_t limit{SRV_TMP_SPACE_ID, threshold}; + mysql_mutex_lock(&buf_pool.mutex); + for (buf_page_t* bpage = UT_LIST_GET_FIRST(buf_pool.LRU); + bpage;) + { + buf_page_t* next= UT_LIST_GET_NEXT(LRU, bpage); + if (bpage->id() >= limit) + { + #ifdef UNIV_DEBUG + if (bpage->lock.u_lock_try(0)) + { + ut_ad(bpage->state() == buf_page_t::FREED); + bpage->lock.u_unlock(); + } + #endif /* UNIV_DEBUG */ + ut_ad(!reinterpret_cast(bpage)->index); + buf_LRU_free_page(bpage, true); + } + bpage= next; + } + mysql_mutex_unlock(&buf_pool.mutex); +} + #ifdef UNIV_DEBUG /** Validate the LRU list. */ void buf_LRU_validate() diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index 7cd2942332c..17b4a6913f5 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -3076,21 +3076,21 @@ std::ostream &fseg_header::to_stream(std::ostream &out) const /** Get the latched extent descriptor page or acquire the extent descriptor page. -@param page_no page number to be acquired +@param page_id page identifier to be acquired @param mtr mini-transaction @param err error code @return block descriptor */ static buf_block_t *fsp_get_latched_xdes_page( - uint32_t page_no, mtr_t *mtr, dberr_t *err) + page_id_t page_id, mtr_t *mtr, dberr_t *err) { buf_block_t *block= nullptr; block= mtr->get_already_latched( - page_id_t{0, page_no}, MTR_MEMO_PAGE_SX_FIX); + page_id, MTR_MEMO_PAGE_SX_FIX); if (block) return block; return buf_page_get_gen( - page_id_t{0, page_no}, 0, RW_SX_LATCH, nullptr, + page_id, 0, RW_SX_LATCH, nullptr, BUF_GET_POSSIBLY_FREED, mtr, err); } @@ -3100,7 +3100,9 @@ old page state */ class fsp_xdes_old_page { std::vector m_old_xdes_pages; + const uint32_t m_space; public: + fsp_xdes_old_page(uint32_t space):m_space(space) {} ulint n_pages() { uint32_t count=0; @@ -3120,7 +3122,8 @@ public: DBUG_EXECUTE_IF("shrink_buffer_pool_full", return DB_OUT_OF_MEMORY;); dberr_t err= DB_SUCCESS; - buf_block_t *block= fsp_get_latched_xdes_page(page_no, mtr, &err); + buf_block_t *block= fsp_get_latched_xdes_page( + page_id_t(m_space, page_no), mtr, &err); if (block) { buf_block_t *old= buf_LRU_get_free_block(have_no_mutex_soft); @@ -3150,7 +3153,7 @@ public: { if (m_old_xdes_pages[i] == nullptr) continue; buf_block_t *block= mtr->get_already_latched( - page_id_t{0, i << srv_page_size_shift}, + page_id_t{m_space, i << srv_page_size_shift}, MTR_MEMO_PAGE_SX_FIX); ut_ad(block); memcpy_aligned( @@ -3158,7 +3161,6 @@ public: } } - fsp_xdes_old_page()=default; ~fsp_xdes_old_page() { for (uint32_t i= 0; i < m_old_xdes_pages.size(); i++) @@ -3184,8 +3186,9 @@ dberr_t fsp_lst_update_skip( uint32_t skip_len, mtr_t *mtr) { dberr_t err= DB_SUCCESS; + uint32_t space_id= header->page.id().space(); buf_block_t *cur= fsp_get_latched_xdes_page( - cur_addr.page, mtr, &err); + page_id_t(space_id, cur_addr.page), mtr, &err); if (!cur) return err; if (last_valid_addr.page == FIL_NULL) @@ -3212,7 +3215,8 @@ dberr_t fsp_lst_update_skip( else { prev= fsp_get_latched_xdes_page( - last_valid_addr.page, mtr, &err); + page_id_t(space_id, last_valid_addr.page), + mtr, &err); if (!prev) return err; } @@ -3294,7 +3298,8 @@ func_exit: cur_addr.page, cur_addr.boffset, mtr); buf_block_t *cur_block= fsp_get_latched_xdes_page( - cur_addr.page, mtr, &err); + page_id_t(header->page.id().space(), cur_addr.page), + mtr, &err); if (!cur_block) return err; @@ -3335,12 +3340,15 @@ dberr_t fsp_shrink_list(buf_block_t *header, uint16_t hdr_offset, for (uint32_t i= len; i > 0; i--) { - ut_ad(addr.page < fil_system.sys_space->size); + ut_d(fil_space_t *space= header->page.id().space() == 0 + ? fil_system.sys_space + : fil_system.temp_space); + ut_ad(addr.page < space->size); ut_ad(!(addr.page & (srv_page_size - 1))); if (!descr_block || descr_block->page.id().page_no() != addr.page) { descr_block= fsp_get_latched_xdes_page( - addr.page, mtr, &err); + page_id_t(header->page.id().space(), addr.page), mtr, &err); if (!descr_block) return err; } @@ -3390,7 +3398,7 @@ dberr_t fsp_shrink_list(buf_block_t *header, uint16_t hdr_offset, @return DB_SUCCESS or error code on failure */ __attribute__((warn_unused_result)) static -dberr_t fsp_xdes_reset(fil_space_t *space, uint32_t threshold, mtr_t *mtr) +dberr_t fsp_xdes_reset(uint32_t space_id, uint32_t threshold, mtr_t *mtr) { if (!(threshold & (srv_page_size - 1))) return DB_SUCCESS; @@ -3404,7 +3412,7 @@ dberr_t fsp_xdes_reset(fil_space_t *space, uint32_t threshold, mtr_t *mtr) last_descr_offset+= XDES_SIZE; dberr_t err= DB_SUCCESS; buf_block_t *block= fsp_get_latched_xdes_page( - cur_descr_page, mtr, &err); + page_id_t(space_id, cur_descr_page), mtr, &err); if (!block) return err; mtr->memset( @@ -3455,7 +3463,9 @@ dberr_t fsp_traverse_extents( { if (!block) { - block= fsp_get_latched_xdes_page(last_descr_page_no, mtr, &err); + block= fsp_get_latched_xdes_page( + page_id_t(space->id, last_descr_page_no), + mtr, &err); if (!block) return err; } @@ -3479,14 +3489,14 @@ dberr_t fsp_traverse_extents( { fil_addr_t prev_addr= flst_get_prev_addr( descr + XDES_FLST_NODE); - ut_ad(prev_addr.page < fil_system.sys_space->size || + ut_ad(prev_addr.page < space->size || prev_addr.page == FIL_NULL); ut_ad(prev_addr.page == FIL_NULL || !(prev_addr.page & (srv_page_size - 1))); fil_addr_t next_addr= flst_get_next_addr( descr + XDES_FLST_NODE); - ut_ad(next_addr.page < fil_system.sys_space->size || + ut_ad(next_addr.page < space->size || next_addr.page == FIL_NULL); ut_ad(next_addr.page == FIL_NULL || !(next_addr.page & (srv_page_size - 1))); @@ -3524,14 +3534,14 @@ dberr_t fsp_traverse_extents( #ifdef UNIV_DEBUG /** Validate the system tablespace list */ __attribute__((warn_unused_result)) -dberr_t fsp_sys_tablespace_validate() +dberr_t fsp_tablespace_validate(fil_space_t *space) { /* Validate all FSP list in system tablespace */ mtr_t local_mtr; dberr_t err= DB_SUCCESS; local_mtr.start(); if (buf_block_t *header= fsp_get_header( - fil_system.sys_space, &local_mtr, &err)) + space, &local_mtr, &err)) { flst_validate(header, FSP_FREE + FSP_HEADER_OFFSET, &local_mtr); flst_validate(header, FSP_FREE_FRAG + FSP_HEADER_OFFSET, @@ -3584,7 +3594,7 @@ func_exit: srv_use_doublewrite_buf= false; buf_block_t *header= nullptr; - ut_ad(!fsp_sys_tablespace_validate()); + ut_ad(!fsp_tablespace_validate(space)); mtr.start(); mtr.x_lock_space(space); @@ -3592,7 +3602,7 @@ func_exit: { /* Take the rough estimation of modified extent descriptor page and store their old state */ - fsp_xdes_old_page old_xdes_list; + fsp_xdes_old_page old_xdes_list(space->id); err= fsp_traverse_extents(space, &last_used_extent, &mtr, &old_xdes_list); if (err == DB_OUT_OF_MEMORY) @@ -3610,7 +3620,8 @@ func_exit: UINT32PF " to " UINT32PF " pages", space->size, last_used_extent); - header= fsp_get_latched_xdes_page(0, &mtr, &err); + header= fsp_get_latched_xdes_page( + page_id_t(space->id, 0), &mtr, &err); if (!header) goto func_exit; @@ -3632,7 +3643,7 @@ func_exit: if (err != DB_SUCCESS) goto func_exit; - err= fsp_xdes_reset(space, last_used_extent, &mtr); + err= fsp_xdes_reset(space->id, last_used_extent, &mtr); if (err != DB_SUCCESS) goto func_exit; @@ -3651,7 +3662,7 @@ mtr_max: old_xdes_list.restore(&mtr); mtr.discard_modifications(); mtr.commit(); - ut_ad(!fsp_sys_tablespace_validate()); + ut_ad(!fsp_tablespace_validate(space)); sql_print_error( "InnoDB: Cannot shrink the system tablespace " "because the mini-transaction log size (%zu bytes) " @@ -3684,3 +3695,119 @@ mtr_max: sql_print_information("InnoDB: System tablespace truncated successfully"); srv_use_doublewrite_buf= old_dblwr_buf; } + +inline void fil_space_t::clear_freed_ranges(uint32_t threshold) +{ + ut_ad(id == SRV_TMP_SPACE_ID); + std::lock_guard freed_lock(freed_range_mutex); + range_set current_ranges; + for (const auto &range : freed_ranges) + { + if (range.first >= threshold) + continue; + else if (range.last > threshold) + { + range_t new_range{range.first, threshold - 1}; + current_ranges.add_range(new_range); + continue; + } + current_ranges.add_range(range); + } + freed_ranges= std::move(current_ranges); +} + +void fsp_shrink_temp_space() +{ + uint32_t last_used_extent= 0; + fil_space_t *space= fil_system.temp_space; + mtr_t mtr; + mtr.start(); + mtr.set_log_mode(MTR_LOG_NO_REDO); + mtr.x_lock_space(space); + dberr_t err= fsp_traverse_extents(space, &last_used_extent, &mtr); + if (err != DB_SUCCESS) + { +func_exit: + sql_print_warning("InnoDB: Cannot shrink the temporary tablespace " + "due to %s", ut_strerr(err)); + mtr.commit(); + return; + } + uint32_t fixed_size= srv_tmp_space.get_min_size(), + header_size= space->size_in_header; + + if (last_used_extent >= header_size || fixed_size >= header_size) + { + /* Tablespace is being used within fixed size */ + mtr.commit(); + return; + } + + /* Set fixed size as threshold to truncate */ + if (fixed_size > last_used_extent) + last_used_extent= fixed_size; + + sql_print_information("InnoDB: Truncating temporary tablespace from " + UINT32PF " to " UINT32PF " pages", space->size, + last_used_extent); + + buf_block_t *header= fsp_get_latched_xdes_page( + page_id_t(space->id, 0), &mtr, &err); + if (!header) + goto func_exit; + + mach_write_to_4( + FSP_HEADER_OFFSET + FSP_SIZE + header->page.frame, + last_used_extent); + + if (space->free_limit > last_used_extent) + mach_write_to_4( + FSP_HEADER_OFFSET + FSP_FREE_LIMIT + header->page.frame, + last_used_extent); + + mtr.set_modified(*header); + + err= fsp_shrink_list(header, FSP_HEADER_OFFSET + FSP_FREE, + last_used_extent, &mtr); + + if (err != DB_SUCCESS) + goto func_exit; + + err= fsp_shrink_list( + header, FSP_HEADER_OFFSET + FSP_FREE_FRAG, + last_used_extent, &mtr); + DBUG_EXECUTE_IF("fail_temp_truncate", err= DB_ERROR;); + if (err != DB_SUCCESS) + goto func_exit; + + err= fsp_xdes_reset(space->id, last_used_extent, &mtr); + if (err != DB_SUCCESS) + goto func_exit; + + space->clear_freed_ranges(last_used_extent); + buf_LRU_truncate_temp(last_used_extent); + mysql_mutex_lock(&fil_system.mutex); + + space->size= last_used_extent; + if (space->free_limit > last_used_extent) + space->free_limit= space->size; + + space->free_len= flst_get_len( + FSP_HEADER_OFFSET + FSP_FREE+ header->page.frame); + + /* Last file new size after truncation */ + uint32_t new_last_file_size= + last_used_extent - + (fixed_size - srv_tmp_space.m_files.at( + srv_tmp_space.m_files.size() - 1).param_size()); + + space->size_in_header= space->size; + space->chain.end->size= new_last_file_size; + srv_tmp_space.set_last_file_size(new_last_file_size); + mysql_mutex_unlock(&fil_system.mutex); + os_file_truncate( + space->chain.end->name, space->chain.end->handle, + os_offset_t{space->chain.end->size} << srv_page_size_shift, true); + mtr.commit(); + sql_print_information("InnoDB: Temporary tablespace truncated successfully"); +} diff --git a/storage/innobase/fut/fut0lst.cc b/storage/innobase/fut/fut0lst.cc index 48cb8bccdc3..1a984596d64 100644 --- a/storage/innobase/fut/fut0lst.cc +++ b/storage/innobase/fut/fut0lst.cc @@ -47,6 +47,14 @@ void flst_write_addr(const buf_block_t &block, byte *faddr, static_assert(FIL_ADDR_BYTE == 4, "compatibility"); static_assert(FIL_ADDR_SIZE == 6, "compatibility"); + if (!mtr->is_logged()) + { + mach_write_to_4(faddr + FIL_ADDR_PAGE, page); + mach_write_to_2(faddr + FIL_ADDR_BYTE, boffset); + mtr->set_modified(block); + return; + } + const bool same_page= mach_read_from_4(faddr + FIL_ADDR_PAGE) == page; const bool same_offset= mach_read_from_2(faddr + FIL_ADDR_BYTE) == boffset; if (same_page) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index bc9a22c4f29..62daa270c2c 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -213,6 +213,8 @@ enum default_row_format_enum { DEFAULT_ROW_FORMAT_DYNAMIC = 2, }; +static my_bool innodb_truncate_temporary_tablespace_now; + /** Whether ROW_FORMAT=COMPRESSED tables are read-only */ static my_bool innodb_read_only_compressed; @@ -18492,6 +18494,20 @@ innodb_encrypt_tables_update(THD*, st_mysql_sys_var*, void*, const void* save) mysql_mutex_lock(&LOCK_global_system_variables); } +/** Truncate the temporary tablespace if the +innodb_truncate_temporary_tablespace_now is enabled. +@param save to-be-assigned value */ +static +void +innodb_trunc_temp_space_update(THD*, st_mysql_sys_var*, void*, const void* save) +{ + if (!*static_cast(save)) + return; + mysql_mutex_unlock(&LOCK_global_system_variables); + fsp_shrink_temp_space(); + mysql_mutex_lock(&LOCK_global_system_variables); +} + static SHOW_VAR innodb_status_variables_export[]= { SHOW_FUNC_ENTRY("Innodb", &show_innodb_vars), {NullS, NullS, SHOW_LONG} @@ -19583,6 +19599,12 @@ static MYSQL_SYSVAR_BOOL(encrypt_temporary_tables, innodb_encrypt_temporary_tabl "Enrypt the temporary table data.", NULL, NULL, false); +static MYSQL_SYSVAR_BOOL(truncate_temporary_tablespace_now, + innodb_truncate_temporary_tablespace_now, + PLUGIN_VAR_OPCMDARG, + "Shrink the temporary tablespace", + NULL, innodb_trunc_temp_space_update, false); + static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(autoextend_increment), MYSQL_SYSVAR(buffer_pool_size), @@ -19741,6 +19763,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(buf_dump_status_frequency), MYSQL_SYSVAR(background_thread), MYSQL_SYSVAR(encrypt_temporary_tables), + MYSQL_SYSVAR(truncate_temporary_tablespace_now), NULL }; diff --git a/storage/innobase/include/buf0lru.h b/storage/innobase/include/buf0lru.h index d8ce8333eb1..1328dbca340 100644 --- a/storage/innobase/include/buf0lru.h +++ b/storage/innobase/include/buf0lru.h @@ -129,6 +129,10 @@ buf_unzip_LRU_add_block( ibool old); /*!< in: TRUE if should be put to the end of the list, else put to the start */ +/** Evict the temporary tablespace pages above the given threshold +@param threshold Above this page to be removed from LRU list */ +void buf_LRU_truncate_temp(uint32_t threshold); + /** Update buf_pool.LRU_old_ratio. @param[in] old_pct Reserve this percentage of the buffer pool for "old" blocks diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 2d1dc4ba873..13f12cb4e5b 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -911,6 +911,11 @@ public: freed_ranges.add_range(range); } + /** Clear the freed range in temporary tablespace + which are in shrinking ranges. + @param threshold to be truncated value*/ + inline void clear_freed_ranges(uint32_t threshold); + /** Set the tablespace size in pages */ void set_sizes(uint32_t s) { diff --git a/storage/innobase/include/fsp0fsp.h b/storage/innobase/include/fsp0fsp.h index 4917584f2be..ddc45e53fe6 100644 --- a/storage/innobase/include/fsp0fsp.h +++ b/storage/innobase/include/fsp0fsp.h @@ -576,6 +576,9 @@ inline void fsp_init_file_page( /** Truncate the system tablespace */ void fsp_system_tablespace_truncate(); +/** Truncate the temporary tablespace */ +void fsp_shrink_temp_space(); + #ifndef UNIV_DEBUG # define fsp_init_file_page(space, block, mtr) fsp_init_file_page(block, mtr) #endif