1
/*****************************************************************************
3
Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved.
5
This program is free software; you can redistribute it and/or modify it under
6
the terms of the GNU General Public License as published by the Free Software
7
Foundation; version 2 of the License.
9
This program is distributed in the hope that it will be useful, but WITHOUT
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
You should have received a copy of the GNU General Public License along with
14
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
15
St, Fifth Floor, Boston, MA 02110-1301 USA
17
*****************************************************************************/
19
/**************************************************//**
23
Created 3/26/1996 Heikki Tuuri
24
*******************************************************/
26
#include "trx0purge.h"
29
#include "trx0purge.ic"
33
#include "mach0data.h"
38
#include "read0read.h"
41
#include "row0purge.h"
45
#include "os0thread.h"
47
/** The global data structure coordinating a purge */
48
UNIV_INTERN trx_purge_t* purge_sys = NULL;
50
/** A dummy undo record used as a return value when we have a whole undo log
51
which needs no purge */
52
UNIV_INTERN trx_undo_rec_t trx_purge_dummy_rec;
54
#ifdef UNIV_PFS_RWLOCK
55
/* Key to register trx_purge_latch with performance schema */
56
UNIV_INTERN mysql_pfs_key_t trx_purge_latch_key;
57
#endif /* UNIV_PFS_RWLOCK */
60
/* Key to register purge_sys_mutex with performance schema */
61
UNIV_INTERN mysql_pfs_key_t purge_sys_mutex_key;
62
#endif /* UNIV_PFS_MUTEX */
64
/*****************************************************************//**
65
Checks if trx_id is >= purge_view: then it is guaranteed that its update
66
undo log still exists in the system.
67
@return TRUE if is sure that it is preserved, also if the function
68
returns FALSE, it is possible that the undo log still exists in the
72
trx_purge_update_undo_must_exist(
73
/*=============================*/
74
trx_id_t trx_id) /*!< in: transaction id */
76
#ifdef UNIV_SYNC_DEBUG
77
ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
78
#endif /* UNIV_SYNC_DEBUG */
80
if (!read_view_sees_trx_id(purge_sys->view, trx_id)) {
88
/*=================== PURGE RECORD ARRAY =============================*/
90
/*******************************************************************//**
91
Stores info of an undo log record during a purge.
92
@return pointer to the storage cell */
95
trx_purge_arr_store_info(
96
/*=====================*/
97
trx_id_t trx_no, /*!< in: transaction number */
98
undo_no_t undo_no)/*!< in: undo number */
100
trx_undo_inf_t* cell;
104
arr = purge_sys->arr;
107
cell = trx_undo_arr_get_nth_info(arr, i);
109
if (!(cell->in_use)) {
110
/* Not in use, we may store here */
111
cell->undo_no = undo_no;
112
cell->trx_no = trx_no;
122
/*******************************************************************//**
123
Removes info of an undo log record during a purge. */
126
trx_purge_arr_remove_info(
127
/*======================*/
128
trx_undo_inf_t* cell) /*!< in: pointer to the storage cell */
132
arr = purge_sys->arr;
134
cell->in_use = FALSE;
136
ut_ad(arr->n_used > 0);
141
/*******************************************************************//**
142
Gets the biggest pair of a trx number and an undo number in a purge array. */
145
trx_purge_arr_get_biggest(
146
/*======================*/
147
trx_undo_arr_t* arr, /*!< in: purge array */
148
trx_id_t* trx_no, /*!< out: transaction number: 0
150
undo_no_t* undo_no)/*!< out: undo number */
152
trx_undo_inf_t* cell;
153
trx_id_t pair_trx_no;
154
undo_no_t pair_undo_no;
164
cell = trx_undo_arr_get_nth_info(arr, i);
170
if ((cell->trx_no > pair_trx_no)
171
|| ((cell->trx_no == pair_trx_no)
172
&& cell->undo_no >= pair_undo_no)) {
174
pair_trx_no = cell->trx_no;
175
pair_undo_no = cell->undo_no;
184
*trx_no = pair_trx_no;
185
*undo_no = pair_undo_no;
188
/****************************************************************//**
189
Builds a purge 'query' graph. The actual purge is performed by executing
191
@return own: the query graph */
194
trx_purge_graph_build(void)
195
/*=======================*/
200
/* que_thr_t* thr2; */
202
heap = mem_heap_create(512);
203
fork = que_fork_create(NULL, NULL, QUE_FORK_PURGE, heap);
204
fork->trx = purge_sys->trx;
206
thr = que_thr_create(fork, heap);
208
thr->child = row_purge_node_create(thr, heap);
210
/* thr2 = que_thr_create(fork, fork, heap);
212
thr2->child = row_purge_node_create(fork, thr2, heap); */
217
/********************************************************************//**
218
Creates the global purge system control structure and inits the history
222
trx_purge_sys_create(void)
223
/*======================*/
225
ut_ad(mutex_own(&kernel_mutex));
227
purge_sys = mem_alloc(sizeof(trx_purge_t));
229
purge_sys->state = TRX_STOP_PURGE;
231
purge_sys->n_pages_handled = 0;
233
purge_sys->purge_trx_no = 0;
234
purge_sys->purge_undo_no = 0;
235
purge_sys->next_stored = FALSE;
237
rw_lock_create(trx_purge_latch_key,
238
&purge_sys->latch, SYNC_PURGE_LATCH);
240
mutex_create(purge_sys_mutex_key,
241
&purge_sys->mutex, SYNC_PURGE_SYS);
243
purge_sys->heap = mem_heap_create(256);
245
purge_sys->arr = trx_undo_arr_create();
247
purge_sys->sess = sess_open();
249
purge_sys->trx = purge_sys->sess->trx;
251
purge_sys->trx->is_purge = 1;
253
ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED));
255
purge_sys->query = trx_purge_graph_build();
257
purge_sys->view = read_view_oldest_copy_or_open_new(0,
261
/************************************************************************
262
Frees the global purge system control structure. */
265
trx_purge_sys_close(void)
266
/*======================*/
268
ut_ad(!mutex_own(&kernel_mutex));
270
que_graph_free(purge_sys->query);
272
ut_a(purge_sys->sess->trx->is_purge);
273
purge_sys->sess->trx->conc_state = TRX_NOT_STARTED;
274
sess_close(purge_sys->sess);
275
purge_sys->sess = NULL;
277
if (purge_sys->view != NULL) {
278
/* Because acquiring the kernel mutex is a pre-condition
279
of read_view_close(). We don't really need it here. */
280
mutex_enter(&kernel_mutex);
282
read_view_close(purge_sys->view);
283
purge_sys->view = NULL;
285
mutex_exit(&kernel_mutex);
288
trx_undo_arr_free(purge_sys->arr);
290
rw_lock_free(&purge_sys->latch);
291
mutex_free(&purge_sys->mutex);
293
mem_heap_free(purge_sys->heap);
299
/*================ UNDO LOG HISTORY LIST =============================*/
301
/********************************************************************//**
302
Adds the update undo log as the first log in the history list. Removes the
303
update undo log segment from the rseg slot if it is too big for reuse. */
306
trx_purge_add_update_undo_to_history(
307
/*=================================*/
308
trx_t* trx, /*!< in: transaction */
309
page_t* undo_page, /*!< in: update undo log header page,
311
mtr_t* mtr) /*!< in: mtr */
315
trx_rsegf_t* rseg_header;
317
trx_usegf_t* seg_header;
318
#endif /* UNIV_DEBUG */
319
trx_ulogf_t* undo_header;
322
undo = trx->update_undo;
328
ut_ad(mutex_own(&(rseg->mutex)));
330
rseg_header = trx_rsegf_get(rseg->space, rseg->zip_size,
333
undo_header = undo_page + undo->hdr_offset;
335
seg_header = undo_page + TRX_UNDO_SEG_HDR;
336
#endif /* UNIV_DEBUG */
338
if (undo->state != TRX_UNDO_CACHED) {
339
/* The undo log segment will not be reused */
341
if (undo->id >= TRX_RSEG_N_SLOTS) {
343
"InnoDB: Error: undo->id is %lu\n",
348
trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr);
350
hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
352
ut_ad(undo->size == flst_get_len(
353
seg_header + TRX_UNDO_PAGE_LIST, mtr));
355
mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
356
hist_size + undo->size, MLOG_4BYTES, mtr);
359
/* Add the log as the first in the history list */
360
flst_add_first(rseg_header + TRX_RSEG_HISTORY,
361
undo_header + TRX_UNDO_HISTORY_NODE, mtr);
362
mutex_enter(&kernel_mutex);
363
trx_sys->rseg_history_len++;
364
mutex_exit(&kernel_mutex);
366
if (!(trx_sys->rseg_history_len % srv_purge_batch_size)) {
367
/* Inform the purge thread that there is work to do. */
368
srv_wake_purge_thread_if_not_active();
371
/* Write the trx number to the undo log header */
372
mlog_write_ull(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);
373
/* Write information about delete markings to the undo log header */
375
if (!undo->del_marks) {
376
mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE,
380
if (rseg->last_page_no == FIL_NULL) {
382
rseg->last_page_no = undo->hdr_page_no;
383
rseg->last_offset = undo->hdr_offset;
384
rseg->last_trx_no = trx->no;
385
rseg->last_del_marks = undo->del_marks;
389
/**********************************************************************//**
390
Frees an undo log segment which is in the history list. Cuts the end of the
391
history list at the youngest undo log in this segment. */
394
trx_purge_free_segment(
395
/*===================*/
396
trx_rseg_t* rseg, /*!< in: rollback segment */
397
fil_addr_t hdr_addr, /*!< in: the file address of log_hdr */
398
ulint n_removed_logs) /*!< in: count of how many undo logs we
399
will cut off from the end of the
403
trx_rsegf_t* rseg_hdr;
404
trx_ulogf_t* log_hdr;
405
trx_usegf_t* seg_hdr;
409
ibool marked = FALSE;
412
/* fputs("Freeing an update undo log segment\n", stderr); */
414
ut_ad(mutex_own(&(purge_sys->mutex)));
417
mutex_enter(&(rseg->mutex));
419
rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
420
rseg->page_no, &mtr);
422
undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
423
hdr_addr.page, &mtr);
424
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
425
log_hdr = undo_page + hdr_addr.boffset;
427
/* Mark the last undo log totally purged, so that if the system
428
crashes, the tail of the undo log will not get accessed again. The
429
list of pages in the undo log tail gets inconsistent during the
430
freeing of the segment, and therefore purge should not try to access
434
mlog_write_ulint(log_hdr + TRX_UNDO_DEL_MARKS, FALSE,
439
freed = fseg_free_step_not_header(seg_hdr + TRX_UNDO_FSEG_HEADER,
442
mutex_exit(&(rseg->mutex));
448
/* The page list may now be inconsistent, but the length field
449
stored in the list base node tells us how big it was before we
450
started the freeing. */
452
seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST, &mtr);
454
/* We may free the undo log segment header page; it must be freed
455
within the same mtr as the undo log header is removed from the
456
history list: otherwise, in case of a database crash, the segment
457
could become inaccessible garbage in the file space. */
459
flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY,
460
log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr);
462
mutex_enter(&kernel_mutex);
463
ut_ad(trx_sys->rseg_history_len >= n_removed_logs);
464
trx_sys->rseg_history_len -= n_removed_logs;
465
mutex_exit(&kernel_mutex);
470
/* Here we assume that a file segment with just the header
471
page can be freed in a few steps, so that the buffer pool
472
is not flooded with bufferfixed pages: see the note in
475
freed = fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER,
479
hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
481
ut_ad(hist_size >= seg_size);
483
mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
484
hist_size - seg_size, MLOG_4BYTES, &mtr);
486
ut_ad(rseg->curr_size >= seg_size);
488
rseg->curr_size -= seg_size;
490
mutex_exit(&(rseg->mutex));
495
/********************************************************************//**
496
Removes unnecessary history data from a rollback segment. */
499
trx_purge_truncate_rseg_history(
500
/*============================*/
501
trx_rseg_t* rseg, /*!< in: rollback segment */
502
trx_id_t limit_trx_no, /*!< in: remove update undo logs whose
503
trx number is < limit_trx_no */
504
undo_no_t limit_undo_no) /*!< in: if transaction number is equal
505
to limit_trx_no, truncate undo records
506
with undo number < limit_undo_no */
509
fil_addr_t prev_hdr_addr;
510
trx_rsegf_t* rseg_hdr;
512
trx_ulogf_t* log_hdr;
513
trx_usegf_t* seg_hdr;
514
ulint n_removed_logs = 0;
516
trx_id_t undo_trx_no;
518
ut_ad(mutex_own(&(purge_sys->mutex)));
521
mutex_enter(&(rseg->mutex));
523
rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
524
rseg->page_no, &mtr);
526
hdr_addr = trx_purge_get_log_from_hist(
527
flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
529
if (hdr_addr.page == FIL_NULL) {
531
mutex_exit(&(rseg->mutex));
538
undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
539
hdr_addr.page, &mtr);
541
log_hdr = undo_page + hdr_addr.boffset;
542
undo_trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
544
if (undo_trx_no >= limit_trx_no) {
545
if (undo_trx_no == limit_trx_no) {
546
trx_undo_truncate_start(rseg, rseg->space,
552
mutex_enter(&kernel_mutex);
553
ut_a(trx_sys->rseg_history_len >= n_removed_logs);
554
trx_sys->rseg_history_len -= n_removed_logs;
555
mutex_exit(&kernel_mutex);
557
flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY,
558
log_hdr + TRX_UNDO_HISTORY_NODE,
559
n_removed_logs, &mtr);
561
mutex_exit(&(rseg->mutex));
567
prev_hdr_addr = trx_purge_get_log_from_hist(
568
flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
571
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
573
if ((mach_read_from_2(seg_hdr + TRX_UNDO_STATE) == TRX_UNDO_TO_PURGE)
574
&& (mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG) == 0)) {
576
/* We can free the whole log segment */
578
mutex_exit(&(rseg->mutex));
581
trx_purge_free_segment(rseg, hdr_addr, n_removed_logs);
585
mutex_exit(&(rseg->mutex));
590
mutex_enter(&(rseg->mutex));
592
rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
593
rseg->page_no, &mtr);
595
hdr_addr = prev_hdr_addr;
600
/********************************************************************//**
601
Removes unnecessary history data from rollback segments. NOTE that when this
602
function is called, the caller must not have any latches on undo log pages! */
605
trx_purge_truncate_history(void)
606
/*============================*/
609
trx_id_t limit_trx_no;
610
undo_no_t limit_undo_no;
612
ut_ad(mutex_own(&(purge_sys->mutex)));
614
trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no,
617
if (limit_trx_no == 0) {
619
limit_trx_no = purge_sys->purge_trx_no;
620
limit_undo_no = purge_sys->purge_undo_no;
623
/* We play safe and set the truncate limit at most to the purge view
624
low_limit number, though this is not necessary */
626
if (limit_trx_no >= purge_sys->view->low_limit_no) {
627
limit_trx_no = purge_sys->view->low_limit_no;
631
ut_ad(limit_trx_no <= purge_sys->view->low_limit_no);
633
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
636
trx_purge_truncate_rseg_history(rseg, limit_trx_no,
638
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
642
/********************************************************************//**
643
Does a truncate if the purge array is empty. NOTE that when this function is
644
called, the caller must not have any latches on undo log pages!
645
@return TRUE if array empty */
648
trx_purge_truncate_if_arr_empty(void)
649
/*=================================*/
651
ut_ad(mutex_own(&(purge_sys->mutex)));
653
if (purge_sys->arr->n_used == 0) {
655
trx_purge_truncate_history();
663
/***********************************************************************//**
664
Updates the last not yet purged history log info in rseg when we have purged
665
a whole undo log. Advances also purge_sys->purge_trx_no past the purged log. */
668
trx_purge_rseg_get_next_history_log(
669
/*================================*/
670
trx_rseg_t* rseg) /*!< in: rollback segment */
673
trx_ulogf_t* log_hdr;
674
fil_addr_t prev_log_addr;
679
ut_ad(mutex_own(&(purge_sys->mutex)));
681
mutex_enter(&(rseg->mutex));
683
ut_a(rseg->last_page_no != FIL_NULL);
685
purge_sys->purge_trx_no = rseg->last_trx_no + 1;
686
purge_sys->purge_undo_no = 0;
687
purge_sys->next_stored = FALSE;
691
undo_page = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
692
rseg->last_page_no, &mtr);
693
log_hdr = undo_page + rseg->last_offset;
695
/* Increase the purge page count by one for every handled log */
697
purge_sys->n_pages_handled++;
699
prev_log_addr = trx_purge_get_log_from_hist(
700
flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
701
if (prev_log_addr.page == FIL_NULL) {
702
/* No logs left in the history list */
704
rseg->last_page_no = FIL_NULL;
706
mutex_exit(&(rseg->mutex));
709
mutex_enter(&kernel_mutex);
711
/* Add debug code to track history list corruption reported
712
on the MySQL mailing list on Nov 9, 2004. The fut0lst.c
713
file-based list was corrupt. The prev node pointer was
714
FIL_NULL, even though the list length was over 8 million nodes!
715
We assume that purge truncates the history list in moderate
716
size pieces, and if we here reach the head of the list, the
717
list cannot be longer than 20 000 undo logs now. */
719
if (trx_sys->rseg_history_len > 20000) {
720
ut_print_timestamp(stderr);
722
" InnoDB: Warning: purge reached the"
723
" head of the history list,\n"
724
"InnoDB: but its length is still"
725
" reported as %lu! Make a detailed bug\n"
726
"InnoDB: report, and submit it"
727
" to http://bugs.mysql.com\n",
728
(ulong) trx_sys->rseg_history_len);
731
mutex_exit(&kernel_mutex);
736
mutex_exit(&(rseg->mutex));
739
/* Read the trx number and del marks from the previous log header */
742
log_hdr = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
743
prev_log_addr.page, &mtr)
744
+ prev_log_addr.boffset;
746
trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
748
del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS);
752
mutex_enter(&(rseg->mutex));
754
rseg->last_page_no = prev_log_addr.page;
755
rseg->last_offset = prev_log_addr.boffset;
756
rseg->last_trx_no = trx_no;
757
rseg->last_del_marks = del_marks;
759
mutex_exit(&(rseg->mutex));
762
/***********************************************************************//**
763
Chooses the next undo log to purge and updates the info in purge_sys. This
764
function is used to initialize purge_sys when the next record to purge is
765
not known, and also to update the purge system info on the next record when
766
purge has handled the whole undo log for a transaction. */
769
trx_purge_choose_next_log(void)
770
/*===========================*/
774
trx_rseg_t* min_rseg;
776
ulint space = 0; /* remove warning (??? bug ???) */
778
ulint page_no = 0; /* remove warning (??? bug ???) */
779
ulint offset = 0; /* remove warning (??? bug ???) */
782
ut_ad(mutex_own(&(purge_sys->mutex)));
783
ut_ad(purge_sys->next_stored == FALSE);
785
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
787
min_trx_no = IB_ULONGLONG_MAX;
792
mutex_enter(&(rseg->mutex));
794
if (rseg->last_page_no != FIL_NULL) {
797
|| min_trx_no > rseg->last_trx_no) {
800
min_trx_no = rseg->last_trx_no;
802
zip_size = rseg->zip_size;
803
ut_a(space == 0); /* We assume in purge of
804
externally stored fields
805
that space id == 0 */
806
page_no = rseg->last_page_no;
807
offset = rseg->last_offset;
811
mutex_exit(&(rseg->mutex));
813
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
816
if (min_rseg == NULL) {
823
if (!min_rseg->last_del_marks) {
824
/* No need to purge this log */
826
rec = &trx_purge_dummy_rec;
828
rec = trx_undo_get_first_rec(space, zip_size, page_no, offset,
833
rec = &trx_purge_dummy_rec;
837
purge_sys->next_stored = TRUE;
838
purge_sys->rseg = min_rseg;
840
purge_sys->hdr_page_no = page_no;
841
purge_sys->hdr_offset = offset;
843
purge_sys->purge_trx_no = min_trx_no;
845
if (rec == &trx_purge_dummy_rec) {
847
purge_sys->purge_undo_no = 0;
848
purge_sys->page_no = page_no;
849
purge_sys->offset = 0;
851
purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec);
853
purge_sys->page_no = page_get_page_no(page_align(rec));
854
purge_sys->offset = page_offset(rec);
860
/***********************************************************************//**
861
Gets the next record to purge and updates the info in the purge system.
862
@return copy of an undo log record or pointer to the dummy undo log record */
865
trx_purge_get_next_rec(
866
/*===================*/
867
mem_heap_t* heap) /*!< in: memory heap where copied */
870
trx_undo_rec_t* rec_copy;
871
trx_undo_rec_t* rec2;
872
trx_undo_rec_t* next_rec;
883
ut_ad(mutex_own(&(purge_sys->mutex)));
884
ut_ad(purge_sys->next_stored);
886
space = purge_sys->rseg->space;
887
zip_size = purge_sys->rseg->zip_size;
888
page_no = purge_sys->page_no;
889
offset = purge_sys->offset;
892
/* It is the dummy undo log record, which means that there is
893
no need to purge this undo log */
895
trx_purge_rseg_get_next_history_log(purge_sys->rseg);
897
/* Look for the next undo log and record to purge */
899
trx_purge_choose_next_log();
901
return(&trx_purge_dummy_rec);
906
undo_page = trx_undo_page_get_s_latched(space, zip_size,
908
rec = undo_page + offset;
913
/* Try first to find the next record which requires a purge
914
operation from the same page of the same undo log */
916
next_rec = trx_undo_page_get_next_rec(rec2,
917
purge_sys->hdr_page_no,
918
purge_sys->hdr_offset);
919
if (next_rec == NULL) {
920
rec2 = trx_undo_get_next_rec(
921
rec2, purge_sys->hdr_page_no,
922
purge_sys->hdr_offset, &mtr);
928
type = trx_undo_rec_get_type(rec2);
930
if (type == TRX_UNDO_DEL_MARK_REC) {
935
cmpl_info = trx_undo_rec_get_cmpl_info(rec2);
937
if (trx_undo_rec_get_extern_storage(rec2)) {
941
if ((type == TRX_UNDO_UPD_EXIST_REC)
942
&& !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
950
trx_purge_rseg_get_next_history_log(purge_sys->rseg);
952
/* Look for the next undo log and record to purge */
954
trx_purge_choose_next_log();
958
undo_page = trx_undo_page_get_s_latched(space, zip_size,
961
rec = undo_page + offset;
963
page = page_align(rec2);
965
purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec2);
966
purge_sys->page_no = page_get_page_no(page);
967
purge_sys->offset = rec2 - page;
969
if (undo_page != page) {
970
/* We advance to a new page of the undo log: */
971
purge_sys->n_pages_handled++;
975
rec_copy = trx_undo_rec_copy(rec, heap);
982
/********************************************************************//**
983
Fetches the next undo log record from the history list to purge. It must be
984
released with the corresponding release function.
985
@return copy of an undo log record or pointer to trx_purge_dummy_rec,
986
if the whole undo log can skipped in purge; NULL if none left */
989
trx_purge_fetch_next_rec(
990
/*=====================*/
991
roll_ptr_t* roll_ptr,/*!< out: roll pointer to undo record */
992
trx_undo_inf_t** cell, /*!< out: storage cell for the record in the
994
mem_heap_t* heap) /*!< in: memory heap where copied */
996
trx_undo_rec_t* undo_rec;
998
mutex_enter(&(purge_sys->mutex));
1000
if (purge_sys->state == TRX_STOP_PURGE) {
1001
trx_purge_truncate_if_arr_empty();
1003
mutex_exit(&(purge_sys->mutex));
1008
if (!purge_sys->next_stored) {
1009
trx_purge_choose_next_log();
1011
if (!purge_sys->next_stored) {
1012
purge_sys->state = TRX_STOP_PURGE;
1014
trx_purge_truncate_if_arr_empty();
1016
if (srv_print_thread_releases) {
1018
"Purge: No logs left in the"
1019
" history list; pages handled %lu\n",
1020
(ulong) purge_sys->n_pages_handled);
1023
mutex_exit(&(purge_sys->mutex));
1029
if (purge_sys->n_pages_handled >= purge_sys->handle_limit) {
1031
purge_sys->state = TRX_STOP_PURGE;
1033
trx_purge_truncate_if_arr_empty();
1035
mutex_exit(&(purge_sys->mutex));
1040
if (purge_sys->purge_trx_no >= purge_sys->view->low_limit_no) {
1041
purge_sys->state = TRX_STOP_PURGE;
1043
trx_purge_truncate_if_arr_empty();
1045
mutex_exit(&(purge_sys->mutex));
1050
/* fprintf(stderr, "Thread %lu purging trx %llu undo record %llu\n",
1051
os_thread_get_curr_id(),
1052
(ullint) purge_sys->purge_trx_no,
1053
(ullint) purge_sys->purge_undo_no); */
1055
*roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id,
1059
*cell = trx_purge_arr_store_info(purge_sys->purge_trx_no,
1060
purge_sys->purge_undo_no);
1062
ut_ad(purge_sys->purge_trx_no < purge_sys->view->low_limit_no);
1064
/* The following call will advance the stored values of purge_trx_no
1065
and purge_undo_no, therefore we had to store them first */
1067
undo_rec = trx_purge_get_next_rec(heap);
1069
mutex_exit(&(purge_sys->mutex));
1074
/*******************************************************************//**
1075
Releases a reserved purge undo record. */
1078
trx_purge_rec_release(
1079
/*==================*/
1080
trx_undo_inf_t* cell) /*!< in: storage cell */
1082
mutex_enter(&(purge_sys->mutex));
1084
trx_purge_arr_remove_info(cell);
1086
mutex_exit(&(purge_sys->mutex));
1089
/*******************************************************************//**
1090
This function runs a purge batch.
1091
@return number of undo log pages handled in the batch */
1096
ulint limit) /*!< in: the maximum number of records to
1097
purge in one batch */
1100
/* que_thr_t* thr2; */
1101
ulint old_pages_handled;
1103
mutex_enter(&(purge_sys->mutex));
1105
if (purge_sys->trx->n_active_thrs > 0) {
1107
mutex_exit(&(purge_sys->mutex));
1109
/* Should not happen */
1116
rw_lock_x_lock(&(purge_sys->latch));
1118
mutex_enter(&kernel_mutex);
1120
/* Close and free the old purge view */
1122
read_view_close(purge_sys->view);
1123
purge_sys->view = NULL;
1124
mem_heap_empty(purge_sys->heap);
1126
/* Determine how much data manipulation language (DML) statements
1127
need to be delayed in order to reduce the lagging of the purge
1129
srv_dml_needed_delay = 0; /* in microseconds; default: no delay */
1131
/* If we cannot advance the 'purge view' because of an old
1132
'consistent read view', then the DML statements cannot be delayed.
1133
Also, srv_max_purge_lag <= 0 means 'infinity'. */
1134
if (srv_max_purge_lag > 0
1135
&& !UT_LIST_GET_LAST(trx_sys->view_list)) {
1136
float ratio = (float) trx_sys->rseg_history_len
1137
/ srv_max_purge_lag;
1138
if (ratio > ULINT_MAX / 10000) {
1139
/* Avoid overflow: maximum delay is 4295 seconds */
1140
srv_dml_needed_delay = ULINT_MAX;
1141
} else if (ratio > 1) {
1142
/* If the history list length exceeds the
1143
innodb_max_purge_lag, the
1144
data manipulation statements are delayed
1145
by at least 5000 microseconds. */
1146
srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
1150
purge_sys->view = read_view_oldest_copy_or_open_new(0,
1152
mutex_exit(&kernel_mutex);
1154
rw_lock_x_unlock(&(purge_sys->latch));
1156
purge_sys->state = TRX_PURGE_ON;
1158
purge_sys->handle_limit = purge_sys->n_pages_handled + limit;
1160
old_pages_handled = purge_sys->n_pages_handled;
1162
mutex_exit(&(purge_sys->mutex));
1164
mutex_enter(&kernel_mutex);
1166
thr = que_fork_start_command(purge_sys->query);
1170
/* thr2 = que_fork_start_command(purge_sys->query);
1175
mutex_exit(&kernel_mutex);
1177
/* srv_que_task_enqueue(thr2); */
1179
if (srv_print_thread_releases) {
1181
fputs("Starting purge\n", stderr);
1184
que_run_threads(thr);
1186
if (srv_print_thread_releases) {
1189
"Purge ends; pages handled %lu\n",
1190
(ulong) purge_sys->n_pages_handled);
1193
return(purge_sys->n_pages_handled - old_pages_handled);
1196
/******************************************************************//**
1197
Prints information of the purge system to stderr. */
1200
trx_purge_sys_print(void)
1201
/*=====================*/
1203
fprintf(stderr, "InnoDB: Purge system view:\n");
1204
read_view_print(purge_sys->view);
1206
fprintf(stderr, "InnoDB: Purge trx n:o " TRX_ID_FMT
1207
", undo n:o " TRX_ID_FMT "\n",
1208
purge_sys->purge_trx_no,
1209
purge_sys->purge_undo_no);
1211
"InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n"
1212
"InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n",
1213
(ulong) purge_sys->next_stored,
1214
(ulong) purge_sys->page_no,
1215
(ulong) purge_sys->offset,
1216
(ulong) purge_sys->hdr_page_no,
1217
(ulong) purge_sys->hdr_offset);