All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sleepycat.je.tree.MapLN Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle Berkeley
 * DB Java Edition made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle Berkeley DB Java Edition for a copy of the
 * license and additional information.
 */

package com.sleepycat.je.tree;

import java.nio.ByteBuffer;

import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.dbi.DatabaseImpl;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.dbi.MemoryBudget;
import com.sleepycat.je.log.LogEntryType;
import com.sleepycat.je.log.Loggable;
import com.sleepycat.je.txn.BasicLocker;
import com.sleepycat.je.txn.LockGrantType;
import com.sleepycat.je.txn.LockResult;
import com.sleepycat.je.txn.LockType;

/**
 * {@literal
 * A MapLN represents a Leaf Node in the JE Db Mapping Tree.
 *
 * MapLNs contain a DatabaseImpl, which in turn contains three categories of
 * information - database configuration information, the per-database File
 * Summary utilization information, and each database's btree root. While LNs
 * are written to the log as the result of API operations which create new data
 * records, MapLNs are written to the log as a result of configuration changes,
 * utilization information changes, or updates to the btree which cascade up
 * the tree and result in a new root. Because they serve as a bridge between
 * the application data btree and the db mapping tree, MapLNs must be written
 * with special rules, and should only be written from DbTree.modifyDbRoot.
 * The basic rule is that in order to ensure that the MapLN contains the
 * proper btree root, the btree root latch is used to protect both any logging
 * of the MapLN, and any updates to the root lsn.
 *
 * Updates to the internal btree nodes obey a strict bottom up approach, in
 * accordance with the log semantics which require that later log entries are
 * known to supercede earlier log entries. In other words, for a btree that
 * looks like
 *      MapLN
 *        |
 *       IN
 *        |
 *       BIN
 *        |
 *       LN
 * we know that update operations cause the btree nodes must be logged in this
 * order: LN, BIN, IN, MapLN, so that the reference to each on disk node is
 * correct. (Note that logging order is special and different when the btree
 * is initially created.)
 *
 * However, MapLNs may need to be written to disk at arbitrary points in time
 * in order to save database config or utilization data. Those writes don't
 * have the time and context to be done in a cascading-upwards fashion.  We
 * ensure that MapLNs are not erroneously written with an out of sync root by
 * requiring that DbTree.modifyDbRoot takes the root latch for the application
 * data btree. RootINs are also written with the root latch, so it serves to
 * ensure that the root doesn't change during the time when the MapLN is
 * written. For example, suppose thread 1 is doing a cascading-up MapLN write,
 * and thread 2 is doing an arbitrary-point MapLN write:
 *
 * Thread 1                   Thread 2
 * --------                   --------
 * latch root                 latch BIN parent of MapLN
 * log root IN
 * log MapLN (Tree root)       wants to log MapLN too -- but has to take
 *  to refer to new root IN    root latch, so we'll get the right rootIN
 *
 * Without latching the root this could produce the following, incorrect log
 *  30 LNa
 *  40 BIN
 *  50 IN (first version of root)
 *  60 MapLN, refers to IN(50)
 *  ...
 *  90 LNb
 *  100 BIN
 *  110 IN (second version of root)
 *  120 CkptStart (the tree is not dirty, no IN will be logged during the
 *   ckpt interval))
 *   ..  something arbirarily writes out the MapLN
 *  130 MapLN refers to first root, IN(50)    <------ impossible
 *
 * While a MapLN can't be written out with the wrong root, it's possible
 * for a rootIN to be logged without the MapLN, and for that rootIN not
 * to be processed at recovery. Suppose a checkpoint begins and ends
 * in the window between when a rootIN is written, and DbTree.modifyDbRoot is
 * called:
 *   300 log new root IN,
 *   update root reference in tree
 *   unlatch root
 *
 *   310 Checkpoint starts
 *   320 Checkpoint ends
 *   ...if we crash here, before the MapLN is logged, , we won't see the new
 *   root IN at lsn 300. However, the IN is non-txnal and will be recreated
 *   during reply of txnal information (LNs) by normal recovery processing.
 * }
 */
public final class MapLN extends LN {

    private static final String BEGIN_TAG = "";
    private static final String END_TAG = "";

    private final DatabaseImpl databaseImpl;
    private boolean deleted;

    /**
     * Create a new MapLn to hold a new databaseImpl. In the ideal world, we'd
     * have a base LN class so that this MapLN doesn't have a superfluous data
     * field, but we want to optimize the LN class for size and speed right
     * now.
     */
    public MapLN(DatabaseImpl db) {
        super(new byte[0]);
        databaseImpl = db;
        deleted = false;
    }

    /**
     * Create an empty MapLN, to be filled in from the log.
     */
    public MapLN() {
        super();
        databaseImpl = new DatabaseImpl();
    }

    @Override
    public boolean isDeleted() {
        return deleted;
    }

    @Override
    void makeDeleted() {
        deleted = true;

        /* Release all references to nodes held by this database. */
        databaseImpl.getTree().setRoot(null, true);
    }

    public DatabaseImpl getDatabase() {
        return databaseImpl;
    }

    @Override
    public boolean isDirty() {
        return super.isDirty() || databaseImpl.isDirty();
    }

    /**
     * Does a fast check without acquiring the MapLN write-lock.  This is
     * important because the overhead of requesting the lock is significant and
     * unnecessary if this DB is open or the root IN is resident.  When there
     * are lots of databases open, this method will be called often during
     * selection of BINs for eviction.  [#13415]
     */
    private boolean isEvictableInexact() {
        /* Always prohibit eviction when je.env.dbEviction=false. */
        return databaseImpl.getEnv().getDbEviction() &&
               !databaseImpl.isInUse() &&
               !databaseImpl.getTree().isRootResident();
    }

    /**
     * Does a guaranteed check by acquiring the write-lock and then calling
     * isEvictableInexact.  [#13415] Be sure to use the idDatabaseImpl, which
     * owns this MapLN, rather than the databaseImpl housed within the MapLN
     * for the lock call. The databaseImpl field refers to the database that
     * the MapLN is representing. [#18524]
     */
    @Override
    boolean isEvictable(long lsn)
        throws DatabaseException {

        boolean evictable = false;

        /* To prevent DB open, get a write-lock on the MapLN. */
        EnvironmentImpl envImpl = databaseImpl.getEnv();
        BasicLocker locker = BasicLocker.createBasicLocker(envImpl);
        DatabaseImpl idDatabaseImpl = envImpl.getDbTree().getIdDatabaseImpl();
        try {
            LockResult lockResult = locker.nonBlockingLock
                (lsn, LockType.WRITE, false /*jumpAheadOfWaiters*/,
                 idDatabaseImpl);

            /*
             * The isEvictableInexact result is guaranteed to hold true during
             * LN stripping if it is still true after acquiring the write-lock.
             */
            if (lockResult.getLockGrant() != LockGrantType.DENIED &&
                isEvictableInexact()) {

                /*
                 * While holding both the BIN latch and a write-lock on the
                 * MapLN, we are guaranteed that the DB is not currently open
                 * or otherwise in use.  It cannot be subsequently opened or
                 * used until the BIN latch is released, since the BIN latch
                 * will block DbTree.getDb (called during DB open and by other
                 * callers needing to use the database).  We will evict the LN
                 * before releasing the BIN latch.  After releasing the BIN
                 * latch, if a caller of DbTree.getDb is waiting on the BIN
                 * latch, then it will fetch the evicted MapLN and proceed to
                 * open/use the database.
                 */
                evictable = true;
            }
        } finally {
            /* Release the write-lock.  The BIN latch is still held. */
            locker.operationEnd();
        }

        return evictable;
    }

    /**
     * Initialize a node that has been faulted in from the log.
     */
    @Override
    public void postFetchInit(DatabaseImpl db, long sourceLsn) {

        super.postFetchInit(db, sourceLsn);

        databaseImpl.setEnvironmentImpl(db.getEnv());
    }

    /**
     * Compute the approximate size of this node in memory for evictor
     * invocation purposes. Don't count the treeAdmin memory, because
     * that goes into a different bucket.
     */
    @Override
    public long getMemorySizeIncludedByParent() {
        return MemoryBudget.MAPLN_OVERHEAD;
    }

    /*
     * Dumping
     */

    @Override
    public String toString() {
        return dumpString(0, true);
    }

    @Override
    public String beginTag() {
        return BEGIN_TAG;
    }

    @Override
    public String endTag() {
        return END_TAG;
    }

    @Override
    public String dumpString(int nSpaces, boolean dumpTags) {
        StringBuilder sb = new StringBuilder();
        sb.append(super.dumpString(nSpaces, dumpTags));
        sb.append('\n');
        sb.append(TreeUtils.indent(nSpaces));
        sb.append("");
        sb.append('\n');
        sb.append(databaseImpl.dumpString(nSpaces));
        return sb.toString();
    }

    /*
     * Logging
     */

    /**
     * Return the correct log entry type for a MapLN depends on whether it's 
     * transactional.
     */
    @Override
    protected LogEntryType getLogType(boolean isInsert,
                                      boolean isTransactional,
                                      DatabaseImpl db) {
        assert(!isTransactional);
        return LogEntryType.LOG_MAPLN;
    }

    @Override
    public int getLogSize(final int logVersion, final boolean forReplication) {
        return super.getLogSize(logVersion, forReplication) +
            databaseImpl.getLogSize() +
            1; // deleted
    }

    @Override
    public void writeToLog(final ByteBuffer logBuffer,
                           final int logVersion,
                           final boolean forReplication) {
        super.writeToLog(logBuffer, logVersion, forReplication);
        databaseImpl.writeToLog(logBuffer);
        byte booleans = (byte) (deleted ? 1 : 0);
        logBuffer.put(booleans);
    }

    @Override
    public void readFromLog(ByteBuffer itemBuffer, int entryVersion) {
        super.readFromLog(itemBuffer, entryVersion);
        databaseImpl.readFromLog(itemBuffer, entryVersion);
        byte booleans = itemBuffer.get();
        deleted = (booleans & 1) != 0;

        /*
         * Clear DbFileSummaryMap unless this is an old deleted MapLN, in
         * which case we may use it for utilization counting.
         */
        if (entryVersion < 16 || !deleted) {
            databaseImpl.clearDbFileSummaries();
        }
    }

    /**
     * Should never be replicated.
     */
    @Override
    public boolean logicalEquals(Loggable other) {
        return false;
    }

    /**
     * Dump additional fields. Done this way so the additional info can be
     * within the XML tags defining the dumped log entry.
     */
    @Override
    protected void dumpLogAdditional(StringBuilder sb, boolean verbose) {
        databaseImpl.dumpLog(sb, verbose);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy