1
/******************************************************
6
Created 3/26/1996 Heikki Tuuri
7
*******************************************************/
12
#include "trx0purge.ic"
16
#include "mach0data.h"
20
#include "read0read.h"
23
#include "row0purge.h"
27
#include "os0thread.h"
29
/* The global data structure coordinating a purge */
30
trx_purge_t* purge_sys = NULL;
32
/* A dummy undo record used as a return value when we have a whole undo log
33
which needs no purge */
34
trx_undo_rec_t trx_purge_dummy_rec;
36
/*********************************************************************
37
Checks if trx_id is >= purge_view: then it is guaranteed that its update
38
undo log still exists in the system. */
41
trx_purge_update_undo_must_exist(
42
/*=============================*/
43
/* out: TRUE if is sure that it is preserved, also
44
if the function returns FALSE, it is possible that
45
the undo log still exists in the system */
46
dulint trx_id) /* in: transaction id */
48
#ifdef UNIV_SYNC_DEBUG
49
ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
50
#endif /* UNIV_SYNC_DEBUG */
52
if (!read_view_sees_trx_id(purge_sys->view, trx_id)) {
60
/*=================== PURGE RECORD ARRAY =============================*/
62
/***********************************************************************
63
Stores info of an undo log record during a purge. */
66
trx_purge_arr_store_info(
67
/*=====================*/
68
/* out: pointer to the storage cell */
69
dulint trx_no, /* in: transaction number */
70
dulint undo_no)/* in: undo number */
79
cell = trx_undo_arr_get_nth_info(arr, i);
81
if (!(cell->in_use)) {
82
/* Not in use, we may store here */
83
cell->undo_no = undo_no;
84
cell->trx_no = trx_no;
94
/***********************************************************************
95
Removes info of an undo log record during a purge. */
98
trx_purge_arr_remove_info(
99
/*======================*/
100
trx_undo_inf_t* cell) /* in: pointer to the storage cell */
104
arr = purge_sys->arr;
106
cell->in_use = FALSE;
108
ut_ad(arr->n_used > 0);
113
/***********************************************************************
114
Gets the biggest pair of a trx number and an undo number in a purge array. */
117
trx_purge_arr_get_biggest(
118
/*======================*/
119
trx_undo_arr_t* arr, /* in: purge array */
120
dulint* trx_no, /* out: transaction number: ut_dulint_zero
122
dulint* undo_no)/* out: undo number */
124
trx_undo_inf_t* cell;
133
n_used = arr->n_used;
134
pair_trx_no = ut_dulint_zero;
135
pair_undo_no = ut_dulint_zero;
138
cell = trx_undo_arr_get_nth_info(arr, i);
142
trx_cmp = ut_dulint_cmp(cell->trx_no, pair_trx_no);
146
&& (ut_dulint_cmp(cell->undo_no,
147
pair_undo_no) >= 0))) {
149
pair_trx_no = cell->trx_no;
150
pair_undo_no = cell->undo_no;
155
*trx_no = pair_trx_no;
156
*undo_no = pair_undo_no;
163
/********************************************************************
164
Builds a purge 'query' graph. The actual purge is performed by executing
168
trx_purge_graph_build(void)
169
/*=======================*/
170
/* out, own: the query graph */
175
/* que_thr_t* thr2; */
177
heap = mem_heap_create(512);
178
fork = que_fork_create(NULL, NULL, QUE_FORK_PURGE, heap);
179
fork->trx = purge_sys->trx;
181
thr = que_thr_create(fork, heap);
183
thr->child = row_purge_node_create(thr, heap);
185
/* thr2 = que_thr_create(fork, fork, heap);
187
thr2->child = row_purge_node_create(fork, thr2, heap); */
192
/************************************************************************
193
Creates the global purge system control structure and inits the history
197
trx_purge_sys_create(void)
198
/*======================*/
200
ut_ad(mutex_own(&kernel_mutex));
202
purge_sys = mem_alloc(sizeof(trx_purge_t));
204
purge_sys->state = TRX_STOP_PURGE;
206
purge_sys->n_pages_handled = 0;
208
purge_sys->purge_trx_no = ut_dulint_zero;
209
purge_sys->purge_undo_no = ut_dulint_zero;
210
purge_sys->next_stored = FALSE;
212
rw_lock_create(&purge_sys->latch, SYNC_PURGE_LATCH);
214
mutex_create(&purge_sys->mutex, SYNC_PURGE_SYS);
216
purge_sys->heap = mem_heap_create(256);
218
purge_sys->arr = trx_undo_arr_create();
220
purge_sys->sess = sess_open();
222
purge_sys->trx = purge_sys->sess->trx;
224
purge_sys->trx->is_purge = 1;
226
ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED));
228
purge_sys->query = trx_purge_graph_build();
230
purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero,
234
/*================ UNDO LOG HISTORY LIST =============================*/
236
/************************************************************************
237
Adds the update undo log as the first log in the history list. Removes the
238
update undo log segment from the rseg slot if it is too big for reuse. */
241
trx_purge_add_update_undo_to_history(
242
/*=================================*/
243
trx_t* trx, /* in: transaction */
244
page_t* undo_page, /* in: update undo log header page,
246
mtr_t* mtr) /* in: mtr */
250
trx_rsegf_t* rseg_header;
251
trx_usegf_t* seg_header;
252
trx_ulogf_t* undo_header;
253
trx_upagef_t* page_header;
256
undo = trx->update_undo;
262
ut_ad(mutex_own(&(rseg->mutex)));
264
rseg_header = trx_rsegf_get(rseg->space, rseg->page_no, mtr);
266
undo_header = undo_page + undo->hdr_offset;
267
seg_header = undo_page + TRX_UNDO_SEG_HDR;
268
page_header = undo_page + TRX_UNDO_PAGE_HDR;
270
if (undo->state != TRX_UNDO_CACHED) {
271
/* The undo log segment will not be reused */
273
if (undo->id >= TRX_RSEG_N_SLOTS) {
275
"InnoDB: Error: undo->id is %lu\n",
280
trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr);
282
hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
284
ut_ad(undo->size == flst_get_len(
285
seg_header + TRX_UNDO_PAGE_LIST, mtr));
287
mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
288
hist_size + undo->size, MLOG_4BYTES, mtr);
291
/* Add the log as the first in the history list */
292
flst_add_first(rseg_header + TRX_RSEG_HISTORY,
293
undo_header + TRX_UNDO_HISTORY_NODE, mtr);
294
mutex_enter(&kernel_mutex);
295
trx_sys->rseg_history_len++;
296
mutex_exit(&kernel_mutex);
298
/* Write the trx number to the undo log header */
299
mlog_write_dulint(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);
300
/* Write information about delete markings to the undo log header */
302
if (!undo->del_marks) {
303
mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE,
307
if (rseg->last_page_no == FIL_NULL) {
309
rseg->last_page_no = undo->hdr_page_no;
310
rseg->last_offset = undo->hdr_offset;
311
rseg->last_trx_no = trx->no;
312
rseg->last_del_marks = undo->del_marks;
316
/**************************************************************************
317
Frees an undo log segment which is in the history list. Cuts the end of the
318
history list at the youngest undo log in this segment. */
321
trx_purge_free_segment(
322
/*===================*/
323
trx_rseg_t* rseg, /* in: rollback segment */
324
fil_addr_t hdr_addr, /* in: the file address of log_hdr */
325
ulint n_removed_logs) /* in: count of how many undo logs we
326
will cut off from the end of the
330
trx_rsegf_t* rseg_hdr;
331
trx_ulogf_t* log_hdr;
332
trx_usegf_t* seg_hdr;
336
ibool marked = FALSE;
339
/* fputs("Freeing an update undo log segment\n", stderr); */
341
ut_ad(mutex_own(&(purge_sys->mutex)));
344
mutex_enter(&(rseg->mutex));
346
rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
348
undo_page = trx_undo_page_get(rseg->space, hdr_addr.page, &mtr);
349
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
350
log_hdr = undo_page + hdr_addr.boffset;
352
/* Mark the last undo log totally purged, so that if the system
353
crashes, the tail of the undo log will not get accessed again. The
354
list of pages in the undo log tail gets inconsistent during the
355
freeing of the segment, and therefore purge should not try to access
359
mlog_write_ulint(log_hdr + TRX_UNDO_DEL_MARKS, FALSE,
364
freed = fseg_free_step_not_header(seg_hdr + TRX_UNDO_FSEG_HEADER,
367
mutex_exit(&(rseg->mutex));
373
/* The page list may now be inconsistent, but the length field
374
stored in the list base node tells us how big it was before we
375
started the freeing. */
377
seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST, &mtr);
379
/* We may free the undo log segment header page; it must be freed
380
within the same mtr as the undo log header is removed from the
381
history list: otherwise, in case of a database crash, the segment
382
could become inaccessible garbage in the file space. */
384
flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY,
385
log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr);
387
mutex_enter(&kernel_mutex);
388
ut_ad(trx_sys->rseg_history_len >= n_removed_logs);
389
trx_sys->rseg_history_len -= n_removed_logs;
390
mutex_exit(&kernel_mutex);
395
/* Here we assume that a file segment with just the header
396
page can be freed in a few steps, so that the buffer pool
397
is not flooded with bufferfixed pages: see the note in
400
freed = fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER,
404
hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
406
ut_ad(hist_size >= seg_size);
408
mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
409
hist_size - seg_size, MLOG_4BYTES, &mtr);
411
ut_ad(rseg->curr_size >= seg_size);
413
rseg->curr_size -= seg_size;
415
mutex_exit(&(rseg->mutex));
420
/************************************************************************
421
Removes unnecessary history data from a rollback segment. */
424
trx_purge_truncate_rseg_history(
425
/*============================*/
426
trx_rseg_t* rseg, /* in: rollback segment */
427
dulint limit_trx_no, /* in: remove update undo logs whose
428
trx number is < limit_trx_no */
429
dulint limit_undo_no) /* in: if transaction number is equal
430
to limit_trx_no, truncate undo records
431
with undo number < limit_undo_no */
434
fil_addr_t prev_hdr_addr;
435
trx_rsegf_t* rseg_hdr;
437
trx_ulogf_t* log_hdr;
438
trx_usegf_t* seg_hdr;
440
ulint n_removed_logs = 0;
443
ut_ad(mutex_own(&(purge_sys->mutex)));
446
mutex_enter(&(rseg->mutex));
448
rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
450
hdr_addr = trx_purge_get_log_from_hist(
451
flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
453
if (hdr_addr.page == FIL_NULL) {
455
mutex_exit(&(rseg->mutex));
462
undo_page = trx_undo_page_get(rseg->space, hdr_addr.page, &mtr);
464
log_hdr = undo_page + hdr_addr.boffset;
466
cmp = ut_dulint_cmp(mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO),
469
trx_undo_truncate_start(rseg, rseg->space, hdr_addr.page,
470
hdr_addr.boffset, limit_undo_no);
474
mutex_enter(&kernel_mutex);
475
ut_a(trx_sys->rseg_history_len >= n_removed_logs);
476
trx_sys->rseg_history_len -= n_removed_logs;
477
mutex_exit(&kernel_mutex);
479
flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY,
480
log_hdr + TRX_UNDO_HISTORY_NODE,
481
n_removed_logs, &mtr);
483
mutex_exit(&(rseg->mutex));
489
prev_hdr_addr = trx_purge_get_log_from_hist(
490
flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
493
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
495
if ((mach_read_from_2(seg_hdr + TRX_UNDO_STATE) == TRX_UNDO_TO_PURGE)
496
&& (mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG) == 0)) {
498
/* We can free the whole log segment */
500
mutex_exit(&(rseg->mutex));
503
trx_purge_free_segment(rseg, hdr_addr, n_removed_logs);
507
mutex_exit(&(rseg->mutex));
512
mutex_enter(&(rseg->mutex));
514
rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
516
hdr_addr = prev_hdr_addr;
521
/************************************************************************
522
Removes unnecessary history data from rollback segments. NOTE that when this
523
function is called, the caller must not have any latches on undo log pages! */
526
trx_purge_truncate_history(void)
527
/*============================*/
531
dulint limit_undo_no;
533
ut_ad(mutex_own(&(purge_sys->mutex)));
535
trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no,
538
if (ut_dulint_cmp(limit_trx_no, ut_dulint_zero) == 0) {
540
limit_trx_no = purge_sys->purge_trx_no;
541
limit_undo_no = purge_sys->purge_undo_no;
544
/* We play safe and set the truncate limit at most to the purge view
545
low_limit number, though this is not necessary */
547
if (ut_dulint_cmp(limit_trx_no, purge_sys->view->low_limit_no) >= 0) {
548
limit_trx_no = purge_sys->view->low_limit_no;
549
limit_undo_no = ut_dulint_zero;
552
ut_ad((ut_dulint_cmp(limit_trx_no,
553
purge_sys->view->low_limit_no) <= 0));
555
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
558
trx_purge_truncate_rseg_history(rseg, limit_trx_no,
560
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
564
/************************************************************************
565
Does a truncate if the purge array is empty. NOTE that when this function is
566
called, the caller must not have any latches on undo log pages! */
569
trx_purge_truncate_if_arr_empty(void)
570
/*=================================*/
571
/* out: TRUE if array empty */
573
ut_ad(mutex_own(&(purge_sys->mutex)));
575
if (purge_sys->arr->n_used == 0) {
577
trx_purge_truncate_history();
585
/***************************************************************************
586
Updates the last not yet purged history log info in rseg when we have purged
587
a whole undo log. Advances also purge_sys->purge_trx_no past the purged log. */
590
trx_purge_rseg_get_next_history_log(
591
/*================================*/
592
trx_rseg_t* rseg) /* in: rollback segment */
595
trx_ulogf_t* log_hdr;
596
trx_usegf_t* seg_hdr;
597
fil_addr_t prev_log_addr;
602
ut_ad(mutex_own(&(purge_sys->mutex)));
604
mutex_enter(&(rseg->mutex));
606
ut_a(rseg->last_page_no != FIL_NULL);
608
purge_sys->purge_trx_no = ut_dulint_add(rseg->last_trx_no, 1);
609
purge_sys->purge_undo_no = ut_dulint_zero;
610
purge_sys->next_stored = FALSE;
614
undo_page = trx_undo_page_get_s_latched(rseg->space,
615
rseg->last_page_no, &mtr);
616
log_hdr = undo_page + rseg->last_offset;
617
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
619
/* Increase the purge page count by one for every handled log */
621
purge_sys->n_pages_handled++;
623
prev_log_addr = trx_purge_get_log_from_hist(
624
flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
625
if (prev_log_addr.page == FIL_NULL) {
626
/* No logs left in the history list */
628
rseg->last_page_no = FIL_NULL;
630
mutex_exit(&(rseg->mutex));
633
mutex_enter(&kernel_mutex);
635
/* Add debug code to track history list corruption reported
636
on the MySQL mailing list on Nov 9, 2004. The fut0lst.c
637
file-based list was corrupt. The prev node pointer was
638
FIL_NULL, even though the list length was over 8 million nodes!
639
We assume that purge truncates the history list in moderate
640
size pieces, and if we here reach the head of the list, the
641
list cannot be longer than 20 000 undo logs now. */
643
if (trx_sys->rseg_history_len > 20000) {
644
ut_print_timestamp(stderr);
646
" InnoDB: Warning: purge reached the"
647
" head of the history list,\n"
648
"InnoDB: but its length is still"
649
" reported as %lu! Make a detailed bug\n"
650
"InnoDB: report, and submit it"
651
" to http://bugs.mysql.com\n",
652
(ulong) trx_sys->rseg_history_len);
655
mutex_exit(&kernel_mutex);
660
mutex_exit(&(rseg->mutex));
663
/* Read the trx number and del marks from the previous log header */
666
log_hdr = trx_undo_page_get_s_latched(rseg->space,
667
prev_log_addr.page, &mtr)
668
+ prev_log_addr.boffset;
670
trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
672
del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS);
676
mutex_enter(&(rseg->mutex));
678
rseg->last_page_no = prev_log_addr.page;
679
rseg->last_offset = prev_log_addr.boffset;
680
rseg->last_trx_no = trx_no;
681
rseg->last_del_marks = del_marks;
683
mutex_exit(&(rseg->mutex));
686
/***************************************************************************
687
Chooses the next undo log to purge and updates the info in purge_sys. This
688
function is used to initialize purge_sys when the next record to purge is
689
not known, and also to update the purge system info on the next record when
690
purge has handled the whole undo log for a transaction. */
693
trx_purge_choose_next_log(void)
694
/*===========================*/
698
trx_rseg_t* min_rseg;
700
ulint space = 0; /* remove warning (??? bug ???) */
701
ulint page_no = 0; /* remove warning (??? bug ???) */
702
ulint offset = 0; /* remove warning (??? bug ???) */
705
ut_ad(mutex_own(&(purge_sys->mutex)));
706
ut_ad(purge_sys->next_stored == FALSE);
708
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
710
min_trx_no = ut_dulint_max;
715
mutex_enter(&(rseg->mutex));
717
if (rseg->last_page_no != FIL_NULL) {
719
if ((min_rseg == NULL)
720
|| (ut_dulint_cmp(min_trx_no,
721
rseg->last_trx_no) > 0)) {
724
min_trx_no = rseg->last_trx_no;
726
ut_a(space == 0); /* We assume in purge of
727
externally stored fields
728
that space id == 0 */
729
page_no = rseg->last_page_no;
730
offset = rseg->last_offset;
734
mutex_exit(&(rseg->mutex));
736
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
739
if (min_rseg == NULL) {
746
if (!min_rseg->last_del_marks) {
747
/* No need to purge this log */
749
rec = &trx_purge_dummy_rec;
751
rec = trx_undo_get_first_rec(space, page_no, offset,
756
rec = &trx_purge_dummy_rec;
760
purge_sys->next_stored = TRUE;
761
purge_sys->rseg = min_rseg;
763
purge_sys->hdr_page_no = page_no;
764
purge_sys->hdr_offset = offset;
766
purge_sys->purge_trx_no = min_trx_no;
768
if (rec == &trx_purge_dummy_rec) {
770
purge_sys->purge_undo_no = ut_dulint_zero;
771
purge_sys->page_no = page_no;
772
purge_sys->offset = 0;
774
purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec);
776
purge_sys->page_no = buf_frame_get_page_no(rec);
777
purge_sys->offset = rec - buf_frame_align(rec);
783
/***************************************************************************
784
Gets the next record to purge and updates the info in the purge system. */
787
trx_purge_get_next_rec(
788
/*===================*/
789
/* out: copy of an undo log record or
790
pointer to the dummy undo log record */
791
mem_heap_t* heap) /* in: memory heap where copied */
794
trx_undo_rec_t* rec_copy;
795
trx_undo_rec_t* rec2;
796
trx_undo_rec_t* next_rec;
806
ut_ad(mutex_own(&(purge_sys->mutex)));
807
ut_ad(purge_sys->next_stored);
809
space = purge_sys->rseg->space;
810
page_no = purge_sys->page_no;
811
offset = purge_sys->offset;
814
/* It is the dummy undo log record, which means that there is
815
no need to purge this undo log */
817
trx_purge_rseg_get_next_history_log(purge_sys->rseg);
819
/* Look for the next undo log and record to purge */
821
trx_purge_choose_next_log();
823
return(&trx_purge_dummy_rec);
828
undo_page = trx_undo_page_get_s_latched(space, page_no, &mtr);
829
rec = undo_page + offset;
834
/* Try first to find the next record which requires a purge
835
operation from the same page of the same undo log */
837
next_rec = trx_undo_page_get_next_rec(rec2,
838
purge_sys->hdr_page_no,
839
purge_sys->hdr_offset);
840
if (next_rec == NULL) {
841
rec2 = trx_undo_get_next_rec(
842
rec2, purge_sys->hdr_page_no,
843
purge_sys->hdr_offset, &mtr);
849
type = trx_undo_rec_get_type(rec2);
851
if (type == TRX_UNDO_DEL_MARK_REC) {
856
cmpl_info = trx_undo_rec_get_cmpl_info(rec2);
858
if (trx_undo_rec_get_extern_storage(rec2)) {
862
if ((type == TRX_UNDO_UPD_EXIST_REC)
863
&& !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
871
trx_purge_rseg_get_next_history_log(purge_sys->rseg);
873
/* Look for the next undo log and record to purge */
875
trx_purge_choose_next_log();
879
undo_page = trx_undo_page_get_s_latched(space, page_no, &mtr);
881
rec = undo_page + offset;
883
page = buf_frame_align(rec2);
885
purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec2);
886
purge_sys->page_no = buf_frame_get_page_no(page);
887
purge_sys->offset = rec2 - page;
889
if (undo_page != page) {
890
/* We advance to a new page of the undo log: */
891
purge_sys->n_pages_handled++;
895
rec_copy = trx_undo_rec_copy(rec, heap);
902
/************************************************************************
903
Fetches the next undo log record from the history list to purge. It must be
904
released with the corresponding release function. */
907
trx_purge_fetch_next_rec(
908
/*=====================*/
909
/* out: copy of an undo log record or
910
pointer to the dummy undo log record
911
&trx_purge_dummy_rec, if the whole undo log
912
can skipped in purge; NULL if none left */
913
dulint* roll_ptr,/* out: roll pointer to undo record */
914
trx_undo_inf_t** cell, /* out: storage cell for the record in the
916
mem_heap_t* heap) /* in: memory heap where copied */
918
trx_undo_rec_t* undo_rec;
920
mutex_enter(&(purge_sys->mutex));
922
if (purge_sys->state == TRX_STOP_PURGE) {
923
trx_purge_truncate_if_arr_empty();
925
mutex_exit(&(purge_sys->mutex));
930
if (!purge_sys->next_stored) {
931
trx_purge_choose_next_log();
933
if (!purge_sys->next_stored) {
934
purge_sys->state = TRX_STOP_PURGE;
936
trx_purge_truncate_if_arr_empty();
938
if (srv_print_thread_releases) {
940
"Purge: No logs left in the"
941
" history list; pages handled %lu\n",
942
(ulong) purge_sys->n_pages_handled);
945
mutex_exit(&(purge_sys->mutex));
951
if (purge_sys->n_pages_handled >= purge_sys->handle_limit) {
953
purge_sys->state = TRX_STOP_PURGE;
955
trx_purge_truncate_if_arr_empty();
957
mutex_exit(&(purge_sys->mutex));
962
if (ut_dulint_cmp(purge_sys->purge_trx_no,
963
purge_sys->view->low_limit_no) >= 0) {
964
purge_sys->state = TRX_STOP_PURGE;
966
trx_purge_truncate_if_arr_empty();
968
mutex_exit(&(purge_sys->mutex));
973
/* fprintf(stderr, "Thread %lu purging trx %lu undo record %lu\n",
974
os_thread_get_curr_id(),
975
ut_dulint_get_low(purge_sys->purge_trx_no),
976
ut_dulint_get_low(purge_sys->purge_undo_no)); */
978
*roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id,
982
*cell = trx_purge_arr_store_info(purge_sys->purge_trx_no,
983
purge_sys->purge_undo_no);
985
ut_ad(ut_dulint_cmp(purge_sys->purge_trx_no,
986
(purge_sys->view)->low_limit_no) < 0);
988
/* The following call will advance the stored values of purge_trx_no
989
and purge_undo_no, therefore we had to store them first */
991
undo_rec = trx_purge_get_next_rec(heap);
993
mutex_exit(&(purge_sys->mutex));
998
/***********************************************************************
999
Releases a reserved purge undo record. */
1002
trx_purge_rec_release(
1003
/*==================*/
1004
trx_undo_inf_t* cell) /* in: storage cell */
1006
trx_undo_arr_t* arr;
1008
mutex_enter(&(purge_sys->mutex));
1010
arr = purge_sys->arr;
1012
trx_purge_arr_remove_info(cell);
1014
mutex_exit(&(purge_sys->mutex));
1017
/***********************************************************************
1018
This function runs a purge batch. */
1023
/* out: number of undo log pages handled in
1027
/* que_thr_t* thr2; */
1028
ulint old_pages_handled;
1030
mutex_enter(&(purge_sys->mutex));
1032
if (purge_sys->trx->n_active_thrs > 0) {
1034
mutex_exit(&(purge_sys->mutex));
1036
/* Should not happen */
1043
rw_lock_x_lock(&(purge_sys->latch));
1045
mutex_enter(&kernel_mutex);
1047
/* Close and free the old purge view */
1049
read_view_close(purge_sys->view);
1050
purge_sys->view = NULL;
1051
mem_heap_empty(purge_sys->heap);
1053
/* Determine how much data manipulation language (DML) statements
1054
need to be delayed in order to reduce the lagging of the purge
1056
srv_dml_needed_delay = 0; /* in microseconds; default: no delay */
1058
/* If we cannot advance the 'purge view' because of an old
1059
'consistent read view', then the DML statements cannot be delayed.
1060
Also, srv_max_purge_lag <= 0 means 'infinity'. */
1061
if (srv_max_purge_lag > 0
1062
&& !UT_LIST_GET_LAST(trx_sys->view_list)) {
1063
float ratio = (float) trx_sys->rseg_history_len
1064
/ srv_max_purge_lag;
1065
if (ratio > ULINT_MAX / 10000) {
1066
/* Avoid overflow: maximum delay is 4295 seconds */
1067
srv_dml_needed_delay = ULINT_MAX;
1068
} else if (ratio > 1) {
1069
/* If the history list length exceeds the
1070
innodb_max_purge_lag, the
1071
data manipulation statements are delayed
1072
by at least 5000 microseconds. */
1073
srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
1077
purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero,
1079
mutex_exit(&kernel_mutex);
1081
rw_lock_x_unlock(&(purge_sys->latch));
1083
purge_sys->state = TRX_PURGE_ON;
1085
/* Handle at most 20 undo log pages in one purge batch */
1087
purge_sys->handle_limit = purge_sys->n_pages_handled + 20;
1089
old_pages_handled = purge_sys->n_pages_handled;
1091
mutex_exit(&(purge_sys->mutex));
1093
mutex_enter(&kernel_mutex);
1095
thr = que_fork_start_command(purge_sys->query);
1099
/* thr2 = que_fork_start_command(purge_sys->query);
1104
mutex_exit(&kernel_mutex);
1106
/* srv_que_task_enqueue(thr2); */
1108
if (srv_print_thread_releases) {
1110
fputs("Starting purge\n", stderr);
1113
que_run_threads(thr);
1115
if (srv_print_thread_releases) {
1118
"Purge ends; pages handled %lu\n",
1119
(ulong) purge_sys->n_pages_handled);
1122
return(purge_sys->n_pages_handled - old_pages_handled);
1125
/**********************************************************************
1126
Prints information of the purge system to stderr. */
1129
trx_purge_sys_print(void)
1130
/*=====================*/
1132
fprintf(stderr, "InnoDB: Purge system view:\n");
1133
read_view_print(purge_sys->view);
1135
fprintf(stderr, "InnoDB: Purge trx n:o %lu %lu, undo n_o %lu %lu\n",
1136
(ulong) ut_dulint_get_high(purge_sys->purge_trx_no),
1137
(ulong) ut_dulint_get_low(purge_sys->purge_trx_no),
1138
(ulong) ut_dulint_get_high(purge_sys->purge_undo_no),
1139
(ulong) ut_dulint_get_low(purge_sys->purge_undo_no));
1141
"InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n"
1142
"InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n",
1143
(ulong) purge_sys->next_stored,
1144
(ulong) purge_sys->page_no,
1145
(ulong) purge_sys->offset,
1146
(ulong) purge_sys->hdr_page_no,
1147
(ulong) purge_sys->hdr_offset);