Drizzled Public API Documentation

trx0purge.cc
1 /*****************************************************************************
2 
3 Copyright (C) 1996, 2009, Innobase Oy. All Rights Reserved.
4 
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.
8 
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.
12 
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
16 
17 *****************************************************************************/
18 
19 /**************************************************/
26 #include "trx0purge.h"
27 
28 #ifdef UNIV_NONINL
29 #include "trx0purge.ic"
30 #endif
31 
32 #include "fsp0fsp.h"
33 #include "mach0data.h"
34 #include "mtr0log.h"
35 #include "trx0rseg.h"
36 #include "trx0trx.h"
37 #include "trx0roll.h"
38 #include "read0read.h"
39 #include "fut0fut.h"
40 #include "que0que.h"
41 #include "row0purge.h"
42 #include "row0upd.h"
43 #include "trx0rec.h"
44 #include "srv0srv.h"
45 #include "os0thread.h"
46 
48 UNIV_INTERN trx_purge_t* purge_sys = NULL;
49 
53 
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 */
58 
59 #ifdef UNIV_PFS_MUTEX
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 */
63 
64 /*****************************************************************/
70 UNIV_INTERN
71 ibool
73 /*=============================*/
74  trx_id_t trx_id)
75 {
76 #ifdef UNIV_SYNC_DEBUG
77  ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
78 #endif /* UNIV_SYNC_DEBUG */
79 
80  if (!read_view_sees_trx_id(purge_sys->view, trx_id)) {
81 
82  return(TRUE);
83  }
84 
85  return(FALSE);
86 }
87 
88 /*=================== PURGE RECORD ARRAY =============================*/
89 
90 /*******************************************************************/
93 static
95 trx_purge_arr_store_info(
96 /*=====================*/
97  trx_id_t trx_no,
98  undo_no_t undo_no)
99 {
100  trx_undo_inf_t* cell;
101  trx_undo_arr_t* arr;
102  ulint i;
103 
104  arr = purge_sys->arr;
105 
106  for (i = 0;; i++) {
107  cell = trx_undo_arr_get_nth_info(arr, i);
108 
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;
113  cell->in_use = TRUE;
114 
115  arr->n_used++;
116 
117  return(cell);
118  }
119  }
120 }
121 
122 /*******************************************************************/
124 UNIV_INLINE
125 void
126 trx_purge_arr_remove_info(
127 /*======================*/
128  trx_undo_inf_t* cell)
129 {
130  trx_undo_arr_t* arr;
131 
132  arr = purge_sys->arr;
133 
134  cell->in_use = FALSE;
135 
136  ut_ad(arr->n_used > 0);
137 
138  arr->n_used--;
139 }
140 
141 /*******************************************************************/
143 static
144 void
145 trx_purge_arr_get_biggest(
146 /*======================*/
147  trx_undo_arr_t* arr,
148  trx_id_t* trx_no,
150  undo_no_t* undo_no)
151 {
152  trx_undo_inf_t* cell;
153  trx_id_t pair_trx_no;
154  undo_no_t pair_undo_no;
155  ulint i;
156  ulint n;
157 
158  n = arr->n_used;
159  pair_trx_no = 0;
160  pair_undo_no = 0;
161 
162  if (n) {
163  for (i = 0;; i++) {
164  cell = trx_undo_arr_get_nth_info(arr, i);
165 
166  if (!cell->in_use) {
167  continue;
168  }
169 
170  if ((cell->trx_no > pair_trx_no)
171  || ((cell->trx_no == pair_trx_no)
172  && cell->undo_no >= pair_undo_no)) {
173 
174  pair_trx_no = cell->trx_no;
175  pair_undo_no = cell->undo_no;
176  }
177 
178  if (!--n) {
179  break;
180  }
181  }
182  }
183 
184  *trx_no = pair_trx_no;
185  *undo_no = pair_undo_no;
186 }
187 
188 /****************************************************************/
192 static
193 que_t*
194 trx_purge_graph_build(void)
195 /*=======================*/
196 {
197  mem_heap_t* heap;
198  que_fork_t* fork;
199  que_thr_t* thr;
200  /* que_thr_t* thr2; */
201 
202  heap = mem_heap_create(512);
203  fork = que_fork_create(NULL, NULL, QUE_FORK_PURGE, heap);
204  fork->trx = purge_sys->trx;
205 
206  thr = que_thr_create(fork, heap);
207 
208  thr->child = row_purge_node_create(thr, heap);
209 
210  /* thr2 = que_thr_create(fork, fork, heap);
211 
212  thr2->child = row_purge_node_create(fork, thr2, heap); */
213 
214  return(fork);
215 }
216 
217 /********************************************************************/
220 UNIV_INTERN
221 void
223 /*======================*/
224 {
225  ut_ad(mutex_own(&kernel_mutex));
226 
227  purge_sys = static_cast<trx_purge_t *>(mem_alloc(sizeof(trx_purge_t)));
228 
229  purge_sys->state = TRX_STOP_PURGE;
230 
232 
233  purge_sys->purge_trx_no = 0;
235  purge_sys->next_stored = FALSE;
236 
237  rw_lock_create(trx_purge_latch_key,
238  &purge_sys->latch, SYNC_PURGE_LATCH);
239 
240  mutex_create(purge_sys_mutex_key,
241  &purge_sys->mutex, SYNC_PURGE_SYS);
242 
244 
246 
247  purge_sys->sess = sess_open();
248 
250 
251  purge_sys->trx->is_purge = 1;
252 
253  ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED));
254 
255  purge_sys->query = trx_purge_graph_build();
256 
258  purge_sys->heap);
259 }
260 
261 /************************************************************************
262 Frees the global purge system control structure. */
263 UNIV_INTERN
264 void
266 /*======================*/
267 {
268  ut_ad(!mutex_own(&kernel_mutex));
269 
271 
273  purge_sys->sess->trx->conc_state = TRX_NOT_STARTED;
275  purge_sys->sess = NULL;
276 
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);
281 
283  purge_sys->view = NULL;
284 
285  mutex_exit(&kernel_mutex);
286  }
287 
289 
290  rw_lock_free(&purge_sys->latch);
291  mutex_free(&purge_sys->mutex);
292 
295 
296  purge_sys = NULL;
297 }
298 
299 /*================ UNDO LOG HISTORY LIST =============================*/
300 
301 /********************************************************************/
304 UNIV_INTERN
305 void
307 /*=================================*/
308  trx_t* trx,
309  page_t* undo_page,
311  mtr_t* mtr)
312 {
313  trx_undo_t* undo;
314  trx_rseg_t* rseg;
315  trx_rsegf_t* rseg_header;
316 #ifdef UNIV_DEBUG
317  trx_usegf_t* seg_header;
318 #endif /* UNIV_DEBUG */
319  trx_ulogf_t* undo_header;
320  ulint hist_size;
321 
322  undo = trx->update_undo;
323 
324  ut_ad(undo);
325 
326  rseg = undo->rseg;
327 
328  ut_ad(mutex_own(&(rseg->mutex)));
329 
330  rseg_header = trx_rsegf_get(rseg->space, rseg->zip_size,
331  rseg->page_no, mtr);
332 
333  undo_header = undo_page + undo->hdr_offset;
334 #ifdef UNIV_DEBUG
335  seg_header = undo_page + TRX_UNDO_SEG_HDR;
336 #endif /* UNIV_DEBUG */
337 
338  if (undo->state != TRX_UNDO_CACHED) {
339  /* The undo log segment will not be reused */
340 
341  if (undo->id >= TRX_RSEG_N_SLOTS) {
342  fprintf(stderr,
343  "InnoDB: Error: undo->id is %lu\n",
344  (ulong) undo->id);
345  ut_error;
346  }
347 
348  trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr);
349 
350  hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
351  MLOG_4BYTES, mtr);
352  ut_ad(undo->size == flst_get_len(
353  seg_header + TRX_UNDO_PAGE_LIST, mtr));
354 
355  mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
356  hist_size + undo->size, MLOG_4BYTES, mtr);
357  }
358 
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);
364  mutex_exit(&kernel_mutex);
365 
366  if (!(trx_sys->rseg_history_len % srv_purge_batch_size)) {
367  /* Inform the purge thread that there is work to do. */
369  }
370 
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 */
374 
375  if (!undo->del_marks) {
376  mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE,
377  MLOG_2BYTES, mtr);
378  }
379 
380  if (rseg->last_page_no == FIL_NULL) {
381 
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;
386  }
387 }
388 
389 /**********************************************************************/
392 static
393 void
394 trx_purge_free_segment(
395 /*===================*/
396  trx_rseg_t* rseg,
397  fil_addr_t hdr_addr,
398  ulint n_removed_logs)
401 {
402  page_t* undo_page;
403  trx_rsegf_t* rseg_hdr;
404  trx_ulogf_t* log_hdr;
405  trx_usegf_t* seg_hdr;
406  ibool freed;
407  ulint seg_size;
408  ulint hist_size;
409  ibool marked = FALSE;
410  mtr_t mtr;
411 
412  /* fputs("Freeing an update undo log segment\n", stderr); */
413 
414  ut_ad(mutex_own(&(purge_sys->mutex)));
415 loop:
416  mtr_start(&mtr);
417  mutex_enter(&(rseg->mutex));
418 
419  rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
420  rseg->page_no, &mtr);
421 
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;
426 
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
431  them again. */
432 
433  if (!marked) {
434  mlog_write_ulint(log_hdr + TRX_UNDO_DEL_MARKS, FALSE,
435  MLOG_2BYTES, &mtr);
436  marked = TRUE;
437  }
438 
439  freed = fseg_free_step_not_header(seg_hdr + TRX_UNDO_FSEG_HEADER,
440  &mtr);
441  if (!freed) {
442  mutex_exit(&(rseg->mutex));
443  mtr_commit(&mtr);
444 
445  goto loop;
446  }
447 
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. */
451 
452  seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST, &mtr);
453 
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. */
458 
459  flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY,
460  log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr);
461 
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);
466 
467  freed = FALSE;
468 
469  while (!freed) {
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
473  fsp0fsp.c. */
474 
475  freed = fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER,
476  &mtr);
477  }
478 
479  hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
480  MLOG_4BYTES, &mtr);
481  ut_ad(hist_size >= seg_size);
482 
483  mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
484  hist_size - seg_size, MLOG_4BYTES, &mtr);
485 
486  ut_ad(rseg->curr_size >= seg_size);
487 
488  rseg->curr_size -= seg_size;
489 
490  mutex_exit(&(rseg->mutex));
491 
492  mtr_commit(&mtr);
493 }
494 
495 /********************************************************************/
497 static
498 void
499 trx_purge_truncate_rseg_history(
500 /*============================*/
501  trx_rseg_t* rseg,
502  trx_id_t limit_trx_no,
504  undo_no_t limit_undo_no)
507 {
508  fil_addr_t hdr_addr;
509  fil_addr_t prev_hdr_addr;
510  trx_rsegf_t* rseg_hdr;
511  page_t* undo_page;
512  trx_ulogf_t* log_hdr;
513  trx_usegf_t* seg_hdr;
514  ulint n_removed_logs = 0;
515  mtr_t mtr;
516  trx_id_t undo_trx_no;
517 
518  ut_ad(mutex_own(&(purge_sys->mutex)));
519 
520  mtr_start(&mtr);
521  mutex_enter(&(rseg->mutex));
522 
523  rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
524  rseg->page_no, &mtr);
525 
526  hdr_addr = trx_purge_get_log_from_hist(
527  flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
528 loop:
529  if (hdr_addr.page == FIL_NULL) {
530 
531  mutex_exit(&(rseg->mutex));
532 
533  mtr_commit(&mtr);
534 
535  return;
536  }
537 
538  undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
539  hdr_addr.page, &mtr);
540 
541  log_hdr = undo_page + hdr_addr.boffset;
542  undo_trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
543 
544  if (undo_trx_no >= limit_trx_no) {
545  if (undo_trx_no == limit_trx_no) {
546  trx_undo_truncate_start(rseg, rseg->space,
547  hdr_addr.page,
548  hdr_addr.boffset,
549  limit_undo_no);
550  }
551 
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);
556 
557  flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY,
558  log_hdr + TRX_UNDO_HISTORY_NODE,
559  n_removed_logs, &mtr);
560 
561  mutex_exit(&(rseg->mutex));
562  mtr_commit(&mtr);
563 
564  return;
565  }
566 
567  prev_hdr_addr = trx_purge_get_log_from_hist(
568  flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
569  n_removed_logs++;
570 
571  seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
572 
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)) {
575 
576  /* We can free the whole log segment */
577 
578  mutex_exit(&(rseg->mutex));
579  mtr_commit(&mtr);
580 
581  trx_purge_free_segment(rseg, hdr_addr, n_removed_logs);
582 
583  n_removed_logs = 0;
584  } else {
585  mutex_exit(&(rseg->mutex));
586  mtr_commit(&mtr);
587  }
588 
589  mtr_start(&mtr);
590  mutex_enter(&(rseg->mutex));
591 
592  rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
593  rseg->page_no, &mtr);
594 
595  hdr_addr = prev_hdr_addr;
596 
597  goto loop;
598 }
599 
600 /********************************************************************/
603 static
604 void
605 trx_purge_truncate_history(void)
606 /*============================*/
607 {
608  trx_rseg_t* rseg;
609  trx_id_t limit_trx_no;
610  undo_no_t limit_undo_no;
611 
612  ut_ad(mutex_own(&(purge_sys->mutex)));
613 
614  trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no,
615  &limit_undo_no);
616 
617  if (limit_trx_no == 0) {
618 
619  limit_trx_no = purge_sys->purge_trx_no;
620  limit_undo_no = purge_sys->purge_undo_no;
621  }
622 
623  /* We play safe and set the truncate limit at most to the purge view
624  low_limit number, though this is not necessary */
625 
626  if (limit_trx_no >= purge_sys->view->low_limit_no) {
627  limit_trx_no = purge_sys->view->low_limit_no;
628  limit_undo_no = 0;
629  }
630 
631  ut_ad(limit_trx_no <= purge_sys->view->low_limit_no);
632 
633  rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
634 
635  while (rseg) {
636  trx_purge_truncate_rseg_history(rseg, limit_trx_no,
637  limit_undo_no);
638  rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
639  }
640 }
641 
642 /********************************************************************/
646 UNIV_INLINE
647 ibool
648 trx_purge_truncate_if_arr_empty(void)
649 /*=================================*/
650 {
651  ut_ad(mutex_own(&(purge_sys->mutex)));
652 
653  if (purge_sys->arr->n_used == 0) {
654 
655  trx_purge_truncate_history();
656 
657  return(TRUE);
658  }
659 
660  return(FALSE);
661 }
662 
663 /***********************************************************************/
666 static
667 void
668 trx_purge_rseg_get_next_history_log(
669 /*================================*/
670  trx_rseg_t* rseg)
671 {
672  page_t* undo_page;
673  trx_ulogf_t* log_hdr;
674  fil_addr_t prev_log_addr;
675  trx_id_t trx_no;
676  ibool del_marks;
677  mtr_t mtr;
678 
679  ut_ad(mutex_own(&(purge_sys->mutex)));
680 
681  mutex_enter(&(rseg->mutex));
682 
683  ut_a(rseg->last_page_no != FIL_NULL);
684 
685  purge_sys->purge_trx_no = rseg->last_trx_no + 1;
687  purge_sys->next_stored = FALSE;
688 
689  mtr_start(&mtr);
690 
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;
694 
695  /* Increase the purge page count by one for every handled log */
696 
698 
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 */
703 
704  rseg->last_page_no = FIL_NULL;
705 
706  mutex_exit(&(rseg->mutex));
707  mtr_commit(&mtr);
708 
709  mutex_enter(&kernel_mutex);
710 
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. */
718 
719  if (trx_sys->rseg_history_len > 20000) {
720  ut_print_timestamp(stderr);
721  fprintf(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);
729  }
730 
731  mutex_exit(&kernel_mutex);
732 
733  return;
734  }
735 
736  mutex_exit(&(rseg->mutex));
737  mtr_commit(&mtr);
738 
739  /* Read the trx number and del marks from the previous log header */
740  mtr_start(&mtr);
741 
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;
745 
746  trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
747 
748  del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS);
749 
750  mtr_commit(&mtr);
751 
752  mutex_enter(&(rseg->mutex));
753 
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;
758 
759  mutex_exit(&(rseg->mutex));
760 }
761 
762 /***********************************************************************/
767 static
768 void
769 trx_purge_choose_next_log(void)
770 /*===========================*/
771 {
772  trx_undo_rec_t* rec;
773  trx_rseg_t* rseg;
774  trx_rseg_t* min_rseg;
775  trx_id_t min_trx_no;
776  ulint space = 0; /* remove warning (??? bug ???) */
777  ulint zip_size = 0;
778  ulint page_no = 0; /* remove warning (??? bug ???) */
779  ulint offset = 0; /* remove warning (??? bug ???) */
780  mtr_t mtr;
781 
782  ut_ad(mutex_own(&(purge_sys->mutex)));
783  ut_ad(purge_sys->next_stored == FALSE);
784 
785  rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
786 
787  min_trx_no = IB_ULONGLONG_MAX;
788 
789  min_rseg = NULL;
790 
791  while (rseg) {
792  mutex_enter(&(rseg->mutex));
793 
794  if (rseg->last_page_no != FIL_NULL) {
795 
796  if (min_rseg == NULL
797  || min_trx_no > rseg->last_trx_no) {
798 
799  min_rseg = rseg;
800  min_trx_no = rseg->last_trx_no;
801  space = rseg->space;
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;
808  }
809  }
810 
811  mutex_exit(&(rseg->mutex));
812 
813  rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
814  }
815 
816  if (min_rseg == NULL) {
817 
818  return;
819  }
820 
821  mtr_start(&mtr);
822 
823  if (!min_rseg->last_del_marks) {
824  /* No need to purge this log */
825 
826  rec = &trx_purge_dummy_rec;
827  } else {
828  rec = trx_undo_get_first_rec(space, zip_size, page_no, offset,
829  RW_S_LATCH, &mtr);
830  if (rec == NULL) {
831  /* Undo log empty */
832 
833  rec = &trx_purge_dummy_rec;
834  }
835  }
836 
837  purge_sys->next_stored = TRUE;
838  purge_sys->rseg = min_rseg;
839 
840  purge_sys->hdr_page_no = page_no;
841  purge_sys->hdr_offset = offset;
842 
843  purge_sys->purge_trx_no = min_trx_no;
844 
845  if (rec == &trx_purge_dummy_rec) {
846 
848  purge_sys->page_no = page_no;
849  purge_sys->offset = 0;
850  } else {
852 
854  purge_sys->offset = page_offset(rec);
855  }
856 
857  mtr_commit(&mtr);
858 }
859 
860 /***********************************************************************/
863 static
865 trx_purge_get_next_rec(
866 /*===================*/
867  mem_heap_t* heap)
868 {
869  trx_undo_rec_t* rec;
871  trx_undo_rec_t* rec2;
872  trx_undo_rec_t* next_rec;
873  page_t* undo_page;
874  page_t* page;
875  ulint offset;
876  ulint page_no;
877  ulint space;
878  ulint zip_size;
879  ulint type;
880  ulint cmpl_info;
881  mtr_t mtr;
882 
883  ut_ad(mutex_own(&(purge_sys->mutex)));
885 
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;
890 
891  if (offset == 0) {
892  /* It is the dummy undo log record, which means that there is
893  no need to purge this undo log */
894 
895  trx_purge_rseg_get_next_history_log(purge_sys->rseg);
896 
897  /* Look for the next undo log and record to purge */
898 
899  trx_purge_choose_next_log();
900 
901  return(&trx_purge_dummy_rec);
902  }
903 
904  mtr_start(&mtr);
905 
906  undo_page = trx_undo_page_get_s_latched(space, zip_size,
907  page_no, &mtr);
908  rec = undo_page + offset;
909 
910  rec2 = rec;
911 
912  for (;;) {
913  /* Try first to find the next record which requires a purge
914  operation from the same page of the same undo log */
915 
916  next_rec = trx_undo_page_get_next_rec(rec2,
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);
923  break;
924  }
925 
926  rec2 = next_rec;
927 
928  type = trx_undo_rec_get_type(rec2);
929 
930  if (type == TRX_UNDO_DEL_MARK_REC) {
931 
932  break;
933  }
934 
935  cmpl_info = trx_undo_rec_get_cmpl_info(rec2);
936 
938  break;
939  }
940 
941  if ((type == TRX_UNDO_UPD_EXIST_REC)
942  && !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
943  break;
944  }
945  }
946 
947  if (rec2 == NULL) {
948  mtr_commit(&mtr);
949 
950  trx_purge_rseg_get_next_history_log(purge_sys->rseg);
951 
952  /* Look for the next undo log and record to purge */
953 
954  trx_purge_choose_next_log();
955 
956  mtr_start(&mtr);
957 
958  undo_page = trx_undo_page_get_s_latched(space, zip_size,
959  page_no, &mtr);
960 
961  rec = undo_page + offset;
962  } else {
963  page = page_align(rec2);
964 
967  purge_sys->offset = rec2 - page;
968 
969  if (undo_page != page) {
970  /* We advance to a new page of the undo log: */
972  }
973  }
974 
975  rec_copy = trx_undo_rec_copy(rec, heap);
976 
977  mtr_commit(&mtr);
978 
979  return(rec_copy);
980 }
981 
982 /********************************************************************/
987 UNIV_INTERN
990 /*=====================*/
991  roll_ptr_t* roll_ptr,
992  trx_undo_inf_t** cell,
994  mem_heap_t* heap)
995 {
996  trx_undo_rec_t* undo_rec;
997 
998  mutex_enter(&(purge_sys->mutex));
999 
1000  if (purge_sys->state == TRX_STOP_PURGE) {
1001  trx_purge_truncate_if_arr_empty();
1002 
1003  mutex_exit(&(purge_sys->mutex));
1004 
1005  return(NULL);
1006  }
1007 
1008  if (!purge_sys->next_stored) {
1009  trx_purge_choose_next_log();
1010 
1011  if (!purge_sys->next_stored) {
1012  purge_sys->state = TRX_STOP_PURGE;
1013 
1014  trx_purge_truncate_if_arr_empty();
1015 
1016  if (srv_print_thread_releases) {
1017  fprintf(stderr,
1018  "Purge: No logs left in the"
1019  " history list; pages handled %lu\n",
1020  (ulong) purge_sys->n_pages_handled);
1021  }
1022 
1023  mutex_exit(&(purge_sys->mutex));
1024 
1025  return(NULL);
1026  }
1027  }
1028 
1030 
1031  purge_sys->state = TRX_STOP_PURGE;
1032 
1033  trx_purge_truncate_if_arr_empty();
1034 
1035  mutex_exit(&(purge_sys->mutex));
1036 
1037  return(NULL);
1038  }
1039 
1041  purge_sys->state = TRX_STOP_PURGE;
1042 
1043  trx_purge_truncate_if_arr_empty();
1044 
1045  mutex_exit(&(purge_sys->mutex));
1046 
1047  return(NULL);
1048  }
1049 
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); */
1054 
1055  *roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id,
1056  purge_sys->page_no,
1057  purge_sys->offset);
1058 
1059  *cell = trx_purge_arr_store_info(purge_sys->purge_trx_no,
1061 
1063 
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 */
1066 
1067  undo_rec = trx_purge_get_next_rec(heap);
1068 
1069  mutex_exit(&(purge_sys->mutex));
1070 
1071  return(undo_rec);
1072 }
1073 
1074 /*******************************************************************/
1076 UNIV_INTERN
1077 void
1079 /*==================*/
1080  trx_undo_inf_t* cell)
1081 {
1082  mutex_enter(&(purge_sys->mutex));
1083 
1084  trx_purge_arr_remove_info(cell);
1085 
1086  mutex_exit(&(purge_sys->mutex));
1087 }
1088 
1089 /*******************************************************************/
1092 UNIV_INTERN
1093 ulint
1095 /*======*/
1096  ulint limit)
1098 {
1099  que_thr_t* thr;
1100  /* que_thr_t* thr2; */
1101  ulint old_pages_handled;
1102 
1103  if (srv_fake_write)
1104  return(0);
1105 
1106  mutex_enter(&(purge_sys->mutex));
1107 
1108  if (purge_sys->trx->n_active_thrs > 0) {
1109 
1110  mutex_exit(&(purge_sys->mutex));
1111 
1112  /* Should not happen */
1113 
1114  ut_error;
1115 
1116  return(0);
1117  }
1118 
1119  rw_lock_x_lock(&(purge_sys->latch));
1120 
1121  mutex_enter(&kernel_mutex);
1122 
1123  /* Close and free the old purge view */
1124 
1126  purge_sys->view = NULL;
1128 
1129  /* Determine how much data manipulation language (DML) statements
1130  need to be delayed in order to reduce the lagging of the purge
1131  thread. */
1132  srv_dml_needed_delay = 0; /* in microseconds; default: no delay */
1133 
1134  /* If we cannot advance the 'purge view' because of an old
1135  'consistent read view', then the DML statements cannot be delayed.
1136  Also, srv_max_purge_lag <= 0 means 'infinity'. */
1137  if (srv_max_purge_lag > 0
1138  && !UT_LIST_GET_LAST(trx_sys->view_list)) {
1139  float ratio = (float) trx_sys->rseg_history_len
1140  / srv_max_purge_lag;
1141  if (ratio > ULINT_MAX / 10000) {
1142  /* Avoid overflow: maximum delay is 4295 seconds */
1143  srv_dml_needed_delay = ULINT_MAX;
1144  } else if (ratio > 1) {
1145  /* If the history list length exceeds the
1146  innodb_max_purge_lag, the
1147  data manipulation statements are delayed
1148  by at least 5000 microseconds. */
1149  srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
1150  }
1151  }
1152 
1154  purge_sys->heap);
1155  mutex_exit(&kernel_mutex);
1156 
1157  rw_lock_x_unlock(&(purge_sys->latch));
1158 
1159  purge_sys->state = TRX_PURGE_ON;
1160 
1162 
1163  old_pages_handled = purge_sys->n_pages_handled;
1164 
1165  mutex_exit(&(purge_sys->mutex));
1166 
1167  mutex_enter(&kernel_mutex);
1168 
1170 
1171  ut_ad(thr);
1172 
1173  /* thr2 = que_fork_start_command(purge_sys->query);
1174 
1175  ut_ad(thr2); */
1176 
1177 
1178  mutex_exit(&kernel_mutex);
1179 
1180  /* srv_que_task_enqueue(thr2); */
1181 
1182  if (srv_print_thread_releases) {
1183 
1184  fputs("Starting purge\n", stderr);
1185  }
1186 
1187  que_run_threads(thr);
1188 
1189  if (srv_print_thread_releases) {
1190 
1191  fprintf(stderr,
1192  "Purge ends; pages handled %lu\n",
1193  (ulong) purge_sys->n_pages_handled);
1194  }
1195 
1196  return(purge_sys->n_pages_handled - old_pages_handled);
1197 }
1198 
1199 /******************************************************************/
1201 UNIV_INTERN
1202 void
1204 /*=====================*/
1205 {
1206  fprintf(stderr, "InnoDB: Purge system view:\n");
1208 
1209  fprintf(stderr, "InnoDB: Purge trx n:o " TRX_ID_FMT
1210  ", undo n:o " TRX_ID_FMT "\n",
1213  fprintf(stderr,
1214  "InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n"
1215  "InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n",
1216  (ulong) purge_sys->next_stored,
1217  (ulong) purge_sys->page_no,
1218  (ulong) purge_sys->offset,
1219  (ulong) purge_sys->hdr_page_no,
1220  (ulong) purge_sys->hdr_offset);
1221 }