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
/*****************************************************************//**
55
Checks if trx_id is >= purge_view: then it is guaranteed that its update
56
undo log still exists in the system.
57
@return TRUE if is sure that it is preserved, also if the function
58
returns FALSE, it is possible that the undo log still exists in the
62
trx_purge_update_undo_must_exist(
63
/*=============================*/
64
trx_id_t trx_id) /*!< in: transaction id */
66
#ifdef UNIV_SYNC_DEBUG
67
ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
68
#endif /* UNIV_SYNC_DEBUG */
70
if (!read_view_sees_trx_id(purge_sys->view, trx_id)) {
78
/*=================== PURGE RECORD ARRAY =============================*/
80
/*******************************************************************//**
81
Stores info of an undo log record during a purge.
82
@return pointer to the storage cell */
85
trx_purge_arr_store_info(
86
/*=====================*/
87
trx_id_t trx_no, /*!< in: transaction number */
88
undo_no_t undo_no)/*!< in: undo number */
97
cell = trx_undo_arr_get_nth_info(arr, i);
99
if (!(cell->in_use)) {
100
/* Not in use, we may store here */
101
cell->undo_no = undo_no;
102
cell->trx_no = trx_no;
112
/*******************************************************************//**
113
Removes info of an undo log record during a purge. */
116
trx_purge_arr_remove_info(
117
/*======================*/
118
trx_undo_inf_t* cell) /*!< in: pointer to the storage cell */
122
arr = purge_sys->arr;
124
cell->in_use = FALSE;
126
ut_ad(arr->n_used > 0);
131
/*******************************************************************//**
132
Gets the biggest pair of a trx number and an undo number in a purge array. */
135
trx_purge_arr_get_biggest(
136
/*======================*/
137
trx_undo_arr_t* arr, /*!< in: purge array */
138
trx_id_t* trx_no, /*!< out: transaction number: ut_dulint_zero
140
undo_no_t* undo_no)/*!< out: undo number */
142
trx_undo_inf_t* cell;
143
trx_id_t pair_trx_no;
144
undo_no_t pair_undo_no;
151
n_used = arr->n_used;
152
pair_trx_no = ut_dulint_zero;
153
pair_undo_no = ut_dulint_zero;
156
cell = trx_undo_arr_get_nth_info(arr, i);
160
trx_cmp = ut_dulint_cmp(cell->trx_no, pair_trx_no);
164
&& (ut_dulint_cmp(cell->undo_no,
165
pair_undo_no) >= 0))) {
167
pair_trx_no = cell->trx_no;
168
pair_undo_no = cell->undo_no;
173
*trx_no = pair_trx_no;
174
*undo_no = pair_undo_no;
181
/****************************************************************//**
182
Builds a purge 'query' graph. The actual purge is performed by executing
184
@return own: the query graph */
187
trx_purge_graph_build(void)
188
/*=======================*/
193
/* que_thr_t* thr2; */
195
heap = mem_heap_create(512);
196
fork = que_fork_create(NULL, NULL, QUE_FORK_PURGE, heap);
197
fork->trx = purge_sys->trx;
199
thr = que_thr_create(fork, heap);
201
thr->child = row_purge_node_create(thr, heap);
203
/* thr2 = que_thr_create(fork, fork, heap);
205
thr2->child = row_purge_node_create(fork, thr2, heap); */
210
/********************************************************************//**
211
Creates the global purge system control structure and inits the history
215
trx_purge_sys_create(void)
216
/*======================*/
218
ut_ad(mutex_own(&kernel_mutex));
220
purge_sys = mem_alloc(sizeof(trx_purge_t));
222
purge_sys->state = TRX_STOP_PURGE;
224
purge_sys->n_pages_handled = 0;
226
purge_sys->purge_trx_no = ut_dulint_zero;
227
purge_sys->purge_undo_no = ut_dulint_zero;
228
purge_sys->next_stored = FALSE;
230
rw_lock_create(&purge_sys->latch, SYNC_PURGE_LATCH);
232
mutex_create(&purge_sys->mutex, SYNC_PURGE_SYS);
234
purge_sys->heap = mem_heap_create(256);
236
purge_sys->arr = trx_undo_arr_create();
238
purge_sys->sess = sess_open();
240
purge_sys->trx = purge_sys->sess->trx;
242
purge_sys->trx->is_purge = 1;
244
ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED));
246
purge_sys->query = trx_purge_graph_build();
248
purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero,
252
/************************************************************************
253
Frees the global purge system control structure. */
256
trx_purge_sys_close(void)
257
/*======================*/
259
ut_ad(!mutex_own(&kernel_mutex));
261
que_graph_free(purge_sys->query);
263
ut_a(purge_sys->sess->trx->is_purge);
264
purge_sys->sess->trx->conc_state = TRX_NOT_STARTED;
265
sess_close(purge_sys->sess);
266
purge_sys->sess = NULL;
268
if (purge_sys->view != NULL) {
269
/* Because acquiring the kernel mutex is a pre-condition
270
of read_view_close(). We don't really need it here. */
271
mutex_enter(&kernel_mutex);
273
read_view_close(purge_sys->view);
274
purge_sys->view = NULL;
276
mutex_exit(&kernel_mutex);
279
trx_undo_arr_free(purge_sys->arr);
281
rw_lock_free(&purge_sys->latch);
282
mutex_free(&purge_sys->mutex);
284
mem_heap_free(purge_sys->heap);
290
/*================ UNDO LOG HISTORY LIST =============================*/
292
/********************************************************************//**
293
Adds the update undo log as the first log in the history list. Removes the
294
update undo log segment from the rseg slot if it is too big for reuse. */
297
trx_purge_add_update_undo_to_history(
298
/*=================================*/
299
trx_t* trx, /*!< in: transaction */
300
page_t* undo_page, /*!< in: update undo log header page,
302
mtr_t* mtr) /*!< in: mtr */
306
trx_rsegf_t* rseg_header;
307
trx_usegf_t* seg_header;
308
trx_ulogf_t* undo_header;
309
trx_upagef_t* page_header;
312
undo = trx->update_undo;
318
ut_ad(mutex_own(&(rseg->mutex)));
320
rseg_header = trx_rsegf_get(rseg->space, rseg->zip_size,
323
undo_header = undo_page + undo->hdr_offset;
324
seg_header = undo_page + TRX_UNDO_SEG_HDR;
325
page_header = undo_page + TRX_UNDO_PAGE_HDR;
327
if (undo->state != TRX_UNDO_CACHED) {
328
/* The undo log segment will not be reused */
330
if (undo->id >= TRX_RSEG_N_SLOTS) {
332
"InnoDB: Error: undo->id is %lu\n",
337
trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr);
339
hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
341
ut_ad(undo->size == flst_get_len(
342
seg_header + TRX_UNDO_PAGE_LIST, mtr));
344
mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
345
hist_size + undo->size, MLOG_4BYTES, mtr);
348
/* Add the log as the first in the history list */
349
flst_add_first(rseg_header + TRX_RSEG_HISTORY,
350
undo_header + TRX_UNDO_HISTORY_NODE, mtr);
351
mutex_enter(&kernel_mutex);
352
trx_sys->rseg_history_len++;
353
mutex_exit(&kernel_mutex);
355
/* Write the trx number to the undo log header */
356
mlog_write_dulint(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);
357
/* Write information about delete markings to the undo log header */
359
if (!undo->del_marks) {
360
mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE,
364
if (rseg->last_page_no == FIL_NULL) {
366
rseg->last_page_no = undo->hdr_page_no;
367
rseg->last_offset = undo->hdr_offset;
368
rseg->last_trx_no = trx->no;
369
rseg->last_del_marks = undo->del_marks;
373
/**********************************************************************//**
374
Frees an undo log segment which is in the history list. Cuts the end of the
375
history list at the youngest undo log in this segment. */
378
trx_purge_free_segment(
379
/*===================*/
380
trx_rseg_t* rseg, /*!< in: rollback segment */
381
fil_addr_t hdr_addr, /*!< in: the file address of log_hdr */
382
ulint n_removed_logs) /*!< in: count of how many undo logs we
383
will cut off from the end of the
387
trx_rsegf_t* rseg_hdr;
388
trx_ulogf_t* log_hdr;
389
trx_usegf_t* seg_hdr;
393
ibool marked = FALSE;
396
/* fputs("Freeing an update undo log segment\n", stderr); */
398
ut_ad(mutex_own(&(purge_sys->mutex)));
401
mutex_enter(&(rseg->mutex));
403
rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
404
rseg->page_no, &mtr);
406
undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
407
hdr_addr.page, &mtr);
408
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
409
log_hdr = undo_page + hdr_addr.boffset;
411
/* Mark the last undo log totally purged, so that if the system
412
crashes, the tail of the undo log will not get accessed again. The
413
list of pages in the undo log tail gets inconsistent during the
414
freeing of the segment, and therefore purge should not try to access
418
mlog_write_ulint(log_hdr + TRX_UNDO_DEL_MARKS, FALSE,
423
freed = fseg_free_step_not_header(seg_hdr + TRX_UNDO_FSEG_HEADER,
426
mutex_exit(&(rseg->mutex));
432
/* The page list may now be inconsistent, but the length field
433
stored in the list base node tells us how big it was before we
434
started the freeing. */
436
seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST, &mtr);
438
/* We may free the undo log segment header page; it must be freed
439
within the same mtr as the undo log header is removed from the
440
history list: otherwise, in case of a database crash, the segment
441
could become inaccessible garbage in the file space. */
443
flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY,
444
log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr);
446
mutex_enter(&kernel_mutex);
447
ut_ad(trx_sys->rseg_history_len >= n_removed_logs);
448
trx_sys->rseg_history_len -= n_removed_logs;
449
mutex_exit(&kernel_mutex);
454
/* Here we assume that a file segment with just the header
455
page can be freed in a few steps, so that the buffer pool
456
is not flooded with bufferfixed pages: see the note in
459
freed = fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER,
463
hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
465
ut_ad(hist_size >= seg_size);
467
mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
468
hist_size - seg_size, MLOG_4BYTES, &mtr);
470
ut_ad(rseg->curr_size >= seg_size);
472
rseg->curr_size -= seg_size;
474
mutex_exit(&(rseg->mutex));
479
/********************************************************************//**
480
Removes unnecessary history data from a rollback segment. */
483
trx_purge_truncate_rseg_history(
484
/*============================*/
485
trx_rseg_t* rseg, /*!< in: rollback segment */
486
trx_id_t limit_trx_no, /*!< in: remove update undo logs whose
487
trx number is < limit_trx_no */
488
undo_no_t limit_undo_no) /*!< in: if transaction number is equal
489
to limit_trx_no, truncate undo records
490
with undo number < limit_undo_no */
493
fil_addr_t prev_hdr_addr;
494
trx_rsegf_t* rseg_hdr;
496
trx_ulogf_t* log_hdr;
497
trx_usegf_t* seg_hdr;
499
ulint n_removed_logs = 0;
502
ut_ad(mutex_own(&(purge_sys->mutex)));
505
mutex_enter(&(rseg->mutex));
507
rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
508
rseg->page_no, &mtr);
510
hdr_addr = trx_purge_get_log_from_hist(
511
flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
513
if (hdr_addr.page == FIL_NULL) {
515
mutex_exit(&(rseg->mutex));
522
undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
523
hdr_addr.page, &mtr);
525
log_hdr = undo_page + hdr_addr.boffset;
527
cmp = ut_dulint_cmp(mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO),
530
trx_undo_truncate_start(rseg, rseg->space, hdr_addr.page,
531
hdr_addr.boffset, limit_undo_no);
535
mutex_enter(&kernel_mutex);
536
ut_a(trx_sys->rseg_history_len >= n_removed_logs);
537
trx_sys->rseg_history_len -= n_removed_logs;
538
mutex_exit(&kernel_mutex);
540
flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY,
541
log_hdr + TRX_UNDO_HISTORY_NODE,
542
n_removed_logs, &mtr);
544
mutex_exit(&(rseg->mutex));
550
prev_hdr_addr = trx_purge_get_log_from_hist(
551
flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
554
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
556
if ((mach_read_from_2(seg_hdr + TRX_UNDO_STATE) == TRX_UNDO_TO_PURGE)
557
&& (mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG) == 0)) {
559
/* We can free the whole log segment */
561
mutex_exit(&(rseg->mutex));
564
trx_purge_free_segment(rseg, hdr_addr, n_removed_logs);
568
mutex_exit(&(rseg->mutex));
573
mutex_enter(&(rseg->mutex));
575
rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
576
rseg->page_no, &mtr);
578
hdr_addr = prev_hdr_addr;
583
/********************************************************************//**
584
Removes unnecessary history data from rollback segments. NOTE that when this
585
function is called, the caller must not have any latches on undo log pages! */
588
trx_purge_truncate_history(void)
589
/*============================*/
592
trx_id_t limit_trx_no;
593
undo_no_t limit_undo_no;
595
ut_ad(mutex_own(&(purge_sys->mutex)));
597
trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no,
600
if (ut_dulint_is_zero(limit_trx_no)) {
602
limit_trx_no = purge_sys->purge_trx_no;
603
limit_undo_no = purge_sys->purge_undo_no;
606
/* We play safe and set the truncate limit at most to the purge view
607
low_limit number, though this is not necessary */
609
if (ut_dulint_cmp(limit_trx_no, purge_sys->view->low_limit_no) >= 0) {
610
limit_trx_no = purge_sys->view->low_limit_no;
611
limit_undo_no = ut_dulint_zero;
614
ut_ad((ut_dulint_cmp(limit_trx_no,
615
purge_sys->view->low_limit_no) <= 0));
617
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
620
trx_purge_truncate_rseg_history(rseg, limit_trx_no,
622
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
626
/********************************************************************//**
627
Does a truncate if the purge array is empty. NOTE that when this function is
628
called, the caller must not have any latches on undo log pages!
629
@return TRUE if array empty */
632
trx_purge_truncate_if_arr_empty(void)
633
/*=================================*/
635
ut_ad(mutex_own(&(purge_sys->mutex)));
637
if (purge_sys->arr->n_used == 0) {
639
trx_purge_truncate_history();
647
/***********************************************************************//**
648
Updates the last not yet purged history log info in rseg when we have purged
649
a whole undo log. Advances also purge_sys->purge_trx_no past the purged log. */
652
trx_purge_rseg_get_next_history_log(
653
/*================================*/
654
trx_rseg_t* rseg) /*!< in: rollback segment */
657
trx_ulogf_t* log_hdr;
658
trx_usegf_t* seg_hdr;
659
fil_addr_t prev_log_addr;
664
ut_ad(mutex_own(&(purge_sys->mutex)));
666
mutex_enter(&(rseg->mutex));
668
ut_a(rseg->last_page_no != FIL_NULL);
670
purge_sys->purge_trx_no = ut_dulint_add(rseg->last_trx_no, 1);
671
purge_sys->purge_undo_no = ut_dulint_zero;
672
purge_sys->next_stored = FALSE;
676
undo_page = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
677
rseg->last_page_no, &mtr);
678
log_hdr = undo_page + rseg->last_offset;
679
seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
681
/* Increase the purge page count by one for every handled log */
683
purge_sys->n_pages_handled++;
685
prev_log_addr = trx_purge_get_log_from_hist(
686
flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
687
if (prev_log_addr.page == FIL_NULL) {
688
/* No logs left in the history list */
690
rseg->last_page_no = FIL_NULL;
692
mutex_exit(&(rseg->mutex));
695
mutex_enter(&kernel_mutex);
697
/* Add debug code to track history list corruption reported
698
on the MySQL mailing list on Nov 9, 2004. The fut0lst.c
699
file-based list was corrupt. The prev node pointer was
700
FIL_NULL, even though the list length was over 8 million nodes!
701
We assume that purge truncates the history list in moderate
702
size pieces, and if we here reach the head of the list, the
703
list cannot be longer than 20 000 undo logs now. */
705
if (trx_sys->rseg_history_len > 20000) {
706
ut_print_timestamp(stderr);
708
" InnoDB: Warning: purge reached the"
709
" head of the history list,\n"
710
"InnoDB: but its length is still"
711
" reported as %lu! Make a detailed bug\n"
712
"InnoDB: report, and submit it"
713
" to http://bugs.mysql.com\n",
714
(ulong) trx_sys->rseg_history_len);
717
mutex_exit(&kernel_mutex);
722
mutex_exit(&(rseg->mutex));
725
/* Read the trx number and del marks from the previous log header */
728
log_hdr = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
729
prev_log_addr.page, &mtr)
730
+ prev_log_addr.boffset;
732
trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
734
del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS);
738
mutex_enter(&(rseg->mutex));
740
rseg->last_page_no = prev_log_addr.page;
741
rseg->last_offset = prev_log_addr.boffset;
742
rseg->last_trx_no = trx_no;
743
rseg->last_del_marks = del_marks;
745
mutex_exit(&(rseg->mutex));
748
/***********************************************************************//**
749
Chooses the next undo log to purge and updates the info in purge_sys. This
750
function is used to initialize purge_sys when the next record to purge is
751
not known, and also to update the purge system info on the next record when
752
purge has handled the whole undo log for a transaction. */
755
trx_purge_choose_next_log(void)
756
/*===========================*/
760
trx_rseg_t* min_rseg;
762
ulint space = 0; /* remove warning (??? bug ???) */
764
ulint page_no = 0; /* remove warning (??? bug ???) */
765
ulint offset = 0; /* remove warning (??? bug ???) */
768
ut_ad(mutex_own(&(purge_sys->mutex)));
769
ut_ad(purge_sys->next_stored == FALSE);
771
rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
773
min_trx_no = ut_dulint_max;
778
mutex_enter(&(rseg->mutex));
780
if (rseg->last_page_no != FIL_NULL) {
782
if ((min_rseg == NULL)
783
|| (ut_dulint_cmp(min_trx_no,
784
rseg->last_trx_no) > 0)) {
787
min_trx_no = rseg->last_trx_no;
789
zip_size = rseg->zip_size;
790
ut_a(space == 0); /* We assume in purge of
791
externally stored fields
792
that space id == 0 */
793
page_no = rseg->last_page_no;
794
offset = rseg->last_offset;
798
mutex_exit(&(rseg->mutex));
800
rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
803
if (min_rseg == NULL) {
810
if (!min_rseg->last_del_marks) {
811
/* No need to purge this log */
813
rec = &trx_purge_dummy_rec;
815
rec = trx_undo_get_first_rec(space, zip_size, page_no, offset,
820
rec = &trx_purge_dummy_rec;
824
purge_sys->next_stored = TRUE;
825
purge_sys->rseg = min_rseg;
827
purge_sys->hdr_page_no = page_no;
828
purge_sys->hdr_offset = offset;
830
purge_sys->purge_trx_no = min_trx_no;
832
if (rec == &trx_purge_dummy_rec) {
834
purge_sys->purge_undo_no = ut_dulint_zero;
835
purge_sys->page_no = page_no;
836
purge_sys->offset = 0;
838
purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec);
840
purge_sys->page_no = page_get_page_no(page_align(rec));
841
purge_sys->offset = page_offset(rec);
847
/***********************************************************************//**
848
Gets the next record to purge and updates the info in the purge system.
849
@return copy of an undo log record or pointer to the dummy undo log record */
852
trx_purge_get_next_rec(
853
/*===================*/
854
mem_heap_t* heap) /*!< in: memory heap where copied */
857
trx_undo_rec_t* rec_copy;
858
trx_undo_rec_t* rec2;
859
trx_undo_rec_t* next_rec;
870
ut_ad(mutex_own(&(purge_sys->mutex)));
871
ut_ad(purge_sys->next_stored);
873
space = purge_sys->rseg->space;
874
zip_size = purge_sys->rseg->zip_size;
875
page_no = purge_sys->page_no;
876
offset = purge_sys->offset;
879
/* It is the dummy undo log record, which means that there is
880
no need to purge this undo log */
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();
888
return(&trx_purge_dummy_rec);
893
undo_page = trx_undo_page_get_s_latched(space, zip_size,
895
rec = undo_page + offset;
900
/* Try first to find the next record which requires a purge
901
operation from the same page of the same undo log */
903
next_rec = trx_undo_page_get_next_rec(rec2,
904
purge_sys->hdr_page_no,
905
purge_sys->hdr_offset);
906
if (next_rec == NULL) {
907
rec2 = trx_undo_get_next_rec(
908
rec2, purge_sys->hdr_page_no,
909
purge_sys->hdr_offset, &mtr);
915
type = trx_undo_rec_get_type(rec2);
917
if (type == TRX_UNDO_DEL_MARK_REC) {
922
cmpl_info = trx_undo_rec_get_cmpl_info(rec2);
924
if (trx_undo_rec_get_extern_storage(rec2)) {
928
if ((type == TRX_UNDO_UPD_EXIST_REC)
929
&& !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
937
trx_purge_rseg_get_next_history_log(purge_sys->rseg);
939
/* Look for the next undo log and record to purge */
941
trx_purge_choose_next_log();
945
undo_page = trx_undo_page_get_s_latched(space, zip_size,
948
rec = undo_page + offset;
950
page = page_align(rec2);
952
purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec2);
953
purge_sys->page_no = page_get_page_no(page);
954
purge_sys->offset = rec2 - page;
956
if (undo_page != page) {
957
/* We advance to a new page of the undo log: */
958
purge_sys->n_pages_handled++;
962
rec_copy = trx_undo_rec_copy(rec, heap);
969
/********************************************************************//**
970
Fetches the next undo log record from the history list to purge. It must be
971
released with the corresponding release function.
972
@return copy of an undo log record or pointer to trx_purge_dummy_rec,
973
if the whole undo log can skipped in purge; NULL if none left */
976
trx_purge_fetch_next_rec(
977
/*=====================*/
978
roll_ptr_t* roll_ptr,/*!< out: roll pointer to undo record */
979
trx_undo_inf_t** cell, /*!< out: storage cell for the record in the
981
mem_heap_t* heap) /*!< in: memory heap where copied */
983
trx_undo_rec_t* undo_rec;
985
mutex_enter(&(purge_sys->mutex));
987
if (purge_sys->state == TRX_STOP_PURGE) {
988
trx_purge_truncate_if_arr_empty();
990
mutex_exit(&(purge_sys->mutex));
995
if (!purge_sys->next_stored) {
996
trx_purge_choose_next_log();
998
if (!purge_sys->next_stored) {
999
purge_sys->state = TRX_STOP_PURGE;
1001
trx_purge_truncate_if_arr_empty();
1003
if (srv_print_thread_releases) {
1005
"Purge: No logs left in the"
1006
" history list; pages handled %lu\n",
1007
(ulong) purge_sys->n_pages_handled);
1010
mutex_exit(&(purge_sys->mutex));
1016
if (purge_sys->n_pages_handled >= purge_sys->handle_limit) {
1018
purge_sys->state = TRX_STOP_PURGE;
1020
trx_purge_truncate_if_arr_empty();
1022
mutex_exit(&(purge_sys->mutex));
1027
if (ut_dulint_cmp(purge_sys->purge_trx_no,
1028
purge_sys->view->low_limit_no) >= 0) {
1029
purge_sys->state = TRX_STOP_PURGE;
1031
trx_purge_truncate_if_arr_empty();
1033
mutex_exit(&(purge_sys->mutex));
1038
/* fprintf(stderr, "Thread %lu purging trx %lu undo record %lu\n",
1039
os_thread_get_curr_id(),
1040
ut_dulint_get_low(purge_sys->purge_trx_no),
1041
ut_dulint_get_low(purge_sys->purge_undo_no)); */
1043
*roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id,
1047
*cell = trx_purge_arr_store_info(purge_sys->purge_trx_no,
1048
purge_sys->purge_undo_no);
1050
ut_ad(ut_dulint_cmp(purge_sys->purge_trx_no,
1051
(purge_sys->view)->low_limit_no) < 0);
1053
/* The following call will advance the stored values of purge_trx_no
1054
and purge_undo_no, therefore we had to store them first */
1056
undo_rec = trx_purge_get_next_rec(heap);
1058
mutex_exit(&(purge_sys->mutex));
1063
/*******************************************************************//**
1064
Releases a reserved purge undo record. */
1067
trx_purge_rec_release(
1068
/*==================*/
1069
trx_undo_inf_t* cell) /*!< in: storage cell */
1071
trx_undo_arr_t* arr;
1073
mutex_enter(&(purge_sys->mutex));
1075
arr = purge_sys->arr;
1077
trx_purge_arr_remove_info(cell);
1079
mutex_exit(&(purge_sys->mutex));
1082
/*******************************************************************//**
1083
This function runs a purge batch.
1084
@return number of undo log pages handled in the batch */
1091
/* que_thr_t* thr2; */
1092
ulint old_pages_handled;
1094
mutex_enter(&(purge_sys->mutex));
1096
if (purge_sys->trx->n_active_thrs > 0) {
1098
mutex_exit(&(purge_sys->mutex));
1100
/* Should not happen */
1107
rw_lock_x_lock(&(purge_sys->latch));
1109
mutex_enter(&kernel_mutex);
1111
/* Close and free the old purge view */
1113
read_view_close(purge_sys->view);
1114
purge_sys->view = NULL;
1115
mem_heap_empty(purge_sys->heap);
1117
/* Determine how much data manipulation language (DML) statements
1118
need to be delayed in order to reduce the lagging of the purge
1120
srv_dml_needed_delay = 0; /* in microseconds; default: no delay */
1122
/* If we cannot advance the 'purge view' because of an old
1123
'consistent read view', then the DML statements cannot be delayed.
1124
Also, srv_max_purge_lag <= 0 means 'infinity'. */
1125
if (srv_max_purge_lag > 0
1126
&& !UT_LIST_GET_LAST(trx_sys->view_list)) {
1127
float ratio = (float) trx_sys->rseg_history_len
1128
/ srv_max_purge_lag;
1129
if (ratio > ULINT_MAX / 10000) {
1130
/* Avoid overflow: maximum delay is 4295 seconds */
1131
srv_dml_needed_delay = ULINT_MAX;
1132
} else if (ratio > 1) {
1133
/* If the history list length exceeds the
1134
innodb_max_purge_lag, the
1135
data manipulation statements are delayed
1136
by at least 5000 microseconds. */
1137
srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
1141
purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero,
1143
mutex_exit(&kernel_mutex);
1145
rw_lock_x_unlock(&(purge_sys->latch));
1147
purge_sys->state = TRX_PURGE_ON;
1149
/* Handle at most 20 undo log pages in one purge batch */
1151
purge_sys->handle_limit = purge_sys->n_pages_handled + 20;
1153
old_pages_handled = purge_sys->n_pages_handled;
1155
mutex_exit(&(purge_sys->mutex));
1157
mutex_enter(&kernel_mutex);
1159
thr = que_fork_start_command(purge_sys->query);
1163
/* thr2 = que_fork_start_command(purge_sys->query);
1168
mutex_exit(&kernel_mutex);
1170
/* srv_que_task_enqueue(thr2); */
1172
if (srv_print_thread_releases) {
1174
fputs("Starting purge\n", stderr);
1177
que_run_threads(thr);
1179
if (srv_print_thread_releases) {
1182
"Purge ends; pages handled %lu\n",
1183
(ulong) purge_sys->n_pages_handled);
1186
return(purge_sys->n_pages_handled - old_pages_handled);
1189
/******************************************************************//**
1190
Prints information of the purge system to stderr. */
1193
trx_purge_sys_print(void)
1194
/*=====================*/
1196
fprintf(stderr, "InnoDB: Purge system view:\n");
1197
read_view_print(purge_sys->view);
1199
fprintf(stderr, "InnoDB: Purge trx n:o " TRX_ID_FMT
1200
", undo n:o " TRX_ID_FMT "\n",
1201
TRX_ID_PREP_PRINTF(purge_sys->purge_trx_no),
1202
TRX_ID_PREP_PRINTF(purge_sys->purge_undo_no));
1204
"InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n"
1205
"InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n",
1206
(ulong) purge_sys->next_stored,
1207
(ulong) purge_sys->page_no,
1208
(ulong) purge_sys->offset,
1209
(ulong) purge_sys->hdr_page_no,
1210
(ulong) purge_sys->hdr_offset);