/* * A checksum error has been encountered. Go to the start of this log file * and read forward until the lower side of the corrupted area has been * found. */ @Override protected boolean resyncReader(long nextGoodRecordPostCorruption, boolean showCorruptedBounds) throws DatabaseException { LastFileReader reader = null; long tryReadBufferFileNum = DbLsn.getFileNumber(nextGoodRecordPostCorruption); while (tryReadBufferFileNum >= 0) { try { reader = new LastFileReader(envImpl, readBufferSize, Long.valueOf(tryReadBufferFileNum)); break; } catch (ChecksumException e) { /* * We encountered a problem opening this file so skip to an * earlier file. */ tryReadBufferFileNum--; continue; } } boolean switchedFiles = tryReadBufferFileNum != DbLsn.getFileNumber(nextGoodRecordPostCorruption); if (!switchedFiles) { /* * Read forward until a checksum fails. This reader will not throw * an exception if a checksum error is hit -- it will just return * false. */ while (reader.readNextEntry()) {} } long lastUsedLsn = reader.getLastValidLsn(); long nextAvailableLsn = reader.getEndOfLog(); if (showCorruptedBounds) { System.err.println("A checksum error was found in the log."); System.err.println("Corruption begins at LSN:\n " + DbLsn.toString(nextAvailableLsn)); System.err.println( "Last known good record before corruption is at LSN:\n " + DbLsn.toString(lastUsedLsn)); System.err.println( "Next known good record after corruption is at LSN:\n " + DbLsn.toString(nextGoodRecordPostCorruption)); } startLsn = lastUsedLsn; initStartingPosition(nextAvailableLsn, null); if (switchedFiles) { currentEntryPrevOffset = 0; } /* Indicate resync is permitted so don't throw exception. */ return true; }
/** Helper for determining the starting position and opening up a file at the desired location. */ protected void initStartingPosition(long endOfFileLsn, Long ignoreSingleFileNumber) { eof = false; if (forward) { /* * Start off at the startLsn. If that's null, start at the * beginning of the log. If there are no log files, set eof. */ if (startLsn != DbLsn.NULL_LSN) { window.initAtFileStart(startLsn); } else { Long firstNum = fileManager.getFirstFileNum(); if (firstNum == null) { eof = true; } else { window.initAtFileStart(DbLsn.makeLsn(firstNum, 0)); } } /* * After we read the first entry, the currentEntry will point here. */ nextEntryOffset = window.getEndOffset(); } else { /* * Make the read buffer look like it's positioned off the end of * the file. Initialize the first LSN we want to read. When * traversing the log backwards, we always start at the very end. */ assert startLsn != DbLsn.NULL_LSN; window.initAtFileStart(endOfFileLsn); /* * currentEntryPrevOffset points to the entry we want to start out * reading when going backwards. If it's 0, the entry we want to * read is in a different file. */ if (DbLsn.getFileNumber(startLsn) == DbLsn.getFileNumber(endOfFileLsn)) { currentEntryPrevOffset = DbLsn.getFileOffset(startLsn); } else { currentEntryPrevOffset = 0; } currentEntryOffset = DbLsn.getFileOffset(endOfFileLsn); } }
/** * Returns whether a given obsolete LSN has already been counted in the utilization profile. If * true is returned, it should not be counted again, to prevent double-counting. */ private boolean isObsoleteLsnAlreadyCounted(long oldLsn, long newLsn) { /* If the file summary follows the new LSN, it was already counted. */ Long fileNum = new Long(DbLsn.getFileNumber(oldLsn)); long fileSummaryLsn = DbLsn.longToLsn((Long) fileSummaryLsns.get(fileNum)); int cmpFsLsnToNewLsn = (fileSummaryLsn != DbLsn.NULL_LSN) ? DbLsn.compareTo(fileSummaryLsn, newLsn) : -1; return (cmpFsLsnToNewLsn >= 0); }
void setPosition(long startLsn) throws ChecksumException, FileNotFoundException, DatabaseException { if (startLsn == DbLsn.NULL_LSN) { return; } /* * An assertion: a reposition should never make the reader lose ground. */ if (forward) { if (DbLsn.compareTo(getLastLsn(), startLsn) > 0) { throw EnvironmentFailureException.unexpectedState( "Feeder forward scanning should not be repositioned to " + " a position earlier than the current position. Current" + " lsn = " + DbLsn.getNoFormatString(getLastLsn()) + " reposition = " + DbLsn.getNoFormatString(startLsn)); } } else { if (DbLsn.compareTo(getLastLsn(), startLsn) < 0) { throw EnvironmentFailureException.unexpectedState( "Feeder backward scanning should not be repositioned to " + " a position later than the current position. Current" + " lsn = " + DbLsn.getNoFormatString(getLastLsn()) + " reposition = " + DbLsn.getNoFormatString(startLsn)); } } long fileNum = DbLsn.getFileNumber(startLsn); long offset = DbLsn.getFileOffset(startLsn); if (window.containsLsn(fileNum, offset)) { window.positionBuffer(offset); } else { window.slideAndFill(fileNum, offset, offset); } if (forward) { nextEntryOffset = offset; } else { currentEntryPrevOffset = offset; } nReposition++; }
public void testFlipFile() throws Throwable { /* * The setUp() method opens a standalone FileManager, but in this test * case we need a regular Environment. On Windows, we can't lock the * file range twice in FileManager.lockEnvironment, so we must close * the standalone FileManager here before opening a regular * environment. */ fileManager.clear(); fileManager.close(); fileManager = null; EnvironmentConfig envConfig = TestUtils.initEnvConfig(); envConfig.setAllowCreate(true); envConfig.setTransactional(true); Environment env = new Environment(envHome, envConfig); EnvironmentImpl envImpl = DbInternal.envGetEnvironmentImpl(env); FileManager fileManager = envImpl.getFileManager(); DatabaseConfig dbConfig = new DatabaseConfig(); dbConfig.setAllowCreate(true); Database exampleDb = env.openDatabase(null, "simpleDb", dbConfig); assertEquals("Should have 0 as current file", 0L, fileManager.getCurrentFileNum()); long flipLsn = envImpl.forceLogFileFlip(); assertEquals("LSN should be 1 post-flip", 1L, DbLsn.getFileNumber(flipLsn)); DatabaseEntry key = new DatabaseEntry(); DatabaseEntry data = new DatabaseEntry(); key.setData("key".getBytes()); data.setData("data".getBytes()); exampleDb.put(null, key, data); assertEquals("Should have 1 as last file", 1L, fileManager.getCurrentFileNum()); exampleDb.close(); env.close(); }
/* * Position this window at this LSN, but leave it empty, it has no data * yet. */ public void initAtFileStart(long startLsn) { setFileNum(DbLsn.getFileNumber(startLsn), LogEntryType.UNKNOWN_FILE_HEADER_VERSION); startOffset = DbLsn.getFileOffset(startLsn); endOffset = startOffset; }
/** * Ensure that the next target is in the window. The default behavior is that the next target is * the next previous entry. * * @throws DatabaseException */ protected void setBackwardPosition() throws ChecksumException, FileNotFoundException, EOFException, DatabaseException { /* * currentEntryPrevOffset is the entry before the current entry. * currentEntryOffset is the entry we just read (or the end of the * file if we're starting out. */ if ((currentEntryPrevOffset != 0) && window.containsOffset(currentEntryPrevOffset)) { /* The next log entry has passed the start LSN. */ long nextLsn = DbLsn.makeLsn(window.currentFileNum(), currentEntryPrevOffset); if (finishLsn != DbLsn.NULL_LSN) { if (DbLsn.compareTo(nextLsn, finishLsn) == -1) { throw new EOFException( "finish=" + DbLsn.getNoFormatString(finishLsn) + "next=" + DbLsn.getNoFormatString(nextLsn)); } } /* This log entry starts in this buffer, just reposition. */ window.positionBuffer(currentEntryPrevOffset); } else { /* * The start of the log entry is not in this read buffer so * we must fill the buffer again. * * 1) The target log entry is in a different file from the * current window's file. Move the window to the previous * file and start the read from the target LSN. * * 2) The target log entry is the same file but the log entry * is larger than the read chunk size. Start the next read * buffer from the target LSN. It's going to take multiple * reads to get the log entry, and we might as well get as * much as possible. * * 3) In the same file, and the log entry fits within one * read buffer. Try to position the next buffer chunk so the * target entry is held within the buffer, all the way at the * end. That way, since we're reading backwards, there will be * more buffered data available for following reads. */ long nextFile; long nextWindowStart; long nextTarget; if (currentEntryPrevOffset == 0) { /* Case 1: Go to another file. */ currentEntryPrevOffset = fileManager.getFileHeaderPrevOffset(window.currentFileNum()); Long prevFileNum = fileManager.getFollowingFileNum(window.currentFileNum(), false); if (prevFileNum == null) { throw new EOFException("No file following " + window.currentFileNum()); } /* * Check finishLSN before proceeding, in case we should stop * the search before attempting to set the file reader to a * position in the previous file. In [#22407] we threw a * spurious EFE complaining that we cannot read backwards over * a cleaned file because the previous file had been cleaned * away. */ if (finishLsn != DbLsn.NULL_LSN && prevFileNum < DbLsn.getFileNumber(finishLsn)) { throw new EOFException( "finish=" + DbLsn.getNoFormatString(finishLsn) + " nextFile=0x" + Long.toHexString(prevFileNum)); } if (window.currentFileNum() - prevFileNum.longValue() != 1) { handleGapInBackwardsScan(prevFileNum); } nextFile = prevFileNum; nextWindowStart = currentEntryPrevOffset; nextTarget = currentEntryPrevOffset; } else if ((currentEntryOffset - currentEntryPrevOffset) > window.capacity()) { /* * Case 2: The entry is in the same file, but is bigger * than one buffer. Position it at the front of the buffer. */ nextFile = window.currentFileNum(); nextWindowStart = currentEntryPrevOffset; nextTarget = currentEntryPrevOffset; } else { /* * Case 3: In same file, but not in this buffer. The target * entry will fit in one buffer. */ nextFile = window.currentFileNum(); long newPosition = currentEntryOffset - window.capacity(); nextWindowStart = (newPosition < 0) ? 0 : newPosition; nextTarget = currentEntryPrevOffset; } /* The next log entry has passed the start LSN. */ long nextLsn = DbLsn.makeLsn(nextFile, currentEntryPrevOffset); if (finishLsn != DbLsn.NULL_LSN) { if (DbLsn.compareTo(nextLsn, finishLsn) == -1) { throw new EOFException( "finish=" + DbLsn.getNoFormatString(finishLsn) + " next=" + DbLsn.getNoFormatString(nextLsn)); } } window.slideAndFill(nextFile, nextWindowStart, nextTarget, forward); } /* The current entry will start at this offset. */ currentEntryOffset = currentEntryPrevOffset; }