diff options
Diffstat (limited to 'storage/innobase/row/row0purge.cc')
-rw-r--r-- | storage/innobase/row/row0purge.cc | 443 |
1 files changed, 276 insertions, 167 deletions
diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index ab28b396920..ee603be453a 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 1997, 2011, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 1997, 2012, Oracle and/or its affiliates. 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 @@ -42,8 +42,10 @@ Created 3/14/1997 Heikki Tuuri #include "row0upd.h" #include "row0vers.h" #include "row0mysql.h" +#include "row0log.h" #include "log0log.h" #include "srv0mon.h" +#include "srv0start.h" /************************************************************************* IMPORTANT NOTE: Any operation that generates redo MUST check that there @@ -110,119 +112,134 @@ row_purge_reposition_pcur( return(node->found_clust); } +/** Status of row_purge_remove_clust() */ +enum row_purge_status { + ROW_PURGE_DONE, /*!< The row has been removed. */ + ROW_PURGE_FAIL, /*!< The purge was not successful. */ + ROW_PURGE_SUSPEND/*!< Cannot purge now, due to online rebuild. */ +}; + /***********************************************************//** Removes a delete marked clustered index record if possible. -@return TRUE if success, or if not found, or if modified after the -delete marking */ -static -ibool +@retval ROW_PURGE_DONE if the row was not found, or it was successfully removed +@retval ROW_PURGE_FAIL if the row was modified after the delete marking +@retval ROW_PURGE_SUSPEND if the row refers to an off-page column and +an online ALTER TABLE (table rebuild) is in progress. */ +static __attribute__((nonnull, warn_unused_result)) +enum row_purge_status row_purge_remove_clust_if_poss_low( /*===============================*/ - purge_node_t* node, /*!< in: row purge node */ + purge_node_t* node, /*!< in/out: row purge node */ ulint mode) /*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ { - dict_index_t* index; - btr_pcur_t* pcur; - btr_cur_t* btr_cur; - ibool success; - ulint err; - mtr_t mtr; - rec_t* rec; - mem_heap_t* heap = NULL; - ulint offsets_[REC_OFFS_NORMAL_SIZE]; + dict_index_t* index; + enum row_purge_status status = ROW_PURGE_DONE; + mtr_t mtr; + rec_t* rec; + mem_heap_t* heap = NULL; + ulint* offsets; + ulint offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); - index = dict_table_get_first_index(node->table); +#ifdef UNIV_SYNC_DEBUG + ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED)); +#endif /* UNIV_SYNC_DEBUG */ - pcur = &node->pcur; - btr_cur = btr_pcur_get_btr_cur(pcur); + index = dict_table_get_first_index(node->table); log_free_check(); mtr_start(&mtr); - success = row_purge_reposition_pcur(mode, node, &mtr); - - if (!success) { - /* The record is already removed */ - - btr_pcur_commit_specify_mtr(pcur, &mtr); - - return(TRUE); + if (!row_purge_reposition_pcur(mode, node, &mtr)) { + /* The record was already removed. */ + goto func_exit; } - rec = btr_pcur_get_rec(pcur); + rec = btr_pcur_get_rec(&node->pcur); - if (node->roll_ptr != row_get_rec_roll_ptr( - rec, index, rec_get_offsets(rec, index, offsets_, - ULINT_UNDEFINED, &heap))) { - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); - } - /* Someone else has modified the record later: do not remove */ - btr_pcur_commit_specify_mtr(pcur, &mtr); + offsets = rec_get_offsets( + rec, index, offsets_, ULINT_UNDEFINED, &heap); - return(TRUE); + if (node->roll_ptr != row_get_rec_roll_ptr(rec, index, offsets)) { + /* Someone else has modified the record later: do not remove */ + goto func_exit; } - if (UNIV_LIKELY_NULL(heap)) { - mem_heap_free(heap); + if (dict_index_get_online_status(index) == ONLINE_INDEX_CREATION + && rec_offs_any_extern(offsets)) { + status = ROW_PURGE_SUSPEND; + goto func_exit; } if (mode == BTR_MODIFY_LEAF) { - success = btr_cur_optimistic_delete(btr_cur, &mtr); + status = btr_cur_optimistic_delete( + btr_pcur_get_btr_cur(&node->pcur), 0, &mtr) + ? ROW_PURGE_DONE : ROW_PURGE_FAIL; } else { + dberr_t err; ut_ad(mode == BTR_MODIFY_TREE); - btr_cur_pessimistic_delete(&err, FALSE, btr_cur, - RB_NONE, &mtr); + btr_cur_pessimistic_delete( + &err, FALSE, btr_pcur_get_btr_cur(&node->pcur), 0, + RB_NONE, &mtr); - if (err == DB_SUCCESS) { - success = TRUE; - } else if (err == DB_OUT_OF_FILE_SPACE) { - success = FALSE; - } else { + switch (err) { + case DB_SUCCESS: + break; + case DB_OUT_OF_FILE_SPACE: + status = ROW_PURGE_FAIL; + break; + default: ut_error; } } - btr_pcur_commit_specify_mtr(pcur, &mtr); +func_exit: + if (heap) { + mem_heap_free(heap); + } - return(success); + btr_pcur_commit_specify_mtr(&node->pcur, &mtr); + + return(status); } /***********************************************************//** Removes a clustered index record if it has not been modified after the delete -marking. */ -static -void +marking. +@retval true if the row was not found, or it was successfully removed +@retval false the purge needs to be suspended, either because of +running out of file space or because the row refers to an off-page +column and an online ALTER TABLE (table rebuild) is in progress. */ +static __attribute__((nonnull, warn_unused_result)) +bool row_purge_remove_clust_if_poss( /*===========================*/ - purge_node_t* node) /*!< in: row purge node */ + purge_node_t* node) /*!< in/out: row purge node */ { - ibool success; - ulint n_tries = 0; - - /* fputs("Purge: Removing clustered record\n", stderr); */ - - success = row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_LEAF); - if (success) { - - return; + switch (row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_LEAF)) { + case ROW_PURGE_DONE: + return(true); + case ROW_PURGE_SUSPEND: + return(false); + case ROW_PURGE_FAIL: + break; } -retry: - success = row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_TREE); - /* The delete operation may fail if we have little - file space left: TODO: easiest to crash the database - and restart with more file space */ - if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) { - n_tries++; - - os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME); - - goto retry; + for (ulint n_tries = 0; + n_tries < BTR_CUR_RETRY_DELETE_N_TIMES; + n_tries++) { + switch (row_purge_remove_clust_if_poss_low( + node, BTR_MODIFY_TREE)) { + case ROW_PURGE_DONE: + return(true); + case ROW_PURGE_SUSPEND: + return(false); + case ROW_PURGE_FAIL: + os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME); + } } - ut_a(success); + return(false); } /***********************************************************//** @@ -234,21 +251,21 @@ is newer than the purge view. NOTE: This function should only be called by the purge thread, only while holding a latch on the leaf page of the secondary index entry (or keeping the buffer pool watch on the page). It is possible that -this function first returns TRUE and then FALSE, if a user transaction +this function first returns true and then false, if a user transaction inserts a record that the secondary index entry would refer to. However, in that case, the user transaction would also re-insert the secondary index entry after purge has removed it and released the leaf page latch. -@return TRUE if the secondary index record can be purged */ +@return true if the secondary index record can be purged */ UNIV_INTERN -ibool +bool row_purge_poss_sec( /*===============*/ purge_node_t* node, /*!< in/out: row purge node */ dict_index_t* index, /*!< in: secondary index */ const dtuple_t* entry) /*!< in: secondary index entry */ { - ibool can_delete; + bool can_delete; mtr_t mtr; ut_ad(!dict_index_is_clust(index)); @@ -268,7 +285,7 @@ row_purge_poss_sec( Removes a secondary index entry if possible, by modifying the index tree. Does not try to buffer the delete. @return TRUE if success or if not found */ -static +static __attribute__((nonnull, warn_unused_result)) ibool row_purge_remove_sec_if_poss_tree( /*==============================*/ @@ -279,13 +296,35 @@ row_purge_remove_sec_if_poss_tree( btr_pcur_t pcur; btr_cur_t* btr_cur; ibool success = TRUE; - ulint err; + dberr_t err; mtr_t mtr; enum row_search_result search_result; log_free_check(); mtr_start(&mtr); + if (*index->name == TEMP_INDEX_PREFIX) { + /* The index->online_status may change if the + index->name starts with TEMP_INDEX_PREFIX (meaning + that the index is or was being created online). It is + protected by index->lock. */ + mtr_x_lock(dict_index_get_lock(index), &mtr); + + if (dict_index_is_online_ddl(index)) { + /* Online secondary index creation will not + copy any delete-marked records. Therefore + there is nothing to be purged. We must also + skip the purge when a completed index is + dropped by rollback_inplace_alter_table(). */ + goto func_exit_no_pcur; + } + } else { + /* For secondary indexes, + index->online_status==ONLINE_INDEX_CREATION unless + index->name starts with TEMP_INDEX_PREFIX. */ + ut_ad(!dict_index_is_online_ddl(index)); + } + search_result = row_search_index_entry(index, entry, BTR_MODIFY_TREE, &pcur, &mtr); @@ -327,7 +366,7 @@ row_purge_remove_sec_if_poss_tree( & rec_get_info_bits(btr_cur_get_rec(btr_cur), dict_table_is_comp(index->table))); - btr_cur_pessimistic_delete(&err, FALSE, btr_cur, + btr_cur_pessimistic_delete(&err, FALSE, btr_cur, 0, RB_NONE, &mtr); switch (UNIV_EXPECT(err, DB_SUCCESS)) { case DB_SUCCESS: @@ -342,6 +381,7 @@ row_purge_remove_sec_if_poss_tree( func_exit: btr_pcur_close(&pcur); +func_exit_no_pcur: mtr_commit(&mtr); return(success); @@ -350,9 +390,10 @@ func_exit: /*************************************************************** Removes a secondary index entry without modifying the index tree, if possible. -@return TRUE if success or if not found */ -static -ibool +@retval true if success or if not found +@retval false if row_purge_remove_sec_if_poss_tree() should be invoked */ +static __attribute__((nonnull, warn_unused_result)) +bool row_purge_remove_sec_if_poss_leaf( /*==============================*/ purge_node_t* node, /*!< in: row purge node */ @@ -361,12 +402,40 @@ row_purge_remove_sec_if_poss_leaf( { mtr_t mtr; btr_pcur_t pcur; + ulint mode; enum row_search_result search_result; + bool success = true; log_free_check(); mtr_start(&mtr); + if (*index->name == TEMP_INDEX_PREFIX) { + /* The index->online_status may change if the + index->name starts with TEMP_INDEX_PREFIX (meaning + that the index is or was being created online). It is + protected by index->lock. */ + mtr_s_lock(dict_index_get_lock(index), &mtr); + + if (dict_index_is_online_ddl(index)) { + /* Online secondary index creation will not + copy any delete-marked records. Therefore + there is nothing to be purged. We must also + skip the purge when a completed index is + dropped by rollback_inplace_alter_table(). */ + goto func_exit_no_pcur; + } + + mode = BTR_MODIFY_LEAF | BTR_ALREADY_S_LATCHED | BTR_DELETE; + } else { + /* For secondary indexes, + index->online_status==ONLINE_INDEX_CREATION unless + index->name starts with TEMP_INDEX_PREFIX. */ + ut_ad(!dict_index_is_online_ddl(index)); + + mode = BTR_MODIFY_LEAF | BTR_DELETE; + } + /* Set the purge node for the call to row_purge_poss_sec(). */ pcur.btr_cur.purge_node = node; /* Set the query thread, so that ibuf_insert_low() will be @@ -374,10 +443,9 @@ row_purge_remove_sec_if_poss_leaf( pcur.btr_cur.thr = static_cast<que_thr_t*>(que_node_get_parent(node)); search_result = row_search_index_entry( - index, entry, BTR_MODIFY_LEAF | BTR_DELETE, &pcur, &mtr); + index, entry, mode, &pcur, &mtr); switch (search_result) { - ibool success; case ROW_FOUND: /* Before attempting to purge a record, check if it is safe to do so. */ @@ -390,11 +458,10 @@ row_purge_remove_sec_if_poss_leaf( btr_cur_get_rec(btr_cur), dict_table_is_comp(index->table))); - if (!btr_cur_optimistic_delete(btr_cur, &mtr)) { + if (!btr_cur_optimistic_delete(btr_cur, 0, &mtr)) { /* The index entry could not be deleted. */ - success = FALSE; - goto func_exit; + success = false; } } /* fall through (the index entry is still needed, @@ -405,9 +472,8 @@ row_purge_remove_sec_if_poss_leaf( /* The deletion was buffered. */ case ROW_NOT_FOUND: /* The index entry does not exist, nothing to do. */ - success = TRUE; - func_exit: btr_pcur_close(&pcur); + func_exit_no_pcur: mtr_commit(&mtr); return(success); } @@ -418,19 +484,26 @@ row_purge_remove_sec_if_poss_leaf( /***********************************************************//** Removes a secondary index entry if possible. */ -UNIV_INLINE +UNIV_INLINE __attribute__((nonnull(1,2))) void row_purge_remove_sec_if_poss( /*=========================*/ purge_node_t* node, /*!< in: row purge node */ dict_index_t* index, /*!< in: index */ - dtuple_t* entry) /*!< in: index entry */ + const dtuple_t* entry) /*!< in: index entry */ { ibool success; ulint n_tries = 0; /* fputs("Purge: Removing secondary record\n", stderr); */ + if (!entry) { + /* The node->row must have lacked some fields of this + index. This is possible when the undo log record was + written before this index was created. */ + return; + } + if (row_purge_remove_sec_if_poss_leaf(node, index, entry)) { return; @@ -454,18 +527,18 @@ retry: } /***********************************************************//** -Purges a delete marking of a record. */ -static -void +Purges a delete marking of a record. +@retval true if the row was not found, or it was successfully removed +@retval false the purge needs to be suspended, either because of +running out of file space or because the row refers to an off-page +column and an online ALTER TABLE (table rebuild) is in progress. */ +static __attribute__((nonnull, warn_unused_result)) +bool row_purge_del_mark( /*===============*/ - purge_node_t* node) /*!< in: row purge node */ + purge_node_t* node) /*!< in/out: row purge node */ { mem_heap_t* heap; - dtuple_t* entry; - dict_index_t* index; - - ut_ad(node); heap = mem_heap_create(1024); @@ -477,13 +550,11 @@ row_purge_del_mark( break; } - index = node->index; - if (node->index->type != DICT_FTS) { - /* Build the index entry */ - entry = row_build_index_entry(node->row, NULL, index, heap); - ut_a(entry); - row_purge_remove_sec_if_poss(node, index, entry); + dtuple_t* entry = row_build_index_entry_low( + node->row, NULL, node->index, heap); + row_purge_remove_sec_if_poss(node, node->index, entry); + mem_heap_empty(heap); } node->index = dict_table_get_next_index(node->index); @@ -491,14 +562,15 @@ row_purge_del_mark( mem_heap_free(heap); - row_purge_remove_clust_if_poss(node); + return(row_purge_remove_clust_if_poss(node)); } /***********************************************************//** Purges an update of an existing record. Also purges an update of a delete -marked record if that record contained an externally stored field. */ -static -void +marked record if that record contained an externally stored field. +@return true if purged, false if skipped */ +static __attribute__((nonnull, warn_unused_result)) +bool row_purge_upd_exist_or_extern_func( /*===============================*/ #ifdef UNIV_DEBUG @@ -508,16 +580,24 @@ row_purge_upd_exist_or_extern_func( trx_undo_rec_t* undo_rec) /*!< in: record to purge */ { mem_heap_t* heap; - dtuple_t* entry; - dict_index_t* index; - ibool is_insert; - ulint rseg_id; - ulint page_no; - ulint offset; - ulint i; - mtr_t mtr; - ut_ad(node); +#ifdef UNIV_SYNC_DEBUG + ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED)); +#endif /* UNIV_SYNC_DEBUG */ + + if (dict_index_get_online_status(dict_table_get_first_index( + node->table)) + == ONLINE_INDEX_CREATION) { + for (ulint i = 0; i < upd_get_n_fields(node->update); i++) { + + const upd_field_t* ufield + = upd_get_nth_field(node->update, i); + + if (dfield_is_ext(&ufield->new_val)) { + return(false); + } + } + } if (node->rec_type == TRX_UNDO_UPD_DEL_REC || (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { @@ -534,15 +614,13 @@ row_purge_upd_exist_or_extern_func( break; } - index = node->index; - if (row_upd_changes_ord_field_binary(node->index, node->update, thr, NULL, NULL)) { /* Build the older version of the index entry */ - entry = row_build_index_entry(node->row, NULL, - index, heap); - ut_a(entry); - row_purge_remove_sec_if_poss(node, index, entry); + dtuple_t* entry = row_build_index_entry_low( + node->row, NULL, node->index, heap); + row_purge_remove_sec_if_poss(node, node->index, entry); + mem_heap_empty(heap); } node->index = dict_table_get_next_index(node->index); @@ -552,7 +630,7 @@ row_purge_upd_exist_or_extern_func( skip_secondaries: /* Free possible externally stored fields */ - for (i = 0; i < upd_get_n_fields(node->update); i++) { + for (ulint i = 0; i < upd_get_n_fields(node->update); i++) { const upd_field_t* ufield = upd_get_nth_field(node->update, i); @@ -562,6 +640,12 @@ skip_secondaries: buf_block_t* block; ulint internal_offset; byte* data_field; + dict_index_t* index; + ibool is_insert; + ulint rseg_id; + ulint page_no; + ulint offset; + mtr_t mtr; /* We use the fact that new_val points to undo_rec and get thus the offset of @@ -590,9 +674,17 @@ skip_secondaries: index tree */ index = dict_table_get_first_index(node->table); - mtr_x_lock(dict_index_get_lock(index), &mtr); - +#ifdef UNIV_DEBUG + switch (dict_index_get_online_status(index)) { + case ONLINE_INDEX_CREATION: + case ONLINE_INDEX_ABORTED_DROPPED: + ut_ad(0); + case ONLINE_INDEX_COMPLETE: + case ONLINE_INDEX_ABORTED: + break; + } +#endif /* UNIV_DEBUG */ /* NOTE: we must also acquire an X-latch to the root page of the tree. We will need it when we free pages from the tree. If the tree is of height 1, @@ -622,6 +714,8 @@ skip_secondaries: mtr_commit(&mtr); } } + + return(true); } #ifdef UNIV_DEBUG @@ -634,14 +728,14 @@ skip_secondaries: /***********************************************************//** Parses the row reference and other info in a modify undo log record. -@return TRUE if purge operation required */ +@return true if purge operation required */ static -ibool +bool row_purge_parse_undo_rec( /*=====================*/ purge_node_t* node, /*!< in: row undo node */ trx_undo_rec_t* undo_rec, /*!< in: record to purge */ - ibool* updated_extern, /*!< out: TRUE if an externally + bool* updated_extern, /*!< out: true if an externally stored field was updated */ que_thr_t* thr) /*!< in: query thread */ { @@ -665,40 +759,29 @@ row_purge_parse_undo_rec( if (type == TRX_UNDO_UPD_DEL_REC && !*updated_extern) { - return(FALSE); + return(false); } ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr, &info_bits); node->table = NULL; - if (type == TRX_UNDO_UPD_EXIST_REC - && node->cmpl_info & UPD_NODE_NO_ORD_CHANGE - && !(*updated_extern)) { - - /* Purge requires no changes to indexes: we may return */ - - return(FALSE); - } - /* Prevent DROP TABLE etc. from running when we are doing the purge for this row */ - rw_lock_s_lock_func(&dict_operation_lock, 0, __FILE__, __LINE__); + rw_lock_s_lock_inline(&dict_operation_lock, 0, __FILE__, __LINE__); - node->table = dict_table_open_on_id(table_id, FALSE); + node->table = dict_table_open_on_id(table_id, FALSE, FALSE); if (node->table == NULL) { -err_exit: /* The table has been dropped: no need to do purge */ - rw_lock_s_unlock_gen(&dict_operation_lock, 0); - return(FALSE); + goto err_exit; } if (node->table->ibd_file_missing) { /* We skip purge of missing .ibd files */ - dict_table_close(node->table, FALSE); + dict_table_close(node->table, FALSE, FALSE); node->table = NULL; @@ -708,12 +791,22 @@ err_exit: clust_index = dict_table_get_first_index(node->table); if (clust_index == NULL) { + /* The table was corrupt in the data dictionary. + dict_set_corrupted() works on an index, and + we do not have an index to call it with. */ +close_exit: + dict_table_close(node->table, FALSE, FALSE); +err_exit: + rw_lock_s_unlock(&dict_operation_lock); + return(false); + } - dict_table_close(node->table, FALSE); - - /* The table was corrupt in the data dictionary */ + if (type == TRX_UNDO_UPD_EXIST_REC + && (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) + && !*updated_extern) { - goto err_exit; + /* Purge requires no changes to indexes: we may return */ + goto close_exit; } ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref), @@ -734,13 +827,14 @@ err_exit: node->heap); } - return(TRUE); + return(true); } /***********************************************************//** -Purges the parsed record. */ -static -void +Purges the parsed record. +@return true if purged, false if skipped */ +static __attribute__((nonnull, warn_unused_result)) +bool row_purge_record_func( /*==================*/ purge_node_t* node, /*!< in: row purge node */ @@ -748,10 +842,11 @@ row_purge_record_func( #ifdef UNIV_DEBUG const que_thr_t*thr, /*!< in: query thread */ #endif /* UNIV_DEBUG */ - ibool updated_extern) /*!< in: TRUE if external columns + bool updated_extern) /*!< in: whether external columns were updated */ { dict_index_t* clust_index; + bool purged = true; clust_index = dict_table_get_first_index(node->table); @@ -759,7 +854,10 @@ row_purge_record_func( switch (node->rec_type) { case TRX_UNDO_DEL_MARK_REC: - row_purge_del_mark(node); + purged = row_purge_del_mark(node); + if (!purged) { + break; + } MONITOR_INC(MONITOR_N_DEL_ROW_PURGE); break; default: @@ -768,20 +866,25 @@ row_purge_record_func( } /* fall through */ case TRX_UNDO_UPD_EXIST_REC: - row_purge_upd_exist_or_extern(thr, node, undo_rec); + purged = row_purge_upd_exist_or_extern(thr, node, undo_rec); + if (!purged) { + break; + } MONITOR_INC(MONITOR_N_UPD_EXIST_EXTERN); break; } if (node->found_clust) { btr_pcur_close(&node->pcur); + node->found_clust = FALSE; } if (node->table != NULL) { - dict_table_close(node->table, FALSE); + dict_table_close(node->table, FALSE, FALSE); node->table = NULL; } + return(purged); } #ifdef UNIV_DEBUG @@ -804,18 +907,24 @@ row_purge( trx_undo_rec_t* undo_rec, /*!< in: record to purge */ que_thr_t* thr) /*!< in: query thread */ { - ut_ad(node); - ut_ad(thr); - if (undo_rec != &trx_purge_dummy_rec) { - ibool updated_extern; + bool updated_extern; - if (row_purge_parse_undo_rec( - node, undo_rec, &updated_extern, thr)) { + while (row_purge_parse_undo_rec( + node, undo_rec, &updated_extern, thr)) { - row_purge_record(node, undo_rec, thr, updated_extern); + bool purged = row_purge_record( + node, undo_rec, thr, updated_extern); + + rw_lock_s_unlock(&dict_operation_lock); + + if (purged + || srv_shutdown_state != SRV_SHUTDOWN_NONE) { + return; + } - rw_lock_s_unlock_gen(&dict_operation_lock, 0); + /* Retry the purge in a second. */ + os_thread_sleep(1000000); } } } |