Drizzled Public API Documentation

trx0purge.cc

00001 /*****************************************************************************
00002 
00003 Copyright (C) 1996, 2009, Innobase Oy. All Rights Reserved.
00004 
00005 This program is free software; you can redistribute it and/or modify it under
00006 the terms of the GNU General Public License as published by the Free Software
00007 Foundation; version 2 of the License.
00008 
00009 This program is distributed in the hope that it will be useful, but WITHOUT
00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00011 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00012 
00013 You should have received a copy of the GNU General Public License along with
00014 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
00015 St, Fifth Floor, Boston, MA 02110-1301 USA
00016 
00017 *****************************************************************************/
00018 
00019 /**************************************************/
00026 #include "trx0purge.h"
00027 
00028 #ifdef UNIV_NONINL
00029 #include "trx0purge.ic"
00030 #endif
00031 
00032 #include "fsp0fsp.h"
00033 #include "mach0data.h"
00034 #include "mtr0log.h"
00035 #include "trx0rseg.h"
00036 #include "trx0trx.h"
00037 #include "trx0roll.h"
00038 #include "read0read.h"
00039 #include "fut0fut.h"
00040 #include "que0que.h"
00041 #include "row0purge.h"
00042 #include "row0upd.h"
00043 #include "trx0rec.h"
00044 #include "srv0srv.h"
00045 #include "os0thread.h"
00046 
00048 UNIV_INTERN trx_purge_t*  purge_sys = NULL;
00049 
00052 UNIV_INTERN trx_undo_rec_t  trx_purge_dummy_rec;
00053 
00054 #ifdef UNIV_PFS_RWLOCK
00055 /* Key to register trx_purge_latch with performance schema */
00056 UNIV_INTERN mysql_pfs_key_t trx_purge_latch_key;
00057 #endif /* UNIV_PFS_RWLOCK */
00058 
00059 #ifdef UNIV_PFS_MUTEX
00060 /* Key to register purge_sys_mutex with performance schema */
00061 UNIV_INTERN mysql_pfs_key_t purge_sys_mutex_key;
00062 #endif /* UNIV_PFS_MUTEX */
00063 
00064 /*****************************************************************/
00070 UNIV_INTERN
00071 ibool
00072 trx_purge_update_undo_must_exist(
00073 /*=============================*/
00074   trx_id_t  trx_id) 
00075 {
00076 #ifdef UNIV_SYNC_DEBUG
00077   ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
00078 #endif /* UNIV_SYNC_DEBUG */
00079 
00080   if (!read_view_sees_trx_id(purge_sys->view, trx_id)) {
00081 
00082     return(TRUE);
00083   }
00084 
00085   return(FALSE);
00086 }
00087 
00088 /*=================== PURGE RECORD ARRAY =============================*/
00089 
00090 /*******************************************************************/
00093 static
00094 trx_undo_inf_t*
00095 trx_purge_arr_store_info(
00096 /*=====================*/
00097   trx_id_t  trx_no, 
00098   undo_no_t undo_no)
00099 {
00100   trx_undo_inf_t* cell;
00101   trx_undo_arr_t* arr;
00102   ulint   i;
00103 
00104   arr = purge_sys->arr;
00105 
00106   for (i = 0;; i++) {
00107     cell = trx_undo_arr_get_nth_info(arr, i);
00108 
00109     if (!(cell->in_use)) {
00110       /* Not in use, we may store here */
00111       cell->undo_no = undo_no;
00112       cell->trx_no = trx_no;
00113       cell->in_use = TRUE;
00114 
00115       arr->n_used++;
00116 
00117       return(cell);
00118     }
00119   }
00120 }
00121 
00122 /*******************************************************************/
00124 UNIV_INLINE
00125 void
00126 trx_purge_arr_remove_info(
00127 /*======================*/
00128   trx_undo_inf_t* cell) 
00129 {
00130   trx_undo_arr_t* arr;
00131 
00132   arr = purge_sys->arr;
00133 
00134   cell->in_use = FALSE;
00135 
00136   ut_ad(arr->n_used > 0);
00137 
00138   arr->n_used--;
00139 }
00140 
00141 /*******************************************************************/
00143 static
00144 void
00145 trx_purge_arr_get_biggest(
00146 /*======================*/
00147   trx_undo_arr_t* arr,  
00148   trx_id_t* trx_no, 
00150   undo_no_t*  undo_no)
00151 {
00152   trx_undo_inf_t* cell;
00153   trx_id_t  pair_trx_no;
00154   undo_no_t pair_undo_no;
00155   ulint   i;
00156   ulint   n;
00157 
00158   n = arr->n_used;
00159   pair_trx_no = 0;
00160   pair_undo_no = 0;
00161 
00162   if (n) {
00163     for (i = 0;; i++) {
00164       cell = trx_undo_arr_get_nth_info(arr, i);
00165 
00166       if (!cell->in_use) {
00167         continue;
00168       }
00169 
00170       if ((cell->trx_no > pair_trx_no)
00171           || ((cell->trx_no == pair_trx_no)
00172         && cell->undo_no >= pair_undo_no)) {
00173 
00174         pair_trx_no = cell->trx_no;
00175         pair_undo_no = cell->undo_no;
00176       }
00177 
00178       if (!--n) {
00179         break;
00180       }
00181     }
00182   }
00183 
00184   *trx_no = pair_trx_no;
00185   *undo_no = pair_undo_no;
00186 }
00187 
00188 /****************************************************************/
00192 static
00193 que_t*
00194 trx_purge_graph_build(void)
00195 /*=======================*/
00196 {
00197   mem_heap_t* heap;
00198   que_fork_t* fork;
00199   que_thr_t*  thr;
00200   /*  que_thr_t*  thr2; */
00201 
00202   heap = mem_heap_create(512);
00203   fork = que_fork_create(NULL, NULL, QUE_FORK_PURGE, heap);
00204   fork->trx = purge_sys->trx;
00205 
00206   thr = que_thr_create(fork, heap);
00207 
00208   thr->child = row_purge_node_create(thr, heap);
00209 
00210   /*  thr2 = que_thr_create(fork, fork, heap);
00211 
00212   thr2->child = row_purge_node_create(fork, thr2, heap);   */
00213 
00214   return(fork);
00215 }
00216 
00217 /********************************************************************/
00220 UNIV_INTERN
00221 void
00222 trx_purge_sys_create(void)
00223 /*======================*/
00224 {
00225   ut_ad(mutex_own(&kernel_mutex));
00226 
00227   purge_sys = static_cast<trx_purge_t *>(mem_alloc(sizeof(trx_purge_t)));
00228 
00229   purge_sys->state = TRX_STOP_PURGE;
00230 
00231   purge_sys->n_pages_handled = 0;
00232 
00233   purge_sys->purge_trx_no = 0;
00234   purge_sys->purge_undo_no = 0;
00235   purge_sys->next_stored = FALSE;
00236 
00237   rw_lock_create(trx_purge_latch_key,
00238            &purge_sys->latch, SYNC_PURGE_LATCH);
00239 
00240   mutex_create(purge_sys_mutex_key,
00241          &purge_sys->mutex, SYNC_PURGE_SYS);
00242 
00243   purge_sys->heap = mem_heap_create(256);
00244 
00245   purge_sys->arr = trx_undo_arr_create();
00246 
00247   purge_sys->sess = sess_open();
00248 
00249   purge_sys->trx = purge_sys->sess->trx;
00250 
00251   purge_sys->trx->is_purge = 1;
00252 
00253   ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED));
00254 
00255   purge_sys->query = trx_purge_graph_build();
00256 
00257   purge_sys->view = read_view_oldest_copy_or_open_new(0,
00258                   purge_sys->heap);
00259 }
00260 
00261 /************************************************************************
00262 Frees the global purge system control structure. */
00263 UNIV_INTERN
00264 void
00265 trx_purge_sys_close(void)
00266 /*======================*/
00267 {
00268   ut_ad(!mutex_own(&kernel_mutex));
00269 
00270   que_graph_free(purge_sys->query);
00271 
00272   ut_a(purge_sys->sess->trx->is_purge);
00273   purge_sys->sess->trx->conc_state = TRX_NOT_STARTED;
00274   sess_close(purge_sys->sess);
00275   purge_sys->sess = NULL;
00276 
00277   if (purge_sys->view != NULL) {
00278     /* Because acquiring the kernel mutex is a pre-condition
00279     of read_view_close(). We don't really need it here. */
00280     mutex_enter(&kernel_mutex);
00281 
00282     read_view_close(purge_sys->view);
00283     purge_sys->view = NULL;
00284 
00285     mutex_exit(&kernel_mutex);
00286   }
00287 
00288   trx_undo_arr_free(purge_sys->arr);
00289 
00290   rw_lock_free(&purge_sys->latch);
00291   mutex_free(&purge_sys->mutex);
00292 
00293   mem_heap_free(purge_sys->heap);
00294   mem_free(purge_sys);
00295 
00296   purge_sys = NULL;
00297 }
00298 
00299 /*================ UNDO LOG HISTORY LIST =============================*/
00300 
00301 /********************************************************************/
00304 UNIV_INTERN
00305 void
00306 trx_purge_add_update_undo_to_history(
00307 /*=================================*/
00308   trx_t*  trx,    
00309   page_t* undo_page,  
00311   mtr_t*  mtr)    
00312 {
00313   trx_undo_t* undo;
00314   trx_rseg_t* rseg;
00315   trx_rsegf_t*  rseg_header;
00316 #ifdef UNIV_DEBUG
00317   trx_usegf_t*  seg_header;
00318 #endif /* UNIV_DEBUG */
00319   trx_ulogf_t*  undo_header;
00320   ulint   hist_size;
00321 
00322   undo = trx->update_undo;
00323 
00324   ut_ad(undo);
00325 
00326   rseg = undo->rseg;
00327 
00328   ut_ad(mutex_own(&(rseg->mutex)));
00329 
00330   rseg_header = trx_rsegf_get(rseg->space, rseg->zip_size,
00331             rseg->page_no, mtr);
00332 
00333   undo_header = undo_page + undo->hdr_offset;
00334 #ifdef UNIV_DEBUG
00335   seg_header  = undo_page + TRX_UNDO_SEG_HDR;
00336 #endif /* UNIV_DEBUG */
00337 
00338   if (undo->state != TRX_UNDO_CACHED) {
00339     /* The undo log segment will not be reused */
00340 
00341     if (undo->id >= TRX_RSEG_N_SLOTS) {
00342       fprintf(stderr,
00343         "InnoDB: Error: undo->id is %lu\n",
00344         (ulong) undo->id);
00345       ut_error;
00346     }
00347 
00348     trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr);
00349 
00350     hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
00351              MLOG_4BYTES, mtr);
00352     ut_ad(undo->size == flst_get_len(
00353             seg_header + TRX_UNDO_PAGE_LIST, mtr));
00354 
00355     mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
00356          hist_size + undo->size, MLOG_4BYTES, mtr);
00357   }
00358 
00359   /* Add the log as the first in the history list */
00360   flst_add_first(rseg_header + TRX_RSEG_HISTORY,
00361            undo_header + TRX_UNDO_HISTORY_NODE, mtr);
00362   mutex_enter(&kernel_mutex);
00363   trx_sys->rseg_history_len++;
00364   mutex_exit(&kernel_mutex);
00365 
00366   if (!(trx_sys->rseg_history_len % srv_purge_batch_size)) {
00367     /* Inform the purge thread that there is work to do. */
00368     srv_wake_purge_thread_if_not_active();
00369   }
00370 
00371   /* Write the trx number to the undo log header */
00372   mlog_write_ull(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);
00373   /* Write information about delete markings to the undo log header */
00374 
00375   if (!undo->del_marks) {
00376     mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE,
00377          MLOG_2BYTES, mtr);
00378   }
00379 
00380   if (rseg->last_page_no == FIL_NULL) {
00381 
00382     rseg->last_page_no = undo->hdr_page_no;
00383     rseg->last_offset = undo->hdr_offset;
00384     rseg->last_trx_no = trx->no;
00385     rseg->last_del_marks = undo->del_marks;
00386   }
00387 }
00388 
00389 /**********************************************************************/
00392 static
00393 void
00394 trx_purge_free_segment(
00395 /*===================*/
00396   trx_rseg_t* rseg,   
00397   fil_addr_t  hdr_addr, 
00398   ulint   n_removed_logs) 
00401 {
00402   page_t*   undo_page;
00403   trx_rsegf_t*  rseg_hdr;
00404   trx_ulogf_t*  log_hdr;
00405   trx_usegf_t*  seg_hdr;
00406   ibool   freed;
00407   ulint   seg_size;
00408   ulint   hist_size;
00409   ibool   marked    = FALSE;
00410   mtr_t   mtr;
00411 
00412   /*  fputs("Freeing an update undo log segment\n", stderr); */
00413 
00414   ut_ad(mutex_own(&(purge_sys->mutex)));
00415 loop:
00416   mtr_start(&mtr);
00417   mutex_enter(&(rseg->mutex));
00418 
00419   rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
00420          rseg->page_no, &mtr);
00421 
00422   undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
00423               hdr_addr.page, &mtr);
00424   seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
00425   log_hdr = undo_page + hdr_addr.boffset;
00426 
00427   /* Mark the last undo log totally purged, so that if the system
00428   crashes, the tail of the undo log will not get accessed again. The
00429   list of pages in the undo log tail gets inconsistent during the
00430   freeing of the segment, and therefore purge should not try to access
00431   them again. */
00432 
00433   if (!marked) {
00434     mlog_write_ulint(log_hdr + TRX_UNDO_DEL_MARKS, FALSE,
00435          MLOG_2BYTES, &mtr);
00436     marked = TRUE;
00437   }
00438 
00439   freed = fseg_free_step_not_header(seg_hdr + TRX_UNDO_FSEG_HEADER,
00440             &mtr);
00441   if (!freed) {
00442     mutex_exit(&(rseg->mutex));
00443     mtr_commit(&mtr);
00444 
00445     goto loop;
00446   }
00447 
00448   /* The page list may now be inconsistent, but the length field
00449   stored in the list base node tells us how big it was before we
00450   started the freeing. */
00451 
00452   seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST, &mtr);
00453 
00454   /* We may free the undo log segment header page; it must be freed
00455   within the same mtr as the undo log header is removed from the
00456   history list: otherwise, in case of a database crash, the segment
00457   could become inaccessible garbage in the file space. */
00458 
00459   flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY,
00460          log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr);
00461 
00462   mutex_enter(&kernel_mutex);
00463   ut_ad(trx_sys->rseg_history_len >= n_removed_logs);
00464   trx_sys->rseg_history_len -= n_removed_logs;
00465   mutex_exit(&kernel_mutex);
00466 
00467   freed = FALSE;
00468 
00469   while (!freed) {
00470     /* Here we assume that a file segment with just the header
00471     page can be freed in a few steps, so that the buffer pool
00472     is not flooded with bufferfixed pages: see the note in
00473     fsp0fsp.c. */
00474 
00475     freed = fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER,
00476                &mtr);
00477   }
00478 
00479   hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
00480            MLOG_4BYTES, &mtr);
00481   ut_ad(hist_size >= seg_size);
00482 
00483   mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
00484        hist_size - seg_size, MLOG_4BYTES, &mtr);
00485 
00486   ut_ad(rseg->curr_size >= seg_size);
00487 
00488   rseg->curr_size -= seg_size;
00489 
00490   mutex_exit(&(rseg->mutex));
00491 
00492   mtr_commit(&mtr);
00493 }
00494 
00495 /********************************************************************/
00497 static
00498 void
00499 trx_purge_truncate_rseg_history(
00500 /*============================*/
00501   trx_rseg_t* rseg,   
00502   trx_id_t  limit_trx_no, 
00504   undo_no_t limit_undo_no)  
00507 {
00508   fil_addr_t  hdr_addr;
00509   fil_addr_t  prev_hdr_addr;
00510   trx_rsegf_t*  rseg_hdr;
00511   page_t*   undo_page;
00512   trx_ulogf_t*  log_hdr;
00513   trx_usegf_t*  seg_hdr;
00514   ulint   n_removed_logs  = 0;
00515   mtr_t   mtr;
00516   trx_id_t  undo_trx_no;
00517 
00518   ut_ad(mutex_own(&(purge_sys->mutex)));
00519 
00520   mtr_start(&mtr);
00521   mutex_enter(&(rseg->mutex));
00522 
00523   rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
00524          rseg->page_no, &mtr);
00525 
00526   hdr_addr = trx_purge_get_log_from_hist(
00527     flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
00528 loop:
00529   if (hdr_addr.page == FIL_NULL) {
00530 
00531     mutex_exit(&(rseg->mutex));
00532 
00533     mtr_commit(&mtr);
00534 
00535     return;
00536   }
00537 
00538   undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
00539               hdr_addr.page, &mtr);
00540 
00541   log_hdr = undo_page + hdr_addr.boffset;
00542   undo_trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
00543 
00544   if (undo_trx_no >= limit_trx_no) {
00545     if (undo_trx_no == limit_trx_no) {
00546       trx_undo_truncate_start(rseg, rseg->space,
00547             hdr_addr.page,
00548             hdr_addr.boffset,
00549             limit_undo_no);
00550     }
00551 
00552     mutex_enter(&kernel_mutex);
00553     ut_a(trx_sys->rseg_history_len >= n_removed_logs);
00554     trx_sys->rseg_history_len -= n_removed_logs;
00555     mutex_exit(&kernel_mutex);
00556 
00557     flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY,
00558           log_hdr + TRX_UNDO_HISTORY_NODE,
00559           n_removed_logs, &mtr);
00560 
00561     mutex_exit(&(rseg->mutex));
00562     mtr_commit(&mtr);
00563 
00564     return;
00565   }
00566 
00567   prev_hdr_addr = trx_purge_get_log_from_hist(
00568     flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
00569   n_removed_logs++;
00570 
00571   seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
00572 
00573   if ((mach_read_from_2(seg_hdr + TRX_UNDO_STATE) == TRX_UNDO_TO_PURGE)
00574       && (mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG) == 0)) {
00575 
00576     /* We can free the whole log segment */
00577 
00578     mutex_exit(&(rseg->mutex));
00579     mtr_commit(&mtr);
00580 
00581     trx_purge_free_segment(rseg, hdr_addr, n_removed_logs);
00582 
00583     n_removed_logs = 0;
00584   } else {
00585     mutex_exit(&(rseg->mutex));
00586     mtr_commit(&mtr);
00587   }
00588 
00589   mtr_start(&mtr);
00590   mutex_enter(&(rseg->mutex));
00591 
00592   rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
00593          rseg->page_no, &mtr);
00594 
00595   hdr_addr = prev_hdr_addr;
00596 
00597   goto loop;
00598 }
00599 
00600 /********************************************************************/
00603 static
00604 void
00605 trx_purge_truncate_history(void)
00606 /*============================*/
00607 {
00608   trx_rseg_t* rseg;
00609   trx_id_t  limit_trx_no;
00610   undo_no_t limit_undo_no;
00611 
00612   ut_ad(mutex_own(&(purge_sys->mutex)));
00613 
00614   trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no,
00615           &limit_undo_no);
00616 
00617   if (limit_trx_no == 0) {
00618 
00619     limit_trx_no = purge_sys->purge_trx_no;
00620     limit_undo_no = purge_sys->purge_undo_no;
00621   }
00622 
00623   /* We play safe and set the truncate limit at most to the purge view
00624   low_limit number, though this is not necessary */
00625 
00626   if (limit_trx_no >= purge_sys->view->low_limit_no) {
00627     limit_trx_no = purge_sys->view->low_limit_no;
00628     limit_undo_no = 0;
00629   }
00630 
00631   ut_ad(limit_trx_no <= purge_sys->view->low_limit_no);
00632 
00633   rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
00634 
00635   while (rseg) {
00636     trx_purge_truncate_rseg_history(rseg, limit_trx_no,
00637             limit_undo_no);
00638     rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
00639   }
00640 }
00641 
00642 /********************************************************************/
00646 UNIV_INLINE
00647 ibool
00648 trx_purge_truncate_if_arr_empty(void)
00649 /*=================================*/
00650 {
00651   ut_ad(mutex_own(&(purge_sys->mutex)));
00652 
00653   if (purge_sys->arr->n_used == 0) {
00654 
00655     trx_purge_truncate_history();
00656 
00657     return(TRUE);
00658   }
00659 
00660   return(FALSE);
00661 }
00662 
00663 /***********************************************************************/
00666 static
00667 void
00668 trx_purge_rseg_get_next_history_log(
00669 /*================================*/
00670   trx_rseg_t* rseg) 
00671 {
00672   page_t*   undo_page;
00673   trx_ulogf_t*  log_hdr;
00674   fil_addr_t  prev_log_addr;
00675   trx_id_t  trx_no;
00676   ibool   del_marks;
00677   mtr_t   mtr;
00678 
00679   ut_ad(mutex_own(&(purge_sys->mutex)));
00680 
00681   mutex_enter(&(rseg->mutex));
00682 
00683   ut_a(rseg->last_page_no != FIL_NULL);
00684 
00685   purge_sys->purge_trx_no = rseg->last_trx_no + 1;
00686   purge_sys->purge_undo_no = 0;
00687   purge_sys->next_stored = FALSE;
00688 
00689   mtr_start(&mtr);
00690 
00691   undo_page = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
00692             rseg->last_page_no, &mtr);
00693   log_hdr = undo_page + rseg->last_offset;
00694 
00695   /* Increase the purge page count by one for every handled log */
00696 
00697   purge_sys->n_pages_handled++;
00698 
00699   prev_log_addr = trx_purge_get_log_from_hist(
00700     flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
00701   if (prev_log_addr.page == FIL_NULL) {
00702     /* No logs left in the history list */
00703 
00704     rseg->last_page_no = FIL_NULL;
00705 
00706     mutex_exit(&(rseg->mutex));
00707     mtr_commit(&mtr);
00708 
00709     mutex_enter(&kernel_mutex);
00710 
00711     /* Add debug code to track history list corruption reported
00712     on the MySQL mailing list on Nov 9, 2004. The fut0lst.c
00713     file-based list was corrupt. The prev node pointer was
00714     FIL_NULL, even though the list length was over 8 million nodes!
00715     We assume that purge truncates the history list in moderate
00716     size pieces, and if we here reach the head of the list, the
00717     list cannot be longer than 20 000 undo logs now. */
00718 
00719     if (trx_sys->rseg_history_len > 20000) {
00720       ut_print_timestamp(stderr);
00721       fprintf(stderr,
00722         "  InnoDB: Warning: purge reached the"
00723         " head of the history list,\n"
00724         "InnoDB: but its length is still"
00725         " reported as %lu! Make a detailed bug\n"
00726         "InnoDB: report, and submit it"
00727         " to http://bugs.mysql.com\n",
00728         (ulong) trx_sys->rseg_history_len);
00729     }
00730 
00731     mutex_exit(&kernel_mutex);
00732 
00733     return;
00734   }
00735 
00736   mutex_exit(&(rseg->mutex));
00737   mtr_commit(&mtr);
00738 
00739   /* Read the trx number and del marks from the previous log header */
00740   mtr_start(&mtr);
00741 
00742   log_hdr = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
00743                 prev_log_addr.page, &mtr)
00744     + prev_log_addr.boffset;
00745 
00746   trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
00747 
00748   del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS);
00749 
00750   mtr_commit(&mtr);
00751 
00752   mutex_enter(&(rseg->mutex));
00753 
00754   rseg->last_page_no = prev_log_addr.page;
00755   rseg->last_offset = prev_log_addr.boffset;
00756   rseg->last_trx_no = trx_no;
00757   rseg->last_del_marks = del_marks;
00758 
00759   mutex_exit(&(rseg->mutex));
00760 }
00761 
00762 /***********************************************************************/
00767 static
00768 void
00769 trx_purge_choose_next_log(void)
00770 /*===========================*/
00771 {
00772   trx_undo_rec_t* rec;
00773   trx_rseg_t* rseg;
00774   trx_rseg_t* min_rseg;
00775   trx_id_t  min_trx_no;
00776   ulint   space = 0;   /* remove warning (??? bug ???) */
00777   ulint   zip_size = 0;
00778   ulint   page_no = 0; /* remove warning (??? bug ???) */
00779   ulint   offset = 0;  /* remove warning (??? bug ???) */
00780   mtr_t   mtr;
00781 
00782   ut_ad(mutex_own(&(purge_sys->mutex)));
00783   ut_ad(purge_sys->next_stored == FALSE);
00784 
00785   rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
00786 
00787   min_trx_no = IB_ULONGLONG_MAX;
00788 
00789   min_rseg = NULL;
00790 
00791   while (rseg) {
00792     mutex_enter(&(rseg->mutex));
00793 
00794     if (rseg->last_page_no != FIL_NULL) {
00795 
00796       if (min_rseg == NULL
00797           || min_trx_no > rseg->last_trx_no) {
00798 
00799         min_rseg = rseg;
00800         min_trx_no = rseg->last_trx_no;
00801         space = rseg->space;
00802         zip_size = rseg->zip_size;
00803         ut_a(space == 0); /* We assume in purge of
00804               externally stored fields
00805               that space id == 0 */
00806         page_no = rseg->last_page_no;
00807         offset = rseg->last_offset;
00808       }
00809     }
00810 
00811     mutex_exit(&(rseg->mutex));
00812 
00813     rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
00814   }
00815 
00816   if (min_rseg == NULL) {
00817 
00818     return;
00819   }
00820 
00821   mtr_start(&mtr);
00822 
00823   if (!min_rseg->last_del_marks) {
00824     /* No need to purge this log */
00825 
00826     rec = &trx_purge_dummy_rec;
00827   } else {
00828     rec = trx_undo_get_first_rec(space, zip_size, page_no, offset,
00829                RW_S_LATCH, &mtr);
00830     if (rec == NULL) {
00831       /* Undo log empty */
00832 
00833       rec = &trx_purge_dummy_rec;
00834     }
00835   }
00836 
00837   purge_sys->next_stored = TRUE;
00838   purge_sys->rseg = min_rseg;
00839 
00840   purge_sys->hdr_page_no = page_no;
00841   purge_sys->hdr_offset = offset;
00842 
00843   purge_sys->purge_trx_no = min_trx_no;
00844 
00845   if (rec == &trx_purge_dummy_rec) {
00846 
00847     purge_sys->purge_undo_no = 0;
00848     purge_sys->page_no = page_no;
00849     purge_sys->offset = 0;
00850   } else {
00851     purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec);
00852 
00853     purge_sys->page_no = page_get_page_no(page_align(rec));
00854     purge_sys->offset = page_offset(rec);
00855   }
00856 
00857   mtr_commit(&mtr);
00858 }
00859 
00860 /***********************************************************************/
00863 static
00864 trx_undo_rec_t*
00865 trx_purge_get_next_rec(
00866 /*===================*/
00867   mem_heap_t* heap) 
00868 {
00869   trx_undo_rec_t* rec;
00870   trx_undo_rec_t* rec_copy;
00871   trx_undo_rec_t* rec2;
00872   trx_undo_rec_t* next_rec;
00873   page_t*   undo_page;
00874   page_t*   page;
00875   ulint   offset;
00876   ulint   page_no;
00877   ulint   space;
00878   ulint   zip_size;
00879   ulint   type;
00880   ulint   cmpl_info;
00881   mtr_t   mtr;
00882 
00883   ut_ad(mutex_own(&(purge_sys->mutex)));
00884   ut_ad(purge_sys->next_stored);
00885 
00886   space = purge_sys->rseg->space;
00887   zip_size = purge_sys->rseg->zip_size;
00888   page_no = purge_sys->page_no;
00889   offset = purge_sys->offset;
00890 
00891   if (offset == 0) {
00892     /* It is the dummy undo log record, which means that there is
00893     no need to purge this undo log */
00894 
00895     trx_purge_rseg_get_next_history_log(purge_sys->rseg);
00896 
00897     /* Look for the next undo log and record to purge */
00898 
00899     trx_purge_choose_next_log();
00900 
00901     return(&trx_purge_dummy_rec);
00902   }
00903 
00904   mtr_start(&mtr);
00905 
00906   undo_page = trx_undo_page_get_s_latched(space, zip_size,
00907             page_no, &mtr);
00908   rec = undo_page + offset;
00909 
00910   rec2 = rec;
00911 
00912   for (;;) {
00913     /* Try first to find the next record which requires a purge
00914     operation from the same page of the same undo log */
00915 
00916     next_rec = trx_undo_page_get_next_rec(rec2,
00917                   purge_sys->hdr_page_no,
00918                   purge_sys->hdr_offset);
00919     if (next_rec == NULL) {
00920       rec2 = trx_undo_get_next_rec(
00921         rec2, purge_sys->hdr_page_no,
00922         purge_sys->hdr_offset, &mtr);
00923       break;
00924     }
00925 
00926     rec2 = next_rec;
00927 
00928     type = trx_undo_rec_get_type(rec2);
00929 
00930     if (type == TRX_UNDO_DEL_MARK_REC) {
00931 
00932       break;
00933     }
00934 
00935     cmpl_info = trx_undo_rec_get_cmpl_info(rec2);
00936 
00937     if (trx_undo_rec_get_extern_storage(rec2)) {
00938       break;
00939     }
00940 
00941     if ((type == TRX_UNDO_UPD_EXIST_REC)
00942         && !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
00943       break;
00944     }
00945   }
00946 
00947   if (rec2 == NULL) {
00948     mtr_commit(&mtr);
00949 
00950     trx_purge_rseg_get_next_history_log(purge_sys->rseg);
00951 
00952     /* Look for the next undo log and record to purge */
00953 
00954     trx_purge_choose_next_log();
00955 
00956     mtr_start(&mtr);
00957 
00958     undo_page = trx_undo_page_get_s_latched(space, zip_size,
00959               page_no, &mtr);
00960 
00961     rec = undo_page + offset;
00962   } else {
00963     page = page_align(rec2);
00964 
00965     purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec2);
00966     purge_sys->page_no = page_get_page_no(page);
00967     purge_sys->offset = rec2 - page;
00968 
00969     if (undo_page != page) {
00970       /* We advance to a new page of the undo log: */
00971       purge_sys->n_pages_handled++;
00972     }
00973   }
00974 
00975   rec_copy = trx_undo_rec_copy(rec, heap);
00976 
00977   mtr_commit(&mtr);
00978 
00979   return(rec_copy);
00980 }
00981 
00982 /********************************************************************/
00987 UNIV_INTERN
00988 trx_undo_rec_t*
00989 trx_purge_fetch_next_rec(
00990 /*=====================*/
00991   roll_ptr_t* roll_ptr,
00992   trx_undo_inf_t** cell,  
00994   mem_heap_t* heap) 
00995 {
00996   trx_undo_rec_t* undo_rec;
00997 
00998   mutex_enter(&(purge_sys->mutex));
00999 
01000   if (purge_sys->state == TRX_STOP_PURGE) {
01001     trx_purge_truncate_if_arr_empty();
01002 
01003     mutex_exit(&(purge_sys->mutex));
01004 
01005     return(NULL);
01006   }
01007 
01008   if (!purge_sys->next_stored) {
01009     trx_purge_choose_next_log();
01010 
01011     if (!purge_sys->next_stored) {
01012       purge_sys->state = TRX_STOP_PURGE;
01013 
01014       trx_purge_truncate_if_arr_empty();
01015 
01016       if (srv_print_thread_releases) {
01017         fprintf(stderr,
01018           "Purge: No logs left in the"
01019           " history list; pages handled %lu\n",
01020           (ulong) purge_sys->n_pages_handled);
01021       }
01022 
01023       mutex_exit(&(purge_sys->mutex));
01024 
01025       return(NULL);
01026     }
01027   }
01028 
01029   if (purge_sys->n_pages_handled >= purge_sys->handle_limit) {
01030 
01031     purge_sys->state = TRX_STOP_PURGE;
01032 
01033     trx_purge_truncate_if_arr_empty();
01034 
01035     mutex_exit(&(purge_sys->mutex));
01036 
01037     return(NULL);
01038   }
01039 
01040   if (purge_sys->purge_trx_no >= purge_sys->view->low_limit_no) {
01041     purge_sys->state = TRX_STOP_PURGE;
01042 
01043     trx_purge_truncate_if_arr_empty();
01044 
01045     mutex_exit(&(purge_sys->mutex));
01046 
01047     return(NULL);
01048   }
01049 
01050   /* fprintf(stderr, "Thread %lu purging trx %llu undo record %llu\n",
01051   os_thread_get_curr_id(),
01052   (ullint) purge_sys->purge_trx_no,
01053   (ullint) purge_sys->purge_undo_no); */
01054 
01055   *roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id,
01056               purge_sys->page_no,
01057               purge_sys->offset);
01058 
01059   *cell = trx_purge_arr_store_info(purge_sys->purge_trx_no,
01060            purge_sys->purge_undo_no);
01061 
01062   ut_ad(purge_sys->purge_trx_no < purge_sys->view->low_limit_no);
01063 
01064   /* The following call will advance the stored values of purge_trx_no
01065   and purge_undo_no, therefore we had to store them first */
01066 
01067   undo_rec = trx_purge_get_next_rec(heap);
01068 
01069   mutex_exit(&(purge_sys->mutex));
01070 
01071   return(undo_rec);
01072 }
01073 
01074 /*******************************************************************/
01076 UNIV_INTERN
01077 void
01078 trx_purge_rec_release(
01079 /*==================*/
01080   trx_undo_inf_t* cell) 
01081 {
01082   mutex_enter(&(purge_sys->mutex));
01083 
01084   trx_purge_arr_remove_info(cell);
01085 
01086   mutex_exit(&(purge_sys->mutex));
01087 }
01088 
01089 /*******************************************************************/
01092 UNIV_INTERN
01093 ulint
01094 trx_purge(
01095 /*======*/
01096   ulint limit)    
01098 {
01099   que_thr_t*  thr;
01100   /*  que_thr_t*  thr2; */
01101   ulint   old_pages_handled;
01102 
01103   mutex_enter(&(purge_sys->mutex));
01104 
01105   if (purge_sys->trx->n_active_thrs > 0) {
01106 
01107     mutex_exit(&(purge_sys->mutex));
01108 
01109     /* Should not happen */
01110 
01111     ut_error;
01112 
01113     return(0);
01114   }
01115 
01116   rw_lock_x_lock(&(purge_sys->latch));
01117 
01118   mutex_enter(&kernel_mutex);
01119 
01120   /* Close and free the old purge view */
01121 
01122   read_view_close(purge_sys->view);
01123   purge_sys->view = NULL;
01124   mem_heap_empty(purge_sys->heap);
01125 
01126   /* Determine how much data manipulation language (DML) statements
01127   need to be delayed in order to reduce the lagging of the purge
01128   thread. */
01129   srv_dml_needed_delay = 0; /* in microseconds; default: no delay */
01130 
01131   /* If we cannot advance the 'purge view' because of an old
01132   'consistent read view', then the DML statements cannot be delayed.
01133   Also, srv_max_purge_lag <= 0 means 'infinity'. */
01134   if (srv_max_purge_lag > 0
01135       && !UT_LIST_GET_LAST(trx_sys->view_list)) {
01136     float ratio = (float) trx_sys->rseg_history_len
01137       / srv_max_purge_lag;
01138     if (ratio > ULINT_MAX / 10000) {
01139       /* Avoid overflow: maximum delay is 4295 seconds */
01140       srv_dml_needed_delay = ULINT_MAX;
01141     } else if (ratio > 1) {
01142       /* If the history list length exceeds the
01143       innodb_max_purge_lag, the
01144       data manipulation statements are delayed
01145       by at least 5000 microseconds. */
01146       srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
01147     }
01148   }
01149 
01150   purge_sys->view = read_view_oldest_copy_or_open_new(0,
01151                   purge_sys->heap);
01152   mutex_exit(&kernel_mutex);
01153 
01154   rw_lock_x_unlock(&(purge_sys->latch));
01155 
01156   purge_sys->state = TRX_PURGE_ON;
01157 
01158   purge_sys->handle_limit = purge_sys->n_pages_handled + limit;
01159 
01160   old_pages_handled = purge_sys->n_pages_handled;
01161 
01162   mutex_exit(&(purge_sys->mutex));
01163 
01164   mutex_enter(&kernel_mutex);
01165 
01166   thr = que_fork_start_command(purge_sys->query);
01167 
01168   ut_ad(thr);
01169 
01170   /*  thr2 = que_fork_start_command(purge_sys->query);
01171 
01172   ut_ad(thr2); */
01173 
01174 
01175   mutex_exit(&kernel_mutex);
01176 
01177   /*  srv_que_task_enqueue(thr2); */
01178 
01179   if (srv_print_thread_releases) {
01180 
01181     fputs("Starting purge\n", stderr);
01182   }
01183 
01184   que_run_threads(thr);
01185 
01186   if (srv_print_thread_releases) {
01187 
01188     fprintf(stderr,
01189       "Purge ends; pages handled %lu\n",
01190       (ulong) purge_sys->n_pages_handled);
01191   }
01192 
01193   return(purge_sys->n_pages_handled - old_pages_handled);
01194 }
01195 
01196 /******************************************************************/
01198 UNIV_INTERN
01199 void
01200 trx_purge_sys_print(void)
01201 /*=====================*/
01202 {
01203   fprintf(stderr, "InnoDB: Purge system view:\n");
01204   read_view_print(purge_sys->view);
01205 
01206   fprintf(stderr, "InnoDB: Purge trx n:o " TRX_ID_FMT
01207     ", undo n:o " TRX_ID_FMT "\n",
01208     purge_sys->purge_trx_no,
01209     purge_sys->purge_undo_no);
01210   fprintf(stderr,
01211     "InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n"
01212     "InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n",
01213     (ulong) purge_sys->next_stored,
01214     (ulong) purge_sys->page_no,
01215     (ulong) purge_sys->offset,
01216     (ulong) purge_sys->hdr_page_no,
01217     (ulong) purge_sys->hdr_offset);
01218 }