Package com.sleepycat.je.dbi

Source Code of com.sleepycat.je.dbi.DatabaseImpl$PreloadLSNTreeWalker$INEntry

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002-2006
*      Sleepycat Software.  All rights reserved.
*
* $Id: DatabaseImpl.java,v 1.141 2006/01/03 21:55:40 bostic Exp $
*/

package com.sleepycat.je.dbi;

import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.sleepycat.je.BtreeStats;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DatabaseStats;
import com.sleepycat.je.DbInternal;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.PreloadConfig;
import com.sleepycat.je.PreloadStats;
import com.sleepycat.je.PreloadStatus;
import com.sleepycat.je.SecondaryDatabase;
import com.sleepycat.je.StatsConfig;
import com.sleepycat.je.VerifyConfig;
import com.sleepycat.je.cleaner.UtilizationTracker;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.dbi.SortedLSNTreeWalker.TreeNodeProcessor;
import com.sleepycat.je.latch.LatchSupport;
import com.sleepycat.je.log.LogEntryType;
import com.sleepycat.je.log.LogException;
import com.sleepycat.je.log.LogReadable;
import com.sleepycat.je.log.LogUtils;
import com.sleepycat.je.log.LogWritable;
import com.sleepycat.je.tree.BIN;
import com.sleepycat.je.tree.ChildReference;
import com.sleepycat.je.tree.DBIN;
import com.sleepycat.je.tree.DIN;
import com.sleepycat.je.tree.DupCountLN;
import com.sleepycat.je.tree.IN;
import com.sleepycat.je.tree.Node;
import com.sleepycat.je.tree.Tree;
import com.sleepycat.je.tree.TreeUtils;
import com.sleepycat.je.tree.TreeWalkerStatsAccumulator;
import com.sleepycat.je.tree.WithRootLatched;
import com.sleepycat.je.txn.Locker;
import com.sleepycat.je.txn.ThreadLocker;
import com.sleepycat.je.utilint.CmdUtil;
import com.sleepycat.je.utilint.DbLsn;
import com.sleepycat.je.utilint.TestHook;

/**
* The underlying object for a given database.
*/
public class DatabaseImpl
    implements LogWritable, LogReadable, Cloneable {

    /*
     * Delete processing states. See design note on database deletion and
     * truncation
     */
    private static final short NOT_DELETED = 1;
    private static final short DELETED_CLEANUP_INLIST_HARVEST = 2;
    private static final short DELETED_CLEANUP_LOG_HARVEST = 3;
    private static final short DELETED = 4;

    private DatabaseId id;        // unique id
    private Tree tree;            
    private EnvironmentImpl envImpl;   // Tree operations find the env this way
    private boolean duplicatesAllowed; // duplicates allowed
    private boolean transactional;// All open handles are transactional
    private Set referringHandles; // Set of open Database handles   
    private BtreeStats stats;     // most recent btree stats w/ !DB_FAST_STAT
    private long eofNodeId;       // Logical EOF node for range locking
    private short deleteState;    // one of four delete states.

    /*
     * The user defined Btree and duplicate comparison functions, if specified.
     */
    private Comparator btreeComparator = null;
    private Comparator duplicateComparator = null;
    private String btreeComparatorName = "";
    private String duplicateComparatorName = "";

    /*
     * Cache some configuration values.
     */
    private int binDeltaPercent;
    private int binMaxDeltas;
    private int maxMainTreeEntriesPerNode;
    private int maxDupTreeEntriesPerNode;

    /*
     * The debugDatabaseName is used for error messages only, to avoid
     * accessing the db mapping tree in error situations. Currently it's not
     * guaranteed to be transactionally correct, nor is it updated by rename.
     */
    private String debugDatabaseName;

    /* For unit tests */
    private TestHook pendingDeletedHook;

    /**
     * Create a database object for a new database.
     */
    public DatabaseImpl(String dbName,
      DatabaseId id,
      EnvironmentImpl envImpl,
      DatabaseConfig dbConfig)
        throws DatabaseException {

        this.id = id;
        this.envImpl = envImpl;
        this.btreeComparator = dbConfig.getBtreeComparator();
        this.duplicateComparator = dbConfig.getDuplicateComparator();
        duplicatesAllowed = dbConfig.getSortedDuplicates();
        transactional = dbConfig.getTransactional();
  maxMainTreeEntriesPerNode = dbConfig.getNodeMaxEntries();
  maxDupTreeEntriesPerNode = dbConfig.getNodeMaxDupTreeEntries();

        initDefaultSettings();

        deleteState = NOT_DELETED;

        /*
         * The tree needs the env, make sure we assign it before
         * allocating the tree.
         */
        tree = new Tree(this);
        referringHandles = Collections.synchronizedSet(new HashSet());

        eofNodeId = Node.getNextNodeId();

        /* For error messages only. */
        debugDatabaseName = dbName;
    }

    /**
     * Create an empty database object for initialization from the log.  Note
     * that the rest of the initialization comes from readFromLog(), except
     * for the debugDatabaseName, which is set by the caller.
     */
    public DatabaseImpl()
        throws DatabaseException {

        id = new DatabaseId();
        envImpl = null;

        deleteState = NOT_DELETED;

        tree = new Tree();
        referringHandles = Collections.synchronizedSet(new HashSet());

        /* initDefaultSettings is called after envImpl is set.  */

        eofNodeId = Node.getNextNodeId();
    }

    public void setDebugDatabaseName(String debugName) {
        debugDatabaseName = debugName;
    }

    public String getDebugName() {
        return debugDatabaseName;
    }

    /* For unit testing only. */
    public void setPendingDeletedHook(TestHook hook) {
        pendingDeletedHook = hook;
    }

    /**
     * Initialize configuration settings when creating a new instance or after
     * reading an instance from the log.  The envImpl field must be set before
     * calling this method.
     */
    private void initDefaultSettings()
        throws DatabaseException {

        DbConfigManager configMgr = envImpl.getConfigManager();

        binDeltaPercent =
            configMgr.getInt(EnvironmentParams.BIN_DELTA_PERCENT);
        binMaxDeltas =
            configMgr.getInt(EnvironmentParams.BIN_MAX_DELTAS);

  if (maxMainTreeEntriesPerNode == 0) {
            maxMainTreeEntriesPerNode =
    configMgr.getInt(EnvironmentParams.NODE_MAX);
  }

  if (maxDupTreeEntriesPerNode == 0) {
            maxDupTreeEntriesPerNode =
    configMgr.getInt(EnvironmentParams.NODE_MAX_DUPTREE);
  }
    }

    /**
     * Clone.  For now just pass off to the super class for a field-by-field
     * copy.
     */
    public Object clone()
        throws CloneNotSupportedException {

        return super.clone();
    }

    /**
     * @return the database tree.
     */
    public Tree getTree() {
        return tree;
    }

    void setTree(Tree tree) {
        this.tree = tree;
    }

    /**
     * @return the database id.
     */
    public DatabaseId getId() {
        return id;
    }

    void setId(DatabaseId id) {
        this.id = id;
    }

    public long getEofNodeId() {
        return eofNodeId;
    }

    /**
     * @return true if this database is transactional.
     */
    public boolean isTransactional() {
        return transactional;
    }

    /**
     * Sets the transactional property for the first opened handle.
     */
    public void setTransactional(boolean transactional) {
        this.transactional = transactional;
    }

    /**
     * @return true if duplicates are allowed in this database.
     */
    public boolean getSortedDuplicates() {
        return duplicatesAllowed;
    }

    public int getNodeMaxEntries() {
  return maxMainTreeEntriesPerNode;
    }

    public int getNodeMaxDupTreeEntries() {
  return maxDupTreeEntriesPerNode;
    }

    /**
     * Set the duplicate comparison function for this database.
     *
     * @param duplicateComparator - The Duplicate Comparison function.
     */
    public void setDuplicateComparator(Comparator duplicateComparator) {
        this.duplicateComparator = duplicateComparator;
    }

    /**
     * Set the btree comparison function for this database.
     *
     * @param btreeComparator - The btree Comparison function.
     */
    public void setBtreeComparator(Comparator btreeComparator) {
        this.btreeComparator = btreeComparator;
    }

    /**
     * @return the btree Comparator object.
     */
    public Comparator getBtreeComparator() {
        return btreeComparator;
    }

    /**
     * @return the duplicate Comparator object.
     */
    public Comparator getDuplicateComparator() {
        return duplicateComparator;
    }

    /**
     * Set the db environment during recovery, after instantiating the database
     * from the log
     */
    public void setEnvironmentImpl(EnvironmentImpl envImpl)
        throws DatabaseException {

        this.envImpl = envImpl;
        initDefaultSettings();
        tree.setDatabase(this);
    }

    /**
     * @return the database environment.
     */
    public EnvironmentImpl getDbEnvironment() {
        return envImpl;
    }

    /**
     * Returns whether one or more handles are open.
     */
    public boolean hasOpenHandles() {
        return referringHandles.size() > 0;
    }

    /**
     * Add a referring handle
     */
    public void addReferringHandle(Database db) {
        referringHandles.add(db);
    }

    /**
     * Decrement the reference count.
     */
    public void removeReferringHandle(Database db) {
        referringHandles.remove(db);
    }

    /**
     * @return the referring handle count.
     */
    synchronized int getReferringHandleCount() {
        return referringHandles.size();
    }

    /**
     * For this secondary database return the primary that it is associated
     * with, or null if not associated with any primary.  Note that not all
     * handles need be associated with a primary.
     */
    public Database findPrimaryDatabase()
        throws DatabaseException {

        for (Iterator i = referringHandles.iterator(); i.hasNext();) {
            Object obj = i.next();
            if (obj instanceof SecondaryDatabase) {
                return ((SecondaryDatabase) obj).getPrimaryDatabase();
            }
        }
        return null;
    }

    public String getName()
        throws DatabaseException {

        return envImpl.getDbMapTree().getDbName(id);
    }

    /*
     * @return true if this database is deleted. Delete cleanup
     * may still be in progress.
     */
    public boolean isDeleted() {
        return !(deleteState == NOT_DELETED);
    }

    /*
     * @return true if this database is deleted and all cleanup is finished.
     */
    public boolean isDeleteFinished() {
        return (deleteState == DELETED);
    }

    /*
     * The delete cleanup is starting. Set this before releasing any
     * write locks held for a db operation.
     */
    public void startDeleteProcessing() {
        assert (deleteState == NOT_DELETED);

        deleteState = DELETED_CLEANUP_INLIST_HARVEST;
    }

    /*
     * Should be called by the SortedLSNTreeWalker when it is finished with
     * the INList.
     */
    void finishedINListHarvest() {
        assert (deleteState == DELETED_CLEANUP_INLIST_HARVEST);

        deleteState = DELETED_CLEANUP_LOG_HARVEST;
    }

    /**
     * Purge a DatabaseImpl and corresponding MapLN in the db mapping tree.
     * Purging consists of removing all related INs from the db mapping tree
     * and deleting the related MapLN.
     * Used at the a transaction end in these cases:
     *  - purge the deleted database after a commit of
     *           Environment.removeDatabase
     *  - purge the deleted database after a commit of
     *           Environment.truncateDatabase
     *  - purge the newly created database after an abort of
     *           Environment.truncateDatabase
     */
    public void deleteAndReleaseINs()
        throws DatabaseException {
       
        startDeleteProcessing();
        releaseDeletedINs();
    }

    public void releaseDeletedINs()
        throws DatabaseException {

        if (pendingDeletedHook != null) {
            pendingDeletedHook.doHook();
        }

        try {
            /*
             * Get the root lsn before deleting the MapLN, as that will null
             * out the root.
             */
            long rootLsn = tree.getRootLsn();
            if (rootLsn == DbLsn.NULL_LSN) {

                /*
                 * There's nothing in this database. (It might be the abort of
                 * a truncation, where we are trying to clean up the new, blank
                 * database. Do delete the MapLN.
                 */
                envImpl.getDbMapTree().deleteMapLN(id);

            } else {

                UtilizationTracker snapshot = new UtilizationTracker(envImpl);

                /* Start by recording the lsn of the root IN as obsolete. */
                snapshot.countObsoleteNodeInexact(rootLsn,
                                                  LogEntryType.LOG_IN);
       

                /* Use the tree walker to visit every child lsn in the tree. */
                ObsoleteProcessor obsoleteProcessor =
                    new ObsoleteProcessor(snapshot);
                SortedLSNTreeWalker walker =
                    new SortedLSNTreeWalker(this,
                                            true,  // remove INs from INList
                                            true,  // set INList finish harvest
                                            rootLsn,
                                            obsoleteProcessor);
                /*
                 * Delete MapLN before the walk. Note that the processing of
                 * the naming tree means this MapLN is never actually
                 * accessible from the current tree, but deleting the MapLN
                 * will do two things:
                 * (a) mark it properly obsolete
                 * (b) null out the database tree, leaving the INList the only
                 * reference to the INs.
                 */
                envImpl.getDbMapTree().deleteMapLN(id);

                /*
                 * At this point, it's possible for the evictor to find an IN
                 * for this database on the INList. It should be ignored.
                 */
                walker.walk();

                /*
                 * Count obsolete nodes for a deleted database at transaction
                 * end time.  Write out the modified file summaries for
                 * recovery.
                 */
                envImpl.getUtilizationProfile().countAndLogSummaries
                    (snapshot.getTrackedFiles());
            }
        } finally {
            deleteState = DELETED;
        }
    }

    public void checkIsDeleted(String operation)
        throws DatabaseException {

        if (isDeleted()) {
            throw new DatabaseException
                ("Attempt to " + operation + " a deleted database");
        }
    }

    /* Mark each LSN obsolete in the utilization tracker. */
    private static class ObsoleteProcessor implements TreeNodeProcessor {

        private UtilizationTracker tracker;

        ObsoleteProcessor(UtilizationTracker tracker) {
            this.tracker = tracker;
        }

        public void processLSN(long childLsn, LogEntryType childType) {
            assert childLsn != DbLsn.NULL_LSN;
            tracker.countObsoleteNodeInexact(childLsn, childType);
        }
    }

    /**
     * Return the count of nodes in the database. Used for truncate, perhaps
     * should be made available through other means? Database should be
     * quiescent.
     */
    long countRecords()
        throws DatabaseException {

        LNCounter lnCounter = new LNCounter();

        /* future enchancement -- use a walker that does not fetch dup trees */
        SortedLSNTreeWalker walker =
            new SortedLSNTreeWalker(this,
                                    false,  // don't remove from INList
                                    false,  // don't set db state
                                    tree.getRootLsn(),
                                    lnCounter);
               
        walker.walk();
        return lnCounter.getCount();
    }

    /*
     * Count each valid record.
     */
    private static class LNCounter implements TreeNodeProcessor {

        private long counter;

        public void processLSN(long childLsn, LogEntryType childType) {
            assert childLsn != DbLsn.NULL_LSN;

            if (childType.equals(LogEntryType.LOG_LN_TRANSACTIONAL) ||
                childType.equals(LogEntryType.LOG_LN)) {
                counter++;
            }
        }

        long getCount() {
            return counter;
        }
    }

    public DatabaseStats stat(StatsConfig config)
        throws DatabaseException {

        if (stats == null) {

            /*
             * Called first time w/ FAST_STATS so just give them an
             * empty one.
             */
            stats = new BtreeStats();
        }

        if (!config.getFast()) {
            if (tree == null) {
                return new BtreeStats();
            }

            PrintStream out = config.getShowProgressStream();
            if (out == null) {
                out = System.err;
            }

      StatsAccumulator statsAcc =
    new StatsAccumulator(out,
             config.getShowProgressInterval(),
                                     getEmptyStats());
      walkDatabaseTree(statsAcc, out, true);
            statsAcc.copyToStats(stats);
        }

        return stats;
    }

    /*
     * @param config verify configuration
     * @param emptyStats empty database stats, to be filled by this method
     * @return true if the verify saw no errors.
     */
    public boolean verify(VerifyConfig config, DatabaseStats emptyStats)
        throws DatabaseException {

  if (tree == null) {
      return true;
  }

  PrintStream out = config.getShowProgressStream();
  if (out == null) {
      out = System.err;
  }

  StatsAccumulator statsAcc =
      new StatsAccumulator(out,
                                 config.getShowProgressInterval(),
                                 emptyStats) {
        void verifyNode(Node node) {

      try {
          node.verify(null);
      } catch (DatabaseException INE) {
          progressStream.println(INE);
      }
        }
    };
  boolean ok = walkDatabaseTree(statsAcc, out, config.getPrintInfo());
  statsAcc.copyToStats(emptyStats);
        return ok;
    }

    /* @return the right kind of stats object for this database. */
    public DatabaseStats getEmptyStats() {
        return new BtreeStats();
    }

    /*
     * @return true if no errors.
     */
    private boolean walkDatabaseTree(TreeWalkerStatsAccumulator statsAcc,
                                     PrintStream out,
                                     boolean verbose)
        throws DatabaseException {

        boolean ok = true;
        Locker locker = new ThreadLocker(envImpl);
        Cursor cursor = null;
  CursorImpl impl = null;
        try {
      EnvironmentImpl.incThreadLocalReferenceCount();
            cursor = DbInternal.newCursor(this, locker, null);
      impl = DbInternal.getCursorImpl(cursor);
      tree.setTreeStatsAccumulator(statsAcc);

      /*
       * This will only be used on the first call for the position()
       * call.
       */
      impl.setTreeStatsAccumulator(statsAcc);
            DatabaseEntry foundData = new DatabaseEntry();
            DatabaseEntry key = new DatabaseEntry();
            OperationStatus status = DbInternal.position
                (cursor, key, foundData, LockMode.READ_UNCOMMITTED, true);
            while (status == OperationStatus.SUCCESS) {
    try {
        status = DbInternal.retrieveNext
      (cursor, key, foundData, LockMode.READ_UNCOMMITTED,
       GetMode.NEXT);
    } catch (DatabaseException DBE) {
                    ok = false;
        if (DbInternal.advanceCursor(cursor, key, foundData)) {
                        if (verbose) {
                            out.println("Error encountered (continuing):");
                            out.println(DBE);
                            printErrorRecord(out, key, foundData);
                        }
                    } else {
                        throw DBE;
                    }
    }
            }
        } finally {
      if (impl != null) {
    impl.setTreeStatsAccumulator(null);
      }
      tree.setTreeStatsAccumulator(null);
      EnvironmentImpl.decThreadLocalReferenceCount();
            if (cursor != null) {
                cursor.close();
            }
        }

        return ok;
    }

    /**
     * Prints the key and data, if available, for a BIN entry that could not be
     * read/verified.  Uses the same format as DbDump and prints both the hex
     * and printable versions of the entries.
     */
    private void printErrorRecord(PrintStream out,
                                  DatabaseEntry key,
                                  DatabaseEntry data) {

        byte[] bytes = key.getData();
        StringBuffer sb = new StringBuffer("Error Key ");
        if (bytes == null) {
            sb.append("UNKNOWN");
        } else {
            CmdUtil.formatEntry(sb, bytes, false);
            sb.append(' ');
            CmdUtil.formatEntry(sb, bytes, true);
        }
        out.println(sb);

        bytes = data.getData();
        sb = new StringBuffer("Error Data ");
        if (bytes == null) {
            sb.append("UNKNOWN");
        } else {
            CmdUtil.formatEntry(sb, bytes, false);
            sb.append(' ');
            CmdUtil.formatEntry(sb, bytes, true);
        }
        out.println(sb);
    }

    static class StatsAccumulator implements TreeWalkerStatsAccumulator {
  private Set inNodeIdsSeen = new HashSet();
  private Set binNodeIdsSeen = new HashSet();
  private Set dinNodeIdsSeen = new HashSet();
  private Set dbinNodeIdsSeen = new HashSet();
  private Set dupCountLNsSeen = new HashSet();
  private long[] insSeenByLevel = null;
  private long[] binsSeenByLevel = null;
  private long[] dinsSeenByLevel = null;
  private long[] dbinsSeenByLevel = null;
  private long lnCount = 0;
  private long deletedLNCount = 0;
  private int mainTreeMaxDepth = 0;
  private int duplicateTreeMaxDepth = 0;
  private DatabaseStats useStats;

  PrintStream progressStream;
  int progressInterval;

  /* The max levels we ever expect to see in a tree. */
  private static final int MAX_LEVELS = 100;

  StatsAccumulator(PrintStream progressStream,
       int progressInterval,
                         DatabaseStats useStats) {

      this.progressStream = progressStream;
      this.progressInterval = progressInterval;

      insSeenByLevel = new long[MAX_LEVELS];
      binsSeenByLevel = new long[MAX_LEVELS];
      dinsSeenByLevel = new long[MAX_LEVELS];
      dbinsSeenByLevel = new long[MAX_LEVELS];
     
      this.useStats = useStats;
  }

  void verifyNode(Node node) {

  }

  public void processIN(IN node, Long nid, int level) {
      if (inNodeIdsSeen.add(nid)) {
    tallyLevel(level, insSeenByLevel);
    verifyNode(node);
      }
  }

  public void processBIN(BIN node, Long nid, int level) {
      if (binNodeIdsSeen.add(nid)) {
    tallyLevel(level, binsSeenByLevel);
    verifyNode(node);
      }
  }

  public void processDIN(DIN node, Long nid, int level) {
      if (dinNodeIdsSeen.add(nid)) {
    tallyLevel(level, dinsSeenByLevel);
    verifyNode(node);
      }
  }

  public void processDBIN(DBIN node, Long nid, int level) {
      if (dbinNodeIdsSeen.add(nid)) {
    tallyLevel(level, dbinsSeenByLevel);
    verifyNode(node);
      }
  }

  public void processDupCountLN(DupCountLN node, Long nid) {
      dupCountLNsSeen.add(nid);
      verifyNode(node);
  }

  private void tallyLevel(int levelArg, long[] nodesSeenByLevel) {
      int level = levelArg;
      if (level >= IN.DBMAP_LEVEL) {
    return;
      }
      if (level >= IN.MAIN_LEVEL) {
    level &= ~IN.MAIN_LEVEL;
    if (level > mainTreeMaxDepth) {
        mainTreeMaxDepth = level;
    }
      } else {
    if (level > duplicateTreeMaxDepth) {
        duplicateTreeMaxDepth = level;
    }
      }

      nodesSeenByLevel[level]++;
  }

  public void incrementLNCount() {
      lnCount++;
      if (progressInterval != 0) {
    if ((lnCount % progressInterval) == 0) {
                    copyToStats(useStats);
        progressStream.println(useStats);
    }
      }
  }

  public void incrementDeletedLNCount() {
      deletedLNCount++;
  }

  Set getINNodeIdsSeen() {
      return inNodeIdsSeen;
  }

  Set getBINNodeIdsSeen() {
      return binNodeIdsSeen;
  }

  Set getDINNodeIdsSeen() {
      return dinNodeIdsSeen;
  }

  Set getDBINNodeIdsSeen() {
      return dbinNodeIdsSeen;
  }

  long[] getINsByLevel() {
      return insSeenByLevel;
  }

  long[] getBINsByLevel() {
      return binsSeenByLevel;
  }

  long[] getDINsByLevel() {
      return dinsSeenByLevel;
  }

  long[] getDBINsByLevel() {
      return dbinsSeenByLevel;
  }

  long getLNCount() {
      return lnCount;
  }

  Set getDupCountLNCount() {
      return dupCountLNsSeen;
  }

  long getDeletedLNCount() {
      return deletedLNCount;
  }

  int getMainTreeMaxDepth() {
      return mainTreeMaxDepth;
  }

  int getDuplicateTreeMaxDepth() {
      return duplicateTreeMaxDepth;
  }

  private void copyToStats(DatabaseStats stats) {
            BtreeStats bStats = (BtreeStats) stats;
      bStats.setInternalNodeCount(getINNodeIdsSeen().size());
      bStats.setBottomInternalNodeCount
    (getBINNodeIdsSeen().size());
      bStats.setDuplicateInternalNodeCount
    (getDINNodeIdsSeen().size());
      bStats.setDuplicateBottomInternalNodeCount
    (getDBINNodeIdsSeen().size());
      bStats.setLeafNodeCount(getLNCount());
      bStats.setDeletedLeafNodeCount(getDeletedLNCount());
      bStats.setDupCountLeafNodeCount
    (getDupCountLNCount().size());
      bStats.setMainTreeMaxDepth(getMainTreeMaxDepth());
      bStats.setDuplicateTreeMaxDepth(getDuplicateTreeMaxDepth());
      bStats.setINsByLevel(getINsByLevel());
      bStats.setBINsByLevel(getBINsByLevel());
      bStats.setDINsByLevel(getDINsByLevel());
      bStats.setDBINsByLevel(getDBINsByLevel());
  }
    }

    /**
     * Preload exceptions, classes, callbacks.
     */

    /**
     * Undeclared exception used to throw through SortedLSNTreeWalker code
     * when preload has either filled the user's max byte or time request.
     */
    private static class HaltPreloadException extends RuntimeException {

  private PreloadStatus status;

  HaltPreloadException(PreloadStatus status) {
      super(status.toString());
      this.status = status;
  }

  PreloadStatus getStatus() {
      return status;
  }
    }

    private static final HaltPreloadException timeExceededPreloadException =
  new HaltPreloadException(PreloadStatus.EXCEEDED_TIME);

    private static final HaltPreloadException memoryExceededPreloadException =
  new HaltPreloadException(PreloadStatus.FILLED_CACHE);

    /**
     * The processLSN() code for PreloadLSNTreeWalker.
     */
    private static class PreloadProcessor implements TreeNodeProcessor {

  private EnvironmentImpl envImpl;
  private long maxBytes;
  private long targetTime;
  private PreloadStats stats;

  PreloadProcessor(EnvironmentImpl envImpl,
       long maxBytes,
       long targetTime,
       PreloadStats stats) {
      this.envImpl = envImpl;
      this.maxBytes = maxBytes;
      this.targetTime = targetTime;
      this.stats = stats;
  }

  /**
   * Called for each LSN that the SortedLSNTreeWalker encounters.
   */
        public void processLSN(long childLsn, LogEntryType childType) {
            assert childLsn != DbLsn.NULL_LSN;

      /*
       * Check if we've exceeded either the max time or max bytes
       * allowed for this preload() call.
       */
      if (System.currentTimeMillis() > targetTime) {
    throw timeExceededPreloadException;
      }

      if (envImpl.getMemoryBudget().getCacheMemoryUsage() >
    maxBytes) {
    throw memoryExceededPreloadException;
      }

      /* Count entry types to return in the PreloadStats. */
      if (childType.equals(LogEntryType.LOG_DUPCOUNTLN_TRANSACTIONAL) ||
    childType.equals(LogEntryType.LOG_DUPCOUNTLN)) {
    stats.nDupCountLNsLoaded++;
      } else if (childType.equals(LogEntryType.LOG_LN_TRANSACTIONAL) ||
           childType.equals(LogEntryType.LOG_LN)) {
    stats.nLNsLoaded++;
            } else if (childType.equals(LogEntryType.LOG_DBIN)) {
    stats.nDBINsLoaded++;
      } else if (childType.equals(LogEntryType.LOG_BIN)) {
    stats.nBINsLoaded++;
      } else if (childType.equals(LogEntryType.LOG_DIN)) {
    stats.nDINsLoaded++;
      } else if (childType.equals(LogEntryType.LOG_IN)) {
    stats.nINsLoaded++;
      }
        }
    }

    /*
     * An extension of SortedLSNTreeWalker that provides an LSN to IN/index
     * map.  When an LSN is processed by the tree walker, the map is used to
     * lookup the parent IN and child entry index of each LSN processed by the
     * tree walker.
     */
    private static class PreloadLSNTreeWalker extends SortedLSNTreeWalker {

  /* LSN -> INEntry */
  private Map lsnINMap = new HashMap();

  /* struct to hold IN/entry-index pair. */
  private static class INEntry {
      INEntry(IN in, int index) {
    this.in = in;
    this.index = index;
      }

      IN in;
      int index;
  }

  PreloadLSNTreeWalker(DatabaseImpl db,
           TreeNodeProcessor callback,
           PreloadConfig conf)
      throws DatabaseException {

      super(db, false, false, db.tree.getRootLsn(), callback);
      accumulateLNs = conf.getLoadLNs();
  }

  private final class PreloadWithRootLatched
      implements WithRootLatched {

      public IN doWork(ChildReference root)
    throws DatabaseException {

    walkInternal();
    return null;
      }
  }

  public void walk()
      throws DatabaseException {

      WithRootLatched preloadWRL = new PreloadWithRootLatched();
      dbImpl.getTree().withRootLatchedExclusive(preloadWRL);
  }

  /*
   * Method to get the Root IN for this DatabaseImpl's tree.  Latches
   * the root IN.
   */
  protected IN getRootIN(long rootLsn)
      throws DatabaseException {

      return dbImpl.getTree().getRootIN(false);
  }

  /*
   * Release the latch on the root IN.
   */
  protected void releaseRootIN(IN root)
      throws DatabaseException {

      root.releaseLatch();
  }

  /*
   * Add an LSN -> IN/index entry to the map.
   */
  protected void addToLsnINMap(Long lsn, IN in, int index) {
      assert in.getDatabase() != null;
      lsnINMap.put(lsn, new INEntry(in, index));
  }

  /*
   * Process an LSN.  Get & remove its INEntry from the map, then fetch
   * the target at the INEntry's IN/index pair.  This method will be
   * called in sorted LSN order.
   */
  protected Node fetchLSN(long lsn)
      throws DatabaseException {

      INEntry inEntry = (INEntry) lsnINMap.remove(new Long(lsn));
      assert (inEntry != null);
      IN in = inEntry.in;
      in.latch();
      try {
    int index = inEntry.index;
    if (in.isEntryKnownDeleted(index) ||
        in.getLsn(index) != lsn) {
        return null;
    }
    return in.fetchTarget(index);
      } finally {
    in.releaseLatch();
      }
  }
    }

    /**
     * Preload the cache, using up to maxBytes bytes or maxMillsecs msec.
     */
    public PreloadStats preload(PreloadConfig config)
  throws DatabaseException {

  long maxBytes = config.getMaxBytes();
  long maxMillisecs = config.getMaxMillisecs();
  long targetTime = Long.MAX_VALUE;
  if (maxMillisecs > 0) {
      targetTime = System.currentTimeMillis() + maxMillisecs;
  }

  long cacheBudget = envImpl.getMemoryBudget().getCacheBudget();
  if (maxBytes == 0) {
            maxBytes = cacheBudget;
  } else if (maxBytes > cacheBudget) {
      throw new IllegalArgumentException
    ("maxBytes parameter to Database.preload() was specified as " +
     maxBytes + " bytes \nbut the cache is only " +
     cacheBudget + " bytes.");
  }

  PreloadStats ret = new PreloadStats();
  PreloadProcessor callback =
      new PreloadProcessor(envImpl, maxBytes, targetTime, ret);
  SortedLSNTreeWalker walker =
      new PreloadLSNTreeWalker(this, callback, config);
  try {
      walker.walk();
  } catch (HaltPreloadException HPE) {
      ret.status = HPE.getStatus();
  }

        assert LatchSupport.countLatchesHeld() == 0;
  return ret;
    }

    /*
     * Dumping
     */
    public String dumpString(int nSpaces) {
        StringBuffer sb = new StringBuffer();
        sb.append(TreeUtils.indent(nSpaces));
        sb.append("<database id=\"" );
        sb.append(id.toString());
        sb.append("\"");
        if (btreeComparator != null) {
            sb.append(" btc=\"");
            sb.append(serializeComparator(btreeComparator));
            sb.append("\"");
        }
        if (duplicateComparator != null) {
            sb.append(" dupc=\"");
            sb.append(serializeComparator(duplicateComparator));
            sb.append("\"");
        }
        sb.append("/>");
        return sb.toString();
    }

    /*
     * Logging support
     */

    /**
     * @see LogWritable#getLogSize
     */
    public int getLogSize() {
        return
            id.getLogSize() +
            tree.getLogSize() +
            LogUtils.getBooleanLogSize() +
            LogUtils.getStringLogSize(serializeComparator(btreeComparator)) +
            LogUtils.getStringLogSize
            (serializeComparator(duplicateComparator)) +
      (LogUtils.getIntLogSize() * 2);
    }

    /**
     * @see LogWritable#writeToLog
     */
    public void writeToLog(ByteBuffer logBuffer) {
        id.writeToLog(logBuffer);
        tree.writeToLog(logBuffer);
        LogUtils.writeBoolean(logBuffer, duplicatesAllowed);
        LogUtils.writeString(logBuffer,
           serializeComparator(btreeComparator));
        LogUtils.writeString(logBuffer,
           serializeComparator(duplicateComparator));
  LogUtils.writeInt(logBuffer, maxMainTreeEntriesPerNode);
  LogUtils.writeInt(logBuffer, maxDupTreeEntriesPerNode);
    }

    /**
     * @see LogReadable#readFromLog
     */
    public void readFromLog(ByteBuffer itemBuffer, byte entryTypeVersion)
        throws LogException {

        id.readFromLog(itemBuffer, entryTypeVersion);
        tree.readFromLog(itemBuffer, entryTypeVersion);
        duplicatesAllowed = LogUtils.readBoolean(itemBuffer);

        btreeComparatorName = LogUtils.readString(itemBuffer);
        duplicateComparatorName = LogUtils.readString(itemBuffer);

  try {
      if (!EnvironmentImpl.getNoComparators()) {

    /*
     * Don't instantiate if comparators are unnecessary
     * (DbPrintLog).
     */
    if (btreeComparatorName.length() != 0) {
        Class btreeComparatorClass =
      Class.forName(btreeComparatorName);
        btreeComparator =
      instantiateComparator(btreeComparatorClass, "Btree");
    }
    if (duplicateComparatorName.length() != 0) {
        Class duplicateComparatorClass =
      Class.forName(duplicateComparatorName);
        duplicateComparator =
      instantiateComparator(duplicateComparatorClass,
                "Duplicate");
    }
      }
  } catch (ClassNotFoundException CNFE) {
      throw new LogException("couldn't instantiate class comparator",
           CNFE);
  }

  if (entryTypeVersion >= 1) {
      maxMainTreeEntriesPerNode = LogUtils.readInt(itemBuffer);
      maxDupTreeEntriesPerNode = LogUtils.readInt(itemBuffer);
        }
    }

    /**
     * @see LogReadable#dumpLog
     */
    public void dumpLog(StringBuffer sb, boolean verbose) {
        sb.append("<database>");
        id.dumpLog(sb, verbose);
        tree.dumpLog(sb, verbose);
        sb.append("<dupsort v=\"").append(duplicatesAllowed);
        sb.append("\"/>");
        sb.append("<btcf name=\"");
        sb.append(btreeComparatorName);
        sb.append("\"/>");
        sb.append("<dupcf name=\"");
        sb.append(duplicateComparatorName);
        sb.append("\"/>");
        sb.append("</database>");
    }

    /**
     * @see LogReadable#logEntryIsTransactional
     */
    public boolean logEntryIsTransactional() {
  return false;
    }

    /**
     * @see LogReadable#getTransactionId
     */
    public long getTransactionId() {
  return 0;
    }

    /**
     * Used both to write to the log and to validate a comparator when set in
     * DatabaseConfig.
     */
    public static String serializeComparator(Comparator comparator) {
        if (comparator != null) {
            return comparator.getClass().getName();
        } else {
            return "";
        }
    }

    /**
     * Used both to read from the log and to validate a comparator when set in
     * DatabaseConfig.
     */
    public static Comparator instantiateComparator(Class comparator,
                                                   String comparatorType)
        throws LogException {

  if (comparator == null) {
      return null;
  }

        try {
      return (Comparator) comparator.newInstance();
        } catch (InstantiationException IE) {
            throw new LogException
                ("Exception while trying to load " + comparatorType +
                 " Comparator class: " + IE);
        } catch (IllegalAccessException IAE) {
            throw new LogException
                ("Exception while trying to load " + comparatorType +
                 " Comparator class: " + IAE);
        }
    }

    public int getBinDeltaPercent() {
        return binDeltaPercent;
    }

    public int getBinMaxDeltas() {
        return binMaxDeltas;
    }
}
TOP

Related Classes of com.sleepycat.je.dbi.DatabaseImpl$PreloadLSNTreeWalker$INEntry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.