Drizzled Public API Documentation

buf0rea.cc
00001 /*****************************************************************************
00002 
00003 Copyright (C) 1995, 2010, 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 "buf0rea.h"
00027 
00028 #include "fil0fil.h"
00029 #include "mtr0mtr.h"
00030 
00031 #include "buf0buf.h"
00032 #include "buf0flu.h"
00033 #include "buf0lru.h"
00034 #include "ibuf0ibuf.h"
00035 #include "log0recv.h"
00036 #include "trx0sys.h"
00037 #include "os0file.h"
00038 #include "srv0start.h"
00039 #include "srv0srv.h"
00040 
00042 #define BUF_READ_AHEAD_LINEAR_AREA  BUF_READ_AHEAD_AREA
00043 
00047 #define BUF_READ_AHEAD_PEND_LIMIT 2
00048 
00049 /********************************************************************/
00059 UNIV_INTERN
00060 ulint
00061 buf_read_page_low(
00062 /*==============*/
00063   ulint*  err,  
00066   ibool sync, 
00067   ulint mode, 
00070   ulint space,  
00071   ulint zip_size,
00072   ibool unzip,  
00073   ib_int64_t tablespace_version, 
00078   ulint offset) 
00079 {
00080   buf_page_t* bpage;
00081   ulint   wake_later;
00082 
00083   *err = DB_SUCCESS;
00084 
00085   wake_later = mode & OS_AIO_SIMULATED_WAKE_LATER;
00086   mode = mode & ~OS_AIO_SIMULATED_WAKE_LATER;
00087 
00088   if (trx_doublewrite && space == TRX_SYS_SPACE
00089       && (   (offset >= trx_doublewrite->block1
00090         && offset < trx_doublewrite->block1
00091         + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE)
00092        || (offset >= trx_doublewrite->block2
00093            && offset < trx_doublewrite->block2
00094            + TRX_SYS_DOUBLEWRITE_BLOCK_SIZE))) {
00095     ut_print_timestamp(stderr);
00096     fprintf(stderr,
00097       "  InnoDB: Warning: trying to read"
00098       " doublewrite buffer page %lu\n",
00099       (ulong) offset);
00100 
00101     return(0);
00102   }
00103 
00104   if (ibuf_bitmap_page(zip_size, offset)
00105       || trx_sys_hdr_page(space, offset)) {
00106 
00107     /* Trx sys header is so low in the latching order that we play
00108     safe and do not leave the i/o-completion to an asynchronous
00109     i/o-thread. Ibuf bitmap pages must always be read with
00110     syncronous i/o, to make sure they do not get involved in
00111     thread deadlocks. */
00112 
00113     sync = TRUE;
00114   }
00115 
00116   /* The following call will also check if the tablespace does not exist
00117   or is being dropped; if we succeed in initing the page in the buffer
00118   pool for read, then DISCARD cannot proceed until the read has
00119   completed */
00120   bpage = buf_page_init_for_read(err, mode, space, zip_size, unzip,
00121                tablespace_version, offset);
00122   if (bpage == NULL) {
00123 
00124     return(0);
00125   }
00126 
00127 #ifdef UNIV_DEBUG
00128   if (buf_debug_prints) {
00129     fprintf(stderr,
00130       "Posting read request for page %lu, sync %lu\n",
00131       (ulong) offset,
00132       (ulong) sync);
00133   }
00134 #endif
00135 
00136   ut_ad(buf_page_in_file(bpage));
00137 
00138   if (zip_size) {
00139     *err = fil_io(OS_FILE_READ | wake_later,
00140             sync, space, zip_size, offset, 0, zip_size,
00141             bpage->zip.data, bpage);
00142   } else {
00143     ut_a(buf_page_get_state(bpage) == BUF_BLOCK_FILE_PAGE);
00144 
00145     *err = fil_io(OS_FILE_READ | wake_later,
00146             sync, space, 0, offset, 0, UNIV_PAGE_SIZE,
00147             ((buf_block_t*) bpage)->frame, bpage);
00148   }
00149   ut_a(*err == DB_SUCCESS);
00150 
00151   if (sync) {
00152     /* The i/o is already completed when we arrive from
00153     fil_read */
00154     buf_page_io_complete(bpage);
00155   }
00156 
00157   return(1);
00158 }
00159 
00160 /********************************************************************/
00166 UNIV_INTERN
00167 ibool
00168 buf_read_page(
00169 /*==========*/
00170   ulint space,  
00171   ulint zip_size,
00172   ulint offset) 
00173 {
00174   buf_pool_t* buf_pool = buf_pool_get(space, offset);
00175   ib_int64_t  tablespace_version;
00176   ulint   count;
00177   ulint   err;
00178 
00179   tablespace_version = fil_space_get_version(space);
00180 
00181   /* We do the i/o in the synchronous aio mode to save thread
00182   switches: hence TRUE */
00183 
00184         count = buf_read_page_low(&err, TRUE, BUF_READ_ANY_PAGE, space,
00185                                   zip_size, FALSE,
00186                                   tablespace_version, offset);
00187         srv_buf_pool_reads += count;
00188   if (err == DB_TABLESPACE_DELETED) {
00189     ut_print_timestamp(stderr);
00190     fprintf(stderr,
00191       "  InnoDB: Error: trying to access"
00192       " tablespace %lu page no. %lu,\n"
00193       "InnoDB: but the tablespace does not exist"
00194       " or is just being dropped.\n",
00195       (ulong) space, (ulong) offset);
00196   }
00197 
00198   /* Flush pages from the end of the LRU list if necessary */
00199   buf_flush_free_margin(buf_pool);
00200 
00201   /* Increment number of I/O operations used for LRU policy. */
00202   buf_LRU_stat_inc_io();
00203 
00204         return(count);
00205 }
00206 
00207 /********************************************************************/
00231 UNIV_INTERN
00232 ulint
00233 buf_read_ahead_linear(
00234 /*==================*/
00235   ulint space,  
00236   ulint zip_size,
00237   ulint offset) 
00239 {
00240   buf_pool_t* buf_pool = buf_pool_get(space, offset);
00241   ib_int64_t  tablespace_version;
00242   buf_page_t* bpage;
00243   buf_frame_t*  frame;
00244   buf_page_t* pred_bpage  = NULL;
00245   ulint   pred_offset;
00246   ulint   succ_offset;
00247   ulint   count;
00248   int   asc_or_desc;
00249   ulint   new_offset;
00250   ulint   fail_count;
00251   ulint   ibuf_mode;
00252   ulint   low, high;
00253   ulint   err;
00254   ulint   i;
00255   const ulint buf_read_ahead_linear_area
00256     = BUF_READ_AHEAD_LINEAR_AREA(buf_pool);
00257   ulint   threshold;
00258 
00259   if ((srv_read_ahead & 2) == false) {
00260     return(0);
00261   }
00262 
00263   if (UNIV_UNLIKELY(srv_startup_is_before_trx_rollback_phase)) {
00264     /* No read-ahead to avoid thread deadlocks */
00265     return(0);
00266   }
00267 
00268   low  = (offset / buf_read_ahead_linear_area)
00269     * buf_read_ahead_linear_area;
00270   high = (offset / buf_read_ahead_linear_area + 1)
00271     * buf_read_ahead_linear_area;
00272 
00273   if ((offset != low) && (offset != high - 1)) {
00274     /* This is not a border page of the area: return */
00275 
00276     return(0);
00277   }
00278 
00279   if (ibuf_bitmap_page(zip_size, offset)
00280       || trx_sys_hdr_page(space, offset)) {
00281 
00282     /* If it is an ibuf bitmap page or trx sys hdr, we do
00283     no read-ahead, as that could break the ibuf page access
00284     order */
00285 
00286     return(0);
00287   }
00288 
00289   /* Remember the tablespace version before we ask te tablespace size
00290   below: if DISCARD + IMPORT changes the actual .ibd file meanwhile, we
00291   do not try to read outside the bounds of the tablespace! */
00292 
00293   tablespace_version = fil_space_get_version(space);
00294 
00295   buf_pool_mutex_enter(buf_pool);
00296 
00297   if (high > fil_space_get_size(space)) {
00298     buf_pool_mutex_exit(buf_pool);
00299     /* The area is not whole, return */
00300 
00301     return(0);
00302   }
00303 
00304   if (buf_pool->n_pend_reads
00305       > buf_pool->curr_size / BUF_READ_AHEAD_PEND_LIMIT) {
00306     buf_pool_mutex_exit(buf_pool);
00307 
00308     return(0);
00309   }
00310 
00311   /* Check that almost all pages in the area have been accessed; if
00312   offset == low, the accesses must be in a descending order, otherwise,
00313   in an ascending order. */
00314 
00315   asc_or_desc = 1;
00316 
00317   if (offset == low) {
00318     asc_or_desc = -1;
00319   }
00320 
00321   /* How many out of order accessed pages can we ignore
00322   when working out the access pattern for linear readahead */
00323   threshold = ut_min((64 - srv_read_ahead_threshold),
00324          BUF_READ_AHEAD_AREA(buf_pool));
00325 
00326   fail_count = 0;
00327 
00328   for (i = low; i < high; i++) {
00329     bpage = buf_page_hash_get(buf_pool, space, i);
00330 
00331     if (bpage == NULL || !buf_page_is_accessed(bpage)) {
00332       /* Not accessed */
00333       fail_count++;
00334 
00335     } else if (pred_bpage) {
00336                        /* Note that buf_page_is_accessed() returns
00337                        the time of the first access.  If some blocks
00338                        of the extent existed in the buffer pool at
00339                        the time of a linear access pattern, the first
00340                        access times may be nonmonotonic, even though
00341                        the latest access times were linear.  The
00342                        threshold (srv_read_ahead_factor) should help
00343                        a little against this. */
00344                        int res = ut_ulint_cmp(
00345                                buf_page_is_accessed(bpage),
00346                                buf_page_is_accessed(pred_bpage));
00347       /* Accesses not in the right order */
00348       if (res != 0 && res != asc_or_desc) {
00349         fail_count++;
00350       }
00351     }
00352 
00353     if (fail_count > threshold) {
00354       /* Too many failures: return */
00355       buf_pool_mutex_exit(buf_pool);
00356       return(0);
00357     }
00358 
00359     if (bpage && buf_page_is_accessed(bpage)) {
00360       pred_bpage = bpage;
00361     }
00362   }
00363 
00364   /* If we got this far, we know that enough pages in the area have
00365   been accessed in the right order: linear read-ahead can be sensible */
00366 
00367   bpage = buf_page_hash_get(buf_pool, space, offset);
00368 
00369   if (bpage == NULL) {
00370     buf_pool_mutex_exit(buf_pool);
00371 
00372     return(0);
00373   }
00374 
00375   switch (buf_page_get_state(bpage)) {
00376   case BUF_BLOCK_ZIP_PAGE:
00377     frame = bpage->zip.data;
00378     break;
00379   case BUF_BLOCK_FILE_PAGE:
00380     frame = ((buf_block_t*) bpage)->frame;
00381     break;
00382   default:
00383     ut_error;
00384     break;
00385   }
00386 
00387   /* Read the natural predecessor and successor page addresses from
00388   the page; NOTE that because the calling thread may have an x-latch
00389   on the page, we do not acquire an s-latch on the page, this is to
00390   prevent deadlocks. Even if we read values which are nonsense, the
00391   algorithm will work. */
00392 
00393   pred_offset = fil_page_get_prev(frame);
00394   succ_offset = fil_page_get_next(frame);
00395 
00396   buf_pool_mutex_exit(buf_pool);
00397 
00398   if ((offset == low) && (succ_offset == offset + 1)) {
00399 
00400     /* This is ok, we can continue */
00401     new_offset = pred_offset;
00402 
00403   } else if ((offset == high - 1) && (pred_offset == offset - 1)) {
00404 
00405     /* This is ok, we can continue */
00406     new_offset = succ_offset;
00407   } else {
00408     /* Successor or predecessor not in the right order */
00409 
00410     return(0);
00411   }
00412 
00413   low  = (new_offset / buf_read_ahead_linear_area)
00414     * buf_read_ahead_linear_area;
00415   high = (new_offset / buf_read_ahead_linear_area + 1)
00416     * buf_read_ahead_linear_area;
00417 
00418   if ((new_offset != low) && (new_offset != high - 1)) {
00419     /* This is not a border page of the area: return */
00420 
00421     return(0);
00422   }
00423 
00424   if (high > fil_space_get_size(space)) {
00425     /* The area is not whole, return */
00426 
00427     return(0);
00428   }
00429 
00430   /* If we got this far, read-ahead can be sensible: do it */
00431 
00432   if (ibuf_inside()) {
00433     ibuf_mode = BUF_READ_IBUF_PAGES_ONLY;
00434   } else {
00435     ibuf_mode = BUF_READ_ANY_PAGE;
00436   }
00437 
00438   count = 0;
00439 
00440   /* Since Windows XP seems to schedule the i/o handler thread
00441   very eagerly, and consequently it does not wait for the
00442   full read batch to be posted, we use special heuristics here */
00443 
00444   os_aio_simulated_put_read_threads_to_sleep();
00445 
00446   for (i = low; i < high; i++) {
00447     /* It is only sensible to do read-ahead in the non-sync
00448     aio mode: hence FALSE as the first parameter */
00449 
00450     if (!ibuf_bitmap_page(zip_size, i)) {
00451       count += buf_read_page_low(
00452         &err, FALSE,
00453         ibuf_mode | OS_AIO_SIMULATED_WAKE_LATER,
00454         space, zip_size, FALSE, tablespace_version, i);
00455       if (err == DB_TABLESPACE_DELETED) {
00456         ut_print_timestamp(stderr);
00457         fprintf(stderr,
00458           "  InnoDB: Warning: in"
00459           " linear readahead trying to access\n"
00460           "InnoDB: tablespace %lu page %lu,\n"
00461           "InnoDB: but the tablespace does not"
00462           " exist or is just being dropped.\n",
00463           (ulong) space, (ulong) i);
00464       }
00465     }
00466   }
00467 
00468   /* In simulated aio we wake the aio handler threads only after
00469   queuing all aio requests, in native aio the following call does
00470   nothing: */
00471 
00472   os_aio_simulated_wake_handler_threads();
00473 
00474   /* Flush pages from the end of the LRU list if necessary */
00475   buf_flush_free_margin(buf_pool);
00476 
00477 #ifdef UNIV_DEBUG
00478   if (buf_debug_prints && (count > 0)) {
00479     fprintf(stderr,
00480       "LINEAR read-ahead space %lu offset %lu pages %lu\n",
00481       (ulong) space, (ulong) offset, (ulong) count);
00482   }
00483 #endif /* UNIV_DEBUG */
00484 
00485   /* Read ahead is considered one I/O operation for the purpose of
00486   LRU policy decision. */
00487   buf_LRU_stat_inc_io();
00488 
00489   buf_pool->stat.n_ra_pages_read += count;
00490   return(count);
00491 }
00492 
00493 /********************************************************************/
00497 UNIV_INTERN
00498 void
00499 buf_read_ibuf_merge_pages(
00500 /*======================*/
00501   ibool   sync,   
00506   const ulint*  space_ids,  
00507   const ib_int64_t* space_versions,
00514   const ulint*  page_nos, 
00518   ulint   n_stored) 
00520 {
00521   ulint i;
00522 
00523   ut_ad(!ibuf_inside());
00524 #ifdef UNIV_IBUF_DEBUG
00525   ut_a(n_stored < UNIV_PAGE_SIZE);
00526 #endif
00527 
00528   for (i = 0; i < n_stored; i++) {
00529     ulint   err;
00530     buf_pool_t* buf_pool;
00531     ulint   zip_size = fil_space_get_zip_size(space_ids[i]);
00532 
00533     buf_pool = buf_pool_get(space_ids[i], space_versions[i]);
00534 
00535     while (buf_pool->n_pend_reads
00536            > buf_pool->curr_size / BUF_READ_AHEAD_PEND_LIMIT) {
00537       os_thread_sleep(500000);
00538     }
00539 
00540     if (UNIV_UNLIKELY(zip_size == ULINT_UNDEFINED)) {
00541 
00542       goto tablespace_deleted;
00543     }
00544 
00545     buf_read_page_low(&err, sync && (i + 1 == n_stored),
00546           BUF_READ_ANY_PAGE, space_ids[i],
00547           zip_size, TRUE, space_versions[i],
00548           page_nos[i]);
00549 
00550     if (UNIV_UNLIKELY(err == DB_TABLESPACE_DELETED)) {
00551 tablespace_deleted:
00552       /* We have deleted or are deleting the single-table
00553       tablespace: remove the entries for that page */
00554 
00555       ibuf_merge_or_delete_for_page(NULL, space_ids[i],
00556                   page_nos[i],
00557                   zip_size, FALSE);
00558     }
00559   }
00560 
00561   os_aio_simulated_wake_handler_threads();
00562 
00563   /* Flush pages from the end of all the LRU lists if necessary */
00564   buf_flush_free_margins();
00565 
00566 #ifdef UNIV_DEBUG
00567   if (buf_debug_prints) {
00568     fprintf(stderr,
00569       "Ibuf merge read-ahead space %lu pages %lu\n",
00570       (ulong) space_ids[0], (ulong) n_stored);
00571   }
00572 #endif /* UNIV_DEBUG */
00573 }
00574 
00575 /********************************************************************/
00577 UNIV_INTERN
00578 void
00579 buf_read_recv_pages(
00580 /*================*/
00581   ibool   sync,   
00586   ulint   space,    
00587   ulint   zip_size, 
00589   const ulint*  page_nos, 
00593   ulint   n_stored) 
00595 {
00596   ib_int64_t  tablespace_version;
00597   ulint   count;
00598   ulint   err;
00599   ulint   i;
00600 
00601   zip_size = fil_space_get_zip_size(space);
00602 
00603   if (UNIV_UNLIKELY(zip_size == ULINT_UNDEFINED)) {
00604     /* It is a single table tablespace and the .ibd file is
00605     missing: do nothing */
00606 
00607     return;
00608   }
00609 
00610   tablespace_version = fil_space_get_version(space);
00611 
00612   for (i = 0; i < n_stored; i++) {
00613     buf_pool_t* buf_pool;
00614 
00615     count = 0;
00616 
00617     os_aio_print_debug = FALSE;
00618     buf_pool = buf_pool_get(space, page_nos[i]);
00619     while (buf_pool->n_pend_reads >= recv_n_pool_free_frames / 2) {
00620 
00621       os_aio_simulated_wake_handler_threads();
00622       os_thread_sleep(10000);
00623 
00624       count++;
00625 
00626       if (count > 1000) {
00627         fprintf(stderr,
00628           "InnoDB: Error: InnoDB has waited for"
00629           " 10 seconds for pending\n"
00630           "InnoDB: reads to the buffer pool to"
00631           " be finished.\n"
00632           "InnoDB: Number of pending reads %lu,"
00633           " pending pread calls %lu\n",
00634           (ulong) buf_pool->n_pend_reads,
00635           (ulong)os_file_n_pending_preads);
00636 
00637         os_aio_print_debug = TRUE;
00638       }
00639     }
00640 
00641     os_aio_print_debug = FALSE;
00642 
00643     if ((i + 1 == n_stored) && sync) {
00644       buf_read_page_low(&err, TRUE, BUF_READ_ANY_PAGE, space,
00645             zip_size, TRUE, tablespace_version,
00646             page_nos[i]);
00647     } else {
00648       buf_read_page_low(&err, FALSE, BUF_READ_ANY_PAGE
00649             | OS_AIO_SIMULATED_WAKE_LATER,
00650             space, zip_size, TRUE,
00651             tablespace_version, page_nos[i]);
00652     }
00653   }
00654 
00655   os_aio_simulated_wake_handler_threads();
00656 
00657   /* Flush pages from the end of all the LRU lists if necessary */
00658   buf_flush_free_margins();
00659 
00660 #ifdef UNIV_DEBUG
00661   if (buf_debug_prints) {
00662     fprintf(stderr,
00663       "Recovery applies read-ahead pages %lu\n",
00664       (ulong) n_stored);
00665   }
00666 #endif /* UNIV_DEBUG */
00667 }