Package com.sleepycat.je.log

Source Code of com.sleepycat.je.log.INFileReader

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002, 2011 Oracle and/or its affiliates.  All rights reserved.
*
*/

package com.sleepycat.je.log;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.cleaner.RecoveryUtilizationTracker;
import com.sleepycat.je.dbi.DatabaseId;
import com.sleepycat.je.dbi.DatabaseImpl;
import com.sleepycat.je.dbi.DbTree;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.log.entry.INContainingEntry;
import com.sleepycat.je.log.entry.INLogEntry;
import com.sleepycat.je.log.entry.LNLogEntry;
import com.sleepycat.je.log.entry.LogEntry;
import com.sleepycat.je.recovery.VLSNRecoveryProxy;
import com.sleepycat.je.tree.FileSummaryLN;
import com.sleepycat.je.tree.IN;
import com.sleepycat.je.tree.MapLN;
import com.sleepycat.je.tree.Node;
import com.sleepycat.je.utilint.DbLsn;

/**
* INFileReader supports recovery by scanning log files during the IN rebuild
* pass. It looks for internal nodes (all types), segregated by whether they
* belong to the main tree or the duplicate trees.
*
* <p>This file reader can also be run in tracking mode to keep track of the
* maximum node ID, database ID and txn ID seen so those sequences can be
* updated properly at recovery.  In this mode it also performs utilization
* counting.  It is only run once in tracking mode per recovery, in the first
* phase of recovery.</p>
*/
public class INFileReader extends FileReader {

    /* Information about the last entry seen. */
    private boolean lastEntryWasDelete;
    private boolean lastEntryWasDupDelete;
    private LogEntryType fromLogType;
    private boolean isProvisional;

    /*
     * targetEntryMap maps DbLogEntryTypes to log entries. We use this
     * collection to find the right LogEntry instance to read in the current
     * entry.
     */
    private Map<LogEntryType, LogEntry> targetEntryMap;
    private LogEntry targetLogEntry;

    /* Set of non-target log entry types for ID tracking. */
    private Set<LogEntryType> idTrackingSet;
    /* Cache of non-target log entries for ID tracking. */
    private Map<LogEntryType, LogEntry> idTrackingMap;

    private boolean trackIds;
    private long minReplicatedNodeId;
    private long maxNodeId;
    private long minReplicatedDbId;
    private long maxDbId;
    private long minReplicatedTxnId;
    private long maxTxnId;
    private boolean mapDbOnly;
    private long ckptEnd;

    /* Used for utilization tracking. */
    private long partialCkptStart;
    private RecoveryUtilizationTracker tracker;

    /* Used for replication. */
    private VLSNRecoveryProxy vlsnProxy;

    /** DBs that may violate the rule for upgrading to log version 8. */
    private Set<DatabaseId> logVersion8UpgradeDbs;

    /**
     * Create this reader to start at a given LSN.
     */
    public INFileReader(EnvironmentImpl env,
                        int readBufferSize,
                        long startLsn,
                        long finishLsn,
                        boolean trackIds,
                        boolean mapDbOnly,
                        long partialCkptStart,
                        long ckptEnd,
                        RecoveryUtilizationTracker tracker,
                        Set<DatabaseId> logVersion8UpgradeDbs)
        throws DatabaseException {

        super(env, readBufferSize, true, startLsn, null,
              DbLsn.NULL_LSN, finishLsn);

        this.trackIds = trackIds;
        this.mapDbOnly = mapDbOnly;
        this.ckptEnd = ckptEnd;
        targetEntryMap = new HashMap<LogEntryType, LogEntry>();

        if (trackIds) {
            maxNodeId = 0;
            maxDbId = 0;
            maxTxnId = 0;
            minReplicatedNodeId = 0;
            minReplicatedDbId = DbTree.NEG_DB_ID_START;
            minReplicatedTxnId = 0;
            this.tracker = tracker;
            this.partialCkptStart = partialCkptStart;

            idTrackingSet = new HashSet<LogEntryType>();
            idTrackingMap = new HashMap<LogEntryType, LogEntry>();

            /*
             * Need all nodes for tracking:
             * - Need all INs for node ID tracking.
             * - Need all LNs for obsolete tracking.
             * - Need txnal LNs for txn ID tracking.
             * - Need FileSummaryLN for obsolete tracking.
             * - Need MapLN for obsolete and DB ID tracking.
             * - Need BINDelta for obsolete tracking.
             */
            for (LogEntryType entryType : LogEntryType.getAllTypes()) {
                if (entryType.isNodeType()) {
                    idTrackingSet.add(entryType);
                }
            }
            idTrackingSet.add(LogEntryType.LOG_BIN_DELTA);

            /* For tracking VLSNs. */
            vlsnProxy = envImpl.getVLSNProxy();
            idTrackingSet.add(LogEntryType.LOG_ROLLBACK_START);

            /* For checking for log version 8 upgrade errors. */
            this.logVersion8UpgradeDbs = logVersion8UpgradeDbs;
        }
    }

    /**
     * Configure this reader to target this kind of entry.
     */
    public void addTargetType(LogEntryType entryType)
        throws DatabaseException {

        targetEntryMap.put(entryType, entryType.getNewLogEntry());
    }

    /*
     * Utilization Tracking
     * --------------------
     * This class counts all new log entries and obsolete INs.  Obsolete LNs,
     * on the other hand, are counted by RecoveryManager undo/redo.
     *
     * Utilization counting is done in the first recovery pass where IDs are
     * tracked (trackIds=true).  Processing is split between isTargetEntry
     * and processEntry as follows.
     *
     * isTargetEntry counts only new non-node entries; this can be done very
     * efficiently using only the LSN and entry type, without reading and
     * unmarshalling the entry.
     *
     * processEntry counts new node entries and obsolete INs.
     *
     * processEntry also resets (sets all counters to zero and clears obsolete
     * offsets) the tracked summary for a file or database when a FileSummaryLN
     * or MapLN is encountered.  This clears the totals that have accumulated
     * during this recovery pass for entries prior to that point.  We only want
     * to count utilization for entries after that point.
     *
     * In addition, when processEntry encounters a FileSummaryLN or MapLN, its
     * LSN is recorded in the tracker.  This information is used during IN and
     * LN utilization counting.  For each file, knowing the LSN of the last
     * logged FileSummaryLN for that file allows the undo/redo code to know
     * whether to perform obsolete countng.  If the LSN of the FileSummaryLN is
     * less than (to the left of) the LN's LSN, obsolete counting should be
     * performed.  If it is greater, obsolete counting is already included in
     * the logged FileSummaryLN and should not be repeated to prevent double
     * counting.  The same thing is true of counting per-database utilization
     * relative to the LSN of the last logged MapLN.
     */

    /**
     * If we're tracking node, database and txn IDs, we want to see all node
     * log entries. If not, we only want to see IN entries.
     */
    @Override
    protected boolean isTargetEntry()
        throws DatabaseException {

        lastEntryWasDelete = false;
        lastEntryWasDupDelete = false;
        targetLogEntry = null;
        isProvisional = currentEntryHeader.getProvisional().isProvisional
            (getLastLsn(), ckptEnd);

        /* Get the log entry type instance we need to read the entry. */
        fromLogType = LogEntryType.findType(currentEntryHeader.getType());
        LogEntry possibleTarget = targetEntryMap.get(fromLogType);

        /* Always select a non-provisional target entry. */
        if (!isProvisional) {
            targetLogEntry = possibleTarget;
        }

        /* Recognize IN deletion. */
        if (LogEntryType.LOG_IN_DELETE_INFO.equals(fromLogType)) {
            lastEntryWasDelete = true;
        }
        if (LogEntryType.LOG_IN_DUPDELETE_INFO.equals(fromLogType)) {
            lastEntryWasDupDelete = true;
        }

        /* If we're not tracking IDs, select only the targeted entry. */
        if (!trackIds) {
            return (targetLogEntry != null);
        }

        /*
         * Count all non-node non-delta entries except for the file header as
         * new.  UtilizationTracker does not count the file header.  Node/delta
         * entries will be counted in processEntry.  Null is passed for the
         * database ID; it is only needed for node entries.
         */
        if (!fromLogType.isNodeType() &&
            !fromLogType.equals(LogEntryType.LOG_BIN_DELTA) &&
            !LogEntryType.LOG_FILE_HEADER.equals(fromLogType)) {
            tracker.countNewLogEntry(getLastLsn(),
                                     fromLogType,
                                     currentEntryHeader.getSize() +
                                     currentEntryHeader.getItemSize(),
                                     null); // DatabaseId
        }

        /*
         * When we encouter a DbTree log entry, reset the tracked summary for
         * the ID and Name mapping DBs.  This clears what we accummulated
         * previously for these databases during this recovery pass. Save the
         * LSN for these databases for use by undo/redo.
         */
        if (LogEntryType.LOG_DBTREE.equals(fromLogType)) {
            tracker.saveLastLoggedMapLN(DbTree.ID_DB_ID, getLastLsn());
            tracker.saveLastLoggedMapLN(DbTree.NAME_DB_ID, getLastLsn());
            tracker.resetDbInfo(DbTree.ID_DB_ID);
            tracker.resetDbInfo(DbTree.NAME_DB_ID);
        }

        /*
         * TODO: For now we track the VLSN for all replicated entries in
         * processTarget, so we return true if getReplicated below.  Remove
         * this when FileReader supports a correct getLastLsn when called from
         * isTargetEntry.
         */

        /* Return true if this entry should be passed on to processEntry. */
        return (targetLogEntry != null) ||
               idTrackingSet.contains(fromLogType) ||
               currentEntryHeader.getReplicated();
    }

    /**
     * This reader returns non-provisional INs and IN delete entries.
     * In tracking mode, it may also scan log entries that aren't returned:
     *  -to set the sequences for txn, node, and database ID.
     *  -to update utilization and obsolete offset information.
     *  -for VLSN mappings for recovery
     */
    protected boolean processEntry(ByteBuffer entryBuffer)
        throws DatabaseException {

        boolean useEntry = false;

        /* Read targeted entry. */
        if (targetLogEntry != null) {
            targetLogEntry.readEntry(envImpl, currentEntryHeader, entryBuffer);
            /* Apply the mapDbOnly setting. */
            DatabaseId dbId = getDatabaseId();
            boolean isMapDb = dbId.equals(DbTree.ID_DB_ID);
            useEntry = (!mapDbOnly || isMapDb);
        }

        /* If we're not tracking IDs, we're done. */
        if (!trackIds) {
            return useEntry;
        }

        /*
         * TODO: For now we track the VLSN for all replicated entries here.
         * Move this into isTargetEntry when FileReader supports a correct
         * getLastLsn when called from isTargetEntry.
         */
        /* Track VLSNs in the log entry header of all replicated entries. */
        if (currentEntryHeader.getReplicated()) {
            vlsnProxy.trackMapping(getLastLsn(),
                                   currentEntryHeader,
                                   null /*targetLogEntry*/);
        }
        /* TODO Also remove this block. */
        if (targetLogEntry == null && !idTrackingSet.contains(fromLogType)) {
            int endPosition = threadSafeBufferPosition(entryBuffer) +
                currentEntryHeader.getItemSize();
            threadSafeBufferPosition(entryBuffer, endPosition);
            return useEntry;
        }

        /* Read non-target entry. */
        if (targetLogEntry == null) {
            assert idTrackingSet.contains(fromLogType);
            targetLogEntry = idTrackingMap.get(fromLogType);
            if (targetLogEntry == null) {
                targetLogEntry = fromLogType.getNewLogEntry();
                idTrackingMap.put(fromLogType, targetLogEntry);
            }
            targetLogEntry.readEntry(envImpl, currentEntryHeader, entryBuffer);
        }

        /*
         * Count node and delta entries as new.  Non-node/delta entries are
         * counted in isTargetEntry.
         */
        if (fromLogType.isNodeType() ||
            fromLogType.equals(LogEntryType.LOG_BIN_DELTA)) {
            tracker.countNewLogEntry(getLastLsn(), fromLogType,
                                     currentEntryHeader.getSize() +
                                     currentEntryHeader.getItemSize(),
                                     targetLogEntry.getDbId());
        }

        /* Track VLSNs in RollbackStart. */
        if (fromLogType.equals(LogEntryType.LOG_ROLLBACK_START)) {
            vlsnProxy.trackMapping(getLastLsn(),
                                   currentEntryHeader,
                                   targetLogEntry);
        }

        DatabaseId dbIdToReset = null;
        long fileNumToReset = -1;

        /* Process LN types. */
        if (fromLogType.isLNType()) {
            LNLogEntry lnEntry = (LNLogEntry) targetLogEntry;

            if (fromLogType.equals(LogEntryType.LOG_MAPLN) ||
                fromLogType.equals(LogEntryType.LOG_MAPLN_TRANSACTIONAL)) {
                MapLN mapLN = (MapLN) lnEntry.getMainItem();
                DatabaseId dbId = mapLN.getDatabase().getId();

                /* Track latest DB ID. */
                long dbIdVal = dbId.getId();
                maxDbId = (dbIdVal > maxDbId) ? dbIdVal : maxDbId;
                minReplicatedDbId = (dbIdVal < minReplicatedDbId) ?
                    dbIdVal : minReplicatedDbId;

                /*
                 * When a MapLN is encountered, reset the tracked information
                 * for that database.  This clears what we accummulated
                 * previously for the database during this recovery pass.
                 */
                dbIdToReset = dbId;

                /* Save the LSN of the MapLN for use by undo/redo. */
                tracker.saveLastLoggedMapLN(dbId, getLastLsn());
            }

            /* Track latest txn ID. */
            if (fromLogType.isTransactional()) {
                long txnId = lnEntry.getTxnId().longValue();
                maxTxnId = (txnId > maxTxnId) ? txnId : maxTxnId;
                minReplicatedTxnId = (txnId < minReplicatedTxnId) ?
                    txnId : minReplicatedTxnId;
            }

            /*
             * When a FileSummaryLN is encountered, reset the tracked summary
             * for that file.  This clears what we accummulated previously for
             * the file during this recovery pass.
             */
            if (LogEntryType.LOG_FILESUMMARYLN.equals(fromLogType)) {
                lnEntry.postFetchInit(false /*isDupDb*/);
                byte[] keyBytes = lnEntry.getKey();
                FileSummaryLN fsln = (FileSummaryLN) lnEntry.getMainItem();
                long fileNum = fsln.getFileNumber(keyBytes);
                fileNumToReset = fileNum;

                /* Save the LSN of the FileSummaryLN for use by undo/redo. */
                tracker.saveLastLoggedFileSummaryLN(fileNum, getLastLsn());

                /*
                 * Do not cache the file summary in the UtilizationProfile here,
                 * since it may be for a deleted log file. [#10395]
                 */
            }
        }

        /* Process IN types. */
        if (fromLogType.isINType()) {
            INLogEntry inEntry = (INLogEntry) targetLogEntry;

            /* Keep track of the largest node ID seen. */
            long nodeId = inEntry.getNodeId();
            assert (nodeId != Node.NULL_NODE_ID);
            maxNodeId = (nodeId > maxNodeId) ? nodeId : maxNodeId;
            minReplicatedNodeId = (nodeId < minReplicatedNodeId) ?
                nodeId : minReplicatedNodeId;
        }

        /* Process INContainingEntry types. */
        if (fromLogType.isINType() ||
            fromLogType.equals(LogEntryType.LOG_BIN_DELTA)) {

            INContainingEntry inEntry =
                (INContainingEntry) targetLogEntry;

            /*
             * Count the obsolete LSN of the previous version, if available and
             * if not already counted.  Use inexact counting for two reasons:
             * 1) we don't always have the full LSN because earlier log
             * versions only had the file number, and 2) we can't guarantee
             * obsoleteness for provisional INs.
             */
            long oldLsn = inEntry.getPrevFullLsn();
            if (oldLsn != DbLsn.NULL_LSN) {
                long newLsn = getLastLsn();
                tracker.countObsoleteIfUncounted
                    (oldLsn, newLsn, fromLogType, 0, inEntry.getDbId(),
                     false /*countExact*/);
            }
            oldLsn = inEntry.getPrevDeltaLsn();
            if (oldLsn != DbLsn.NULL_LSN) {
                long newLsn = getLastLsn();
                tracker.countObsoleteIfUncounted
                    (oldLsn, newLsn, fromLogType, 0, inEntry.getDbId(),
                     false /*countExact*/);
            }

            /*
             * Count a provisional IN as obsolete if it follows
             * partialCkptStart.  It cannot have been already counted, because
             * provisional INs are not normally counted as obsolete; they are
             * only considered obsolete when they are part of a partial
             * checkpoint.
             *
             * Depending on the exact point at which the checkpoint was
             * aborted, this technique is not always accurate; therefore
             * inexact counting must be used.
             */
            if (isProvisional && partialCkptStart != DbLsn.NULL_LSN) {
                oldLsn = getLastLsn();
                if (DbLsn.compareTo(partialCkptStart, oldLsn) < 0) {
                    tracker.countObsoleteUnconditional
                        (oldLsn, fromLogType, 0, inEntry.getDbId(),
                         false /*countExact*/);
                }
            }
        }

        /*
         * Reset file and database utilization info only after counting a new
         * or obsolete node.  The MapLN itself is a node and will be counted as
         * new above, and we must reset that count as well.
         */
        if (fileNumToReset != -1) {
            tracker.resetFileInfo(fileNumToReset);
        }
        if (dbIdToReset != null) {
            tracker.resetDbInfo(dbIdToReset);
        }

        /*
         * Add candidate DB IDs to be checked for log version 8 upgrade
         * violations.
         */
        if (logVersion8UpgradeDbs != null && fromLogType.isNodeType() &&
            currentEntryHeader.getVersion() < 8) {
            logVersion8UpgradeDbs.add(targetLogEntry.getDbId());
        }

        /* Return true if this is a targeted entry. */
        return useEntry;
    }

    /**
     * Get the last IN seen by the reader.
     */
    public IN getIN(DatabaseImpl dbImpl)
        throws DatabaseException {

        return ((INContainingEntry) targetLogEntry).getIN(dbImpl);
    }

    /**
     * Get the last databaseId seen by the reader.
     */
    public DatabaseId getDatabaseId() {
        return ((INContainingEntry) targetLogEntry).getDbId();
    }

    /**
     * Get the maximum node ID seen by the reader.
     */
    public long getMaxNodeId() {
        return maxNodeId;
    }

    public long getMinReplicatedNodeId() {
        return minReplicatedNodeId;
    }

    /**
     * Get the maximum DB ID seen by the reader.
     */
    public long getMaxDbId() {
        return maxDbId;
    }

    public long getMinReplicatedDbId() {
        return minReplicatedDbId;
    }

    /**
     * Get the maximum txn ID seen by the reader.
     */
    public long getMaxTxnId() {
        return maxTxnId;
    }

    public long getMinReplicatedTxnId() {
        return minReplicatedTxnId;
    }

    /**
     * @return true if the last entry was a delete info entry.
     */
    public boolean isDeleteInfo() {
        return lastEntryWasDelete;
    }

    /**
     * @return true if the last entry was a dup delete info entry.
     */
    public boolean isDupDeleteInfo() {
        return lastEntryWasDupDelete;
    }

    /**
     * @return true if the last entry was a BINDelta.
     */
    public boolean isBINDelta() {
        return targetLogEntry.getLogType().equals(LogEntryType.LOG_BIN_DELTA);
    }

    public VLSNRecoveryProxy getVLSNProxy() {
        return vlsnProxy;
    }
}
TOP

Related Classes of com.sleepycat.je.log.INFileReader

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.