0001: /*-
0002: * See the file LICENSE for redistribution information.
0003: *
0004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
0005: *
0006: * $Id: DatabaseImpl.java,v 1.157.2.12 2008/01/07 15:14:09 cwl Exp $
0007: */
0008:
0009: package com.sleepycat.je.dbi;
0010:
0011: import java.io.ByteArrayInputStream;
0012: import java.io.ByteArrayOutputStream;
0013: import java.io.IOException;
0014: import java.io.ObjectInputStream;
0015: import java.io.ObjectOutputStream;
0016: import java.io.PrintStream;
0017: import java.nio.ByteBuffer;
0018: import java.util.Arrays;
0019: import java.util.Collections;
0020: import java.util.Comparator;
0021: import java.util.HashMap;
0022: import java.util.HashSet;
0023: import java.util.Iterator;
0024: import java.util.Map;
0025: import java.util.Set;
0026:
0027: import com.sleepycat.je.BtreeStats;
0028: import com.sleepycat.je.Cursor;
0029: import com.sleepycat.je.Database;
0030: import com.sleepycat.je.DatabaseConfig;
0031: import com.sleepycat.je.DatabaseEntry;
0032: import com.sleepycat.je.DatabaseException;
0033: import com.sleepycat.je.DatabaseStats;
0034: import com.sleepycat.je.DbInternal;
0035: import com.sleepycat.je.LockMode;
0036: import com.sleepycat.je.OperationStatus;
0037: import com.sleepycat.je.PreloadConfig;
0038: import com.sleepycat.je.PreloadStats;
0039: import com.sleepycat.je.PreloadStatus;
0040: import com.sleepycat.je.SecondaryDatabase;
0041: import com.sleepycat.je.StatsConfig;
0042: import com.sleepycat.je.VerifyConfig;
0043: import com.sleepycat.je.cleaner.UtilizationTracker;
0044: import com.sleepycat.je.config.EnvironmentParams;
0045: import com.sleepycat.je.dbi.SortedLSNTreeWalker.ExceptionPredicate;
0046: import com.sleepycat.je.dbi.SortedLSNTreeWalker.TreeNodeProcessor;
0047: import com.sleepycat.je.latch.LatchSupport;
0048: import com.sleepycat.je.log.LogEntryType;
0049: import com.sleepycat.je.log.LogException;
0050: import com.sleepycat.je.log.LogFileNotFoundException;
0051: import com.sleepycat.je.log.LogUtils;
0052: import com.sleepycat.je.log.Loggable;
0053: import com.sleepycat.je.recovery.Checkpointer;
0054: import com.sleepycat.je.tree.BIN;
0055: import com.sleepycat.je.tree.ChildReference;
0056: import com.sleepycat.je.tree.DBIN;
0057: import com.sleepycat.je.tree.DIN;
0058: import com.sleepycat.je.tree.DupCountLN;
0059: import com.sleepycat.je.tree.IN;
0060: import com.sleepycat.je.tree.LN;
0061: import com.sleepycat.je.tree.Node;
0062: import com.sleepycat.je.tree.Tree;
0063: import com.sleepycat.je.tree.TreeUtils;
0064: import com.sleepycat.je.tree.TreeWalkerStatsAccumulator;
0065: import com.sleepycat.je.tree.WithRootLatched;
0066: import com.sleepycat.je.txn.Locker;
0067: import com.sleepycat.je.txn.ThreadLocker;
0068: import com.sleepycat.je.utilint.CmdUtil;
0069: import com.sleepycat.je.utilint.DbLsn;
0070: import com.sleepycat.je.utilint.TestHook;
0071:
0072: /**
0073: * The underlying object for a given database.
0074: */
0075: public class DatabaseImpl implements Loggable, Cloneable {
0076:
0077: /*
0078: * Delete processing states. See design note on database deletion and
0079: * truncation
0080: */
0081: private static final short NOT_DELETED = 1;
0082: private static final short DELETED_CLEANUP_INLIST_HARVEST = 2;
0083: private static final short DELETED_CLEANUP_LOG_HARVEST = 3;
0084: private static final short DELETED = 4;
0085:
0086: private DatabaseId id; // unique id
0087: private Tree tree;
0088: private EnvironmentImpl envImpl; // Tree operations find the env this way
0089: private boolean duplicatesAllowed; // duplicates allowed
0090: private boolean transactional; // All open handles are transactional
0091: private boolean deferredWrite; // deferred write mode set
0092: private Set referringHandles; // Set of open Database handles
0093: private BtreeStats stats; // most recent btree stats w/ !DB_FAST_STAT
0094: private long eofNodeId; // Logical EOF node for range locking
0095: private short deleteState; // one of four delete states.
0096: private int useCount = 0; // If non-zero, eviction is prohibited
0097: private Object useCountLock = new Object();
0098:
0099: /*
0100: * The user defined Btree and duplicate comparison functions, if specified.
0101: */
0102: private Comparator btreeComparator = null;
0103: private Comparator duplicateComparator = null;
0104: private byte[] btreeComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
0105: private byte[] duplicateComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
0106: private boolean btreeComparatorByClassName = false;
0107: private boolean duplicateComparatorByClassName = false;
0108:
0109: /*
0110: * Cache some configuration values.
0111: */
0112: private int binDeltaPercent;
0113: private int binMaxDeltas;
0114: private int maxMainTreeEntriesPerNode;
0115: private int maxDupTreeEntriesPerNode;
0116:
0117: /*
0118: * The debugDatabaseName is used for error messages only, to avoid
0119: * accessing the db mapping tree in error situations. Currently it's not
0120: * guaranteed to be transactionally correct, nor is it updated by rename.
0121: */
0122: private String debugDatabaseName;
0123:
0124: /* For unit tests */
0125: private TestHook pendingDeletedHook;
0126:
0127: /**
0128: * Create a database object for a new database.
0129: */
0130: public DatabaseImpl(String dbName, DatabaseId id,
0131: EnvironmentImpl envImpl, DatabaseConfig dbConfig)
0132: throws DatabaseException {
0133:
0134: this .id = id;
0135: this .envImpl = envImpl;
0136: setBtreeComparator(dbConfig.getBtreeComparator(), dbConfig
0137: .getBtreeComparatorByClassName());
0138: setDuplicateComparator(dbConfig.getDuplicateComparator(),
0139: dbConfig.getDuplicateComparatorByClassName());
0140: duplicatesAllowed = dbConfig.getSortedDuplicates();
0141: transactional = dbConfig.getTransactional();
0142: deferredWrite = dbConfig.getDeferredWrite();
0143: maxMainTreeEntriesPerNode = dbConfig.getNodeMaxEntries();
0144: maxDupTreeEntriesPerNode = dbConfig.getNodeMaxDupTreeEntries();
0145:
0146: initDefaultSettings();
0147:
0148: deleteState = NOT_DELETED;
0149:
0150: /*
0151: * The tree needs the env, make sure we assign it before
0152: * allocating the tree.
0153: */
0154: tree = new Tree(this );
0155: referringHandles = Collections.synchronizedSet(new HashSet());
0156:
0157: eofNodeId = Node.getNextNodeId();
0158:
0159: /* For error messages only. */
0160: debugDatabaseName = dbName;
0161: }
0162:
0163: /**
0164: * Create an empty database object for initialization from the log. Note
0165: * that the rest of the initialization comes from readFromLog(), except
0166: * for the debugDatabaseName, which is set by the caller.
0167: */
0168: public DatabaseImpl() throws DatabaseException {
0169:
0170: id = new DatabaseId();
0171: envImpl = null;
0172:
0173: deleteState = NOT_DELETED;
0174:
0175: tree = new Tree();
0176: referringHandles = Collections.synchronizedSet(new HashSet());
0177:
0178: /* initDefaultSettings is called after envImpl is set. */
0179:
0180: eofNodeId = Node.getNextNodeId();
0181: }
0182:
0183: public void setDebugDatabaseName(String debugName) {
0184: debugDatabaseName = debugName;
0185: }
0186:
0187: public String getDebugName() {
0188: return debugDatabaseName;
0189: }
0190:
0191: /* For unit testing only. */
0192: public void setPendingDeletedHook(TestHook hook) {
0193: pendingDeletedHook = hook;
0194: }
0195:
0196: /**
0197: * Initialize configuration settings when creating a new instance or after
0198: * reading an instance from the log. The envImpl field must be set before
0199: * calling this method.
0200: */
0201: private void initDefaultSettings() throws DatabaseException {
0202:
0203: DbConfigManager configMgr = envImpl.getConfigManager();
0204:
0205: binDeltaPercent = configMgr
0206: .getInt(EnvironmentParams.BIN_DELTA_PERCENT);
0207: binMaxDeltas = configMgr
0208: .getInt(EnvironmentParams.BIN_MAX_DELTAS);
0209:
0210: if (maxMainTreeEntriesPerNode == 0) {
0211: maxMainTreeEntriesPerNode = configMgr
0212: .getInt(EnvironmentParams.NODE_MAX);
0213: }
0214:
0215: if (maxDupTreeEntriesPerNode == 0) {
0216: maxDupTreeEntriesPerNode = configMgr
0217: .getInt(EnvironmentParams.NODE_MAX_DUPTREE);
0218: }
0219: }
0220:
0221: /**
0222: * Clone. For the most part, just pass off to the super class for a
0223: * field-by-field copy.
0224: */
0225: public DatabaseImpl cloneDb() throws DatabaseException {
0226:
0227: try {
0228: DatabaseImpl newDb = (DatabaseImpl) clone();
0229: /* The cloned DB could have a non-zero use count. [#13415] */
0230: newDb.useCount = 0;
0231: return newDb;
0232: } catch (CloneNotSupportedException e) {
0233: throw new DatabaseException(e);
0234: }
0235: }
0236:
0237: /**
0238: * @return the database tree.
0239: */
0240: public Tree getTree() {
0241: return tree;
0242: }
0243:
0244: void setTree(Tree tree) {
0245: this .tree = tree;
0246: }
0247:
0248: /**
0249: * @return the database id.
0250: */
0251: public DatabaseId getId() {
0252: return id;
0253: }
0254:
0255: void setId(DatabaseId id) {
0256: this .id = id;
0257: }
0258:
0259: public long getEofNodeId() {
0260: return eofNodeId;
0261: }
0262:
0263: /**
0264: * @return true if this database is transactional.
0265: */
0266: public boolean isTransactional() {
0267: return transactional;
0268: }
0269:
0270: /**
0271: * Sets the transactional property for the first opened handle.
0272: */
0273: public void setTransactional(boolean transactional) {
0274: this .transactional = transactional;
0275: }
0276:
0277: /**
0278: * @return true if this database is in deferred write mode.
0279: */
0280: public boolean isDeferredWrite() {
0281: return deferredWrite;
0282: }
0283:
0284: /*
0285: * Set the deferred write property for the first opened handle.
0286: */
0287: public void setDeferredWrite(boolean deferredWrite) {
0288: this .deferredWrite = deferredWrite;
0289: }
0290:
0291: /**
0292: * @return true if duplicates are allowed in this database.
0293: */
0294: public boolean getSortedDuplicates() {
0295: return duplicatesAllowed;
0296: }
0297:
0298: public int getNodeMaxEntries() {
0299: return maxMainTreeEntriesPerNode;
0300: }
0301:
0302: public int getNodeMaxDupTreeEntries() {
0303: return maxDupTreeEntriesPerNode;
0304: }
0305:
0306: /**
0307: * Returns the memory size that should be added to MAPLN_OVERHEAD.
0308: *
0309: * This is a start at budgeting per-Database memory. For future reference,
0310: * other things that could be budgeted are:
0311: * - debugDatabaseName as it is set
0312: * - Database handles as they are added/removed in referringHandles
0313: */
0314: public int getAdditionalMemorySize() {
0315:
0316: int val = 0;
0317:
0318: /*
0319: * If the comparator object is non-null we double the size of the
0320: * serialized form to account for the approximate size of the user's
0321: * comparator object. This is only an approximation of course, and is
0322: * not a very good one if we have serialized the class name, but we
0323: * have no way to know the size of the user's object.
0324: */
0325: if (btreeComparator != null) {
0326: val += 2 * MemoryBudget
0327: .byteArraySize(btreeComparatorBytes.length);
0328: }
0329: if (duplicateComparator != null) {
0330: val += 2 * MemoryBudget
0331: .byteArraySize(duplicateComparatorBytes.length);
0332: }
0333: return val;
0334: }
0335:
0336: /**
0337: * Set the duplicate comparison function for this database.
0338: *
0339: * @return true if the comparator was actually changed
0340: *
0341: * @param duplicateComparator - The Duplicate Comparison function.
0342: */
0343: public boolean setDuplicateComparator(Comparator comparator,
0344: boolean byClassName) throws DatabaseException {
0345:
0346: duplicateComparator = comparator;
0347: byte[] newDuplicateComparatorBytes = comparatorToBytes(
0348: comparator, byClassName, "Duplicate");
0349: boolean ret = Arrays.equals(newDuplicateComparatorBytes,
0350: duplicateComparatorBytes);
0351: duplicateComparatorBytes = newDuplicateComparatorBytes;
0352: duplicateComparatorByClassName = byClassName;
0353: return !ret;
0354: }
0355:
0356: /**
0357: * Set the btree comparison function for this database.
0358: *
0359: * @return true if the comparator was actually changed
0360: *
0361: * @param btreeComparator - The btree Comparison function.
0362: */
0363: public boolean setBtreeComparator(Comparator comparator,
0364: boolean byClassName) throws DatabaseException {
0365:
0366: btreeComparator = comparator;
0367: byte[] newBtreeComparatorBytes = comparatorToBytes(comparator,
0368: byClassName, "Btree");
0369: boolean ret = Arrays.equals(newBtreeComparatorBytes,
0370: btreeComparatorBytes);
0371: btreeComparatorBytes = newBtreeComparatorBytes;
0372: btreeComparatorByClassName = byClassName;
0373: return !ret;
0374: }
0375:
0376: /**
0377: * @return the btree Comparator object.
0378: */
0379: public Comparator getBtreeComparator() {
0380: return btreeComparator;
0381: }
0382:
0383: /**
0384: * @return the duplicate Comparator object.
0385: */
0386: public Comparator getDuplicateComparator() {
0387: return duplicateComparator;
0388: }
0389:
0390: /**
0391: * @return whether Comparator is set by class name, not by serializable
0392: * Comparator object.
0393: */
0394: public boolean getBtreeComparatorByClass() {
0395: return btreeComparatorByClassName;
0396: }
0397:
0398: /**
0399: * @return whether Comparator is set by class name, not by serializable
0400: * Comparator object.
0401: */
0402: public boolean getDuplicateComparatorByClass() {
0403: return duplicateComparatorByClassName;
0404: }
0405:
0406: /**
0407: * Set the db environment during recovery, after instantiating the database
0408: * from the log
0409: */
0410: public void setEnvironmentImpl(EnvironmentImpl envImpl)
0411: throws DatabaseException {
0412:
0413: this .envImpl = envImpl;
0414: initDefaultSettings();
0415: tree.setDatabase(this );
0416: }
0417:
0418: /**
0419: * @return the database environment.
0420: */
0421: public EnvironmentImpl getDbEnvironment() {
0422: return envImpl;
0423: }
0424:
0425: /**
0426: * Returns whether one or more handles are open.
0427: */
0428: public boolean hasOpenHandles() {
0429: return referringHandles.size() > 0;
0430: }
0431:
0432: /**
0433: * Add a referring handle
0434: */
0435: public void addReferringHandle(Database db) {
0436: referringHandles.add(db);
0437: }
0438:
0439: /**
0440: * Decrement the reference count.
0441: */
0442: public void removeReferringHandle(Database db) {
0443: referringHandles.remove(db);
0444: }
0445:
0446: /**
0447: * @return the referring handle count.
0448: */
0449: synchronized int getReferringHandleCount() {
0450: return referringHandles.size();
0451: }
0452:
0453: /**
0454: * Increments the use count of this DB to prevent it from being
0455: * evicted. Called by the DbTree.createDb/getDb methods that return a
0456: * DatabaseImpl. Must be called while holding a lock on the MapLN. See
0457: * isInUse. [#13415]
0458: */
0459: void incrementUseCount() {
0460: /* Synchronize to update useCount atomically. */
0461: synchronized (useCountLock) {
0462: useCount += 1;
0463: }
0464: }
0465:
0466: /**
0467: * Decrements the use count of this DB, allowing it to be evicted if the
0468: * use count reaches zero. Called via DbTree.releaseDb to release a
0469: * DatabaseImpl that was returned by a DbTree.createDb/getDb method. See
0470: * isInUse. [#13415]
0471: */
0472: void decrementUseCount() {
0473: /* Synchronize to update useCount atomically. */
0474: synchronized (useCountLock) {
0475: assert useCount > 0;
0476: useCount -= 1;
0477: }
0478: }
0479:
0480: /**
0481: * Returns whether this DB is in use and cannot be evicted. Called by
0482: * MapLN.isEvictable while holding a write-lock on the MapLN and a latch on
0483: * its parent BIN. [#13415]
0484: *
0485: * When isInUse returns false (while holding a write-lock on the MapLN and
0486: * a latch on the parent BIN), it guarantees that the database object
0487: * is not in use and cannot be acquired by another thread (via
0488: * DbTree.createDb/getDb) until both the MapLN lock and BIN latch are
0489: * released. This guarantee is due to the fact that DbTree.createDb/getDb
0490: * only increment the use count while holding a read-lock on the MapLN.
0491: * Therefore, it is safe to evict the MapLN when isInUse returns false.
0492: *
0493: * When isInUse returns true, it is possible that another thread may
0494: * decrement the use count at any time, since no locking or latching is
0495: * performed when calling DbTree.releaseDb (which calls decrementUseCount).
0496: * Therefore, it is not guaranteed that the MapLN is in use when isInUse
0497: * returns true. A true result means: the DB may be in use, so it is not
0498: * safe to evict it.
0499: */
0500: public boolean isInUse() {
0501: /* Synchronize to read the up-to-date value of useCount. */
0502: synchronized (useCountLock) {
0503: return (useCount > 0);
0504: }
0505: }
0506:
0507: /**
0508: * Checks whether a database is in use during a remove or truncate database
0509: * operation.
0510: */
0511: boolean isInUseDuringDbRemove() {
0512: /* Synchronize to read the up-to-date value of useCount. */
0513: synchronized (useCountLock) {
0514:
0515: /*
0516: * The use count is at least one here, because remove/truncate has
0517: * called getDb but releaseDb has not yet been called. Normally
0518: * the database must be closed in order to remove or truncate it
0519: * and referringHandles will be empty. But when the deprecated
0520: * Database.truncate is called, the database is open and the use
0521: * count includes the number of open handles. [#15805]
0522: */
0523: return useCount > 1 + referringHandles.size();
0524: }
0525: }
0526:
0527: /**
0528: * Flush all dirty nodes for this database to disk.
0529: */
0530: public synchronized void sync(boolean flushLog)
0531: throws DatabaseException {
0532:
0533: if (!isDeferredWrite()) {
0534: throw new DatabaseException(
0535: "Database.sync() is only supported "
0536: + "for deferred-write databases");
0537: }
0538:
0539: if (tree.rootExists()) {
0540: Checkpointer.syncDatabase(envImpl, this , flushLog);
0541: }
0542: }
0543:
0544: /**
0545: * For this secondary database return the primary that it is associated
0546: * with, or null if not associated with any primary. Note that not all
0547: * handles need be associated with a primary.
0548: */
0549: public Database findPrimaryDatabase() throws DatabaseException {
0550:
0551: for (Iterator i = referringHandles.iterator(); i.hasNext();) {
0552: Object obj = i.next();
0553: if (obj instanceof SecondaryDatabase) {
0554: return ((SecondaryDatabase) obj).getPrimaryDatabase();
0555: }
0556: }
0557: return null;
0558: }
0559:
0560: public String getName() throws DatabaseException {
0561:
0562: return envImpl.getDbMapTree().getDbName(id);
0563: }
0564:
0565: /*
0566: * @return true if this database is deleted. Delete cleanup
0567: * may still be in progress.
0568: */
0569: public boolean isDeleted() {
0570: return !(deleteState == NOT_DELETED);
0571: }
0572:
0573: /*
0574: * @return true if this database is deleted and all cleanup is finished.
0575: */
0576: public boolean isDeleteFinished() {
0577: return (deleteState == DELETED);
0578: }
0579:
0580: /*
0581: * The delete cleanup is starting. Set this before releasing any
0582: * write locks held for a db operation.
0583: */
0584: public void startDeleteProcessing() {
0585: assert (deleteState == NOT_DELETED);
0586:
0587: deleteState = DELETED_CLEANUP_INLIST_HARVEST;
0588: }
0589:
0590: /*
0591: * Should be called by the SortedLSNTreeWalker when it is finished with
0592: * the INList.
0593: */
0594: void finishedINListHarvest() {
0595: assert (deleteState == DELETED_CLEANUP_INLIST_HARVEST);
0596:
0597: deleteState = DELETED_CLEANUP_LOG_HARVEST;
0598: }
0599:
0600: /**
0601: * Purge a DatabaseImpl and corresponding MapLN in the db mapping tree.
0602: * Purging consists of removing all related INs from the db mapping tree
0603: * and deleting the related MapLN.
0604: * Used at the transaction end in these cases:
0605: * - purge the deleted database after a commit of
0606: * Environment.removeDatabase
0607: * - purge the deleted database after a commit of
0608: * Environment.truncateDatabase
0609: * - purge the newly created database after an abort of
0610: * Environment.truncateDatabase
0611: */
0612: public void deleteAndReleaseINs() throws DatabaseException {
0613:
0614: startDeleteProcessing();
0615: releaseDeletedINs();
0616: }
0617:
0618: public void releaseDeletedINs() throws DatabaseException {
0619:
0620: if (pendingDeletedHook != null) {
0621: pendingDeletedHook.doHook();
0622: }
0623:
0624: try {
0625:
0626: /*
0627: * Get the root LSN before deleting the MapLN, as that will null
0628: * out the root.
0629: */
0630: long rootLsn = tree.getRootLsn();
0631:
0632: /*
0633: * Use a snapshot tracker that is accumulated under the log write
0634: * latch when we're doing counting. Start by recording the LSN of
0635: * the root IN as obsolete. A zero size is passed for the last
0636: * parameter because it is too expensive to fetch the node.
0637: */
0638: UtilizationTracker snapshot = new UtilizationTracker(
0639: envImpl);
0640: if (rootLsn != DbLsn.NULL_LSN) {
0641: snapshot.countObsoleteNodeInexact(rootLsn,
0642: LogEntryType.LOG_IN, 0);
0643: }
0644:
0645: /* Fetch LNs to count LN sizes only if so configured. */
0646: boolean fetchLNSize = envImpl.getCleaner()
0647: .getFetchObsoleteSize();
0648:
0649: /* Use the tree walker to visit every child lsn in the tree. */
0650: ObsoleteProcessor obsoleteProcessor = new ObsoleteProcessor(
0651: snapshot);
0652: SortedLSNTreeWalker walker = new ObsoleteTreeWalker(this ,
0653: rootLsn, fetchLNSize, obsoleteProcessor);
0654:
0655: /*
0656: * Delete MapLN before the walk. Note that the processing of
0657: * the naming tree means this MapLN is never actually
0658: * accessible from the current tree, but deleting the MapLN
0659: * will do two things:
0660: * (a) mark it properly obsolete
0661: * (b) null out the database tree, leaving the INList the only
0662: * reference to the INs.
0663: */
0664: envImpl.getDbMapTree().deleteMapLN(id);
0665:
0666: /*
0667: * At this point, it's possible for the evictor to find an IN
0668: * for this database on the INList. It should be ignored.
0669: */
0670: walker.walk();
0671:
0672: /*
0673: * Count obsolete nodes for a deleted database at transaction
0674: * end time. Write out the modified file summaries for
0675: * recovery.
0676: */
0677: envImpl.getUtilizationProfile().countAndLogSummaries(
0678: snapshot.getTrackedFiles());
0679: } finally {
0680: deleteState = DELETED;
0681: /* releaseDb to balance getDb called by truncate/remove. */
0682: envImpl.releaseDb(this );
0683: }
0684: }
0685:
0686: public void checkIsDeleted(String operation)
0687: throws DatabaseException {
0688:
0689: if (isDeleted()) {
0690: throw new DatabaseException("Attempt to " + operation
0691: + " a deleted database");
0692: }
0693: }
0694:
0695: private static class ObsoleteTreeWalker extends SortedLSNTreeWalker {
0696:
0697: private ObsoleteTreeWalker(DatabaseImpl dbImpl, long rootLsn,
0698: boolean fetchLNSize, TreeNodeProcessor callback)
0699: throws DatabaseException {
0700:
0701: super (dbImpl, true, // remove INs from INList
0702: true, // set INList finish harvest
0703: rootLsn, callback, null, /* savedException */
0704: null); /* exception predicate */
0705:
0706: accumulateLNs = fetchLNSize;
0707: }
0708: }
0709:
0710: /* Mark each LSN obsolete in the utilization tracker. */
0711: private static class ObsoleteProcessor implements TreeNodeProcessor {
0712:
0713: private UtilizationTracker tracker;
0714:
0715: ObsoleteProcessor(UtilizationTracker tracker) {
0716: this .tracker = tracker;
0717: }
0718:
0719: public void processLSN(long childLsn, LogEntryType childType,
0720: Node node, byte[] lnKey) throws DatabaseException {
0721:
0722: assert childLsn != DbLsn.NULL_LSN;
0723:
0724: /*
0725: * Count the LN log size if an LN node and key are available. But
0726: * do not count the size if the LN is dirty, since the logged LN is
0727: * not available. [#15365]
0728: */
0729: int size = 0;
0730: if (lnKey != null && node instanceof LN) {
0731: LN ln = (LN) node;
0732: size = ln.getLastLoggedSize();
0733: }
0734:
0735: tracker.countObsoleteNodeInexact(childLsn, childType, size);
0736: }
0737:
0738: public void processDirtyDeletedLN(long childLsn, LN ln,
0739: byte[] lnKey) throws DatabaseException {
0740:
0741: assert ln != null;
0742:
0743: /*
0744: * Do not count the size (pass zero) because the LN is dirty and
0745: * the logged LN is not available.
0746: */
0747: tracker.countObsoleteNodeInexact(childLsn, ln.getLogType(),
0748: 0);
0749: }
0750:
0751: public void processDupCount(long ignore) {
0752: }
0753: }
0754:
0755: public DatabaseStats stat(StatsConfig config)
0756: throws DatabaseException {
0757:
0758: if (stats == null) {
0759:
0760: /*
0761: * Called first time w/ FAST_STATS so just give them an
0762: * empty one.
0763: */
0764: stats = new BtreeStats();
0765: }
0766:
0767: if (!config.getFast()) {
0768: if (tree == null) {
0769: return new BtreeStats();
0770: }
0771:
0772: PrintStream out = config.getShowProgressStream();
0773: if (out == null) {
0774: out = System.err;
0775: }
0776:
0777: StatsAccumulator statsAcc = new StatsAccumulator(out,
0778: config.getShowProgressInterval(), getEmptyStats());
0779: walkDatabaseTree(statsAcc, out, true);
0780: statsAcc.copyToStats(stats);
0781: }
0782:
0783: return stats;
0784: }
0785:
0786: /*
0787: * @param config verify configuration
0788: * @param emptyStats empty database stats, to be filled by this method
0789: * @return true if the verify saw no errors.
0790: */
0791: public boolean verify(VerifyConfig config, DatabaseStats emptyStats)
0792: throws DatabaseException {
0793:
0794: if (tree == null) {
0795: return true;
0796: }
0797:
0798: PrintStream out = config.getShowProgressStream();
0799: if (out == null) {
0800: out = System.err;
0801: }
0802:
0803: StatsAccumulator statsAcc = new StatsAccumulator(out, config
0804: .getShowProgressInterval(), emptyStats) {
0805: void verifyNode(Node node) {
0806:
0807: try {
0808: node.verify(null);
0809: } catch (DatabaseException INE) {
0810: progressStream.println(INE);
0811: }
0812: }
0813: };
0814: boolean ok = walkDatabaseTree(statsAcc, out, config
0815: .getPrintInfo());
0816: statsAcc.copyToStats(emptyStats);
0817: return ok;
0818: }
0819:
0820: /* @return the right kind of stats object for this database. */
0821: public DatabaseStats getEmptyStats() {
0822: return new BtreeStats();
0823: }
0824:
0825: /*
0826: * @return true if no errors.
0827: */
0828: private boolean walkDatabaseTree(
0829: TreeWalkerStatsAccumulator statsAcc, PrintStream out,
0830: boolean verbose) throws DatabaseException {
0831:
0832: boolean ok = true;
0833: Locker locker = new ThreadLocker(envImpl);
0834: Cursor cursor = null;
0835: CursorImpl impl = null;
0836: try {
0837: EnvironmentImpl.incThreadLocalReferenceCount();
0838: cursor = DbInternal.newCursor(this , locker, null);
0839: impl = DbInternal.getCursorImpl(cursor);
0840: tree.setTreeStatsAccumulator(statsAcc);
0841:
0842: /*
0843: * This will only be used on the first call for the position()
0844: * call.
0845: */
0846: impl.setTreeStatsAccumulator(statsAcc);
0847: DatabaseEntry foundData = new DatabaseEntry();
0848: DatabaseEntry key = new DatabaseEntry();
0849: OperationStatus status = DbInternal.position(cursor, key,
0850: foundData, LockMode.READ_UNCOMMITTED, true);
0851: while (status == OperationStatus.SUCCESS) {
0852: try {
0853: status = DbInternal.retrieveNext(cursor, key,
0854: foundData, LockMode.READ_UNCOMMITTED,
0855: GetMode.NEXT);
0856: } catch (DatabaseException DBE) {
0857: ok = false;
0858: if (DbInternal
0859: .advanceCursor(cursor, key, foundData)) {
0860: if (verbose) {
0861: out
0862: .println("Error encountered (continuing):");
0863: out.println(DBE);
0864: printErrorRecord(out, key, foundData);
0865: }
0866: } else {
0867: throw DBE;
0868: }
0869: }
0870: }
0871: } finally {
0872: if (impl != null) {
0873: impl.setTreeStatsAccumulator(null);
0874: }
0875: tree.setTreeStatsAccumulator(null);
0876: EnvironmentImpl.decThreadLocalReferenceCount();
0877: if (cursor != null) {
0878: cursor.close();
0879: }
0880: }
0881:
0882: return ok;
0883: }
0884:
0885: /**
0886: * Prints the key and data, if available, for a BIN entry that could not be
0887: * read/verified. Uses the same format as DbDump and prints both the hex
0888: * and printable versions of the entries.
0889: */
0890: private void printErrorRecord(PrintStream out, DatabaseEntry key,
0891: DatabaseEntry data) {
0892:
0893: byte[] bytes = key.getData();
0894: StringBuffer sb = new StringBuffer("Error Key ");
0895: if (bytes == null) {
0896: sb.append("UNKNOWN");
0897: } else {
0898: CmdUtil.formatEntry(sb, bytes, false);
0899: sb.append(' ');
0900: CmdUtil.formatEntry(sb, bytes, true);
0901: }
0902: out.println(sb);
0903:
0904: bytes = data.getData();
0905: sb = new StringBuffer("Error Data ");
0906: if (bytes == null) {
0907: sb.append("UNKNOWN");
0908: } else {
0909: CmdUtil.formatEntry(sb, bytes, false);
0910: sb.append(' ');
0911: CmdUtil.formatEntry(sb, bytes, true);
0912: }
0913: out.println(sb);
0914: }
0915:
0916: static class StatsAccumulator implements TreeWalkerStatsAccumulator {
0917: private Set inNodeIdsSeen = new HashSet();
0918: private Set binNodeIdsSeen = new HashSet();
0919: private Set dinNodeIdsSeen = new HashSet();
0920: private Set dbinNodeIdsSeen = new HashSet();
0921: private Set dupCountLNsSeen = new HashSet();
0922: private long[] insSeenByLevel = null;
0923: private long[] binsSeenByLevel = null;
0924: private long[] dinsSeenByLevel = null;
0925: private long[] dbinsSeenByLevel = null;
0926: private long lnCount = 0;
0927: private long deletedLNCount = 0;
0928: private int mainTreeMaxDepth = 0;
0929: private int duplicateTreeMaxDepth = 0;
0930: private DatabaseStats useStats;
0931:
0932: PrintStream progressStream;
0933: int progressInterval;
0934:
0935: /* The max levels we ever expect to see in a tree. */
0936: private static final int MAX_LEVELS = 100;
0937:
0938: StatsAccumulator(PrintStream progressStream,
0939: int progressInterval, DatabaseStats useStats) {
0940:
0941: this .progressStream = progressStream;
0942: this .progressInterval = progressInterval;
0943:
0944: insSeenByLevel = new long[MAX_LEVELS];
0945: binsSeenByLevel = new long[MAX_LEVELS];
0946: dinsSeenByLevel = new long[MAX_LEVELS];
0947: dbinsSeenByLevel = new long[MAX_LEVELS];
0948:
0949: this .useStats = useStats;
0950: }
0951:
0952: void verifyNode(Node node) {
0953:
0954: }
0955:
0956: public void processIN(IN node, Long nid, int level) {
0957: if (inNodeIdsSeen.add(nid)) {
0958: tallyLevel(level, insSeenByLevel);
0959: verifyNode(node);
0960: }
0961: }
0962:
0963: public void processBIN(BIN node, Long nid, int level) {
0964: if (binNodeIdsSeen.add(nid)) {
0965: tallyLevel(level, binsSeenByLevel);
0966: verifyNode(node);
0967: }
0968: }
0969:
0970: public void processDIN(DIN node, Long nid, int level) {
0971: if (dinNodeIdsSeen.add(nid)) {
0972: tallyLevel(level, dinsSeenByLevel);
0973: verifyNode(node);
0974: }
0975: }
0976:
0977: public void processDBIN(DBIN node, Long nid, int level) {
0978: if (dbinNodeIdsSeen.add(nid)) {
0979: tallyLevel(level, dbinsSeenByLevel);
0980: verifyNode(node);
0981: }
0982: }
0983:
0984: public void processDupCountLN(DupCountLN node, Long nid) {
0985: dupCountLNsSeen.add(nid);
0986: verifyNode(node);
0987: }
0988:
0989: private void tallyLevel(int levelArg, long[] nodesSeenByLevel) {
0990: int level = levelArg;
0991: if (level >= IN.DBMAP_LEVEL) {
0992: return;
0993: }
0994: if (level >= IN.MAIN_LEVEL) {
0995: level &= ~IN.MAIN_LEVEL;
0996: if (level > mainTreeMaxDepth) {
0997: mainTreeMaxDepth = level;
0998: }
0999: } else {
1000: if (level > duplicateTreeMaxDepth) {
1001: duplicateTreeMaxDepth = level;
1002: }
1003: }
1004:
1005: nodesSeenByLevel[level]++;
1006: }
1007:
1008: public void incrementLNCount() {
1009: lnCount++;
1010: if (progressInterval != 0) {
1011: if ((lnCount % progressInterval) == 0) {
1012: copyToStats(useStats);
1013: progressStream.println(useStats);
1014: }
1015: }
1016: }
1017:
1018: public void incrementDeletedLNCount() {
1019: deletedLNCount++;
1020: }
1021:
1022: Set getINNodeIdsSeen() {
1023: return inNodeIdsSeen;
1024: }
1025:
1026: Set getBINNodeIdsSeen() {
1027: return binNodeIdsSeen;
1028: }
1029:
1030: Set getDINNodeIdsSeen() {
1031: return dinNodeIdsSeen;
1032: }
1033:
1034: Set getDBINNodeIdsSeen() {
1035: return dbinNodeIdsSeen;
1036: }
1037:
1038: long[] getINsByLevel() {
1039: return insSeenByLevel;
1040: }
1041:
1042: long[] getBINsByLevel() {
1043: return binsSeenByLevel;
1044: }
1045:
1046: long[] getDINsByLevel() {
1047: return dinsSeenByLevel;
1048: }
1049:
1050: long[] getDBINsByLevel() {
1051: return dbinsSeenByLevel;
1052: }
1053:
1054: long getLNCount() {
1055: return lnCount;
1056: }
1057:
1058: Set getDupCountLNCount() {
1059: return dupCountLNsSeen;
1060: }
1061:
1062: long getDeletedLNCount() {
1063: return deletedLNCount;
1064: }
1065:
1066: int getMainTreeMaxDepth() {
1067: return mainTreeMaxDepth;
1068: }
1069:
1070: int getDuplicateTreeMaxDepth() {
1071: return duplicateTreeMaxDepth;
1072: }
1073:
1074: private void copyToStats(DatabaseStats stats) {
1075: BtreeStats bStats = (BtreeStats) stats;
1076: bStats.setInternalNodeCount(getINNodeIdsSeen().size());
1077: bStats.setBottomInternalNodeCount(getBINNodeIdsSeen()
1078: .size());
1079: bStats.setDuplicateInternalNodeCount(getDINNodeIdsSeen()
1080: .size());
1081: bStats
1082: .setDuplicateBottomInternalNodeCount(getDBINNodeIdsSeen()
1083: .size());
1084: bStats.setLeafNodeCount(getLNCount());
1085: bStats.setDeletedLeafNodeCount(getDeletedLNCount());
1086: bStats
1087: .setDupCountLeafNodeCount(getDupCountLNCount()
1088: .size());
1089: bStats.setMainTreeMaxDepth(getMainTreeMaxDepth());
1090: bStats.setDuplicateTreeMaxDepth(getDuplicateTreeMaxDepth());
1091: bStats.setINsByLevel(getINsByLevel());
1092: bStats.setBINsByLevel(getBINsByLevel());
1093: bStats.setDINsByLevel(getDINsByLevel());
1094: bStats.setDBINsByLevel(getDBINsByLevel());
1095: }
1096: }
1097:
1098: /**
1099: * Preload exceptions, classes, callbacks.
1100: */
1101:
1102: /**
1103: * Undeclared exception used to throw through SortedLSNTreeWalker code
1104: * when preload has either filled the user's max byte or time request.
1105: */
1106: private static class HaltPreloadException extends RuntimeException {
1107:
1108: private PreloadStatus status;
1109:
1110: HaltPreloadException(PreloadStatus status) {
1111: super (status.toString());
1112: this .status = status;
1113: }
1114:
1115: PreloadStatus getStatus() {
1116: return status;
1117: }
1118: }
1119:
1120: private static final HaltPreloadException TIME_EXCEEDED_PRELOAD_EXCEPTION = new HaltPreloadException(
1121: PreloadStatus.EXCEEDED_TIME);
1122:
1123: private static final HaltPreloadException MEMORY_EXCEEDED_PRELOAD_EXCEPTION = new HaltPreloadException(
1124: PreloadStatus.FILLED_CACHE);
1125:
1126: /**
1127: * The processLSN() code for PreloadLSNTreeWalker.
1128: */
1129: private static class PreloadProcessor implements TreeNodeProcessor {
1130:
1131: private EnvironmentImpl envImpl;
1132: private long maxBytes;
1133: private long targetTime;
1134: private PreloadStats stats;
1135:
1136: PreloadProcessor(EnvironmentImpl envImpl, long maxBytes,
1137: long targetTime, PreloadStats stats) {
1138: this .envImpl = envImpl;
1139: this .maxBytes = maxBytes;
1140: this .targetTime = targetTime;
1141: this .stats = stats;
1142: }
1143:
1144: /**
1145: * Called for each LSN that the SortedLSNTreeWalker encounters.
1146: */
1147: public void processLSN(long childLsn, LogEntryType childType,
1148: Node ignore, byte[] ignore2) throws DatabaseException {
1149:
1150: assert childLsn != DbLsn.NULL_LSN;
1151:
1152: /*
1153: * Check if we've exceeded either the max time or max bytes
1154: * allowed for this preload() call.
1155: */
1156: if (System.currentTimeMillis() > targetTime) {
1157: throw TIME_EXCEEDED_PRELOAD_EXCEPTION;
1158: }
1159:
1160: if (envImpl.getMemoryBudget().getCacheMemoryUsage() > maxBytes) {
1161: throw MEMORY_EXCEEDED_PRELOAD_EXCEPTION;
1162: }
1163:
1164: /* Count entry types to return in the PreloadStats. */
1165: if (childType
1166: .equals(LogEntryType.LOG_DUPCOUNTLN_TRANSACTIONAL)
1167: || childType.equals(LogEntryType.LOG_DUPCOUNTLN)) {
1168: stats.nDupCountLNsLoaded++;
1169: } else if (childType
1170: .equals(LogEntryType.LOG_LN_TRANSACTIONAL)
1171: || childType.equals(LogEntryType.LOG_LN)) {
1172: stats.nLNsLoaded++;
1173: } else if (childType.equals(LogEntryType.LOG_DBIN)) {
1174: stats.nDBINsLoaded++;
1175: } else if (childType.equals(LogEntryType.LOG_BIN)) {
1176: stats.nBINsLoaded++;
1177: } else if (childType.equals(LogEntryType.LOG_DIN)) {
1178: stats.nDINsLoaded++;
1179: } else if (childType.equals(LogEntryType.LOG_IN)) {
1180: stats.nINsLoaded++;
1181: }
1182: }
1183:
1184: public void processDirtyDeletedLN(long childLsn, LN ln,
1185: byte[] lnKey) throws DatabaseException {
1186: }
1187:
1188: public void processDupCount(long ignore) {
1189: }
1190: }
1191:
1192: /*
1193: * An extension of SortedLSNTreeWalker that provides an LSN to IN/index
1194: * map. When an LSN is processed by the tree walker, the map is used to
1195: * lookup the parent IN and child entry index of each LSN processed by the
1196: * tree walker.
1197: */
1198: private static class PreloadLSNTreeWalker extends
1199: SortedLSNTreeWalker {
1200:
1201: /* LSN -> INEntry */
1202: private Map lsnINMap = new HashMap();
1203:
1204: /* struct to hold IN/entry-index pair. */
1205: private static class INEntry {
1206: INEntry(IN in, int index) {
1207: this .in = in;
1208: this .index = index;
1209: }
1210:
1211: IN in;
1212: int index;
1213: }
1214:
1215: PreloadLSNTreeWalker(DatabaseImpl db,
1216: TreeNodeProcessor callback, PreloadConfig conf)
1217: throws DatabaseException {
1218:
1219: super (db, false, false, db.tree.getRootLsn(), callback,
1220: null, null); /* savedException, exception predicate */
1221: accumulateLNs = conf.getLoadLNs();
1222: }
1223:
1224: private final class PreloadWithRootLatched implements
1225: WithRootLatched {
1226:
1227: public IN doWork(ChildReference root)
1228: throws DatabaseException {
1229:
1230: walkInternal();
1231: return null;
1232: }
1233: }
1234:
1235: public void walk() throws DatabaseException {
1236:
1237: WithRootLatched preloadWRL = new PreloadWithRootLatched();
1238: dbImpl.getTree().withRootLatchedExclusive(preloadWRL);
1239: }
1240:
1241: /*
1242: * Method to get the Root IN for this DatabaseImpl's tree. Latches
1243: * the root IN.
1244: */
1245: protected IN getRootIN(long rootLsn) throws DatabaseException {
1246:
1247: return dbImpl.getTree().getRootIN(false);
1248: }
1249:
1250: /*
1251: * Release the latch on the root IN.
1252: */
1253: protected void releaseRootIN(IN root) throws DatabaseException {
1254:
1255: root.releaseLatch();
1256: }
1257:
1258: /*
1259: * Add an LSN -> IN/index entry to the map.
1260: */
1261: protected void addToLsnINMap(Long lsn, IN in, int index) {
1262: assert in.getDatabase() != null;
1263: lsnINMap.put(lsn, new INEntry(in, index));
1264: }
1265:
1266: /*
1267: * Process an LSN. Get & remove its INEntry from the map, then fetch
1268: * the target at the INEntry's IN/index pair. This method will be
1269: * called in sorted LSN order.
1270: *
1271: * We do not bother to set the lnkeyEntry because we never use the
1272: * lnKey parameter in the processLSN method.
1273: */
1274: protected Node fetchLSN(long lsn, DatabaseEntry lnKeyEntry)
1275: throws DatabaseException {
1276:
1277: INEntry inEntry = (INEntry) lsnINMap.remove(new Long(lsn));
1278: assert (inEntry != null);
1279: IN in = inEntry.in;
1280: in.latch();
1281: try {
1282: int index = inEntry.index;
1283: if (index < 0) {
1284: /* Negative index signifies a DupCountLN. */
1285: DIN din = (DIN) in;
1286: return din.getDupCountLN();
1287: } else {
1288: if (in.isEntryKnownDeleted(index)
1289: || in.getLsn(index) != lsn) {
1290: return null;
1291: }
1292: }
1293: return in.fetchTarget(index);
1294: } finally {
1295: in.releaseLatch();
1296: }
1297: }
1298: }
1299:
1300: /**
1301: * Preload the cache, using up to maxBytes bytes or maxMillsecs msec.
1302: */
1303: public PreloadStats preload(PreloadConfig config)
1304: throws DatabaseException {
1305:
1306: try {
1307: long maxBytes = config.getMaxBytes();
1308: long maxMillisecs = config.getMaxMillisecs();
1309: long targetTime = Long.MAX_VALUE;
1310: if (maxMillisecs > 0) {
1311: targetTime = System.currentTimeMillis() + maxMillisecs;
1312: }
1313:
1314: long cacheBudget = envImpl.getMemoryBudget()
1315: .getCacheBudget();
1316: if (maxBytes == 0) {
1317: maxBytes = cacheBudget;
1318: } else if (maxBytes > cacheBudget) {
1319: throw new IllegalArgumentException(
1320: "maxBytes parameter to Database.preload() was "
1321: + "specified as " + maxBytes
1322: + " bytes \nbut the cache is only "
1323: + cacheBudget + " bytes.");
1324: }
1325:
1326: PreloadStats ret = new PreloadStats();
1327: PreloadProcessor callback = new PreloadProcessor(envImpl,
1328: maxBytes, targetTime, ret);
1329: SortedLSNTreeWalker walker = new PreloadLSNTreeWalker(this ,
1330: callback, config);
1331: try {
1332: walker.walk();
1333: } catch (HaltPreloadException HPE) {
1334: ret.status = HPE.getStatus();
1335: }
1336:
1337: assert LatchSupport.countLatchesHeld() == 0;
1338: return ret;
1339: } catch (Error E) {
1340: envImpl.invalidate(E);
1341: throw E;
1342: }
1343: }
1344:
1345: /**
1346: * The processLSN() code for PreloadLSNTreeWalker.
1347: */
1348: private static class CountProcessor implements TreeNodeProcessor {
1349:
1350: private EnvironmentImpl envImpl;
1351: /* Use PreloadStats in case we ever want to count more than LNs. */
1352: private PreloadStats stats;
1353:
1354: CountProcessor(EnvironmentImpl envImpl, PreloadStats stats) {
1355: this .envImpl = envImpl;
1356: this .stats = stats;
1357: }
1358:
1359: /**
1360: * Called for each LSN that the SortedLSNTreeWalker encounters.
1361: */
1362: public void processLSN(long childLsn, LogEntryType childType,
1363: Node ignore, byte[] ignore2) throws DatabaseException {
1364:
1365: /* Count entry types to return in the PreloadStats. */
1366: if (childType
1367: .equals(LogEntryType.LOG_DUPCOUNTLN_TRANSACTIONAL)
1368: || childType.equals(LogEntryType.LOG_DUPCOUNTLN)) {
1369: /* Don't descend down into the dup tree -- just use the DCL. */
1370: long dupCount = 0;
1371: DupCountLN dcl = (DupCountLN) envImpl.getLogManager()
1372: .get(childLsn);
1373: dupCount = dcl.getDupCount();
1374: stats.nLNsLoaded += dupCount;
1375: } else if (childType
1376: .equals(LogEntryType.LOG_LN_TRANSACTIONAL)
1377: || childType.equals(LogEntryType.LOG_LN)) {
1378: stats.nLNsLoaded++;
1379: }
1380: }
1381:
1382: public void processDirtyDeletedLN(long childLsn, LN ln,
1383: byte[] lnKey) throws DatabaseException {
1384: }
1385:
1386: /* Used when processing Deferred Write dbs and there are no LSNs. */
1387: public void processDupCount(long count) {
1388: stats.nLNsLoaded += count;
1389: }
1390: }
1391:
1392: private static class CountExceptionPredicate implements
1393: ExceptionPredicate {
1394:
1395: /*
1396: * Return true if the exception can be ignored.
1397: * LogFileNotFoundException is the only one so far.
1398: */
1399: public boolean ignoreException(Exception e) {
1400: if (e instanceof LogFileNotFoundException) {
1401: return true;
1402: }
1403: return false;
1404: }
1405: }
1406:
1407: /**
1408: * Count entries in the database including dups, but don't dirty the cache.
1409: */
1410: public long count() throws DatabaseException {
1411:
1412: try {
1413: PreloadStats ret = new PreloadStats();
1414:
1415: CountProcessor callback = new CountProcessor(envImpl, ret);
1416: ExceptionPredicate excPredicate = new CountExceptionPredicate();
1417: SortedLSNTreeWalker walker = new SortedLSNTreeWalker(this ,
1418: false, false, tree.getRootLsn(), callback, null,
1419: excPredicate);
1420: /* Don't descend down into the dup tree. Use the DupCountLN. */
1421: walker.setProcessDupTree(false);
1422: if (deferredWrite) {
1423: walker.setPassNullLSNNodes(true);
1424: }
1425: walker.walk();
1426:
1427: assert LatchSupport.countLatchesHeld() == 0;
1428: return ret.nLNsLoaded;
1429: } catch (Error E) {
1430: envImpl.invalidate(E);
1431: throw E;
1432: }
1433: }
1434:
1435: /*
1436: * Dumping
1437: */
1438: public String dumpString(int nSpaces) {
1439: StringBuffer sb = new StringBuffer();
1440: sb.append(TreeUtils.indent(nSpaces));
1441: sb.append("<database id=\"");
1442: sb.append(id.toString());
1443: sb.append("\"");
1444: if (btreeComparator != null) {
1445: sb.append(" btc=\"");
1446: sb.append(getComparatorClassName(btreeComparator));
1447: sb.append("\"");
1448: }
1449: if (duplicateComparator != null) {
1450: sb.append(" dupc=\"");
1451: sb.append(getComparatorClassName(duplicateComparator));
1452: sb.append("\"");
1453: }
1454: sb.append("/>");
1455: return sb.toString();
1456: }
1457:
1458: /*
1459: * Logging support
1460: */
1461:
1462: /**
1463: * @see Loggable#getLogSize
1464: */
1465: public int getLogSize() {
1466: return id.getLogSize()
1467: + tree.getLogSize()
1468: + LogUtils.getBooleanLogSize()
1469: + LogUtils.getByteArrayLogSize(btreeComparatorBytes)
1470: + LogUtils
1471: .getByteArrayLogSize(duplicateComparatorBytes)
1472: + (LogUtils.getIntLogSize() * 2);
1473: }
1474:
1475: /**
1476: * @see Loggable#writeToLog
1477: */
1478: public void writeToLog(ByteBuffer logBuffer) {
1479: id.writeToLog(logBuffer);
1480: tree.writeToLog(logBuffer);
1481: LogUtils.writeBoolean(logBuffer, duplicatesAllowed);
1482: LogUtils.writeByteArray(logBuffer, btreeComparatorBytes);
1483: LogUtils.writeByteArray(logBuffer, duplicateComparatorBytes);
1484: LogUtils.writeInt(logBuffer, maxMainTreeEntriesPerNode);
1485: LogUtils.writeInt(logBuffer, maxDupTreeEntriesPerNode);
1486: }
1487:
1488: /**
1489: * @see Loggable#readFromLog
1490: */
1491: public void readFromLog(ByteBuffer itemBuffer, byte entryTypeVersion)
1492: throws LogException {
1493:
1494: id.readFromLog(itemBuffer, entryTypeVersion);
1495: tree.readFromLog(itemBuffer, entryTypeVersion);
1496: duplicatesAllowed = LogUtils.readBoolean(itemBuffer);
1497:
1498: if (entryTypeVersion >= 2) {
1499: btreeComparatorBytes = LogUtils.readByteArray(itemBuffer);
1500: duplicateComparatorBytes = LogUtils
1501: .readByteArray(itemBuffer);
1502: } else {
1503: String btreeClassName = LogUtils.readString(itemBuffer);
1504: String dupClassName = LogUtils.readString(itemBuffer);
1505: if (btreeClassName.length() == 0) {
1506: btreeComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
1507: } else {
1508: btreeComparatorBytes = objectToBytes(btreeClassName,
1509: "Btree");
1510: }
1511: if (dupClassName.length() == 0) {
1512: duplicateComparatorBytes = LogUtils.ZERO_LENGTH_BYTE_ARRAY;
1513: } else {
1514: duplicateComparatorBytes = objectToBytes(dupClassName,
1515: "Duplicate");
1516: }
1517: }
1518:
1519: /* Don't instantiate if comparators are unnecessary (DbPrintLog). */
1520: if (!EnvironmentImpl.getNoComparators()) {
1521: try {
1522: if (btreeComparatorBytes.length != 0) {
1523: Object obj = bytesToObject(btreeComparatorBytes,
1524: "Btree");
1525: if (obj instanceof String) {
1526: Class cls = Class.forName((String) obj);
1527: btreeComparator = instantiateComparator(cls,
1528: "Btree");
1529: btreeComparatorByClassName = true;
1530: } else if (obj instanceof Comparator) {
1531: btreeComparator = (Comparator) obj;
1532: btreeComparatorByClassName = false;
1533: } else {
1534: assert false : obj.getClass().getName();
1535: }
1536: } else {
1537: btreeComparator = null;
1538: btreeComparatorByClassName = false;
1539: }
1540: if (duplicateComparatorBytes.length != 0) {
1541: Object obj = bytesToObject(
1542: duplicateComparatorBytes, "Duplicate");
1543: if (obj instanceof String) {
1544: Class cls = Class.forName((String) obj);
1545: duplicateComparator = instantiateComparator(
1546: cls, "Duplicate");
1547: duplicateComparatorByClassName = true;
1548: } else if (obj instanceof Comparator) {
1549: duplicateComparator = (Comparator) obj;
1550: duplicateComparatorByClassName = false;
1551: } else {
1552: assert false : obj.getClass().getName();
1553: }
1554: } else {
1555: duplicateComparator = null;
1556: duplicateComparatorByClassName = false;
1557: }
1558: } catch (ClassNotFoundException CNFE) {
1559: throw new LogException(
1560: "couldn't instantiate class comparator", CNFE);
1561: }
1562: }
1563:
1564: if (entryTypeVersion >= 1) {
1565: maxMainTreeEntriesPerNode = LogUtils.readInt(itemBuffer);
1566: maxDupTreeEntriesPerNode = LogUtils.readInt(itemBuffer);
1567: }
1568: }
1569:
1570: /**
1571: * @see Loggable#dumpLog
1572: */
1573: public void dumpLog(StringBuffer sb, boolean verbose) {
1574: sb.append("<database>");
1575: id.dumpLog(sb, verbose);
1576: tree.dumpLog(sb, verbose);
1577: sb.append("<dupsort v=\"").append(duplicatesAllowed);
1578: sb.append("\"/>");
1579: sb.append("<btcf name=\"");
1580: sb.append(getComparatorClassName(btreeComparator));
1581: sb.append("\"/>");
1582: sb.append("<dupcf name=\"");
1583: sb.append(getComparatorClassName(duplicateComparator));
1584: sb.append("\"/>");
1585: sb.append("</database>");
1586: }
1587:
1588: /**
1589: * @see Loggable#getTransactionId
1590: */
1591: public long getTransactionId() {
1592: return 0;
1593: }
1594:
1595: /**
1596: * Used for log dumping.
1597: */
1598: private static String getComparatorClassName(Comparator comparator) {
1599: if (comparator != null) {
1600: return comparator.getClass().getName();
1601: } else {
1602: return "";
1603: }
1604: }
1605:
1606: /**
1607: * Used both to read from the log and to validate a comparator when set in
1608: * DatabaseConfig.
1609: */
1610: public static Comparator instantiateComparator(Class comparator,
1611: String comparatorType) throws LogException {
1612:
1613: if (comparator == null) {
1614: return null;
1615: }
1616:
1617: try {
1618: return (Comparator) comparator.newInstance();
1619: } catch (InstantiationException IE) {
1620: throw new LogException("Exception while trying to load "
1621: + comparatorType + " Comparator class: " + IE);
1622: } catch (IllegalAccessException IAE) {
1623: throw new LogException("Exception while trying to load "
1624: + comparatorType + " Comparator class: " + IAE);
1625: }
1626: }
1627:
1628: /**
1629: * Used to validate a comparator when set in DatabaseConfig.
1630: */
1631: public static Comparator instantiateComparator(
1632: Comparator comparator, String comparatorType)
1633: throws DatabaseException {
1634:
1635: if (comparator == null) {
1636: return null;
1637: }
1638:
1639: return (Comparator) bytesToObject(objectToBytes(comparator,
1640: comparatorType), comparatorType);
1641: }
1642:
1643: /**
1644: * Converts a comparator object to a serialized byte array, converting to
1645: * a class name String object if byClassName is true.
1646: *
1647: * @throws LogException if the object cannot be serialized.
1648: */
1649: private static byte[] comparatorToBytes(Comparator comparator,
1650: boolean byClassName, String comparatorType)
1651: throws DatabaseException {
1652:
1653: if (comparator == null) {
1654: return LogUtils.ZERO_LENGTH_BYTE_ARRAY;
1655: } else {
1656: Object obj;
1657: if (byClassName) {
1658: obj = comparator.getClass().getName();
1659: } else {
1660: obj = comparator;
1661: }
1662: return objectToBytes(obj, comparatorType);
1663: }
1664: }
1665:
1666: /**
1667: * Converts an arbitrary object to a serialized byte array. Assumes that
1668: * the object given is non-null.
1669: */
1670: public static byte[] objectToBytes(Object obj, String comparatorType)
1671: throws LogException {
1672:
1673: try {
1674: ByteArrayOutputStream baos = new ByteArrayOutputStream();
1675: ObjectOutputStream oos = new ObjectOutputStream(baos);
1676: oos.writeObject(obj);
1677: return baos.toByteArray();
1678: } catch (IOException e) {
1679: throw new LogException("Exception while trying to load "
1680: + comparatorType + ": " + e);
1681: }
1682: }
1683:
1684: /**
1685: * Converts an arbitrary serialized byte array to an object. Assumes that
1686: * the byte array given is non-null and has a non-zero length.
1687: */
1688: private static Object bytesToObject(byte[] bytes,
1689: String comparatorType) throws LogException {
1690:
1691: try {
1692: ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
1693: ObjectInputStream ois = new ObjectInputStream(bais);
1694: return ois.readObject();
1695: } catch (IOException e) {
1696: throw new LogException("Exception while trying to load "
1697: + comparatorType + ": " + e);
1698: } catch (ClassNotFoundException e) {
1699: throw new LogException("Exception while trying to load "
1700: + comparatorType + ": " + e);
1701: }
1702: }
1703:
1704: public int getBinDeltaPercent() {
1705: return binDeltaPercent;
1706: }
1707:
1708: public int getBinMaxDeltas() {
1709: return binMaxDeltas;
1710: }
1711: }
|