📄 filecontainer.java
字号:
* This blocks all page read (getPage) from accessing this * alloc page in this container until the alloc page is * unlatched. Those who already have a page handle into * this container are unaffected. * * In other words, allocation blocks out reader (of any * page that is managed by this alloc page) by the latch * on the allocation page. * * Note that write page can proceed as usual. */ allocPage = findAllocPageForAdd(allocHandle, ntt, startSearch); allocCache.invalidate(allocPage, allocPage.getPageNumber()); } if (SanityManager.DEBUG) { if (allocPage == null) allocCache.dumpAllocationCache(); SanityManager.ASSERT(allocPage != null, "findAllocPageForAdd returned a null alloc page"); } // // get the next free page's number. // for case 1, page number > lastPreallocPage // for case 2, page number <= lastPage // for case 3, lastPage < page number <= lastPreallocPage // pageNumber = allocPage.nextFreePageNumber(startSearch); // need to distinguish between the following 3 cases: // 1) the page has not been allocate or initalized. // Create it in the page cache and sync it to disk. // 2) the page is being re-allocated. // We need to read it in to re-initialize it // 3) the page has been preallocated. // Create it in the page cache and don't sync it to disk // // first find out the current last initialized page and // preallocated page before the new page is added lastPage = allocPage.getLastPagenum(); lastPreallocPage = allocPage.getLastPreallocPagenum(); reuse = pageNumber <= lastPage; // no address translation necessary pkey = new PageKey(identity, pageNumber); if (reuse) { // if re-useing a page, make sure the deallocLock on the new // page is not held. We only need a zero duration lock on // the new page because the allocPage is latched and this // is the only thread which can be looking at this // pageNumber. RecordHandle deallocLock = BasePage.MakeRecordHandle(pkey, RecordHandle.DEALLOCATE_PROTECTION_HANDLE); if (!getDeallocLock(allocHandle, deallocLock, false /* nowait */, true /* zeroDuration */)) { // The transaction which deallocated this page has not // committed yet. Try going to some other page. If // this is the first time we fail to get the dealloc // lock, try from the beginning of the allocated page. // If we already did that and still fail, keep going // until we get a brand new page. if (numtries == 0) { startSearch = ContainerHandle.INVALID_PAGE_NUMBER; lastAllocatedPage = pageNumber; } else // continue from where we were startSearch = pageNumber; numtries++; // We have to unlatch the allocPage so that if that // transaction rolls back, it won't deadlock with this // transaction. allocPage.unlatch(); allocPage = null; retry = true; } else { // we got the lock, next time start from there lastAllocatedPage = pageNumber; } } else { // we got a new page, next time, start from beginning of // the bit map again if we suspect there are some some // deallocated pages if (numtries > 0) lastAllocatedPage = ContainerHandle.INVALID_PAGE_NUMBER; else lastAllocatedPage = pageNumber; } // Retry from the beginning if necessary. if (retry) continue; // If we get past here must have (retry == false) if (SanityManager.DEBUG) { SanityManager.ASSERT(retry == false); } // Now we have verified that the allocPage is latched and we // can get the zeroDuration deallocLock nowait. This means the // transaction which freed the page has committed. Had that // transaction aborted, we would have retried. if (SanityManager.DEBUG) { // ASSERT lastPage <= lastPreallocPage if (lastPage > lastPreallocPage) { SanityManager.THROWASSERT("last page " + lastPage + " > lastPreallocPage " + lastPreallocPage); } } // No I/O at all if this new page is requested as part of a // create and load statement or this new page is in a temporary // container. // // In the former case, BaseContainer will allow the // MODE_UNLOGGED bit to go thru to the nested top transaction // alloc handle. In the later case, there is no nested top // transaction and the alloc handle is the user handle, which // is UNLOGGED. boolean noIO = (allocHandle.getMode() & ContainerHandle.MODE_UNLOGGED) == ContainerHandle.MODE_UNLOGGED; // If we do not need the I/O (either because we are in a // create_unlogged mode or we are dealing with a temp table), // don't do any preallocation. Otherwise, see if we should be // pre-Allocating page by now. We don't call it before // nextFreePageNumber because finding a reusable page may be // expensive and we don't want to start preAllocation unless // there is no more reusable page. Unless we are called // explicitly to bulk increase the container size in a preload // or in a create container. if (!noIO && (bulkIncreaseContainerSize || (pageNumber > lastPreallocPage && pageNumber > PreAllocThreshold))) { allocPage.preAllocatePage(this, PreAllocThreshold, PreAllocSize); } // update last preAllocated Page, it may have been changed by // the preAllocatePage call. We don't want to do the sync if // preAllocatePage already took care of it. lastPreallocPage = allocPage.getLastPreallocPagenum(); boolean prealloced = pageNumber <= lastPreallocPage; // Argument to the create is an array of ints. // The array is only used for new page creation or for creating // a preallocated page, not for reuse. // 0'th element is the page format // 1'st element is whether or not to sync the page to disk // 2'nd element is pagesize // 3'rd element is spareSpace int[] createPageArgs = new int[STORED_PAGE_ARG_NUM]; createPageArgs[0] = StoredPage.FORMAT_NUMBER; createPageArgs[1] = prealloced ? 0 : (noIO ? 0 : CachedPage.WRITE_SYNC); createPageArgs[2] = pageSize; createPageArgs[3] = spareSpace; createPageArgs[4] = minimumRecordSize; // RESOLVE: right now, there is no re-mapping of pages, so // pageOffset = pageNumber*pageSize long pageOffset = pageNumber * pageSize; // initialize a new user page // we first use the NTT to initialize the new page - in case the // allocation failed, it is rolled back with the NTT. // Later, we transfer the latch to the userHandle so it won't be // released when the ntt commits try { page = initPage(allocHandle, pkey, createPageArgs, pageOffset, reuse, isOverflow); } catch (StandardException se) { if (SanityManager.DEBUG) { SanityManager.DEBUG_PRINT("FileContainer", "got exception from initPage:" + "\nreuse = " + reuse + "\ncreatePageArgs[1] = " + createPageArgs[1] + "\nallocPage = " + allocPage ); } allocCache.dumpAllocationCache(); throw se; } if (SanityManager.DEBUG) { SanityManager.ASSERT( page != null, "initPage returns null page"); SanityManager.ASSERT( page.isLatched(), "initPage returns unlatched page"); } // allocate the page in the allocation page bit map allocPage.addPage(this, pageNumber, ntt, userHandle); if (useNTT) { // transfer the page latch from NTT to UT. // // after the page is unlatched by NTT, it is still // protected from being found by almost everybody else // because the alloc page is still latched and the alloc // cache is invalidated. // // However (beetle 3942) it is possible for the page to be // found by threads who specifically ask for this // pagenumber (e.g. HeapPostCommit). // We may find that such a thread has latched the page. // We shouldn't wait for it because we have the alloc page // latch, and this could cause deadlock (e.g. // HeapPostCommit might call removePage and this would wait // on the alloc page). // // We may instead find that we can latch the page, but that // another thread has managed to get hold of it during the // transfer and either deallocate it or otherwise change it // (add rows, delete rows etc.) // // Since this doesn't happen very often, we retry in these // 2 cases (we give up the alloc page and page and we start // this method from scratch). // // If the lock manager were changed to allow latches to be // transferred between transactions, wouldn't need to // unlatch to do the transfer, and would avoid having to // retry in these cases (beetle 4011). page.unlatch(); page = null; // need to find it in the cache again since unlatch also // unkept the page from the cache page = (BasePage)pageCache.find(pkey); page = latchPage( userHandle, page, false /* don't wait, it might deadlock */); if (page == null || // recordCount will only return true if there are no // rows (including deleted rows) page.recordCount() != 0 || page.getPageStatus() != BasePage.VALID_PAGE) { retry = true; if (page != null) { page.unlatch(); page = null; } allocPage.unlatch(); allocPage = null; } } // if ntt is null, no need to transfer. Page is latched by user // transaction already. Will be no need to retry. // the alloc page is unlatched in the finally block. } while (retry == true); // At this point, should have a page suitable for returning if (SanityManager.DEBUG) SanityManager.ASSERT(page.isLatched()); } catch (StandardException se) { if (page != null) page.unlatch(); page = null; throw se; // rethrow error } finally { if (!useNTT && allocPage != null) { allocPage.unlatch(); allocPage = null; } // NTT is committed by the caller } if (SanityManager.DEBUG) SanityManager.ASSERT(page.isLatched()); // if bulkIncreaseContainerSize is set, that means this newPage call // may have greatly expanded the container size due to preallocation. // Regardless of how many page it actually created, reset preAllocSize // to the default so we won't attempt to always preallocate 1000 pages // at a time in the future. if (bulkIncreaseContainerSize) { bulkIncreaseContainerSize = false; PreAllocSize = DEFAULT_PRE_ALLOC_SIZE; } if (!isOverflow && page != null) setLastInsertedPage(pageNumber); // increase estimated page count - without any synchronization or // logging, this is an estimate only if (estimatedPageCount >= 0) estimatedPageCount++; if (!this.identity.equals(page.getPageId().getContainerId())) { if (SanityManager.DEBUG) { SanityManager.THROWASSERT( "just created a new page from a different container" + "\n this.identity = " + this.identity + "\n page.getPageId().getContainerId() = " + page.getPageId().getContainerId() + "\n userHandle is: " + userHandle + "\n allocHandle is: " + allocHandle + "\n this container is: " + this); } throw StandardException.newException( SQLState.DATA_DIFFERENT_CONTAINER, this.identity, page.getPageId().getContainerId()); } return page; // return the newly added page } protected void clearPreallocThreshold() { // start life with preallocated page if possible PreAllocThreshold = 0; } protected void prepareForBulkLoad(BaseContainerHandle handle, int numPage) { clearPreallocThreshold(); RawTransaction tran = handle.getTransaction(); // find the last allocation page - do not invalidate the alloc cache, // we don't want to prevent other people from reading or writing // pages. AllocPage allocPage = findLastAllocPage(handle, tran); // preallocate numPages. Do whatever this allocPage can handle, if it // is full, too bad. We don't guarentee that we will preallocate this // many pages, we only promise to try. if (allocPage != null) { allocPage.preAllocatePage(this, 0, numPage); a
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -