Skip to content

Commit 2027c48

Browse files
committed
MDEV-32050: Hold exclusive purge_sys.rseg->latch longer
Let the purge_coordinator_task acquire purge_sys.rseg->latch less frequently and hold it longer at a time. This may throttle concurrent DML and prevent purge lag a little. Remove an unnecessary std::this_thread::yield(), because the trx_purge_attach_undo_recs() is supposed to terminate the scan when running out of undo log records. Ultimately, this will result in purge_coordinator_state::do_purge() and purge_coordinator_callback() returning control to the thread pool. Reviewed by: Vladislav Lesin and Vladislav Vaintroub
1 parent 44689eb commit 2027c48

File tree

2 files changed

+93
-105
lines changed

2 files changed

+93
-105
lines changed

storage/innobase/include/trx0purge.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ struct TrxUndoRsegsIterator {
109109
TrxUndoRsegsIterator();
110110
/** Sets the next rseg to purge in purge_sys.
111111
Executed in the purge coordinator thread.
112-
@return whether anything is to be purged */
112+
@retval false when nothing is to be purged
113+
@retval true when purge_sys.rseg->latch was locked */
113114
inline bool set_next();
114115

115116
private:

storage/innobase/trx/trx0purge.cc

Lines changed: 91 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@ TrxUndoRsegsIterator::TrxUndoRsegsIterator()
6666

6767
/** Sets the next rseg to purge in purge_sys.
6868
Executed in the purge coordinator thread.
69-
@return whether anything is to be purged */
70-
TRANSACTIONAL_INLINE inline bool TrxUndoRsegsIterator::set_next()
69+
@retval false when nothing is to be purged
70+
@retval true when purge_sys.rseg->latch was locked */
71+
inline bool TrxUndoRsegsIterator::set_next()
7172
{
73+
ut_ad(!purge_sys.next_stored);
7274
mysql_mutex_lock(&purge_sys.pq_mutex);
7375

7476
/* Only purge consumes events from the priority queue, user
@@ -106,23 +108,10 @@ TRANSACTIONAL_INLINE inline bool TrxUndoRsegsIterator::set_next()
106108
ut_ad(purge_sys.rseg->space->id == TRX_SYS_SPACE
107109
|| srv_is_undo_tablespace(purge_sys.rseg->space->id));
108110

109-
trx_id_t last_trx_no;
110-
{
111-
#ifdef SUX_LOCK_GENERIC
112-
purge_sys.rseg->latch.rd_lock(SRW_LOCK_CALL);
113-
#else
114-
transactional_shared_lock_guard<srw_spin_lock> rg
115-
{purge_sys.rseg->latch};
116-
#endif
117-
last_trx_no = purge_sys.rseg->last_trx_no();
118-
119-
purge_sys.hdr_offset = purge_sys.rseg->last_offset();
120-
purge_sys.hdr_page_no = purge_sys.rseg->last_page_no;
121-
122-
#ifdef SUX_LOCK_GENERIC
123-
purge_sys.rseg->latch.rd_unlock();
124-
#endif
125-
}
111+
purge_sys.rseg->latch.wr_lock(SRW_LOCK_CALL);
112+
trx_id_t last_trx_no = purge_sys.rseg->last_trx_no();
113+
purge_sys.hdr_offset = purge_sys.rseg->last_offset();
114+
purge_sys.hdr_page_no = purge_sys.rseg->last_page_no;
126115

127116
/* Only the purge_coordinator_task will access this object
128117
purge_sys.rseg_iter, or any of purge_sys.hdr_page_no,
@@ -839,18 +828,17 @@ TRANSACTIONAL_TARGET void trx_purge_truncate_history()
839828

840829
/***********************************************************************//**
841830
Updates the last not yet purged history log info in rseg when we have purged
842-
a whole undo log. Advances also purge_sys.purge_trx_no past the purged log. */
843-
static void trx_purge_rseg_get_next_history_log(
844-
ulint* n_pages_handled)/*!< in/out: number of UNDO pages
845-
handled */
831+
a whole undo log. Advances also purge_sys.purge_trx_no past the purged log.
832+
833+
@param n_pages_handled number of UNDO pages handled */
834+
static void trx_purge_rseg_get_next_history_log(ulint *n_pages_handled)
846835
{
847836
fil_addr_t prev_log_addr;
848837
mtr_t mtr;
849838

850839
mtr.start();
851840

852-
purge_sys.rseg->latch.wr_lock(SRW_LOCK_CALL);
853-
841+
ut_ad(purge_sys.rseg->latch.is_write_locked());
854842
ut_a(purge_sys.rseg->last_page_no != FIL_NULL);
855843

856844
purge_sys.tail.trx_no= purge_sys.rseg->last_trx_no() + 1;
@@ -874,50 +862,44 @@ static void trx_purge_rseg_get_next_history_log(
874862
else
875863
prev_log_addr.page= FIL_NULL;
876864

877-
const bool empty= prev_log_addr.page == FIL_NULL;
878-
879-
if (empty)
880-
/* No logs left in the history list */
881-
purge_sys.rseg->last_page_no= FIL_NULL;
882-
883-
purge_sys.rseg->latch.wr_unlock();
884865
mtr.commit();
885866

886-
if (empty)
887-
return;
888-
889-
/* Read the previous log header. */
890-
mtr.start();
891-
892-
trx_id_t trx_no= 0;
893-
894-
if (const buf_block_t* undo_page=
895-
buf_page_get_gen(page_id_t(purge_sys.rseg->space->id, prev_log_addr.page),
896-
0, RW_S_LATCH, nullptr, BUF_GET_POSSIBLY_FREED, &mtr))
867+
if (prev_log_addr.page == FIL_NULL)
868+
purge_sys.rseg->last_page_no= FIL_NULL;
869+
else
897870
{
898-
const byte *log_hdr= undo_page->page.frame + prev_log_addr.boffset;
871+
/* Read the previous log header. */
872+
mtr.start();
899873

900-
trx_no= mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
901-
ut_ad(mach_read_from_2(log_hdr + TRX_UNDO_NEEDS_PURGE) <= 1);
902-
}
874+
trx_id_t trx_no= 0;
875+
if (const buf_block_t* undo_page=
876+
buf_page_get_gen(page_id_t(purge_sys.rseg->space->id,
877+
prev_log_addr.page),
878+
0, RW_S_LATCH, nullptr, BUF_GET_POSSIBLY_FREED, &mtr))
879+
{
880+
const byte *log_hdr= undo_page->page.frame + prev_log_addr.boffset;
881+
trx_no= mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
882+
ut_ad(mach_read_from_2(log_hdr + TRX_UNDO_NEEDS_PURGE) <= 1);
883+
}
903884

904-
mtr.commit();
885+
mtr.commit();
905886

906-
if (UNIV_UNLIKELY(!trx_no))
907-
return;
887+
if (UNIV_LIKELY(trx_no != 0))
888+
{
889+
purge_sys.rseg->last_page_no= prev_log_addr.page;
890+
purge_sys.rseg->set_last_commit(prev_log_addr.boffset, trx_no);
908891

909-
purge_sys.rseg->latch.wr_lock(SRW_LOCK_CALL);
910-
purge_sys.rseg->last_page_no= prev_log_addr.page;
911-
purge_sys.rseg->set_last_commit(prev_log_addr.boffset, trx_no);
892+
/* Purge can also produce events, however these are already
893+
ordered in the rollback segment and any user generated event
894+
will be greater than the events that Purge produces. ie. Purge
895+
can never produce events from an empty rollback segment. */
912896

913-
/* Purge can also produce events, however these are already ordered
914-
in the rollback segment and any user generated event will be greater
915-
than the events that Purge produces. ie. Purge can never produce
916-
events from an empty rollback segment. */
897+
mysql_mutex_lock(&purge_sys.pq_mutex);
898+
purge_sys.purge_queue.push(*purge_sys.rseg);
899+
mysql_mutex_unlock(&purge_sys.pq_mutex);
900+
}
901+
}
917902

918-
mysql_mutex_lock(&purge_sys.pq_mutex);
919-
purge_sys.purge_queue.push(*purge_sys.rseg);
920-
mysql_mutex_unlock(&purge_sys.pq_mutex);
921903
purge_sys.rseg->latch.wr_unlock();
922904
}
923905

@@ -965,16 +947,16 @@ static void trx_purge_read_undo_rec()
965947
Chooses the next undo log to purge and updates the info in purge_sys. This
966948
function is used to initialize purge_sys when the next record to purge is
967949
not known, and also to update the purge system info on the next record when
968-
purge has handled the whole undo log for a transaction. */
969-
TRANSACTIONAL_TARGET static void trx_purge_choose_next_log()
950+
purge has handled the whole undo log for a transaction.
951+
@retval false when nothing is to be purged
952+
@retval true when purge_sys.rseg->latch was locked */
953+
static bool trx_purge_choose_next_log()
970954
{
971-
ut_ad(!purge_sys.next_stored);
972-
973955
if (purge_sys.rseg_iter.set_next()) {
974956
trx_purge_read_undo_rec();
957+
return true;
975958
} else {
976-
/* There is nothing to do yet. */
977-
std::this_thread::yield();
959+
return false;
978960
}
979961
}
980962

@@ -995,9 +977,11 @@ trx_purge_get_next_rec(
995977

996978
ut_ad(purge_sys.next_stored);
997979
ut_ad(purge_sys.tail.trx_no < purge_sys.low_limit_no());
980+
ut_ad(purge_sys.rseg->latch.is_write_locked());
998981

999982
const page_id_t page_id{purge_sys.rseg->space->id, purge_sys.page_no};
1000983
const uint16_t offset = purge_sys.offset;
984+
bool locked = true;
1001985

1002986
if (offset == 0) {
1003987
/* It is the dummy undo log record, which means that there is
@@ -1007,19 +991,24 @@ trx_purge_get_next_rec(
1007991

1008992
/* Look for the next undo log and record to purge */
1009993

1010-
trx_purge_choose_next_log();
994+
if (trx_purge_choose_next_log()) {
995+
purge_sys.rseg->latch.wr_unlock();
996+
}
1011997
return reinterpret_cast<trx_undo_rec_t*>(-1);
1012998
}
1013999

10141000
mtr.start();
1015-
1001+
trx_undo_rec_t *rec_copy = nullptr;
10161002
const buf_block_t* undo_page
10171003
= buf_page_get_gen(page_id, 0, RW_S_LATCH, nullptr,
10181004
BUF_GET_POSSIBLY_FREED, &mtr);
10191005
if (UNIV_UNLIKELY(!undo_page)) {
1020-
corrupted:
1006+
func_exit:
1007+
if (locked) {
1008+
purge_sys.rseg->latch.wr_unlock();
1009+
}
10211010
mtr.commit();
1022-
return nullptr;
1011+
return rec_copy;
10231012
}
10241013

10251014
const buf_block_t* rec2_page = undo_page;
@@ -1040,15 +1029,15 @@ trx_purge_get_next_rec(
10401029

10411030
/* Look for the next undo log and record to purge */
10421031

1043-
trx_purge_choose_next_log();
1032+
locked = trx_purge_choose_next_log();
10441033

10451034
mtr_start(&mtr);
10461035

10471036
undo_page = buf_page_get_gen(page_id, 0, RW_S_LATCH,
10481037
nullptr, BUF_GET_POSSIBLY_FREED,
10491038
&mtr);
10501039
if (UNIV_UNLIKELY(!undo_page)) {
1051-
goto corrupted;
1040+
goto func_exit;
10521041
}
10531042
} else {
10541043
purge_sys.offset = page_offset(rec2);
@@ -1061,11 +1050,8 @@ trx_purge_get_next_rec(
10611050
}
10621051
}
10631052

1064-
trx_undo_rec_t* rec_copy = trx_undo_rec_copy(undo_page->page.frame
1065-
+ offset, heap);
1066-
1067-
mtr.commit();
1068-
return rec_copy;
1053+
rec_copy = trx_undo_rec_copy(undo_page->page.frame + offset, heap);
1054+
goto func_exit;
10691055
}
10701056

10711057
/********************************************************************//**
@@ -1083,34 +1069,35 @@ trx_purge_fetch_next_rec(
10831069
handled */
10841070
mem_heap_t* heap) /*!< in: memory heap where copied */
10851071
{
1086-
if (!purge_sys.next_stored) {
1087-
trx_purge_choose_next_log();
1088-
1089-
if (!purge_sys.next_stored) {
1090-
DBUG_PRINT("ib_purge",
1091-
("no logs left in the history list"));
1092-
return nullptr;
1093-
}
1094-
}
1095-
1096-
if (purge_sys.tail.trx_no >= purge_sys.low_limit_no()) {
1097-
return nullptr;
1098-
}
1099-
1100-
/* fprintf(stderr, "Thread %lu purging trx %llu undo record %llu\n",
1101-
pthread_self(), iter->trx_no, iter->undo_no); */
1102-
1103-
*roll_ptr = trx_undo_build_roll_ptr(
1104-
/* row_purge_record_func() will later set
1105-
ROLL_PTR_INSERT_FLAG for TRX_UNDO_INSERT_REC */
1106-
false,
1107-
trx_sys.rseg_id(purge_sys.rseg, true),
1108-
purge_sys.page_no, purge_sys.offset);
1109-
1110-
/* The following call will advance the stored values of the
1111-
purge iterator. */
1072+
if (!purge_sys.next_stored)
1073+
{
1074+
bool locked= trx_purge_choose_next_log();
1075+
ut_ad(locked == purge_sys.next_stored);
1076+
if (!locked)
1077+
return nullptr;
1078+
if (purge_sys.tail.trx_no >= purge_sys.low_limit_no())
1079+
{
1080+
purge_sys.rseg->latch.wr_unlock();
1081+
return nullptr;
1082+
}
1083+
/* row_purge_record_func() will later set ROLL_PTR_INSERT_FLAG for
1084+
TRX_UNDO_INSERT_REC */
1085+
*roll_ptr= trx_undo_build_roll_ptr(false,
1086+
trx_sys.rseg_id(purge_sys.rseg, true),
1087+
purge_sys.page_no, purge_sys.offset);
1088+
}
1089+
else if (purge_sys.tail.trx_no >= purge_sys.low_limit_no())
1090+
return nullptr;
1091+
else
1092+
{
1093+
*roll_ptr= trx_undo_build_roll_ptr(false,
1094+
trx_sys.rseg_id(purge_sys.rseg, true),
1095+
purge_sys.page_no, purge_sys.offset);
1096+
purge_sys.rseg->latch.wr_lock(SRW_LOCK_CALL);
1097+
}
11121098

1113-
return trx_purge_get_next_rec(n_pages_handled, heap);
1099+
/* The following will advance the purge iterator. */
1100+
return trx_purge_get_next_rec(n_pages_handled, heap);
11141101
}
11151102

11161103
/** Run a purge batch.

0 commit comments

Comments
 (0)