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
UNIV_INTERN 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
UNIV_INTERN 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->zip_size,
267
undo_header = undo_page + undo->hdr_offset;
268
seg_header = undo_page + TRX_UNDO_SEG_HDR;
269
page_header = undo_page + TRX_UNDO_PAGE_HDR;
271
if (undo->state != TRX_UNDO_CACHED) {
272
/* The undo log segment will not be reused */
274
if (undo->id >= TRX_RSEG_N_SLOTS) {
276
"InnoDB: Error: undo->id is %lu\n",
281
trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr);
283
hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
285
ut_ad(undo->size == flst_get_len(
286
seg_header + TRX_UNDO_PAGE_LIST, mtr));
288
mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
289
hist_size + undo->size, MLOG_4BYTES, mtr);
292
/* Add the log as the first in the history list */
293
flst_add_first(rseg_header + TRX_RSEG_HISTORY,
294
undo_header + TRX_UNDO_HISTORY_NODE, mtr);
295
mutex_enter(&kernel_mutex);
296
trx_sys->rseg_history_len++;
297
mutex_exit(&kernel_mutex);
299
/* Write the trx number to the undo log header */
300
mlog_write_dulint(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);
301
/* Write information about delete markings to the undo log header */
303
if (!undo->del_marks) {
304
mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE,
308
if (rseg->last_page_no == FIL_NULL) {
310
rseg->last_page_no = undo->hdr_page_no;
311
rseg->last_offset = undo->hdr_offset;
312
rseg->last_trx_no = trx->no;
313
rseg->last_del_marks = undo->del_marks;
317
/**************************************************************************
318
Frees an undo log segment which is in the history list. Cuts the end of the
319
history list at the youngest undo log in this segment. */
322
trx_purge_free_segment(
323
/*===================*/
324
trx_rseg_t* rseg, /* in: rollback segment */
325
fil_addr_t hdr_addr, /* in: the file address of log_hdr */
326
ulint n_removed_logs) /* in: count of how many undo logs we
327
will cut off from the end of the
331
trx_rsegf_t* rseg_hdr;
332
trx_ulogf_t* log_hdr;
333
trx_usegf_t* seg_hdr;
337
ibool marked = FALSE;
340
/* fputs("Freeing an update undo log segment\n", stderr); */
342
ut_ad(mutex_own(&(purge_sys->mutex)));
345
mutex_enter(&(rseg->mutex));
347
rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
348
rseg->page_no, &mtr);
350
undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
351
hdr_addr.page, &mtr);
352
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
353
log_hdr = undo_page + hdr_addr.boffset;
355
/* Mark the last undo log totally purged, so that if the system
356
crashes, the tail of the undo log will not get accessed again. The
357
list of pages in the undo log tail gets inconsistent during the
358
freeing of the segment, and therefore purge should not try to access
362
mlog_write_ulint(log_hdr + TRX_UNDO_DEL_MARKS, FALSE,
367
freed = fseg_free_step_not_header(seg_hdr + TRX_UNDO_FSEG_HEADER,
370
mutex_exit(&(rseg->mutex));
376
/* The page list may now be inconsistent, but the length field
377
stored in the list base node tells us how big it was before we
378
started the freeing. */
380
seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST, &mtr);
382
/* We may free the undo log segment header page; it must be freed
383
within the same mtr as the undo log header is removed from the
384
history list: otherwise, in case of a database crash, the segment
385
could become inaccessible garbage in the file space. */
387
flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY,
388
log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr);
390
mutex_enter(&kernel_mutex);
391
ut_ad(trx_sys->rseg_history_len >= n_removed_logs);
392
trx_sys->rseg_history_len -= n_removed_logs;
393
mutex_exit(&kernel_mutex);
398
/* Here we assume that a file segment with just the header
399
page can be freed in a few steps, so that the buffer pool
400
is not flooded with bufferfixed pages: see the note in
403
freed = fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER,
407
hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
409
ut_ad(hist_size >= seg_size);
411
mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
412
hist_size - seg_size, MLOG_4BYTES, &mtr);
414
ut_ad(rseg->curr_size >= seg_size);
416
rseg->curr_size -= seg_size;
418
mutex_exit(&(rseg->mutex));
423
/************************************************************************
424
Removes unnecessary history data from a rollback segment. */
427
trx_purge_truncate_rseg_history(
428
/*============================*/
429
trx_rseg_t* rseg, /* in: rollback segment */
430
dulint limit_trx_no, /* in: remove update undo logs whose
431
trx number is < limit_trx_no */
432
dulint limit_undo_no) /* in: if transaction number is equal
433
to limit_trx_no, truncate undo records
434
with undo number < limit_undo_no */
437
fil_addr_t prev_hdr_addr;
438
trx_rsegf_t* rseg_hdr;
440
trx_ulogf_t* log_hdr;
441
trx_usegf_t* seg_hdr;
443
ulint n_removed_logs = 0;
446
ut_ad(mutex_own(&(purge_sys->mutex)));
449
mutex_enter(&(rseg->mutex));
451
rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
452
rseg->page_no, &mtr);
454
hdr_addr = trx_purge_get_log_from_hist(
455
flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
457
if (hdr_addr.page == FIL_NULL) {
459
mutex_exit(&(rseg->mutex));
466
undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
467
hdr_addr.page, &mtr);
469
log_hdr = undo_page + hdr_addr.boffset;
471
cmp = ut_dulint_cmp(mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO),
474
trx_undo_truncate_start(rseg, rseg->space, hdr_addr.page,
475
hdr_addr.boffset, limit_undo_no);
479
mutex_enter(&kernel_mutex);
480
ut_a(trx_sys->rseg_history_len >= n_removed_logs);
481
trx_sys->rseg_history_len -= n_removed_logs;
482
mutex_exit(&kernel_mutex);
484
flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY,
485
log_hdr + TRX_UNDO_HISTORY_NODE,
486
n_removed_logs, &mtr);
488
mutex_exit(&(rseg->mutex));
494
prev_hdr_addr = trx_purge_get_log_from_hist(
495
flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
498
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
500
if ((mach_read_from_2(seg_hdr + TRX_UNDO_STATE) == TRX_UNDO_TO_PURGE)
501
&& (mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG) == 0)) {
503
/* We can free the whole log segment */
505
mutex_exit(&(rseg->mutex));
508
trx_purge_free_segment(rseg, hdr_addr, n_removed_logs);
512
mutex_exit(&(rseg->mutex));
517
mutex_enter(&(rseg->mutex));
519
rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
520
rseg->page_no, &mtr);
522
hdr_addr = prev_hdr_addr;
527
/************************************************************************
528
Removes unnecessary history data from rollback segments. NOTE that when this
529
function is called, the caller must not have any latches on undo log pages! */
532
trx_purge_truncate_history(void)
533
/*============================*/
537
dulint limit_undo_no;
539
ut_ad(mutex_own(&(purge_sys->mutex)));
541
trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no,
544
if (ut_dulint_is_zero(limit_trx_no)) {
546
limit_trx_no = purge_sys->purge_trx_no;
547
limit_undo_no = purge_sys->purge_undo_no;
550
/* We play safe and set the truncate limit at most to the purge view
551
low_limit number, though this is not necessary */
553
if (ut_dulint_cmp(limit_trx_no, purge_sys->view->low_limit_no) >= 0) {
554
limit_trx_no = purge_sys->view->low_limit_no;
555
limit_undo_no = ut_dulint_zero;
558
ut_ad((ut_dulint_cmp(limit_trx_no,
559
purge_sys->view->low_limit_no) <= 0));
561
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
564
trx_purge_truncate_rseg_history(rseg, limit_trx_no,
566
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
570
/************************************************************************
571
Does a truncate if the purge array is empty. NOTE that when this function is
572
called, the caller must not have any latches on undo log pages! */
575
trx_purge_truncate_if_arr_empty(void)
576
/*=================================*/
577
/* out: TRUE if array empty */
579
ut_ad(mutex_own(&(purge_sys->mutex)));
581
if (purge_sys->arr->n_used == 0) {
583
trx_purge_truncate_history();
591
/***************************************************************************
592
Updates the last not yet purged history log info in rseg when we have purged
593
a whole undo log. Advances also purge_sys->purge_trx_no past the purged log. */
596
trx_purge_rseg_get_next_history_log(
597
/*================================*/
598
trx_rseg_t* rseg) /* in: rollback segment */
601
trx_ulogf_t* log_hdr;
602
trx_usegf_t* seg_hdr;
603
fil_addr_t prev_log_addr;
608
ut_ad(mutex_own(&(purge_sys->mutex)));
610
mutex_enter(&(rseg->mutex));
612
ut_a(rseg->last_page_no != FIL_NULL);
614
purge_sys->purge_trx_no = ut_dulint_add(rseg->last_trx_no, 1);
615
purge_sys->purge_undo_no = ut_dulint_zero;
616
purge_sys->next_stored = FALSE;
620
undo_page = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
621
rseg->last_page_no, &mtr);
622
log_hdr = undo_page + rseg->last_offset;
623
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
625
/* Increase the purge page count by one for every handled log */
627
purge_sys->n_pages_handled++;
629
prev_log_addr = trx_purge_get_log_from_hist(
630
flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
631
if (prev_log_addr.page == FIL_NULL) {
632
/* No logs left in the history list */
634
rseg->last_page_no = FIL_NULL;
636
mutex_exit(&(rseg->mutex));
639
mutex_enter(&kernel_mutex);
641
/* Add debug code to track history list corruption reported
642
on the MySQL mailing list on Nov 9, 2004. The fut0lst.c
643
file-based list was corrupt. The prev node pointer was
644
FIL_NULL, even though the list length was over 8 million nodes!
645
We assume that purge truncates the history list in moderate
646
size pieces, and if we here reach the head of the list, the
647
list cannot be longer than 20 000 undo logs now. */
649
if (trx_sys->rseg_history_len > 20000) {
650
ut_print_timestamp(stderr);
652
" InnoDB: Warning: purge reached the"
653
" head of the history list,\n"
654
"InnoDB: but its length is still"
655
" reported as %lu! Make a detailed bug\n"
656
"InnoDB: report, and submit it"
657
" to http://bugs.mysql.com\n",
658
(ulong) trx_sys->rseg_history_len);
661
mutex_exit(&kernel_mutex);
666
mutex_exit(&(rseg->mutex));
669
/* Read the trx number and del marks from the previous log header */
672
log_hdr = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
673
prev_log_addr.page, &mtr)
674
+ prev_log_addr.boffset;
676
trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
678
del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS);
682
mutex_enter(&(rseg->mutex));
684
rseg->last_page_no = prev_log_addr.page;
685
rseg->last_offset = prev_log_addr.boffset;
686
rseg->last_trx_no = trx_no;
687
rseg->last_del_marks = del_marks;
689
mutex_exit(&(rseg->mutex));
692
/***************************************************************************
693
Chooses the next undo log to purge and updates the info in purge_sys. This
694
function is used to initialize purge_sys when the next record to purge is
695
not known, and also to update the purge system info on the next record when
696
purge has handled the whole undo log for a transaction. */
699
trx_purge_choose_next_log(void)
700
/*===========================*/
704
trx_rseg_t* min_rseg;
706
ulint space = 0; /* remove warning (??? bug ???) */
708
ulint page_no = 0; /* remove warning (??? bug ???) */
709
ulint offset = 0; /* remove warning (??? bug ???) */
712
ut_ad(mutex_own(&(purge_sys->mutex)));
713
ut_ad(purge_sys->next_stored == FALSE);
715
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
717
min_trx_no = ut_dulint_max;
722
mutex_enter(&(rseg->mutex));
724
if (rseg->last_page_no != FIL_NULL) {
726
if ((min_rseg == NULL)
727
|| (ut_dulint_cmp(min_trx_no,
728
rseg->last_trx_no) > 0)) {
731
min_trx_no = rseg->last_trx_no;
733
zip_size = rseg->zip_size;
734
ut_a(space == 0); /* We assume in purge of
735
externally stored fields
736
that space id == 0 */
737
page_no = rseg->last_page_no;
738
offset = rseg->last_offset;
742
mutex_exit(&(rseg->mutex));
744
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
747
if (min_rseg == NULL) {
754
if (!min_rseg->last_del_marks) {
755
/* No need to purge this log */
757
rec = &trx_purge_dummy_rec;
759
rec = trx_undo_get_first_rec(space, zip_size, page_no, offset,
764
rec = &trx_purge_dummy_rec;
768
purge_sys->next_stored = TRUE;
769
purge_sys->rseg = min_rseg;
771
purge_sys->hdr_page_no = page_no;
772
purge_sys->hdr_offset = offset;
774
purge_sys->purge_trx_no = min_trx_no;
776
if (rec == &trx_purge_dummy_rec) {
778
purge_sys->purge_undo_no = ut_dulint_zero;
779
purge_sys->page_no = page_no;
780
purge_sys->offset = 0;
782
purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec);
784
purge_sys->page_no = page_get_page_no(page_align(rec));
785
purge_sys->offset = page_offset(rec);
791
/***************************************************************************
792
Gets the next record to purge and updates the info in the purge system. */
795
trx_purge_get_next_rec(
796
/*===================*/
797
/* out: copy of an undo log record or
798
pointer to the dummy undo log record */
799
mem_heap_t* heap) /* in: memory heap where copied */
802
trx_undo_rec_t* rec_copy;
803
trx_undo_rec_t* rec2;
804
trx_undo_rec_t* next_rec;
815
ut_ad(mutex_own(&(purge_sys->mutex)));
816
ut_ad(purge_sys->next_stored);
818
space = purge_sys->rseg->space;
819
zip_size = purge_sys->rseg->zip_size;
820
page_no = purge_sys->page_no;
821
offset = purge_sys->offset;
824
/* It is the dummy undo log record, which means that there is
825
no need to purge this undo log */
827
trx_purge_rseg_get_next_history_log(purge_sys->rseg);
829
/* Look for the next undo log and record to purge */
831
trx_purge_choose_next_log();
833
return(&trx_purge_dummy_rec);
838
undo_page = trx_undo_page_get_s_latched(space, zip_size,
840
rec = undo_page + offset;
845
/* Try first to find the next record which requires a purge
846
operation from the same page of the same undo log */
848
next_rec = trx_undo_page_get_next_rec(rec2,
849
purge_sys->hdr_page_no,
850
purge_sys->hdr_offset);
851
if (next_rec == NULL) {
852
rec2 = trx_undo_get_next_rec(
853
rec2, purge_sys->hdr_page_no,
854
purge_sys->hdr_offset, &mtr);
860
type = trx_undo_rec_get_type(rec2);
862
if (type == TRX_UNDO_DEL_MARK_REC) {
867
cmpl_info = trx_undo_rec_get_cmpl_info(rec2);
869
if (trx_undo_rec_get_extern_storage(rec2)) {
873
if ((type == TRX_UNDO_UPD_EXIST_REC)
874
&& !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
882
trx_purge_rseg_get_next_history_log(purge_sys->rseg);
884
/* Look for the next undo log and record to purge */
886
trx_purge_choose_next_log();
890
undo_page = trx_undo_page_get_s_latched(space, zip_size,
893
rec = undo_page + offset;
895
page = page_align(rec2);
897
purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec2);
898
purge_sys->page_no = page_get_page_no(page);
899
purge_sys->offset = rec2 - page;
901
if (undo_page != page) {
902
/* We advance to a new page of the undo log: */
903
purge_sys->n_pages_handled++;
907
rec_copy = trx_undo_rec_copy(rec, heap);
914
/************************************************************************
915
Fetches the next undo log record from the history list to purge. It must be
916
released with the corresponding release function. */
919
trx_purge_fetch_next_rec(
920
/*=====================*/
921
/* out: copy of an undo log record or
922
pointer to the dummy undo log record
923
&trx_purge_dummy_rec, if the whole undo log
924
can skipped in purge; NULL if none left */
925
dulint* roll_ptr,/* out: roll pointer to undo record */
926
trx_undo_inf_t** cell, /* out: storage cell for the record in the
928
mem_heap_t* heap) /* in: memory heap where copied */
930
trx_undo_rec_t* undo_rec;
932
mutex_enter(&(purge_sys->mutex));
934
if (purge_sys->state == TRX_STOP_PURGE) {
935
trx_purge_truncate_if_arr_empty();
937
mutex_exit(&(purge_sys->mutex));
942
if (!purge_sys->next_stored) {
943
trx_purge_choose_next_log();
945
if (!purge_sys->next_stored) {
946
purge_sys->state = TRX_STOP_PURGE;
948
trx_purge_truncate_if_arr_empty();
950
if (srv_print_thread_releases) {
952
"Purge: No logs left in the"
953
" history list; pages handled %lu\n",
954
(ulong) purge_sys->n_pages_handled);
957
mutex_exit(&(purge_sys->mutex));
963
if (purge_sys->n_pages_handled >= purge_sys->handle_limit) {
965
purge_sys->state = TRX_STOP_PURGE;
967
trx_purge_truncate_if_arr_empty();
969
mutex_exit(&(purge_sys->mutex));
974
if (ut_dulint_cmp(purge_sys->purge_trx_no,
975
purge_sys->view->low_limit_no) >= 0) {
976
purge_sys->state = TRX_STOP_PURGE;
978
trx_purge_truncate_if_arr_empty();
980
mutex_exit(&(purge_sys->mutex));
985
/* fprintf(stderr, "Thread %lu purging trx %lu undo record %lu\n",
986
os_thread_get_curr_id(),
987
ut_dulint_get_low(purge_sys->purge_trx_no),
988
ut_dulint_get_low(purge_sys->purge_undo_no)); */
990
*roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id,
994
*cell = trx_purge_arr_store_info(purge_sys->purge_trx_no,
995
purge_sys->purge_undo_no);
997
ut_ad(ut_dulint_cmp(purge_sys->purge_trx_no,
998
(purge_sys->view)->low_limit_no) < 0);
1000
/* The following call will advance the stored values of purge_trx_no
1001
and purge_undo_no, therefore we had to store them first */
1003
undo_rec = trx_purge_get_next_rec(heap);
1005
mutex_exit(&(purge_sys->mutex));
1010
/***********************************************************************
1011
Releases a reserved purge undo record. */
1014
trx_purge_rec_release(
1015
/*==================*/
1016
trx_undo_inf_t* cell) /* in: storage cell */
1018
trx_undo_arr_t* arr;
1020
mutex_enter(&(purge_sys->mutex));
1022
arr = purge_sys->arr;
1024
trx_purge_arr_remove_info(cell);
1026
mutex_exit(&(purge_sys->mutex));
1029
/***********************************************************************
1030
This function runs a purge batch. */
1035
/* out: number of undo log pages handled in
1039
/* que_thr_t* thr2; */
1040
ulint old_pages_handled;
1042
mutex_enter(&(purge_sys->mutex));
1044
if (purge_sys->trx->n_active_thrs > 0) {
1046
mutex_exit(&(purge_sys->mutex));
1048
/* Should not happen */
1055
rw_lock_x_lock(&(purge_sys->latch));
1057
mutex_enter(&kernel_mutex);
1059
/* Close and free the old purge view */
1061
read_view_close(purge_sys->view);
1062
purge_sys->view = NULL;
1063
mem_heap_empty(purge_sys->heap);
1065
/* Determine how much data manipulation language (DML) statements
1066
need to be delayed in order to reduce the lagging of the purge
1068
srv_dml_needed_delay = 0; /* in microseconds; default: no delay */
1070
/* If we cannot advance the 'purge view' because of an old
1071
'consistent read view', then the DML statements cannot be delayed.
1072
Also, srv_max_purge_lag <= 0 means 'infinity'. */
1073
if (srv_max_purge_lag > 0
1074
&& !UT_LIST_GET_LAST(trx_sys->view_list)) {
1075
float ratio = (float) trx_sys->rseg_history_len
1076
/ srv_max_purge_lag;
1077
if (ratio > ULINT_MAX / 10000) {
1078
/* Avoid overflow: maximum delay is 4295 seconds */
1079
srv_dml_needed_delay = ULINT_MAX;
1080
} else if (ratio > 1) {
1081
/* If the history list length exceeds the
1082
innodb_max_purge_lag, the
1083
data manipulation statements are delayed
1084
by at least 5000 microseconds. */
1085
srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
1089
purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero,
1091
mutex_exit(&kernel_mutex);
1093
rw_lock_x_unlock(&(purge_sys->latch));
1095
purge_sys->state = TRX_PURGE_ON;
1097
/* Handle at most 20 undo log pages in one purge batch */
1099
purge_sys->handle_limit = purge_sys->n_pages_handled + 20;
1101
old_pages_handled = purge_sys->n_pages_handled;
1103
mutex_exit(&(purge_sys->mutex));
1105
mutex_enter(&kernel_mutex);
1107
thr = que_fork_start_command(purge_sys->query);
1111
/* thr2 = que_fork_start_command(purge_sys->query);
1116
mutex_exit(&kernel_mutex);
1118
/* srv_que_task_enqueue(thr2); */
1120
if (srv_print_thread_releases) {
1122
fputs("Starting purge\n", stderr);
1125
que_run_threads(thr);
1127
if (srv_print_thread_releases) {
1130
"Purge ends; pages handled %lu\n",
1131
(ulong) purge_sys->n_pages_handled);
1134
return(purge_sys->n_pages_handled - old_pages_handled);
1137
/**********************************************************************
1138
Prints information of the purge system to stderr. */
1141
trx_purge_sys_print(void)
1142
/*=====================*/
1144
fprintf(stderr, "InnoDB: Purge system view:\n");
1145
read_view_print(purge_sys->view);
1147
fprintf(stderr, "InnoDB: Purge trx n:o " TRX_ID_FMT
1148
", undo n:o " TRX_ID_FMT "\n",
1149
TRX_ID_PREP_PRINTF(purge_sys->purge_trx_no),
1150
TRX_ID_PREP_PRINTF(purge_sys->purge_undo_no));
1152
"InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n"
1153
"InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n",
1154
(ulong) purge_sys->next_stored,
1155
(ulong) purge_sys->page_no,
1156
(ulong) purge_sys->offset,
1157
(ulong) purge_sys->hdr_page_no,
1158
(ulong) purge_sys->hdr_offset);