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

com.sleepycat.je.log.INFileReader 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.log;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.cleaner.ExtinctionScanner;
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.
 *
 * 

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.

*/ 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 targetEntryMap; private LogEntry targetLogEntry; /* Set of non-target log entry types for ID tracking. */ private Set idTrackingSet; /* Cache of non-target log entries for ID tracking. */ private Map idTrackingMap; private boolean trackIds; private long minReplicatedNodeId; private long maxNodeId; private long minReplicatedDbId; private long maxDbId; private long minReplicatedTxnId; private long maxTxnId; private long minReplicatedExtinctionId; private long maxExtinctionId; 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 logVersion8UpgradeDbs; private AtomicBoolean logVersion8UpgradeDeltas; /** * Create this reader to start at a given LSN. */ public INFileReader(EnvironmentImpl env, int readBufferSize, long startLsn, long finishLsn, boolean trackIds, long partialCkptStart, long ckptEnd, RecoveryUtilizationTracker tracker) { this(env, readBufferSize, startLsn, finishLsn, trackIds, partialCkptStart, ckptEnd, tracker, null /*logVersion8UpgradeDbs*/, null /*logVersion8UpgradeDeltas*/); } /** * Create with logVersion8UpgradeDbs and logVersion8UpgradeDeltas params. */ public INFileReader(EnvironmentImpl env, int readBufferSize, long startLsn, long finishLsn, boolean trackIds, long partialCkptStart, long ckptEnd, RecoveryUtilizationTracker tracker, Set logVersion8UpgradeDbs, AtomicBoolean logVersion8UpgradeDeltas) throws DatabaseException { /* * IN/LN file readers do not verify checksums since they are run only * during recovery. See RecoveryManager.VerifyCheckpointInterval. */ super(env, readBufferSize, true, startLsn, null, DbLsn.NULL_LSN, finishLsn, false /*doChecksumOnRead*/); this.trackIds = trackIds; this.ckptEnd = ckptEnd; targetEntryMap = new HashMap(); if (trackIds) { maxNodeId = 0; maxDbId = 0; maxTxnId = 0; minReplicatedNodeId = 0; minReplicatedDbId = DbTree.NEG_DB_ID_START; minReplicatedTxnId = 0; minReplicatedExtinctionId = 0; maxExtinctionId = 0; this.tracker = tracker; this.partialCkptStart = partialCkptStart; idTrackingSet = new HashSet(); idTrackingMap = new HashMap(); /* * 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 BIN-delta for obsolete tracking. */ for (LogEntryType entryType : LogEntryType.getAllTypes()) { if (entryType.isNodeType()) { idTrackingSet.add(entryType); } } idTrackingSet.add(LogEntryType.LOG_BIN_DELTA); idTrackingSet.add(LogEntryType.LOG_OLD_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; this.logVersion8UpgradeDeltas = logVersion8UpgradeDeltas; } } /** * 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) && !fromLogType.equals(LogEntryType.LOG_OLD_BIN_DELTA) && !LogEntryType.LOG_FILE_HEADER.equals(fromLogType)) { tracker.countNewLogEntry(getLastLsn(), fromLogType, currentEntryHeader.getSize() + currentEntryHeader.getItemSize()); } /* Track VLSNs in the log entry header of all replicated entries. */ if (currentEntryHeader.getReplicated()) { vlsnProxy.trackMapping(getLastLsn(), currentEntryHeader, null /*targetLogEntry*/); } /* Return true if this logrec should be passed on to processEntry. */ return (targetLogEntry != null || idTrackingSet.contains(fromLogType)); } /** * 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); useEntry = true; } /* If we're not tracking IDs, we're done. */ if (!trackIds) { 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) || fromLogType.equals(LogEntryType.LOG_OLD_BIN_DELTA)) { tracker.countNewLogEntry(getLastLsn(), fromLogType, currentEntryHeader.getSize() + currentEntryHeader.getItemSize()); } /* Track VLSNs in RollbackStart. */ if (fromLogType.equals(LogEntryType.LOG_ROLLBACK_START)) { vlsnProxy.trackMapping(getLastLsn(), currentEntryHeader, targetLogEntry); } /* Process LN types. */ if (fromLogType.isLNType()) { LNLogEntry lnEntry = (LNLogEntry) targetLogEntry; /* * When a MapLN is encountered, reset the tracked info for that * DB. This clears what we have accummulated so far for the DB * during this recovery pass. This is important to eliminate * potential double counting of obsolete logrecs done earlier * in this recovery pass. * * Also, save the LSN of the MapLN for use in utilization counting * during LN undo/redo. */ if (fromLogType.equals(LogEntryType.LOG_MAPLN)) { 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); } /* Track latest extinction scan ID. */ if (fromLogType.equals(LogEntryType.LOG_EXTINCT_SCAN_LN) || fromLogType.equals( LogEntryType.LOG_EXTINCT_SCAN_LN_TRANSACTIONAL)) { DatabaseEntry entry = new DatabaseEntry(); lnEntry.postFetchInit(false); lnEntry.getUserKeyData(entry, null); long id = ExtinctionScanner.materializeKey(entry); maxExtinctionId = Math.max(id, maxExtinctionId); minReplicatedExtinctionId = Math.min( id, minReplicatedExtinctionId); } /* Track latest txn ID. */ if (fromLogType.isTransactional()) { long txnId = lnEntry.getTxnId(); maxTxnId = (txnId > maxTxnId ? txnId : maxTxnId); minReplicatedTxnId = (txnId < minReplicatedTxnId ? txnId : minReplicatedTxnId); } /* * When a FSLN is encountered, reset the tracked summary info for * that file. This clears what we have accummulated so far for the * file during this recovery pass. This is important to eliminate * potential double counting of obsolete logrecs done earlier * in this recovery pass. * * Also, save the LSN of the FSLN for use in utilization counting * during LN undo/redo. */ if (LogEntryType.LOG_FILESUMMARYLN.equals(fromLogType)) { lnEntry.postFetchInit(false /*isDupDb*/); long fileNum = FileSummaryLN.getFileNumber(lnEntry.getKey()); tracker.resetFileInfo(fileNum); 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_OLD_BIN_DELTA)) { INContainingEntry inEntry = (INContainingEntry) targetLogEntry; DatabaseId dbId = inEntry.getDbId(); long newLsn = getLastLsn(); /* * Count the previous version of this IN as obsolete. If lsn * (i.e. the current version) is non-provisional, then oldLsn is * indeed obsolete. However, if lsn is provisional, oldLsn is * obsolete only if an ancestor of this IN has been logged non- * provisionally later in the log, and unless lsn < CKPT_END * we cannot know if this is true or not. For this reason, we * conservatively assume that oldLsn is indeed obsolete, but we * use inexact counting, so that the oldLsn value will not be * recorded by the tracker. (another reason is that earlier log * versions did not have a full LSN in oldLsn; they had only the * file number). * * Notice also that there may be an FSLN logrec after lsn that * includes oldLsn as an obsolete IN. If so, we are double counting * here, but the double counting will go away when we later process * that FSLN and as a result wipe-out the utilization info we have * collected so far about the log file containing oldLsn. */ long oldLsn = inEntry.getPrevFullLsn(); if (oldLsn != DbLsn.NULL_LSN && !inEntry.isBINDelta()) { tracker.countObsoleteUnconditional( oldLsn, fromLogType, 0, false /*countExact*/); } oldLsn = inEntry.getPrevDeltaLsn(); if (oldLsn != DbLsn.NULL_LSN) { tracker.countObsoleteUnconditional( oldLsn, fromLogType, 0, false /*countExact*/); } /* * Count the current IN version as obsolete if lsn is * provisional and is after partialCkptStart. In this case, the * crash occurred during a ckpt and it the crash event itself * that may make the current logrec be obsolete. Again, whether * the lsn is indeed obsolete or not depends on whether an * ancestor of this IN has been logged non-provisionally later * in the log. At this point we cannot know if this is true or * not. For this reason, we conservatively assume that lsn * is indeed obsolete, but we use inexact counting, so that the * lsn value will not be recorded by the tracker. As explained * above, we may be double counting here, but this will be fixed * if we later find an FSLN for the same log file as lsn. * * We are too conservative here in assuming that lsn is * obsolete. This is because of the "grouping" behaviour of the * checpointer. Specifically: * * Most of the provisional IN logrecs in this region of the log * (i.e., after the start of an incomplete ckpt) are for BINs * logged by the checkpointer (eviction during a ckpt logs dirty * BINs and UINs provisionally as well). The checkpointer logs * all the dirty BIN siblings and then logs their parent non- * provisionally, before logging any other BINs. So, not taking * eviction into account, there can be at most 128 BIN logrecs * after the partialCkptStart that are trully obsolete. * * Note that older versions of the checkpointer did not use to * group together the logging of siblings BINs and their parent. * Without this grouping, the assumption done here that most * provisional logrecs after partialCkptStart are obsolete is * much more accurate. * * A potential solution: instead of counting these logrecs as * obsolete here, save their LSNs on-the-side inside the tracker. * When, later, a UIN N is replayed and attached to the tree, * remove from the saved LSN set any LSNs that appear in the slots * of N. After all REDO-INs passes are done, count as obsolete any * LSNs remaining in the saved set. */ if (isProvisional && partialCkptStart != DbLsn.NULL_LSN && DbLsn.compareTo(partialCkptStart, newLsn) < 0) { tracker.countObsoleteUnconditional( newLsn, fromLogType, 0, false /*countExact*/); } } /* * Add candidate DB IDs and note deltas for possible log version 8 * upgrade violations. */ if (currentEntryHeader.getVersion() < 8) { if (logVersion8UpgradeDbs != null && fromLogType.isNodeType()) { logVersion8UpgradeDbs.add(targetLogEntry.getDbId()); } if (logVersion8UpgradeDeltas != null && (fromLogType.equals(LogEntryType.LOG_OLD_BIN_DELTA) || fromLogType.equals(LogEntryType.LOG_OLD_DUP_BIN_DELTA))) { logVersion8UpgradeDeltas.set(true); } } /* 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; } /** * Get the maximum extinction scan ID seen by the reader. */ public long getMaxExtinctionId() { return maxExtinctionId; } /** * Get the minimum extinction scan ID seen by the reader. */ public long getMinReplicatedExtinctionId() { return minReplicatedExtinctionId; } /** * @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 BIN-delta. */ public boolean isBINDelta() { return targetLogEntry.getLogType().equals(LogEntryType.LOG_BIN_DELTA) || targetLogEntry.getLogType().equals(LogEntryType.LOG_OLD_BIN_DELTA); } public VLSNRecoveryProxy getVLSNProxy() { return vlsnProxy; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy