BTreePrefetchSearchExecStream.cpp

Go to the documentation of this file.
00001 /*
00002 // $Id: //open/dev/fennel/ftrs/BTreePrefetchSearchExecStream.cpp#7 $
00003 // Fennel is a library of data storage and processing components.
00004 // Copyright (C) 2005-2009 The Eigenbase Project
00005 // Copyright (C) 2005-2009 SQLstream, Inc.
00006 // Copyright (C) 2005-2009 LucidEra, Inc.
00007 // Portions Copyright (C) 2004-2009 John V. Sichi
00008 //
00009 // This program is free software; you can redistribute it and/or modify it
00010 // under the terms of the GNU General Public License as published by the Free
00011 // Software Foundation; either version 2 of the License, or (at your option)
00012 // any later version approved by The Eigenbase Project.
00013 //
00014 // This program is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 // GNU General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU General Public License
00020 // along with this program; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 */
00023 
00024 #include "fennel/common/CommonPreamble.h"
00025 #include "fennel/btree/BTreeNonLeafReader.h"
00026 #include "fennel/btree/BTreeLeafReader.h"
00027 #include "fennel/btree/BTreeNodeAccessor.h"
00028 #include "fennel/segment/SegPageEntryIterImpl.h"
00029 #include "fennel/exec/ExecStreamBufAccessor.h"
00030 #include "fennel/ftrs/BTreePrefetchSearchExecStream.h"
00031 
00032 #include "math.h"
00033 
00034 FENNEL_BEGIN_CPPFILE("$Id: //open/dev/fennel/ftrs/BTreePrefetchSearchExecStream.cpp#7 $");
00035 
00036 BTreePrefetchSearchExecStream::BTreePrefetchSearchExecStream() : leafPageQueue()
00037 {
00038 }
00039 
00040 void BTreePrefetchSearchExecStream::prepare(
00041     BTreePrefetchSearchExecStreamParams const &params)
00042 {
00043     BTreeSearchExecStream::prepare(params);
00044 
00045     savedLowerBoundAccessor.compute(inputKeyDesc);
00046     pfLowerBoundData.compute(inputKeyDesc);
00047     if (upperBoundDesc.size() == 0) {
00048         savedUpperBoundAccessor.compute(inputKeyDesc);
00049         pfUpperBoundData.compute(inputKeyDesc);
00050     } else {
00051         savedUpperBoundAccessor.compute(upperBoundDesc);
00052         pfUpperBoundData.compute(upperBoundDesc);
00053     }
00054 
00055     scratchLock.accessSegment(scratchAccessor);
00056     scratchPageSize = scratchAccessor.pSegment->getUsablePageSize();
00057 }
00058 
00059 void BTreePrefetchSearchExecStream::getResourceRequirements(
00060     ExecStreamResourceQuantity &minQuantity,
00061     ExecStreamResourceQuantity &optQuantity,
00062     ExecStreamResourceSettingType &optType)
00063 {
00064     BTreeSearchExecStream::getResourceRequirements(minQuantity, optQuantity);
00065 
00066     // Need one more page because there are two btree readers
00067     minQuantity.nCachePages += 1;
00068     optQuantity.nCachePages += 1;
00069     nMiscCachePages = minQuantity.nCachePages;
00070 
00071     // Set aside pages for storing search key information associated with
00072     // pre-fetched index leaf pages.  At a minimum, we need one page, but ask
00073     // for extra if it's available, based on the space required for the
00074     // search key information, up to a max of 8K pre-fetch entries.
00075     minQuantity.nCachePages += 1;
00076     keyValuesSize = savedLowerBoundAccessor.getMaxByteCount() * 2;
00077 
00078     // The actual size of the keys should never exceed a page size, but we're
00079     // trying to store at least 2 keys on each page.  If the max size is such
00080     // that we can only store a single key on a page, then set aside 2 entire
00081     // pages, one per key.
00082     if (keyValuesSize > scratchPageSize) {
00083         bigMaxKey = true;
00084         keyValuesSize = scratchPageSize;
00085         minQuantity.nCachePages += 1;
00086     } else {
00087         bigMaxKey = false;
00088     }
00089 
00090     nEntriesPerScratchPage = scratchPageSize / keyValuesSize;
00091     uint optNumPages = (uint) ceil(8192.0 / nEntriesPerScratchPage);
00092     assert(optNumPages >= 1);
00093     optQuantity.nCachePages += optNumPages;
00094     optType = EXEC_RESOURCE_ACCURATE;
00095 }
00096 
00097 void BTreePrefetchSearchExecStream::setResourceAllocation(
00098     ExecStreamResourceQuantity &quantity)
00099 {
00100     BTreeSearchExecStream::setResourceAllocation(quantity);
00101     nPrefetchScratchPages = quantity.nCachePages - nMiscCachePages;
00102 }
00103 
00104 void BTreePrefetchSearchExecStream::open(bool restart)
00105 {
00106     BTreeSearchExecStream::open(restart);
00107     pNonLeafReader =
00108         SharedBTreeNonLeafReader(new BTreeNonLeafReader(treeDescriptor));
00109     initialPrefetchDone = false;
00110     processingDone = false;
00111     prevLeafSearchRc = true;
00112     returnedRoot = false;
00113     rootOnly = (pNonLeafReader->isRootOnly());
00114 
00115     if (!restart) {
00116         uint nPrefetchEntries =
00117             (bigMaxKey) ?
00118                 nPrefetchScratchPages / 2 :
00119                 nPrefetchScratchPages * nEntriesPerScratchPage;
00120         leafPageQueue.resize(nPrefetchEntries);
00121         allocateScratchPages();
00122         leafPageQueue.setPrefetchSource(*this);
00123     }
00124 }
00125 
00126 SharedBTreeReader BTreePrefetchSearchExecStream::newReader()
00127 {
00128     pLeafReader = SharedBTreeLeafReader(new BTreeLeafReader(treeDescriptor));
00129     pBTreeAccessBase = pBTreeReader = pLeafReader;
00130     return pBTreeReader;
00131 }
00132 
00133 void BTreePrefetchSearchExecStream::allocateScratchPages()
00134 {
00135     for (uint i = 0; i < nPrefetchScratchPages; i++) {
00136         scratchLock.allocatePage();
00137         PBuffer page = scratchLock.getPage().getWritableData();
00138         scratchPages.push_back(page);
00139     }
00140     currPage = 0;
00141     currPageEntry = 0;
00142 }
00143 
00144 void BTreePrefetchSearchExecStream::initPrefetchEntry(
00145     BTreePrefetchSearchKey &searchKey)
00146 {
00147     if (bigMaxKey) {
00148         searchKey.lowerKeyBuffer = scratchPages[currPage++];
00149         searchKey.upperKeyBuffer = scratchPages[currPage++];
00150     } else {
00151         searchKey.lowerKeyBuffer =
00152             scratchPages[currPage] + currPageEntry * keyValuesSize;
00153         searchKey.upperKeyBuffer = searchKey.lowerKeyBuffer + keyValuesSize / 2;
00154         if (++currPageEntry == nEntriesPerScratchPage) {
00155             currPage++;
00156             currPageEntry = 0;
00157         }
00158     }
00159 }
00160 
00161 ExecStreamResult BTreePrefetchSearchExecStream::execute(
00162     ExecStreamQuantum const &quantum)
00163 {
00164     uint nTuples = 0;
00165 
00166     // Iterate over each input search key, locating and pre-fetching
00167     // leaf pages that contain matching keys.
00168     for (;;) {
00169         // Position within a pre-fetched leaf page.
00170         if (!innerSearchLoop()) {
00171             return EXECRC_BUF_UNDERFLOW;
00172         }
00173 
00174         // If there are no more search keys in the input and all input has
00175         // been processed, then we've completed execution.
00176         if (pInAccessor->getState() == EXECBUF_EOS && processingDone) {
00177             pOutAccessor->markEOS();
00178             return EXECRC_EOS;
00179         }
00180 
00181         // Then, fetch matching records from that leaf page until we hit
00182         // a record outside the desired search range.
00183         ExecStreamResult rc = innerFetchLoop(quantum, nTuples);
00184         if (rc != EXECRC_YIELD) {
00185             return rc;
00186         }
00187     }
00188 }
00189 
00190 bool BTreePrefetchSearchExecStream::innerSearchLoop()
00191 {
00192     // If we're already positioned within a leaf page, then nothing further
00193     // needs to be done here.
00194     while (!pReader->isPositioned()) {
00195         // Make sure there's input available, in case we're going to
00196         // pre-fetch some pages.
00197         if (pInAccessor->getState() != EXECBUF_EOS &&
00198             !pInAccessor->demandData())
00199         {
00200             return false;
00201         }
00202 
00203         if (!initialPrefetchDone) {
00204             if (pInAccessor->getState() == EXECBUF_EOS) {
00205                 processingDone = true;
00206                 break;
00207             }
00208             getPrefetchSearchKey();
00209             leafPageQueue.mapRange(
00210                 treeDescriptor.segmentAccessor,
00211                 NULL_PAGE_ID);
00212             initialPrefetchDone = true;
00213         } else {
00214             ++leafPageQueue;
00215         }
00216 
00217         std::pair<PageId, BTreePrefetchSearchKey> &prefetchEntry =
00218             *leafPageQueue;
00219         // When we hit the terminating page, then all input search ranges
00220         // have been processed.
00221         if (prefetchEntry.first == NULL_PAGE_ID) {
00222             processingDone = true;
00223             break;
00224         }
00225 
00226         // Setup the search directives and keys to match the criteria
00227         // used to locate the pre-fetch page.  Note that we need to do
00228         // this even in the case where we don't need to search the
00229         // page because the directives and bounds are still needed to
00230         // determine where to end the search.
00231         setUpSearchKey(prefetchEntry.second);
00232         pLeafReader->setCurrentPageId(prefetchEntry.first);
00233 
00234         // If the previous leaf search yielded a non-match, then we need
00235         // to search the current page, even though this is a continuation
00236         // of a previous search.  Otherwise, just start at the first key
00237         // in the new page.
00238         if (prefetchEntry.second.newSearch || !prevLeafSearchRc) {
00239             prevLeafSearchRc = searchForKey();
00240             if (prevLeafSearchRc) {
00241                 break;
00242             }
00243         } else {
00244             if (pReader->searchFirst() && testInterval()) {
00245                 projAccessor.unmarshal(tupleData.begin());
00246                 break;
00247             }
00248         }
00249         // If the current leaf page yielded no matches, end the search on it,
00250         // and loop back to search the next leaf page.
00251         pReader->endSearch();
00252     }
00253     return true;
00254 }
00255 
00256 void BTreePrefetchSearchExecStream::getPrefetchSearchKey()
00257 {
00258     readSearchKey();
00259     readDirectives();
00260     setAdditionalKeys();
00261     readUpperBoundKey();
00262     pfLowerBoundDirective = lowerBoundDirective;
00263     pfUpperBoundDirective = upperBoundDirective;
00264     pfLowerBoundData = *pSearchKey;
00265     pfUpperBoundData = upperBoundData;
00266 }
00267 
00268 void BTreePrefetchSearchExecStream::setUpSearchKey(
00269     BTreePrefetchSearchKey const &searchKey)
00270 {
00271     lowerBoundDirective = searchKey.lowerBoundDirective;
00272     upperBoundDirective = searchKey.upperBoundDirective;
00273     setLowerBoundKey(searchKey.lowerKeyBuffer);
00274     savedUpperBoundAccessor.setCurrentTupleBuf(searchKey.upperKeyBuffer);
00275     savedUpperBoundAccessor.unmarshal(upperBoundData);
00276 }
00277 
00278 void BTreePrefetchSearchExecStream::setLowerBoundKey(PConstBuffer buf)
00279 {
00280     savedLowerBoundAccessor.setCurrentTupleBuf(buf);
00281     savedLowerBoundAccessor.unmarshal(inputKeyData);
00282     pSearchKey = &inputKeyData;
00283 }
00284 
00285 void BTreePrefetchSearchExecStream::setAdditionalKeys()
00286 {
00287     pSearchKey = &inputKeyData;
00288 }
00289 
00290 PageId BTreePrefetchSearchExecStream::getNextPageForPrefetch(
00291     BTreePrefetchSearchKey &searchKey,
00292     bool &found)
00293 {
00294     found = true;
00295 
00296     // Handle the special case where the tree consists of a single root page
00297     // by simply pre-fetching that root page for each search range.  Note
00298     // that the page will actually only be pre-fetched once.
00299     if (rootOnly) {
00300         while (true) {
00301             if (returnedRoot) {
00302                 if (pInAccessor->getState() == EXECBUF_EOS) {
00303                     return NULL_PAGE_ID;
00304                 } else if (!pInAccessor->demandData()) {
00305                     found = false;
00306                     return NULL_PAGE_ID;
00307                 }
00308                 returnedRoot = false;
00309                 getPrefetchSearchKey();
00310             }
00311             returnedRoot = true;
00312             setSearchKeyData(true, searchKey);
00313             pInAccessor->consumeTuple();
00314             return pNonLeafReader->getRootPageId();
00315         }
00316     }
00317 
00318     bool first;
00319 
00320     while (true) {
00321         // If we're already positioned within the non-leaf page, then continue
00322         // searching the page, unless the previous key was larger than the
00323         // upper bound, in which case, the previous page is the last matching
00324         // leaf page, and therefore, we should end the current search.
00325         if (pNonLeafReader->isPositioned()) {
00326             first = false;
00327             if (endOnNextKey) {
00328                 pInAccessor->consumeTuple();
00329                 pNonLeafReader->endSearch();
00330                 continue;
00331             } else {
00332                 pNonLeafReader->searchNext();
00333             }
00334 
00335         // Otherwise, initiate a search on the non-leaf page based on the lower
00336         // bound directive.  If necessary, first read the search directives
00337         // from the input stream, provided there's input available.
00338         } else {
00339             if (!pInAccessor->isTupleConsumptionPending()) {
00340                 // If the current input stream buffer is exhausted, passing
00341                 // back a NULL_PAGE_ID will signal innerSearchLoop() to
00342                 // map a new range of pre-fetches.
00343                 if (pInAccessor->getState() == EXECBUF_EOS) {
00344                     return NULL_PAGE_ID;
00345                 } else if (!pInAccessor->demandData()) {
00346                     found = false;
00347                     return NULL_PAGE_ID;
00348                 }
00349                 getPrefetchSearchKey();
00350             }
00351             first = true;
00352             switch (pfLowerBoundDirective) {
00353             case SEARCH_UNBOUNDED_LOWER:
00354                 if (pfLowerBoundData.size() <= 1) {
00355                     pNonLeafReader->searchFirst();
00356                     break;
00357                 }
00358             // Fall through to handle the case where we have > 1 key and a
00359             // non-equality search on the last key.  In this case, we need to
00360             // position to the equality portion of the key
00361             case SEARCH_CLOSED_LOWER:
00362                 pNonLeafReader->searchForKey(
00363                     pfLowerBoundData, DUP_SEEK_BEGIN, leastUpper);
00364                 break;
00365             case SEARCH_OPEN_LOWER:
00366                 pNonLeafReader->searchForKey(
00367                     pfLowerBoundData, DUP_SEEK_END, leastUpper);
00368                 break;
00369             default:
00370                 permFail(
00371                     "unexpected lower bound directive:  "
00372                     << (char) lowerBoundDirective);
00373             }
00374         }
00375 
00376         // See if the key found corresponds to the last possible matching
00377         // key before passing back the pre-fetch info.  Note that we should
00378         // always find a key because at a minimum, we'll find the infinity
00379         // key.
00380         assert(!pNonLeafReader->isSingular());
00381         endOnNextKey = testNonLeafInterval();
00382         setSearchKeyData(first, searchKey);
00383         return pNonLeafReader->getChildForCurrent();
00384     }
00385 }
00386 
00387 void BTreePrefetchSearchExecStream::setSearchKeyData(
00388     bool newSearch,
00389     BTreePrefetchSearchKey &searchKey)
00390 {
00391     searchKey.newSearch = newSearch;
00392     searchKey.lowerBoundDirective =
00393         pfLowerBoundDirective;
00394     searchKey.upperBoundDirective =
00395         pfUpperBoundDirective;
00396     if (bigMaxKey) {
00397         assert(
00398             savedLowerBoundAccessor.getByteCount(pfLowerBoundData)
00399                 <= keyValuesSize);
00400         assert(
00401             savedUpperBoundAccessor.getByteCount(pfUpperBoundData)
00402                 <= keyValuesSize);
00403     }
00404     savedLowerBoundAccessor.marshal(pfLowerBoundData, searchKey.lowerKeyBuffer);
00405     savedUpperBoundAccessor.marshal(pfUpperBoundData, searchKey.upperKeyBuffer);
00406 }
00407 
00408 bool BTreePrefetchSearchExecStream::testNonLeafInterval()
00409 {
00410     // If the search has positioned all the way to the right-most node, then
00411     // this is the last non-leaf key.
00412     if (pNonLeafReader->isPositionedOnInfinityKey()) {
00413         return true;
00414     }
00415 
00416     BTreeNodeAccessor &nodeAccessor =
00417         pNonLeafReader->getNonLeafNodeAccessor();
00418     if (pfUpperBoundDirective == SEARCH_UNBOUNDED_UPPER) {
00419         // If there is more one search key in an unbounded search, then the
00420         // first part of the key is an equality.  In this case, we have
00421         // to check the equality part of the key to determine whether this
00422         // will be the last matching key.
00423         if (pfLowerBoundData.size() > 1) {
00424             nodeAccessor.unmarshalKey(readerKeyData);
00425             int c =
00426                 inputKeyDesc.compareTuplesKey(
00427                     pfLowerBoundData,
00428                     readerKeyData,
00429                     pfLowerBoundData.size() - 1);
00430             // Should never hit c > 0 because the lower bound search should
00431             // not position on a key that the lower bound is greater than.
00432             permAssert(c <= 0);
00433             return (c < 0);
00434         } else {
00435             return false;
00436         }
00437     } else {
00438         nodeAccessor.unmarshalKey(readerKeyData);
00439         int c = inputKeyDesc.compareTuples(pfUpperBoundData, readerKeyData);
00440         // If the upper bound is less than, or equal to the non-leaf key in
00441         // an open upper search, then this is the last possible matching key.
00442         if (c == 0) {
00443             if (pfUpperBoundDirective == SEARCH_OPEN_UPPER) {
00444                 c = -1;
00445             }
00446         }
00447         return (c < 0);
00448     }
00449 }
00450 
00451 void BTreePrefetchSearchExecStream::closeImpl()
00452 {
00453     BTreeSearchExecStream::closeImpl();
00454     scratchPages.clear();
00455     if (scratchAccessor.pSegment) {
00456         scratchAccessor.pSegment->deallocatePageRange(
00457             NULL_PAGE_ID, NULL_PAGE_ID);
00458     }
00459 }
00460 
00461 FENNEL_END_CPPFILE("$Id: //open/dev/fennel/ftrs/BTreePrefetchSearchExecStream.cpp#7 $");
00462 
00463 // End BTreePrefetchSearchExecStream.cpp

Generated on Mon Jun 22 04:00:19 2009 for Fennel by  doxygen 1.5.1