📄 scan.java
字号:
/* Derby - Class org.apache.derby.impl.store.raw.log.Scan Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */package org.apache.derby.impl.store.raw.log;import org.apache.derby.iapi.reference.SQLState;import org.apache.derby.iapi.services.io.ArrayInputStream;import org.apache.derby.iapi.services.sanity.SanityManager;import org.apache.derby.iapi.error.StandardException;import org.apache.derby.iapi.store.raw.log.LogInstant;import org.apache.derby.iapi.store.raw.xact.TransactionId;import org.apache.derby.impl.store.raw.log.LogCounter;import org.apache.derby.impl.store.raw.log.LogRecord;import org.apache.derby.impl.store.raw.log.StreamLogScan;import org.apache.derby.io.StorageRandomAccessFile;import java.io.IOException;import org.apache.derby.iapi.store.raw.Loggable;/** Scan the the log which is implemented by a series of log files.n This log scan knows how to move across log file if it is positioned at the boundary of a log file and needs to getNextRecord. <PRE> 4 bytes - length of user data, i.e. N 8 bytes - long representing log instant N bytes of supplied data 4 bytes - length of user data, i.e. N </PRE>*/public class Scan implements StreamLogScan { // value for scanDirection public static final byte FORWARD = 1; public static final byte BACKWARD = 2; public static final byte BACKWARD_FROM_LOG_END = 4; private StorageRandomAccessFile scan; // an output stream to the log file private LogToFile logFactory; // log factory knows how to to skip // from log file to log file private long currentLogFileNumber; // the log file the scan is currently on private long currentLogFileLength; // the size of the current log file // used only for FORWARD scan to determine when // to switch the next log file private long knownGoodLogEnd; // For FORWARD scan only // during recovery, we need to determine the end // of the log. Everytime a complete log record // is read in, knownGoodLogEnd is set to the // log instant of the next log record if it is // on the same log file. // // only valid afer a successfull getNextRecord // on a FOWARD scan. private long currentInstant; // the log instant the scan is // currently on - only valid after a // successful getNextRecord private long stopAt; // scan until we find a log record whose // log instance < stopAt if we scan BACKWARD // log instance > stopAt if we scan FORWARD // log instance >= stopAt if we scan FORWARD_FLUSHED private byte scanDirection; // BACKWARD or FORWARD private boolean fuzzyLogEnd = false; //get sets to true during forward scan //for recovery, if there were //partial writes at the end of the log before crash; //during forward scan for recovery. /** For backward scan, we expect a scan positioned at the end of the next log record. For forward scan, we expect a scan positioned at the beginning of the next log record. For forward flushed scan, we expect stopAt to be the instant for the first not-flushed log record. Like any forward scan, we expect a scan positioned at the beginning of the next log record. @exception StandardException Standard Cloudscape error policy @exception IOException cannot access the log at the new position. */ public Scan(LogToFile logFactory, long startAt, LogInstant stopAt, byte direction) throws IOException, StandardException { if (SanityManager.DEBUG) SanityManager.ASSERT(startAt != LogCounter.INVALID_LOG_INSTANT, "cannot start scan on an invalid log instant"); this.logFactory = logFactory; currentLogFileNumber = LogCounter.getLogFileNumber(startAt); currentLogFileLength = -1; knownGoodLogEnd = LogCounter.INVALID_LOG_INSTANT;// set at getNextRecord for FORWARD scan currentInstant = LogCounter.INVALID_LOG_INSTANT; // set at getNextRecord if (stopAt != null) this.stopAt = ((LogCounter) stopAt).getValueAsLong(); else this.stopAt = LogCounter.INVALID_LOG_INSTANT; switch(direction) { case FORWARD: scan = logFactory.getLogFileAtPosition(startAt); scanDirection = FORWARD; if (SanityManager.DEBUG) if (scan == null) SanityManager.THROWASSERT( "scan null at " + LogCounter.toDebugString(startAt)); // NOTE: just get the length of the file without syncing. // this only works because the only place forward scan is used // right now is on recovery redo and nothing is being added to // the current log file. When the forward scan is used for some // other purpose, need to sync access to the end of the log currentLogFileLength = scan.length(); break; case BACKWARD: // startAt is at the front of the log record, for backward // scan we need to be positioned at the end of the log record scan = logFactory.getLogFileAtPosition(startAt); int logsize = scan.readInt(); // skip forward over the log record and all the overhead, but remember // we just read an int off the overhead scan.seek(scan.getFilePointer() + logsize + LogToFile.LOG_RECORD_OVERHEAD - 4); scanDirection = BACKWARD; break; case BACKWARD_FROM_LOG_END: // startAt is at the end of the log, no need to skip the log record scan = logFactory.getLogFileAtPosition(startAt); scanDirection = BACKWARD; break; } } /* ** Methods of StreamLogScan */ /** Read the next log record. Switching log to a previous log file if necessary, Resize the input stream byte array if necessary. @see StreamLogScan#getNextRecord @return the next LogRecord, or null if the end of the scan has been reached. @exception StandardException Standard Cloudscape error policy */ public LogRecord getNextRecord(ArrayInputStream input, TransactionId tranId, int groupmask) throws StandardException { if (scan == null) return null; if (SanityManager.DEBUG) SanityManager.ASSERT(scanDirection != 0, "scan has been secretly closed!"); LogRecord lr = null; try { if (scanDirection == BACKWARD) lr = getNextRecordBackward(input, tranId, groupmask); else if (scanDirection == FORWARD) lr = getNextRecordForward(input, tranId, groupmask); return lr; } catch (IOException ioe) { if (SanityManager.DEBUG) ioe.printStackTrace(); throw logFactory.markCorrupt( StandardException.newException(SQLState.LOG_CORRUPTED, ioe)); } catch (ClassNotFoundException cnfe) { if (SanityManager.DEBUG) cnfe.printStackTrace(); throw logFactory.markCorrupt( StandardException.newException(SQLState.LOG_CORRUPTED, cnfe)); } finally { if (lr == null) close(); // no more log record, close the scan } } /** Read the previous log record. Switching log to a previous log file if necessary, Resize the input stream byte array if necessary. @see StreamLogScan#getNextRecord Side effects include: on a successful read, setting currentInstant. on a log file switch, setting currentLogFileNumber. @return the previous LogRecord, or null if the end of the scan has been reached. */ private LogRecord getNextRecordBackward(ArrayInputStream input, TransactionId tranId, int groupmask) throws StandardException, IOException, ClassNotFoundException { if (SanityManager.DEBUG) SanityManager.ASSERT(scanDirection == BACKWARD, "can only called by backward scan"); // scan is positioned just past the last byte of the record, or // right at the beginning of the file (end of the file header) // may need to switch log file boolean candidate; // if we have filtering, peek at the group and/or the transaction id, // do them in one read rather than 2 reads. int peekAmount = LogRecord.formatOverhead() + LogRecord.maxGroupStoredSize(); if (tranId != null) peekAmount += LogRecord.maxTransactionIdStoredSize(tranId); int readAmount; // the number of bytes actually read LogRecord lr; long curpos = scan.getFilePointer(); do { // this log record is a candidate unless proven otherwise candidate = true; lr = null; readAmount = -1; if (curpos == LogToFile.LOG_FILE_HEADER_SIZE) { // don't go thru the trouble of switching log file if we // will have gone past stopAt if (stopAt != LogCounter.INVALID_LOG_INSTANT && LogCounter.getLogFileNumber(stopAt) == currentLogFileNumber) { if (SanityManager.DEBUG) { if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG)) { SanityManager.DEBUG(LogToFile.DBG_FLAG, "stopping at " + currentLogFileNumber); } } return null; // no more log record } // figure out where the last log record is in the previous // log file scan.seek(LogToFile.LOG_FILE_HEADER_PREVIOUS_LOG_INSTANT_OFFSET); long previousLogInstant = scan.readLong(); scan.close(); if (SanityManager.DEBUG) { SanityManager.ASSERT(previousLogInstant != LogCounter.INVALID_LOG_INSTANT, "scanning backward beyond the first log file"); if (currentLogFileNumber != LogCounter.getLogFileNumber(previousLogInstant) + 1) SanityManager.THROWASSERT( "scanning backward but get incorrect log file number " + "expected " + (currentLogFileNumber -1) + "get " + LogCounter.getLogFileNumber(previousLogInstant)); SanityManager.ASSERT(LogCounter.getLogFilePosition(previousLogInstant) > LogToFile.LOG_FILE_HEADER_SIZE, "scanning backward encounter completely empty log file"); SanityManager.DEBUG(LogToFile.DBG_FLAG, "scanning backwards from log file " + currentLogFileNumber + ", switch to (" + LogCounter.getLogFileNumber(previousLogInstant) + "," + LogCounter.getLogFilePosition(previousLogInstant) + ")" ); } // log file switch, set this.currentLogFileNumber currentLogFileNumber = LogCounter.getLogFileNumber(previousLogInstant); scan = logFactory.getLogFileAtPosition(previousLogInstant); // scan is located right past the last byte of the last log // record in the previous log file. currentLogFileNumber is // set. We asserted that the scan is not located right at the // end of the file header, in other words, there is at least // one log record in this log file. curpos = scan.getFilePointer(); // if the log file happens to be empty skip and proceed. // ideally this case should never occur because log switch is // not suppose to happen on an empty log file. // But it is safer to put following check incase if it ever // happens to avoid any recovery issues. if (curpos == LogToFile.LOG_FILE_HEADER_SIZE) continue; } scan.seek(curpos - 4); int recordLength = scan.readInt(); // get the length after the log record // calculate where this log record started. // include the eight bytes for the long log instant at the front // the four bytes of length in the front and the four bytes we just read long recordStartPosition = curpos - recordLength - LogToFile.LOG_RECORD_OVERHEAD; if (SanityManager.DEBUG) { if (recordStartPosition < LogToFile.LOG_FILE_HEADER_SIZE) SanityManager.THROWASSERT( "next position " + recordStartPosition + " recordLength " + recordLength + " current file position " + scan.getFilePointer()); scan.seek(recordStartPosition); // read the length before the log record and check it against the // length after the log record int checkLength = scan.readInt(); if (checkLength != recordLength) { long inst = LogCounter.makeLogInstantAsLong(currentLogFileNumber, recordStartPosition); throw logFactory.markCorrupt( StandardException.newException( SQLState.LOG_RECORD_CORRUPTED, new Long(checkLength), new Long(recordLength), new Long(inst), new Long(currentLogFileNumber))); } } else { // skip over the length in insane scan.seek(recordStartPosition+4); } // scan is positioned just before the log instant // read the current log instant - this is the currentInstant if we have not // exceeded the scan limit currentInstant = scan.readLong(); if (SanityManager.DEBUG) { // sanity check the current instant against the scan position if (LogCounter.getLogFileNumber(currentInstant) != currentLogFileNumber || LogCounter.getLogFilePosition(currentInstant) != recordStartPosition) SanityManager.THROWASSERT( "Wrong LogInstant on log record " + LogCounter.toDebugString(currentInstant) + " version real position (" + currentLogFileNumber + "," + recordStartPosition + ")"); } // if stopAt == INVALID_LOG_INSTANT, no stop instant, read till
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -