0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.store.raw.data.AllocPage
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.store.raw.data;
0023:
0024: import org.apache.derby.iapi.services.sanity.SanityManager;
0025:
0026: import org.apache.derby.iapi.reference.SQLState;
0027:
0028: import org.apache.derby.iapi.services.io.TypedFormat;
0029: import org.apache.derby.iapi.services.io.FormatIdUtil;
0030: import org.apache.derby.iapi.services.io.StoredFormatIds;
0031:
0032: import org.apache.derby.iapi.error.StandardException;
0033:
0034: import org.apache.derby.iapi.store.raw.ContainerHandle;
0035: import org.apache.derby.iapi.store.raw.Loggable;
0036: import org.apache.derby.iapi.store.raw.PageKey;
0037: import org.apache.derby.iapi.store.raw.PageTimeStamp;
0038: import org.apache.derby.iapi.store.raw.RawStoreFactory;
0039:
0040: import org.apache.derby.iapi.store.raw.log.LogInstant;
0041: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
0042:
0043: import org.apache.derby.impl.store.raw.data.BaseContainerHandle;
0044: import org.apache.derby.impl.store.raw.data.BasePage;
0045: import org.apache.derby.impl.store.raw.data.PageVersion;
0046:
0047: import java.io.IOException;
0048:
0049: import java.io.ObjectOutput;
0050: import java.io.ObjectInput;
0051:
0052: import org.apache.derby.iapi.services.io.ArrayInputStream;
0053:
0054: /**
0055: An allocation page of the file container.
0056: <P>
0057: This class extends a normal Stored page, with the exception that a hunk of
0058: space may be 'borrowed' by the file container to store the file header.
0059: <P>
0060: The borrowed space is not visible to the alloc page even though it is
0061: present in the page data array. It is accessed directly by the
0062: FileContainer. Any change made to the borrowed space is not managed or
0063: seen by the allocation page.
0064: <P
0065: The reason for having this borrowed space is so that the container header
0066: does not need to have a page of its own.
0067:
0068: <P><B>Page Format</B><BR>
0069: An allocation page extends a stored page, the on disk format is different
0070: from a stored page in that N bytes are 'borrowed' by the container and the
0071: page header of an allocation page will be slightly bigger than a normal
0072: stored page. This N bytes are stored between the page header and the
0073: record space.
0074: <P>
0075: The reason why this N bytes can't simply be a row is because it needs to be
0076: statically accessible by the container object to avoid a chicken and egg
0077: problem of the container object needing to instantiate an alloc page object
0078: before it can be objectified, and an alloc page object needing to
0079: instantiate a container object before it can be objectified. So this N
0080: bytes must be stored outside of the normal record interface yet it must be
0081: settable because only the first alloc page has this borrowed space. Other
0082: (non-first) alloc page have N == 0.
0083:
0084: <PRE>
0085: <-- borrowed ->
0086: +----------+-------------+---+---------+-------------------+-------------+--------+
0087: | FormatId | page header | N | N bytes | alloc extend rows | slot offset |checksum|
0088: +----------+-------------+---+---------+-------------------+-------------+--------+
0089: </PRE>
0090:
0091: N is a byte that indicates the size of the borrowed space. Once an alloc
0092: page is initialized, the value of N cannot change.
0093: <P>
0094: The maximum space that can be borrowed by the container is 256 bytes.
0095: <P>
0096: The allocation page are of the same page size as any other pages in the
0097: container. The first allocation page of the FileContainer starts at the
0098: first physical byte of the container. Subsequent allocation pages are
0099: chained via the nextAllocPageOffset. Each allocation page is expected to
0100: manage at least 1000 user pages (for 1K page size) so this chaining may not
0101: be a severe performance hit. The logical -> physical mapping of an
0102: allocation page is stored in the previous allocation page. The container
0103: object will need to maintain this mapping.
0104: <P>
0105: The following fields are stored in the page header
0106: <PRE>
0107: @format_id RAW_STORE_ALLOC_PAGE
0108: @purpose manage page allocation
0109: @upgrade
0110: @disk_layout
0111: FormatId(int)
0112: StoredPageHeader see StoredPage
0113: nextAllocPageNubmer(long) the next allocation page's number
0114: nextAllocPageOffset(long) the file offset of the next allocation page
0115: reserved1(long) reserved for future usage
0116: reserved2(long) reserved for future usage
0117: reserved3(long) reserved for future usage
0118: reserved4(long) reserved for future usage
0119: N(byte) the size of the borrowed container info
0120: containerInfo(byte[N]) the content of the borrowed container info
0121: AllocExtent the one and only extent on this alloc page
0122:
0123: @end_format
0124: </PRE>
0125:
0126: <P>
0127: The allocation page contains allocation extent rows. In this first cut
0128: implementation, there is only 1 allocation extent row per allocation page.
0129: <P>
0130: The allocation extent row is an externalizable object and is directly
0131: written on to the page by the alloc page. In other words, it will not be
0132: converted in to a storeableRow. This is to cut down overhead, enhance
0133: performance and gives more control of the size and layout of the allocation
0134: extent row to the alloc page.
0135: <P>
0136: <HR WIDTH="100%">
0137: <BR> DETAIL implmentation notes <BR>
0138: <HR WIDTH="100%">
0139: <P>
0140: Create Container - an embryonic allocation page is formatted on disk by a
0141: spcial static function to avoid instantiating a full AllocPage object.
0142: This embryonic allocation has enough information that it can find the
0143: file header and not much else. Then the allocation page is perperly
0144: initiated by creating the first extent.
0145: <P>
0146: Open Container - A static AllocPage method will be used to read off the
0147: container information directly from disk. Even if
0148: the first alloc page (page 0) is already in the page cache, it will not be
0149: used because cleaning the alloc page will introduce a deadlock if the
0150: container is not in the container cache. Long term, the first alloc page
0151: should probably live in the container cache rather than in the page cache.
0152: <P>
0153: Get Page - The first alloc page (page 0) will be read into the page cache.
0154: Continue to follow the alloc page chain until the alloc page that manages
0155: the specified page is found. From the alloc page, the physical offset of
0156: the specified page is located.
0157: <P>
0158: Cleaning alloc page - the alloc page is written out the same way any page
0159: is written out. The container object will provide a call back to the alloc
0160: page to write the current version of the container object back into the
0161: borrowed space before the alloc page itself is written out.
0162: <P>
0163: Cleaning the container object - get the the first alloc page, dirty it and
0164: clean it (which will cause it to call the container object to write itself
0165: out into the borrowed space). The versioning of the container is
0166: independent of the versioning of the alloc page. The container version is
0167: stored inside the borrowed space and is opaque to the alloc page.
0168: <P>
0169: For the fields in an allocation extent row
0170:
0171: @see AllocExtent
0172: */
0173:
0174: public class AllocPage extends StoredPage {
0175: /*
0176: * typed format
0177: */
0178: public static final int FORMAT_NUMBER = StoredFormatIds.RAW_STORE_ALLOC_PAGE;
0179:
0180: // format Id must fit in 4 bytes
0181:
0182: /**
0183: Return my format identifier.
0184: */
0185: public int getTypeFormatId() {
0186: return StoredFormatIds.RAW_STORE_ALLOC_PAGE;
0187: }
0188:
0189: /*****************************************************************
0190: * alloc page header
0191: *****************************************************************/
0192: private long nextAllocPageNumber; // if isLast, nextAllocPageNumber == INVALID_PAGE_NUMBER
0193: private long nextAllocPageOffset;
0194: private long reserved1;
0195: private long reserved2;
0196: private long reserved3;
0197: private long reserved4;
0198:
0199: private AllocExtent extent;
0200:
0201: private int borrowedSpace;
0202:
0203: /*****************************************************************
0204: * constants
0205: *****************************************************************/
0206:
0207: /*
0208: * allocation page header
0209: * 8 bytes long next alloc page number
0210: * 8 bytes long next alloc page physical offset
0211: * 8 bytes long reserved1
0212: * 8 bytes long reserved2
0213: * 8 bytes long reserved3
0214: * 8 bytes long reserved4
0215: */
0216: protected static final int ALLOC_PAGE_HEADER_OFFSET = StoredPage.PAGE_HEADER_OFFSET
0217: + StoredPage.PAGE_HEADER_SIZE;
0218:
0219: protected static final int ALLOC_PAGE_HEADER_SIZE = 8 + 8 + (4 * 8);
0220:
0221: /* borrowed_SPACE_OFFSET is where the borrowed space len is kept */
0222: protected static final int BORROWED_SPACE_OFFSET = ALLOC_PAGE_HEADER_OFFSET
0223: + ALLOC_PAGE_HEADER_SIZE;
0224:
0225: /* size of the borrowed space length */
0226: protected static final int BORROWED_SPACE_LEN = 1; // 1 byte to store the containerInfo length
0227:
0228: /*
0229: * BORROWED_SPACE_OFFSET + BORROWED_SPACE_LEN is the beginning offset of
0230: * the borrowed space
0231: */
0232:
0233: /*
0234: * the entire borrowed space must live within MAX_BORROWED_SPACE of the
0235: * alloc page
0236: */
0237: protected static final int MAX_BORROWED_SPACE = RawStoreFactory.PAGE_SIZE_MINIMUM / 5; // cannot take more then 1/5 of the page
0238:
0239: public AllocPage() {
0240: super ();
0241: }
0242:
0243: /*
0244: * overwriting StoredPage methods
0245: */
0246:
0247: protected int getMaxFreeSpace() {
0248:
0249: // the maximum free space is reduced by the allocation page header the
0250: // size of the borrowed space. In all allocation page except the first
0251: // one, there is no borrowed space and this is indeed the max free
0252: // space. In the first allocation page, need to further subtract
0253: // the borrowed space
0254:
0255: return super .getMaxFreeSpace() - ALLOC_PAGE_HEADER_SIZE
0256: - BORROWED_SPACE_LEN - borrowedSpace;
0257: }
0258:
0259: /*
0260: * Methods of cachedPage - create, read and write up a page
0261: * Overwriting StoredPage's CachedPage methods
0262: */
0263:
0264: /**
0265: * Create a new alloc page.
0266: *
0267: * @exception StandardException Standard exception policy.
0268: **/
0269: protected void createPage(PageKey newIdentity, int[] args)
0270: throws StandardException {
0271:
0272: super .createPage(newIdentity, args);
0273:
0274: // args[0] is the format id
0275: // args[1] is whether to sync the page to disk or not
0276: // args[2] is the pagesize (used by StoredPage)
0277: // args[3] is the spareSize (used by StoredPage)
0278: // args[4] is the number of bytes to reserve for container header
0279: // args[5] is the minimumRecordSize
0280: // NOTE: the arg list here must match the one in FileContainer
0281: int pageSize = args[2];
0282: int minimumRecordSize = args[5];
0283: borrowedSpace = args[4];
0284:
0285: if (SanityManager.DEBUG) {
0286: // MAX_BORROWED_SPACE can't be bigger than what can be represented in 1 byte space
0287: SanityManager.ASSERT(MAX_BORROWED_SPACE <= 255);
0288:
0289: if (!(borrowedSpace + BORROWED_SPACE_LEN
0290: + BORROWED_SPACE_OFFSET < MAX_BORROWED_SPACE)) {
0291: SanityManager.THROWASSERT("borrowedSpace too big = "
0292: + borrowedSpace);
0293: }
0294: SanityManager.ASSERT(pageData != null);
0295: }
0296: pageData[BORROWED_SPACE_OFFSET] = (byte) borrowedSpace;
0297:
0298: // remember that the borrowed space have been wiped out now, it
0299: // needs to be put back when the page is written out.
0300: // blot out borrowed space before checksum is verified
0301: if (borrowedSpace > 0) {
0302: clearSection(BORROWED_SPACE_OFFSET + BORROWED_SPACE_LEN,
0303: borrowedSpace);
0304: }
0305:
0306: // init the rest of the header and the allocation extent
0307: nextAllocPageNumber = ContainerHandle.INVALID_PAGE_NUMBER;
0308: nextAllocPageOffset = 0;
0309: reserved1 = reserved2 = reserved3 = reserved4 = 0;
0310:
0311: // calculate how much space we have left for the extent map
0312: int maxSpace = getMaxFreeSpace();
0313:
0314: // the pages this extent is going to manage starts from pageNum+1
0315: // starting physical offset is pageSize*(pageNum+1) since we have
0316: // no logical to physical mapping yet...
0317: extent = createExtent(newIdentity.getPageNumber() + 1,
0318: pageSize, 0 /* pagesAlloced */, maxSpace);
0319: }
0320:
0321: private AllocExtent createExtent(long pageNum, int pageSize,
0322: int pagesAlloced, int availspace) {
0323: int maxPages = AllocExtent.MAX_RANGE(availspace);
0324:
0325: if (SanityManager.DEBUG)
0326: SanityManager.ASSERT(maxPages > 8,
0327: "cannot manage > 8 pages");
0328:
0329: if (SanityManager.DEBUG) {
0330: if (SanityManager.DEBUG_ON(TEST_MULTIPLE_ALLOC_PAGE)) {
0331: maxPages = 2; // 2 pages per alloc page
0332: }
0333: }
0334:
0335: return new AllocExtent(pageNum * pageSize, // starting offset
0336: pageNum, // extent start page number
0337: pagesAlloced, // #pages already allocated
0338: pageSize, // page size
0339: maxPages); // max #pages to manage
0340: }
0341:
0342: /**
0343: Initialize in memory structure using the buffer in pageData
0344:
0345: @exception StandardException If the page cannot be read correctly, or is inconsistent.
0346: */
0347: protected void initFromData(FileContainer myContainer,
0348: PageKey newIdentity) throws StandardException {
0349: if (pageData.length < BORROWED_SPACE_OFFSET
0350: + BORROWED_SPACE_LEN) {
0351: throw dataFactory.markCorrupt(StandardException
0352: .newException(SQLState.DATA_CORRUPT_PAGE,
0353: newIdentity));
0354: }
0355:
0356: byte n = pageData[BORROWED_SPACE_OFFSET];
0357: borrowedSpace = (int) n;
0358:
0359: if (pageData.length < BORROWED_SPACE_OFFSET
0360: + BORROWED_SPACE_LEN + n) {
0361: throw dataFactory.markCorrupt(StandardException
0362: .newException(SQLState.DATA_CORRUPT_PAGE,
0363: newIdentity));
0364: }
0365:
0366: // blot out borrowed space before checksum is verified
0367: if (borrowedSpace > 0) {
0368: clearSection(BORROWED_SPACE_OFFSET + BORROWED_SPACE_LEN,
0369: borrowedSpace);
0370: }
0371:
0372: super .initFromData(myContainer, newIdentity);
0373:
0374: try {
0375: // now init alloc page header fields
0376: readAllocPageHeader();
0377:
0378: // now init the allocation extent - read it from offset
0379: int offset = BORROWED_SPACE_OFFSET + BORROWED_SPACE_LEN
0380: + borrowedSpace;
0381: extent = readExtent(offset);
0382: } catch (IOException ioe) {
0383: throw dataFactory.markCorrupt(StandardException
0384: .newException(SQLState.DATA_CORRUPT_PAGE, ioe,
0385: newIdentity));
0386: } catch (ClassNotFoundException cnfe) {
0387: throw dataFactory.markCorrupt(StandardException
0388: .newException(SQLState.DATA_CORRUPT_PAGE, cnfe,
0389: newIdentity));
0390: }
0391: }
0392:
0393: /**
0394: Write the page out
0395:
0396: @exception StandardException If the page cannot be written
0397: */
0398: protected void writePage(PageKey identity) throws StandardException {
0399: try {
0400: updateAllocPageHeader(); // write out the next alloc page chain
0401:
0402: // blot out borrowed space before checksum is calculated - even
0403: // though the page is first read in with borrowed space blotted
0404: // out, whenever this page got cleaned the container will overlay
0405: // the container info in the borrowed space.
0406: int n = (int) pageData[BORROWED_SPACE_OFFSET];
0407: if (SanityManager.DEBUG) {
0408: if (n != borrowedSpace)
0409: SanityManager
0410: .THROWASSERT("different borrowed space "
0411: + n + ", " + borrowedSpace);
0412: }
0413: if (n > 0) {
0414: clearSection(
0415: BORROWED_SPACE_OFFSET + BORROWED_SPACE_LEN, n);
0416: }
0417:
0418: int offset = BORROWED_SPACE_OFFSET + BORROWED_SPACE_LEN + n;
0419: writeExtent(offset);
0420: } catch (IOException ioe) {
0421: // i/o methods on the byte array have thrown an IOException
0422: throw dataFactory.markCorrupt(StandardException
0423: .newException(SQLState.DATA_CORRUPT_PAGE, ioe,
0424: identity));
0425: }
0426:
0427: // let store page write out the rest and do the checksum
0428: super .writePage(identity);
0429: }
0430:
0431: private void readAllocPageHeader() throws IOException {
0432: ArrayInputStream lrdi = rawDataIn;
0433: lrdi.setPosition(ALLOC_PAGE_HEADER_OFFSET);
0434:
0435: nextAllocPageNumber = lrdi.readLong();
0436: nextAllocPageOffset = lrdi.readLong();
0437: reserved1 = lrdi.readLong();
0438: reserved2 = lrdi.readLong();
0439: reserved3 = lrdi.readLong();
0440: reserved4 = lrdi.readLong();
0441: }
0442:
0443: private void updateAllocPageHeader() throws IOException {
0444: // rawDataOut and logicalDataOut are defined by StoredPage
0445: rawDataOut.setPosition(ALLOC_PAGE_HEADER_OFFSET);
0446: logicalDataOut.writeLong(nextAllocPageNumber);
0447: logicalDataOut.writeLong(nextAllocPageOffset);
0448: logicalDataOut.writeLong(0); // reserved1
0449: logicalDataOut.writeLong(0); // reserved2
0450: logicalDataOut.writeLong(0); // reserved3
0451: logicalDataOut.writeLong(0); // reserved4
0452: }
0453:
0454: private AllocExtent readExtent(int offset) throws IOException,
0455: ClassNotFoundException {
0456: ArrayInputStream lrdi = rawDataIn;
0457: rawDataIn.setPosition(offset);
0458: AllocExtent newExtent = new AllocExtent();
0459: newExtent.readExternal(lrdi);
0460:
0461: // in 1.3 or later, make sure the upgrade from before 1.3 is legal.
0462: if (SanityManager.DEBUG) {
0463: int max_range = newExtent.MAX_RANGE(getMaxFreeSpace());
0464: long extent_start = newExtent.getFirstPagenum();
0465: long extent_end = newExtent.getExtentEnd();
0466:
0467: // extent_start + max_range - 1 is the absolute last page this
0468: // extent can hope to manage. See if it did the calculation
0469: // correctly after upgrade.
0470:
0471: if ((extent_start + max_range - 1) < extent_end) {
0472: SanityManager
0473: .THROWASSERT("extent range exceed what extent's space can handle ");
0474: }
0475: }
0476:
0477: return newExtent;
0478: }
0479:
0480: private void writeExtent(int offset) throws IOException {
0481: // rawDataOut and logicalDataOut are defined by StoredPage
0482: rawDataOut.setPosition(offset);
0483: extent.writeExternal(logicalDataOut);
0484: }
0485:
0486: /*
0487: * borrowed space management
0488: */
0489:
0490: /**
0491: Write the container information into the container information area.
0492:
0493: @param containerInfo the container information
0494:
0495: @param epage the allocation page data which may not be fully formed,
0496: but is guarenteed to be big enough to cover the area inhibited by the
0497: container info
0498:
0499: @param create if create, write out the length of the container info
0500: also. Else check to make sure epage's original container info is of the
0501: same length
0502:
0503: @exception StandardException Cloudscape standard error policy
0504: */
0505: public static void WriteContainerInfo(byte[] containerInfo,
0506: byte[] epage, boolean create) throws StandardException {
0507: int N = (containerInfo == null) ? 0 : containerInfo.length;
0508:
0509: if (SanityManager.DEBUG) {
0510: if (create)
0511: SanityManager.ASSERT(containerInfo != null,
0512: "containerInfo is null");
0513:
0514: SanityManager.ASSERT(epage != null, "page array is null");
0515:
0516: if (!((containerInfo == null) || ((containerInfo.length
0517: + BORROWED_SPACE_OFFSET + BORROWED_SPACE_LEN) < epage.length))) {
0518: SanityManager
0519: .THROWASSERT("containerInfo too big for page array: "
0520: + containerInfo.length);
0521: }
0522:
0523: if (BORROWED_SPACE_OFFSET + BORROWED_SPACE_LEN + N >= MAX_BORROWED_SPACE)
0524: SanityManager
0525: .THROWASSERT("exceed max borrowable space: "
0526: + N);
0527: }
0528:
0529: if ((N + BORROWED_SPACE_LEN + BORROWED_SPACE_OFFSET) > epage.length) {
0530: if (SanityManager.DEBUG)
0531: SanityManager
0532: .THROWASSERT("exceed max borrowable space on page: "
0533: + N);
0534: }
0535:
0536: if (create) {
0537: epage[BORROWED_SPACE_OFFSET] = (byte) N;
0538: } else {
0539: int oldN = (int) epage[BORROWED_SPACE_OFFSET];
0540: if (oldN != N) {
0541: throw StandardException.newException(
0542: SQLState.DATA_CHANGING_CONTAINER_INFO,
0543: new Long(oldN), new Long(N));
0544: }
0545: }
0546:
0547: if (N != 0)
0548: System.arraycopy(containerInfo, 0, epage,
0549: BORROWED_SPACE_OFFSET + BORROWED_SPACE_LEN, N);
0550: }
0551:
0552: /**
0553: Extract the container information from epage.
0554:
0555: @param containerInfo where to put the extracted information
0556:
0557: @param epage the allocation page which has the container information.
0558: Epage may not be fully formed, but is guarenteed to be big enough to
0559: cover the area inhibited by the container info
0560: */
0561: public static void ReadContainerInfo(byte[] containerInfo,
0562: byte[] epage) {
0563: int N = (int) epage[BORROWED_SPACE_OFFSET];
0564:
0565: if (SanityManager.DEBUG) {
0566: if (N != containerInfo.length)
0567: SanityManager.THROWASSERT("N not what is expected : "
0568: + N);
0569:
0570: if (BORROWED_SPACE_OFFSET + BORROWED_SPACE_LEN + N >= MAX_BORROWED_SPACE) {
0571: SanityManager
0572: .THROWASSERT("exceed max borrowable space: "
0573: + N);
0574: }
0575: }
0576:
0577: if (N != 0)
0578: System.arraycopy(epage, BORROWED_SPACE_OFFSET
0579: + BORROWED_SPACE_LEN, containerInfo, 0, N);
0580: }
0581:
0582: /*
0583: * specific methods to AllocPage
0584: */
0585:
0586: /**
0587: Return the next free page number after given page number
0588: */
0589: public long nextFreePageNumber(long pnum) {
0590: return extent.getFreePageNumber(pnum);
0591: }
0592:
0593: /**
0594: Add a page which is managed by this alloc page.
0595: Return the page number of the newly added page.
0596:
0597: <BR> MT - thread aware (latched)
0598:
0599: @param mycontainer (future) allows the alloc page to call back to the
0600: container to grow the container by creating and syncing multiple
0601: pages at once
0602: @param ntt the nested top action that is the allocation transaction.
0603: NTT will comit before the user transaction
0604: @param userHandle the container handle that is opened by the user
0605: transaction. Use the userHandle to latch the new page so that
0606: it may remain latched after NTT is committed so the user
0607: transaction can guarentee to have an empty page
0608:
0609: @exception StandardException If the page cannot be added
0610: */
0611: public void addPage(FileContainer mycontainer, long newPageNumber,
0612: RawTransaction ntt, BaseContainerHandle userHandle)
0613: throws StandardException {
0614: // RESOLVED:
0615: //
0616: // to prevent allocating a free page before the freeing transaction has
0617: // commit, need to grab the DEALLOCATE_PROTECTION_HANDLE
0618: // the lock probably should be gotten in FileContainer
0619: // and not here
0620:
0621: // page allocation is logged under the nested top action
0622: owner.getAllocationActionSet().actionAllocatePage(ntt, this ,
0623: newPageNumber, AllocExtent.ALLOCATED_PAGE,
0624: AllocExtent.FREE_PAGE);
0625: }
0626:
0627: /*
0628: Deallocate page
0629: */
0630: public void deallocatePage(BaseContainerHandle userHandle,
0631: long pageNumber) throws StandardException {
0632: if (SanityManager.DEBUG) {
0633: SanityManager.ASSERT(isLatched());
0634: }
0635:
0636: // RESOLVED:
0637: //
0638: // to prevent this page from being freed before this transaction commits,
0639: // need to grab the DEALLOCATE_PROTECTION_HANDLE lock on the
0640: // deallocated page
0641: // the lock probably should be gotten in FileContainer
0642: // and not here
0643:
0644: owner.getAllocationActionSet().actionAllocatePage(
0645: userHandle.getTransaction(), this , pageNumber,
0646: AllocExtent.DEALLOCATED_PAGE,
0647: AllocExtent.ALLOCATED_PAGE);
0648: }
0649:
0650: /*
0651: * update unfilled page information
0652: * We will be using inputExtent's unfilledPage bitmap as the new bitmap, so
0653: * caller of this routine need to not touch the bitmap after this call (in
0654: * other words, call this ONLY in allocationCache invalidate and throw away
0655: * the reference to the bitImpl)
0656: */
0657: protected void updateUnfilledPageInfo(AllocExtent inputExtent) {
0658: if (SanityManager.DEBUG) {
0659: SanityManager.ASSERT(isLatched());
0660: }
0661:
0662: // update the unfilled page bit map unlogged - it is just a hint, not
0663: // worth logging it - don't dirty the page either, since we didn't log
0664: // it. It will be dirtied soon enough by addPage or deallocPage,
0665: // that is the only reasons why we are invalidataing the
0666: // allocation cache and updating the unfilled page info.
0667: // If we dirty the page, the BI will be copied to the side log
0668: extent.updateUnfilledPageInfo(inputExtent);
0669:
0670: }
0671:
0672: public boolean canAddFreePage(long lastAllocatedPage) {
0673: if (SanityManager.DEBUG)
0674: SanityManager.ASSERT(isLatched());
0675:
0676: if (extent.isRetired())
0677: return false;
0678:
0679: // if we want to try allocating not from the beginning of the bit map
0680: // and this alloc page is before that point and this is not the last
0681: // alloc page, then skip over this alloc page
0682: if (lastAllocatedPage != ContainerHandle.INVALID_PAGE_NUMBER
0683: && extent.getLastPagenum() <= lastAllocatedPage
0684: && !isLast())
0685: return false;
0686:
0687: // Else we either want to start examining from this alloc page, or this
0688: // is the last page, see if we can add a page.
0689: return extent.canAddFreePage(lastAllocatedPage);
0690: }
0691:
0692: public long getNextAllocPageOffset() {
0693: if (SanityManager.DEBUG) {
0694: SanityManager.ASSERT(!isLast(),
0695: "next alloc page not present for last alloc page");
0696:
0697: SanityManager.ASSERT(isLatched());
0698: }
0699:
0700: return nextAllocPageOffset;
0701: }
0702:
0703: public void chainNewAllocPage(BaseContainerHandle allocHandle,
0704: long newAllocPageNum, long newAllocPageOffset)
0705: throws StandardException {
0706: if (SanityManager.DEBUG) {
0707: SanityManager.ASSERT(isLatched());
0708: if (SanityManager.DEBUG_ON(FileContainer.SPACE_TRACE))
0709: SanityManager.DEBUG(FileContainer.SPACE_TRACE,
0710: "chaining new alloc page " + newAllocPageNum
0711: + " to " + getPageNumber());
0712: }
0713:
0714: owner.getAllocationActionSet().actionChainAllocPage(
0715: allocHandle.getTransaction(), this , newAllocPageNum,
0716: newAllocPageOffset);
0717: }
0718:
0719: public long getNextAllocPageNumber() {
0720: if (SanityManager.DEBUG) {
0721: SanityManager.ASSERT(isLatched());
0722: SanityManager.ASSERT(!isLast(),
0723: "next alloc page not present for last alloc page");
0724: }
0725: return nextAllocPageNumber;
0726: }
0727:
0728: public boolean isLast() {
0729: if (SanityManager.DEBUG)
0730: SanityManager.ASSERT(isLatched());
0731: return nextAllocPageNumber == ContainerHandle.INVALID_PAGE_NUMBER;
0732: }
0733:
0734: /*
0735: * get the last pagenumber currently managed by this alloc page
0736: */
0737: public long getLastPagenum() {
0738: if (SanityManager.DEBUG)
0739: SanityManager.ASSERT(isLatched());
0740:
0741: return extent.getLastPagenum();
0742: }
0743:
0744: /*
0745: * get the largest page number this alloc page can manage.
0746: * This is the different from the last pagenumber currently managed by this
0747: * alloc page unless the alloc page is full and all the pages have been
0748: * allocated
0749: */
0750: public long getMaxPagenum() {
0751: return extent.getExtentEnd();
0752: }
0753:
0754: /*
0755: * get the last preallocated pagenumber managed by this alloc page
0756: */
0757: protected long getLastPreallocPagenum() {
0758: if (SanityManager.DEBUG)
0759: SanityManager.ASSERT(isLatched());
0760:
0761: return extent.getLastPreallocPagenum();
0762: }
0763:
0764: protected int getPageStatus(long pageNumber) {
0765: if (SanityManager.DEBUG)
0766: SanityManager.ASSERT(isLatched());
0767:
0768: return extent.getPageStatus(pageNumber);
0769: }
0770:
0771: /**
0772: Do the actual page allocation/deallocation/ree underneath a log operation.
0773: Change the page status to new status
0774:
0775: @exception StandardException If the page cannot be allocated
0776: */
0777: protected void setPageStatus(LogInstant instant, long pageNumber,
0778: int newStatus) throws StandardException {
0779: if (SanityManager.DEBUG) {
0780: SanityManager.ASSERT(isLatched(), "page is not latched");
0781: SanityManager.ASSERT(extent != null, "extent is null");
0782: }
0783:
0784: logAction(instant);
0785:
0786: switch (newStatus) {
0787: case AllocExtent.ALLOCATED_PAGE:
0788: extent.allocPage(pageNumber);
0789: break;
0790: case AllocExtent.DEALLOCATED_PAGE:
0791: extent.deallocPage(pageNumber);
0792: break;
0793: case AllocExtent.FREE_PAGE:
0794: extent.deallocPage(pageNumber);
0795: break;
0796: }
0797: }
0798:
0799: /**
0800: Chain the next page number and offset underneath a log record
0801:
0802: @exception StandardException Standard Cloudscape error policy
0803: */
0804: protected void chainNextAllocPage(LogInstant instant,
0805: long newAllocPageNum, long newAllocPageOffset)
0806: throws StandardException {
0807: if (SanityManager.DEBUG)
0808: SanityManager.ASSERT(isLatched(), "page is not latched");
0809:
0810: logAction(instant);
0811:
0812: nextAllocPageNumber = newAllocPageNum;
0813: nextAllocPageOffset = newAllocPageOffset;
0814: }
0815:
0816: /**
0817: * Compress free pages.
0818: * <p>
0819: * Compress the free pages at the end of the range maintained by
0820: * this allocation page. All pages being compressed should be FREE.
0821: * Only pages in the last allocation page can be compressed.
0822: * <p>
0823: *
0824: * @param instant log address for this operation.
0825: * @param new_highest_page The new highest page on this allocation
0826: * page. The number is the offset of the page
0827: * in the array of pages maintained by this
0828: * allocation page, for instance a value of 0
0829: * indicates all page except the first one are
0830: * to be truncated. If all pages are
0831: * truncated then the offset is set to -1.
0832: * @param num_pages_truncated The number of allocated pages in this
0833: * allocation page prior to the truncate.
0834: * Note that all pages from NewHighestPage+1
0835: * through newHighestPage+num_pages_truncated
0836: * should be FREE.
0837: *
0838: * @exception StandardException Standard exception policy.
0839: **/
0840: protected void compressSpace(LogInstant instant,
0841: int new_highest_page, int num_pages_truncated)
0842: throws StandardException {
0843: if (SanityManager.DEBUG) {
0844: SanityManager.ASSERT(isLatched(), "page is not latched");
0845: SanityManager.ASSERT(isLast(),
0846: "compress on non last alloc page.");
0847: SanityManager.ASSERT(new_highest_page >= 0,
0848: "negative new high page.");
0849: }
0850:
0851: logAction(instant);
0852:
0853: extent.compressPages(new_highest_page, num_pages_truncated);
0854: }
0855:
0856: /**
0857: * Handle undo of compress space operation.
0858: **/
0859: protected void undoCompressSpace(LogInstant instant,
0860: int new_highest_page, int num_pages_truncated)
0861: throws StandardException {
0862: logAction(instant);
0863:
0864: extent.undoCompressPages(new_highest_page, num_pages_truncated);
0865:
0866: }
0867:
0868: public String toString() {
0869: if (SanityManager.DEBUG) {
0870: String str = "*** Alloc page ***\n"
0871: + "nextAllocPageNumber = " + nextAllocPageNumber
0872: + "\nnextAllocPageOffset = " + nextAllocPageOffset
0873: + "\nreserved1 = " + reserved1 + "\nreserved2 = "
0874: + reserved2 + "\nreserved3 = " + reserved3
0875: + "\nreserved4 = " + reserved4
0876: + "\nborrowedSpace = " + borrowedSpace
0877: + "\nextent = " + extent.toDebugString() + "\n"
0878: + super .toString();
0879: return str;
0880: } else {
0881: return null;
0882: }
0883: }
0884:
0885: /**
0886: Return a copy of the allocExtent to be cached by the container.
0887: the container must take care to maintain its coherency by
0888: invalidating the cache before any update.
0889: */
0890: protected AllocExtent getAllocExtent() {
0891: return extent;
0892:
0893: // return new AllocExtent(extent);
0894: }
0895:
0896: /**
0897: Preallocate user page if needed.
0898:
0899: @param myContainer the container object
0900: @param preAllocThreshold start preallocating after this threshold
0901: @param preAllocSize preallocate this number of pages
0902: */
0903: protected void preAllocatePage(FileContainer myContainer,
0904: int preAllocThreshold, int preAllocSize) {
0905: if (SanityManager.DEBUG)
0906: SanityManager.ASSERT(isLatched(), "page is not latched");
0907:
0908: long lastPreallocatedPagenum = extent.getLastPreallocPagenum();
0909:
0910: if (lastPreallocatedPagenum < preAllocThreshold)
0911: return;
0912:
0913: // don't pre-allocate more than we the extent can handle - this is
0914: // because if I preallocate the next alloc page as a stored page,
0915: // that's going to be problem when we try to get it as an alloc page
0916: // later. We don't handle changing from a store page type to an alloc
0917: // page type on disk very well.
0918: if (extent.getExtentEnd() < (lastPreallocatedPagenum + preAllocSize))
0919: preAllocSize = (int) (extent.getExtentEnd() - lastPreallocatedPagenum);
0920:
0921: if (preAllocSize <= 0)
0922: return;
0923:
0924: // pre-allocate - only a container knows how to write pages
0925: // preAllocSize may exceed what this allocation page can really
0926: // handle, but no harm done. The next allocation page will benefit
0927: // from the work we have done...
0928: int n = myContainer.preAllocate(lastPreallocatedPagenum,
0929: preAllocSize);
0930:
0931: if (n > 0) // successfully preallocated some pages
0932: {
0933: // this is purely a performance issue during runtime. During
0934: // recovery, any page that is actually initialized will have its
0935: // own initPage log record. Update extent's preAllocpageNumber
0936: // unlogged.
0937: //
0938: // We could have logged a redo-only log record, but we are counting
0939: // on myContainer.preAllocate to do the right thing if we recovered
0940: // and have out of date preallocate information. A reason why
0941: // logging this is undesirable is that the alloc page may think the
0942: // preallocation happened, but the container may actually choose to
0943: // lie about it - if it thinks there is no advantage in actually
0944: // doing the I/O now. So best to leave it alone.
0945: extent.setLastPreallocPagenum(lastPreallocatedPagenum + n);
0946:
0947: // don't dirty the page - the new preAlloc page number is only set
0948: // in memory. A page should only get 'dirtied' by a log operation,
0949: // we are cheating here. Same with updating the extentStatus bit
0950: // without logging.
0951: }
0952:
0953: }
0954:
0955: /**
0956: * compress out empty pages at end of container.
0957: * <p>
0958: * Call the extent to update the data structure make the bit map look
0959: * like contiguous free pages at the end of the extent no longer exist.
0960: * Similar to preallocate do the operation unlogged, need to force the
0961: * change to the extent before actually removing the space from the
0962: * file.
0963: * <p>
0964: * The sequence is:
0965: * 1) update extent data structure
0966: * 2) force extent changes to disk
0967: * 3) truncate pages
0968: *
0969: * If the system crashes between 1 and 2 then no changes are on disk.
0970: * If the system crashes between 2 and 3 then there are extra pages in
0971: * the file that extent does not know about, this is the same case
0972: * as preallocation which the code already handes. It will handle
0973: * any set of pages from 0 to all of the intended pages being
0974: * truncated. The next allocate looks at actual size of file as
0975: * does the right thing.
0976: *
0977: * <p)
0978: * MT - expect Container level X lock
0979: *
0980: * @exception StandardException Standard exception policy.
0981: **/
0982: protected boolean compress(RawTransaction ntt,
0983: FileContainer myContainer) throws StandardException {
0984: boolean all_pages_compressed = false;
0985:
0986: if (SanityManager.DEBUG) {
0987: SanityManager.ASSERT(isLatched(), "page is not latched");
0988: }
0989:
0990: int last_valid_page = extent.compress(owner, ntt, this );
0991:
0992: if (last_valid_page >= 0) {
0993: // a non-negative return means that pages can be returned to
0994: // the operating system.
0995: myContainer.truncatePages(extent
0996: .getPagenum(last_valid_page));
0997:
0998: if (last_valid_page == this .getPageNumber()) {
0999: // all pages of the extent have been returned to OS.
1000: all_pages_compressed = true;
1001: }
1002: }
1003:
1004: return (all_pages_compressed);
1005: }
1006:
1007: /*********************************************************************
1008: * Extent Testing
1009: *
1010: * Use these strings to simulate error conditions for
1011: * testing purposes.
1012: *
1013: *********************************************************************/
1014: public static final String TEST_MULTIPLE_ALLOC_PAGE = SanityManager.DEBUG ? "TEST_MULTI_ALLOC_PAGE"
1015: : null;
1016: }
|