SnapshotRandomAllocationSegment.cpp

Go to the documentation of this file.
00001 /*
00002 // $Id: //open/dev/fennel/segment/SnapshotRandomAllocationSegment.cpp#16 $
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) 1999-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/cache/PagePredicate.h"
00026 #include "fennel/segment/SnapshotRandomAllocationSegment.h"
00027 #include "fennel/segment/SegmentFactory.h"
00028 #include "fennel/segment/SegmentAccessor.h"
00029 
00030 FENNEL_BEGIN_CPPFILE("$Id: //open/dev/fennel/segment/SnapshotRandomAllocationSegment.cpp#16 $");
00031 
00032 SnapshotRandomAllocationSegment::SnapshotRandomAllocationSegment(
00033     SharedSegment delegateSegment,
00034     SharedSegment versionedSegment,
00035     TxnId snapshotCsnInit,
00036     bool readOnlyCommittedDataInit)
00037     : DelegatingSegment(delegateSegment)
00038 {
00039     pVersionedRandomSegment =
00040         SegmentFactory::dynamicCast<VersionedRandomAllocationSegment *>(
00041             versionedSegment);
00042     assert(pVersionedRandomSegment);
00043 
00044     snapshotCsn = snapshotCsnInit;
00045     readOnlyCommittedData = readOnlyCommittedDataInit;
00046     needPageFlush = false;
00047     forceCacheUnmap = false;
00048 }
00049 
00050 TxnId SnapshotRandomAllocationSegment::getSnapshotCsn()
00051 {
00052     return snapshotCsn;
00053 }
00054 
00055 BlockId SnapshotRandomAllocationSegment::translatePageId(PageId pageId)
00056 {
00057     PageId snapshotId = getSnapshotId(pageId);
00058     return DelegatingSegment::translatePageId(snapshotId);
00059 }
00060 
00061 PageId SnapshotRandomAllocationSegment::getSnapshotId(PageId pageId)
00062 {
00063     StrictMutexGuard mutexGuard(snapshotPageMapMutex);
00064 
00065     // If possible, use the mapping we've previously cached
00066     PageMapConstIter pSnapshotPageId = snapshotPageMap.find(pageId);
00067     if (pSnapshotPageId != snapshotPageMap.end()) {
00068         return pSnapshotPageId->second;
00069     }
00070 
00071     VersionedPageEntry pageEntry;
00072     pVersionedRandomSegment->getLatestPageEntryCopy(pageId, pageEntry);
00073     // Handle the special case where there's no chain
00074     if (pageEntry.versionChainPageId == pageId) {
00075         assert(snapshotCsn >= pageEntry.allocationCsn);
00076         snapshotPageMap[pageId] = pageId;
00077         return pageId;
00078     }
00079 
00080     // If we have to walk through the page chain, then we need to be starting
00081     // from the anchor.  Note that there's no need to acquire the deallocation
00082     // mutex while walking through the page chain looking for the appropriate
00083     // snapshot page because we always start at the anchor and walk from
00084     // newer pages to older pages.  Therefore, we should never try reading
00085     // the pageEntry for an older page that's going to be deallocated.
00086     assert(pageId == getAnchorPageId(pageId));
00087     PageId chainPageId = pageEntry.versionChainPageId;
00088     do {
00089         pVersionedRandomSegment->getLatestPageEntryCopy(chainPageId, pageEntry);
00090         if (snapshotCsn >= pageEntry.allocationCsn) {
00091             // only consider uncommitted pageEntry's if they correspond to
00092             // the current txn
00093             if ((!readOnlyCommittedData &&
00094                     pageEntry.ownerId == UNCOMMITTED_PAGE_OWNER_ID &&
00095                     snapshotCsn == pageEntry.allocationCsn) ||
00096                 pageEntry.ownerId != UNCOMMITTED_PAGE_OWNER_ID)
00097             {
00098                 snapshotPageMap[pageId] = chainPageId;
00099                 return chainPageId;
00100             }
00101         }
00102         // permAssert to prevent an infinite loop
00103         permAssert(chainPageId != pageId);
00104         chainPageId = pageEntry.versionChainPageId;
00105     } while (true);
00106 }
00107 
00108 PageId SnapshotRandomAllocationSegment::getPageSuccessor(PageId pageId)
00109 {
00110     PageId snapshotId = getSnapshotId(pageId);
00111     return DelegatingSegment::getPageSuccessor(snapshotId);
00112 }
00113 
00114 void SnapshotRandomAllocationSegment::setPageSuccessor(
00115     PageId pageId, PageId successorId)
00116 {
00117     assert(!readOnlyCommittedData);
00118 
00119     // The successor should be set in the latest page version.  The
00120     // pageId passed in may correspond to the anchor.
00121     PageId snapshotId = getSnapshotId(pageId);
00122     DelegatingSegment::setPageSuccessor(snapshotId, successorId);
00123 
00124     SXMutexExclusiveGuard mapGuard(modPageMapMutex);
00125     incrPageUpdateCount(
00126         snapshotId,
00127         ANON_PAGE_OWNER_ID,
00128         ModifiedPageEntry::MODIFIED);
00129 }
00130 
00131 void SnapshotRandomAllocationSegment::incrPageUpdateCount(
00132     PageId pageId,
00133     PageOwnerId ownerId,
00134     ModifiedPageEntry::ModType modType)
00135 {
00136     assert(modPageMapMutex.isLocked(LOCKMODE_X));
00137 
00138     needPageFlush = true;
00139 
00140     // Add an entry into the map if it's not there yet.  Otherwise, increment
00141     // the count for the existing entry.
00142     ModifiedPageEntryMapIter iter = modPageEntriesMap.find(pageId);
00143     SharedModifiedPageEntry pModPageEntry;
00144     if (iter == modPageEntriesMap.end()) {
00145         pModPageEntry = SharedModifiedPageEntry(new ModifiedPageEntry());
00146         pModPageEntry->updateCount = 0;
00147         pModPageEntry->allocationCount = 0;
00148         pModPageEntry->lastModType = modType;
00149         pModPageEntry->ownerId = ownerId;
00150     } else {
00151         pModPageEntry = iter->second;
00152         // Once we've deallocated a page, we should not be reusing the
00153         // pageId
00154         assert(pModPageEntry->lastModType != ModifiedPageEntry::DEALLOCATED);
00155     }
00156 
00157     // Update counts corresponding to updates to the SegmentAllocationNode
00158     if (modType == ModifiedPageEntry::ALLOCATED) {
00159         pModPageEntry->allocationCount++;
00160         assert(pModPageEntry->allocationCount <= 1);
00161         pModPageEntry->lastModType = modType;
00162         pModPageEntry->ownerId = ownerId;
00163     } else if (modType == ModifiedPageEntry::DEALLOCATED) {
00164         pModPageEntry->lastModType = modType;
00165     }
00166 
00167     // Update count corresponding to the VersionedExtentAllocationNode
00168     pModPageEntry->updateCount++;
00169 
00170     if (iter == modPageEntriesMap.end()) {
00171         modPageEntriesMap.insert(
00172             ModifiedPageEntryMap::value_type(pageId, pModPageEntry));
00173     }
00174 }
00175 
00176 PageId SnapshotRandomAllocationSegment::getAnchorPageId(PageId snapshotId)
00177 {
00178     // Acquire the deallocation mutex to prevent the page chain from being
00179     // modified while we're walking through it
00180     SXMutexSharedGuard(pVersionedRandomSegment->getDeallocationMutex());
00181 
00182     // Walk the page chain to find the anchor, looking for the entry with
00183     // the minimum allocationCsn.
00184     PageId chainPageId = snapshotId;
00185     VersionedPageEntry pageEntry;
00186     PageId anchorPageId = NULL_PAGE_ID;
00187     TxnId minCsn = NULL_TXN_ID;
00188     do {
00189         pVersionedRandomSegment->getLatestPageEntryCopy(chainPageId, pageEntry);
00190         if (chainPageId == snapshotId || pageEntry.allocationCsn < minCsn) {
00191             minCsn = pageEntry.allocationCsn;
00192             anchorPageId = chainPageId;
00193         } else if (pageEntry.allocationCsn > minCsn) {
00194             break;
00195         }
00196         chainPageId = pageEntry.versionChainPageId;
00197     } while (chainPageId != snapshotId);
00198 
00199     return anchorPageId;
00200 }
00201 
00202 PageId SnapshotRandomAllocationSegment::allocatePageId(PageOwnerId ownerId)
00203 {
00204     assert(!readOnlyCommittedData);
00205     SXMutexExclusiveGuard mapGuard(modPageMapMutex);
00206 
00207     PageId pageId =
00208         DelegatingSegment::allocatePageId(UNCOMMITTED_PAGE_OWNER_ID);
00209     incrPageUpdateCount(pageId, ownerId, ModifiedPageEntry::ALLOCATED);
00210     pVersionedRandomSegment->initPageEntry(
00211         pageId,
00212         pageId,
00213         snapshotCsn);
00214     incrPageUpdateCount(
00215         pageId,
00216         ANON_PAGE_OWNER_ID,
00217         ModifiedPageEntry::MODIFIED);
00218     return pageId;
00219 }
00220 
00221 void SnapshotRandomAllocationSegment::deallocatePageRange(
00222     PageId startPageId,
00223     PageId endPageId)
00224 {
00225     assert(!readOnlyCommittedData);
00226     permAssert(startPageId != NULL_PAGE_ID);
00227     permAssert(startPageId == endPageId);
00228 
00229     SXMutexExclusiveGuard mapGuard(modPageMapMutex);
00230     StrictMutexGuard mutexGuard(snapshotPageMapMutex);
00231 
00232     // Mark the pages in the page chain as deallocation-deferred.  The actual
00233     // deallocation of these pages will be done by an ALTER SYSTEM DEALLOCATE
00234     // OLD.
00235 
00236     // Note that we cannot discard snapshot pages from cache because they
00237     // really haven't been freed yet and still may be referenced by other
00238     // threads.  The pages will be removed from the cache when they are
00239     // actually freed.
00240 
00241     PageId chainPageId = startPageId;
00242     VersionedPageEntry pageEntry;
00243     do {
00244         pVersionedRandomSegment->getLatestPageEntryCopy(chainPageId, pageEntry);
00245         DelegatingSegment::deallocatePageRange(chainPageId, chainPageId);
00246         incrPageUpdateCount(
00247             chainPageId,
00248             ANON_PAGE_OWNER_ID,
00249             ModifiedPageEntry::DEALLOCATED);
00250         snapshotPageMap.erase(chainPageId);
00251 
00252         chainPageId = pageEntry.versionChainPageId;
00253     } while (chainPageId != startPageId);
00254 }
00255 
00256 PageId SnapshotRandomAllocationSegment::updatePage(
00257     PageId pageId,
00258     bool needsTranslation)
00259 {
00260     assert(!readOnlyCommittedData);
00261 
00262     PageId anchorPageId;
00263     PageId snapshotId;
00264     PageOwnerId ownerId;
00265 
00266     // If the snapshot page is newly allocated, then we can update the page
00267     // in-place.  The page we would have updated should be the page chained
00268     // from the anchor, which corresponds to the latest version of the page.
00269     // That, in turn, should correspond to the snapshot we would have read.
00270 
00271     if (needsTranslation) {
00272         assert(pageId == getAnchorPageId(pageId));
00273         VersionedPageEntry pageEntry;
00274         pVersionedRandomSegment->getLatestPageEntryCopy(pageId, pageEntry);
00275         assert(pageEntry.versionChainPageId == getSnapshotId(pageId));
00276         if (isPageNewlyAllocated(pageEntry.versionChainPageId)) {
00277             return NULL_PAGE_ID;
00278         }
00279 
00280         anchorPageId = pageId;
00281         snapshotId = pageEntry.versionChainPageId;
00282         ownerId = pageEntry.ownerId;
00283 
00284     } else {
00285         if (isPageNewlyAllocated(pageId)) {
00286             return NULL_PAGE_ID;
00287         }
00288         VersionedPageEntry pageEntry;
00289         anchorPageId = getAnchorPageId(pageId);
00290         pVersionedRandomSegment->getLatestPageEntryCopy(
00291             anchorPageId,
00292             pageEntry);
00293         assert(pageEntry.versionChainPageId == pageId);
00294         assert(pageId == getSnapshotId(anchorPageId));
00295 
00296         snapshotId = pageId;
00297         ownerId = pageEntry.ownerId;
00298     }
00299 
00300     // Otherwise, this is the first time we're modifying the page.
00301     // Allocate a new page and chain it into the existing version chain.
00302     // Also set the successor page on the new page to the successor set
00303     // on the current snapshot page.
00304 
00305     PageId newPageId = allocatePageId(ownerId);
00306 
00307     SXMutexExclusiveGuard mapGuard(modPageMapMutex);
00308 
00309     VersionedPageEntry pageEntry;
00310     pVersionedRandomSegment->getLatestPageEntryCopy(snapshotId, pageEntry);
00311     chainPageEntries(
00312         newPageId,
00313         snapshotId,
00314         pageEntry.successorId);
00315     chainPageEntries(anchorPageId, newPageId, NULL_PAGE_ID);
00316 
00317     // Store a mapping of the new page to itself so when we later need to
00318     // update that page, our cached mapping will tell us to directly
00319     // access that page.
00320     StrictMutexGuard mutexGuard(snapshotPageMapMutex);
00321     snapshotPageMap[newPageId] = newPageId;
00322     snapshotPageMap[anchorPageId] = newPageId;
00323     return newPageId;
00324 }
00325 
00326 void SnapshotRandomAllocationSegment::chainPageEntries(
00327     PageId pageId,
00328     PageId versionChainPageId,
00329     PageId successorId)
00330 {
00331     pVersionedRandomSegment->chainPageEntries(
00332         pageId,
00333         versionChainPageId,
00334         successorId);
00335     incrPageUpdateCount(
00336         pageId,
00337         ANON_PAGE_OWNER_ID,
00338         ModifiedPageEntry::MODIFIED);
00339 }
00340 
00341 bool SnapshotRandomAllocationSegment::isPageNewlyAllocated(PageId pageId)
00342 {
00343     SXMutexSharedGuard mapGuard(modPageMapMutex);
00344 
00345     ModifiedPageEntryMapIter iter = modPageEntriesMap.find(pageId);
00346     if (iter != modPageEntriesMap.end()) {
00347         SharedModifiedPageEntry pModPageEntry = iter->second;
00348         if (pModPageEntry->lastModType == ModifiedPageEntry::ALLOCATED) {
00349             return true;
00350         }
00351     }
00352     return false;
00353 }
00354 
00355 void SnapshotRandomAllocationSegment::commitChanges(TxnId commitCsn)
00356 {
00357     assert(!readOnlyCommittedData);
00358     SXMutexExclusiveGuard mapGuard(modPageMapMutex);
00359 
00360     pVersionedRandomSegment->updateAllocNodes(
00361         modPageEntriesMap,
00362         commitCsn,
00363         true,
00364         getTracingSegment());
00365     modPageEntriesMap.clear();
00366     snapshotPageMap.clear();
00367 }
00368 
00369 void SnapshotRandomAllocationSegment::rollbackChanges()
00370 {
00371     assert(!readOnlyCommittedData);
00372     SXMutexExclusiveGuard mapGuard(modPageMapMutex);
00373 
00374     pVersionedRandomSegment->updateAllocNodes(
00375         modPageEntriesMap,
00376         NULL_TXN_ID,
00377         false,
00378         getTracingSegment());
00379     modPageEntriesMap.clear();
00380     snapshotPageMap.clear();
00381 }
00382 
00383 MappedPageListener *SnapshotRandomAllocationSegment::getMappedPageListener(
00384     BlockId blockId)
00385 {
00386     PageId snapshotId = translateBlockId(blockId);
00387     if (isPageNewlyAllocated(snapshotId)) {
00388         return this;
00389     } else {
00390         return pVersionedRandomSegment;
00391     }
00392 }
00393 
00394 void SnapshotRandomAllocationSegment::setForceCacheUnmap()
00395 {
00396     forceCacheUnmap = true;
00397 }
00398 
00399 void SnapshotRandomAllocationSegment::delegatedCheckpoint(
00400     Segment &delegatingSegment,
00401     CheckpointType checkpointType)
00402 {
00403     // Execute the checkpoint only if we have dirty pages or if we're in a
00404     // mode where we need to execute the checkpoint to discard old entries from
00405     // the cache.
00406     //
00407     // Note that we need to define this method to avoid calling
00408     // delegatedCheckpoint on the underlying VersionedRandomAllocationSegment.
00409     if (needPageFlush ||
00410         (checkpointType == CHECKPOINT_FLUSH_AND_UNMAP && forceCacheUnmap))
00411     {
00412         MappedPageListenerPredicate pagePredicate(delegatingSegment);
00413         pCache->checkpointPages(pagePredicate,checkpointType);
00414         needPageFlush = false;
00415     }
00416 }
00417 
00418 MappedPageListener
00419 *SnapshotRandomAllocationSegment::notifyAfterPageCheckpointFlush(
00420     CachePage &page)
00421 {
00422     assert(!readOnlyCommittedData);
00423     return pVersionedRandomSegment;
00424 }
00425 
00426 bool SnapshotRandomAllocationSegment::canFlushPage(CachePage &page)
00427 {
00428     assert(!readOnlyCommittedData);
00429 
00430     // We can always flush snapshot pages since dirty snapshot pages are
00431     // always new so we don't have to worry about flushing corresponding
00432     // log pages
00433     return true;
00434 }
00435 
00436 void SnapshotRandomAllocationSegment::notifyPageDirty(
00437     CachePage &page,
00438     bool bDataValid)
00439 {
00440     assert(!readOnlyCommittedData);
00441 
00442     // Since snapshot pages are always new, we only need to call notifyPageDirty
00443     // when the page is first allocated.  That is indicated by bDataValid being
00444     // false.  bDataValid will be passed in as true in the case where we're
00445     // copying an existing page into a newly allocated snapshot page.
00446     // Since we've already called notifyPageDirty on the newly allocated
00447     // snapshot page, there's no need to call it again.
00448     if (!bDataValid) {
00449         DelegatingSegment::notifyPageDirty(page, bDataValid);
00450     }
00451 }
00452 
00453 void SnapshotRandomAllocationSegment::versionPage(
00454     PageId destAnchorPageId,
00455     PageId srcAnchorPageId)
00456 {
00457     assert(!readOnlyCommittedData);
00458     assert(destAnchorPageId == getAnchorPageId(destAnchorPageId));
00459     assert(srcAnchorPageId == getAnchorPageId(srcAnchorPageId));
00460 
00461     VersionedPageEntry pageEntry;
00462     pVersionedRandomSegment->getLatestPageEntryCopy(
00463         destAnchorPageId,
00464         pageEntry);
00465     TxnId largestDestCsn = pageEntry.allocationCsn;
00466     if (pageEntry.versionChainPageId != destAnchorPageId) {
00467         pVersionedRandomSegment->getLatestPageEntryCopy(
00468             pageEntry.versionChainPageId,
00469             pageEntry);
00470         largestDestCsn = pageEntry.allocationCsn;
00471     }
00472 
00473     SXMutexExclusiveGuard mapGuard(modPageMapMutex);
00474 
00475     // Link the destination anchor to the page following the source anchor,
00476     // and then link the source anchor to the original page following the
00477     // destination anchor.
00478     pVersionedRandomSegment->getLatestPageEntryCopy(
00479         srcAnchorPageId,
00480         pageEntry);
00481     assert(pageEntry.allocationCsn >= largestDestCsn);
00482     PageId pageIdAfterSrcAnchor = pageEntry.versionChainPageId;
00483     pVersionedRandomSegment->getLatestPageEntryCopy(
00484         destAnchorPageId,
00485         pageEntry);
00486     PageId pageIdAfterDestAnchor = pageEntry.versionChainPageId;
00487 
00488     chainPageEntries(destAnchorPageId, pageIdAfterSrcAnchor, NULL_PAGE_ID);
00489     chainPageEntries(srcAnchorPageId, pageIdAfterDestAnchor, NULL_PAGE_ID);
00490 
00491     StrictMutexGuard mutexGuard(snapshotPageMapMutex);
00492     snapshotPageMap[destAnchorPageId] = pageIdAfterSrcAnchor;
00493 }
00494 
00495 bool SnapshotRandomAllocationSegment::isWriteVersioned()
00496 {
00497     return true;
00498 }
00499 
00500 FENNEL_END_CPPFILE("$Id: //open/dev/fennel/segment/SnapshotRandomAllocationSegment.cpp#16 $");
00501 
00502 // End SnapshotRandomAllocationSegment.cpp

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