/** * readNextEntry will stop at a bad entry. * * @return true if an element has been read. */ public boolean readNextEntry() throws DatabaseException, IOException { boolean foundEntry = false; try { /* * At this point, * currentEntryOffset is the entry we just read. * nextEntryOffset is the entry we're about to read. * currentEntryPrevOffset is 2 entries ago. * Note that readNextEntry() moves all the offset pointers up. */ foundEntry = super.readNextEntry(); /* * Note that initStartingPosition() makes sure that the file header * entry is valid. So by the time we get to this method, we know * we're at a file with a valid file header entry. */ lastValidOffset = currentEntryOffset; nextUnprovenOffset = nextEntryOffset; } catch (DbChecksumException e) { Tracer.trace( Level.INFO, envImpl, "Found checksum exception while searching " + " for end of log. Last valid entry is at " + DbLsn.toString(DbLsn.makeLsn(readBufferFileNum, lastValidOffset)) + " Bad entry is at " + DbLsn.makeLsn(readBufferFileNum, nextUnprovenOffset)); } return foundEntry; }
/** * 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); }
/* * 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; }
/** * Ensure that the next target is in the window. The default behavior is that the next target is * the next, following entry, so we can assume that it's in the window. All we have to do is to * check if we've gone past the specified end point. * * @throws DatabaseException * @throws FileNotFoundException * @throws ChecksumException */ protected void setForwardPosition() throws EOFException, DatabaseException, ChecksumException, FileNotFoundException { if (finishLsn != DbLsn.NULL_LSN) { /* The next log entry has passed the end LSN. */ long nextLsn = DbLsn.makeLsn(window.currentFileNum(), nextEntryOffset); if (DbLsn.compareTo(nextLsn, finishLsn) >= 0) { throw new EOFException(); } } }
/** Read in an IN entry. */ public void readEntry(EnvironmentImpl envImpl, LogEntryHeader header, ByteBuffer entryBuffer) { int logVersion = header.getVersion(); boolean version6OrLater = (logVersion >= 6); if (version6OrLater) { dbId = new DatabaseId(); dbId.readFromLog(entryBuffer, logVersion); prevFullLsn = LogUtils.readLong(entryBuffer, false /*unpacked*/); if (logVersion >= 8) { prevDeltaLsn = LogUtils.readPackedLong(entryBuffer); } } /* Read IN. */ in = newInstanceOfType(); in.readFromLog(entryBuffer, logVersion); if (!version6OrLater) { dbId = new DatabaseId(); dbId.readFromLog(entryBuffer, logVersion); } if (logVersion < 1) { prevFullLsn = DbLsn.NULL_LSN; } else if (logVersion == 1) { long fileNum = LogUtils.readUnsignedInt(entryBuffer); if (fileNum == 0xffffffffL) { prevFullLsn = DbLsn.NULL_LSN; } else { prevFullLsn = DbLsn.makeLsn(fileNum, 0); } } else if (!version6OrLater) { prevFullLsn = LogUtils.readLong(entryBuffer, true /*unpacked*/); } }
/* Position the readBuffer to the targetOffset. */ public void positionBuffer(long targetOffset) { assert containsOffset(targetOffset) : this + " doesn't contain " + DbLsn.getNoFormatString(targetOffset); threadSafeBufferPosition(readBuffer, (int) (targetOffset - startOffset)); }
/** * Rollback all write operations that are logged with an LSN > the matchpointLsn parameter. This * is logically a truncation of the log entries written by this transaction. Any log entries * created by this transaction are marked obsolete. * * <p>Note that this is by no means a complete implementation of what would be needed to support * user visible savepoints. This method only rolls back write operations and doesn't handle other * types of state, like read locks and open cursors. * * <p>There are several key assumptions: - the transaction does not hold read locks. - the * transaction will either be resumed, and any rolled back operations will be repeated, or the * transaction will be aborted in its entirety. * * <p>If all operations in the transaction are rolled back, this transaction is also unregistered * and closed. * * <p>Rolling back a log entry through rollback is akin to truncating the transactional log. The * on-disk entries should not be referred to by anything in the in-memory tree or the transaction * chain. JE's append only storage and the fact that the transactional log entries are intertwined * through the physical log prohibits any log truncation. To mimic log truncation, any rolled back * log entry is marked as obsolete. Since only the last version of any data record is alive, any * future uses of this transaction must use the obsoleteDupsAllowed option (see * Txn.countObsoleteExact) to prevent asserts about duplicate obsolete offsets. For example, * suppose the transaction logs this: * * <p>100 LNa (version1) 200 LNa (version2) 300 LNa (version3) * * <p>At this point in time, LSN 100 and 200 are obsolete. * * <p>Now, suppose we roll back to LSN 100. LSNs 200 and 300 are marked obsolete by the * rollback.(although LSN 200 was already obsolete). It is true that for an instance in time LSN * 100 is incorrectly marked as obsolete, when it's really alive. But this transaction is going to * either abort or resume exactly as it was before, so LSN 100 is going to be obsolete again. * * <p>Suppose txn.abort() is called. The abort() logic will mark LSN 100 as obsolete, since it is * the latest version of the record in the transaction. Using the obsoleteDupsAllowed option * avoids an assertion on the double recording of LSN 100. * * <p>Alternatively, suppose LNa (version2) is retransmitted and logged as LSN 400. Normal * execution of LN.log() marks LSN 100 as obsolete, which would trigger the assertion were it not * for obsoleteDupsAllowed. * * @return list of LSNs that were rolled back */ public Collection<Long> rollback(long matchpointLsn) throws DatabaseException { List<Long> rollbackLsns = new ArrayList<Long>(); LoggerUtils.finest(logger, envImpl, "Partial Rollback of " + this); synchronized (this) { checkState(true); /* This transaction didn't log anything, nothing to rollback. */ if (lastLoggedLsn == NULL_LSN) { return rollbackLsns; } /* * This transaction doesn't include any operations that are after * the matchpointLsn. There is nothing to rollback. */ if (DbLsn.compareTo(lastLoggedLsn, matchpointLsn) <= 0) { return rollbackLsns; } setRollback(); undoWrites(matchpointLsn, rollbackLsns); } /* * The call to undoWrites() may have rolled everything back, and set * lastLoggedLsn to NULL_LSN. */ if (lastLoggedLsn == NULL_LSN) { /* Everything was rolled back. */ try { /* * Purge any databaseImpls not needed as a result of the abort. * Be sure to do this outside the synchronization block, to * avoid conflict w/checkpointer. */ cleanupDatabaseImpls(false); } finally { close(false /* isCommit */); } } /* * We don't expect there to be any database handles associated with * a ReplayTxn, because only DatabaseImpls are used. Because of that, * there should be no cleanup needed. */ if (handleToHandleLockMap != null) { throw EnvironmentFailureException.unexpectedState( "Replay Txn " + getId() + " has a handleToHandleLockMap"); } /* * There is no need to call cleanupDatabaseImpls if the txn still holds * locks. The operations in this txn will either be entirely aborted, * or will be repeated, so any cleanup will happen when the txn ends. */ return rollbackLsns; }
/** Print out the contents of an entry. */ @Override public StringBuilder dumpEntry(StringBuilder sb, boolean verbose) { in.dumpLog(sb, verbose); dbId.dumpLog(sb, verbose); if (prevFullLsn != DbLsn.NULL_LSN) { sb.append("<prevFullLsn>"); sb.append(DbLsn.getNoFormatString(prevFullLsn)); sb.append("</prevFullLsn>"); } if (prevDeltaLsn != DbLsn.NULL_LSN) { sb.append("<prevDeltaLsn>"); sb.append(DbLsn.getNoFormatString(prevDeltaLsn)); sb.append("</prevDeltaLsn>"); } return sb; }
/** 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); } }
/** @return The last LSN seen in the log for this kind of entry, or null. */ public long getLastSeen(LogEntryType type) { Long typeNumber = (Long) lastOffsetSeen.get(type); if (typeNumber != null) { return DbLsn.makeLsn(readBufferFileNum, typeNumber.longValue()); } else { return DbLsn.NULL_LSN; } }
/* Wrap the call to logger to reduce runtime overhead. */ private void logFinest(long lsn, UndoReader undo, RevertInfo revertTo) { if ((logger != null) && (logger.isLoggable(Level.FINEST))) { LoggerUtils.finest( logger, envImpl, "undoLsn=" + DbLsn.getNoFormatString(lsn) + " undo=" + undo + " revertInfo=" + revertTo); } }
/** @return The last LSN seen in the log for this kind of entry, or null. */ public long getLastSeen(LogEntryType type) { Long typeNumber = lastOffsetSeen.get(type); if (typeNumber != null) { return DbLsn.makeLsn(window.currentFileNum(), typeNumber.longValue()); } else { return DbLsn.NULL_LSN; } }
/** Test initializing the last position in the logs. */ public void testSetLastPosition() throws DatabaseException { /* * Pretend that the last file is file 79. */ fileManager.setLastPosition( // next available LSN DbLsn.makeLsn(79L, 88L), DbLsn.makeLsn(79L, 77), 66L); /* Put an entry down, should fit within file 79. */ fileManager.bumpLsn(11L); assertEquals(DbLsn.makeLsn(79L, 88L), fileManager.getLastUsedLsn()); assertEquals(77L, fileManager.getPrevEntryOffset()); /* Put another entry in, should go to the next file. */ fileManager.bumpLsn(22L); assertEquals( DbLsn.makeLsn(80L, FileManager.firstLogEntryOffset()), fileManager.getLastUsedLsn()); assertEquals(0, fileManager.getPrevEntryOffset()); }
/** Force a rewrite of the map tree root if required. */ public void rewriteMapTreeRoot(long cleanerTargetLsn) throws DatabaseException { if (DbLsn.compareTo(cleanerTargetLsn, mapTreeRootLsn) == 0) { /* * The root entry targetted for cleaning is in use. Write a new * copy. */ mapTreeRootLsn = logManager.log(dbMapTree); } }
/* * Reposition to the specified file, and fill starting at * startOffset. Position the window's buffer to point at the log entry * indicated by targetOffset */ public void slideAndFill( long windowfileNum, long windowStartOffset, long targetOffset, boolean forward) throws ChecksumException, FileNotFoundException, DatabaseException { FileHandle fileHandle = fileManager.getFileHandle(windowfileNum); try { startOffset = windowStartOffset; setFileNum(windowfileNum, fileHandle.getLogVersion()); boolean foundData = fillFromFile(fileHandle, targetOffset); /* * When reading backwards, we need to guarantee there is no log * gap, throws out an EnvironmentFailreException if it exists. */ if (!foundData && !forward) { throw EnvironmentFailureException.unexpectedState( "Detected a log file gap when reading backwards. " + "Target position = " + DbLsn.getNoFormatString(DbLsn.makeLsn(windowfileNum, targetOffset)) + " starting position = " + DbLsn.getNoFormatString(DbLsn.makeLsn(windowfileNum, windowStartOffset)) + " end position = " + DbLsn.getNoFormatString(DbLsn.makeLsn(windowfileNum, endOffset))); } } finally { fileHandle.release(); } }
/** * readNextEntry will stop at a bad entry. * * @return true if an element has been read. */ @Override public boolean readNextEntry() { boolean foundEntry = false; try { /* * At this point, * currentEntryOffset is the entry we just read. * nextEntryOffset is the entry we're about to read. * currentEntryPrevOffset is 2 entries ago. * Note that readNextEntry() moves all the offset pointers up. */ foundEntry = super.readNextEntryAllowExceptions(); /* * Note that initStartingPosition() makes sure that the file header * entry is valid. So by the time we get to this method, we know * we're at a file with a valid file header entry. */ lastValidOffset = currentEntryOffset; nextUnprovenOffset = nextEntryOffset; } catch (FileNotFoundException e) { throw new EnvironmentFailureException( envImpl, EnvironmentFailureReason.LOG_FILE_NOT_FOUND, e); } catch (ChecksumException e) { LoggerUtils.fine( logger, envImpl, "Found checksum exception while searching for end of log. " + "Last valid entry is at " + DbLsn.toString(DbLsn.makeLsn(window.currentFileNum(), lastValidOffset)) + " Bad entry is at " + DbLsn.makeLsn(window.currentFileNum(), nextUnprovenOffset)); } return foundEntry; }
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++; }
/* * Read the next entry. If a checksum exception is encountered, attempt * to find the other side of the corrupted area and try to re-read this * file. */ @Override public boolean readNextEntry() { long saveCurrentEntryOffset = currentEntryOffset; try { return super.readNextEntryAllowExceptions(); } catch (FileNotFoundException e) { throw new EnvironmentFailureException( envImpl, EnvironmentFailureReason.LOG_FILE_NOT_FOUND, e); } catch (ChecksumException e) { resyncReader( DbLsn.makeLsn(window.currentFileNum(), saveCurrentEntryOffset), dumpCorruptedBounds); return super.readNextEntry(); } }
/** @see Loggable#dumpLog */ public void dumpLog(StringBuilder sb, boolean verbose) { sb.append(" matchpointVLSN=").append(matchpointVLSN.getSequence()); sb.append(" matchpointLSN="); sb.append(DbLsn.getNoFormatString(matchpointLSN)); /* Make sure the active txns are listed in order, partially for the sake * of the LoggableTest unit test, which expects the toString() for two * equivalent objects to always display the same, and partially for * ease of debugging. */ List<Long> displayTxnIds = new ArrayList<Long>(activeTxnIds); Collections.sort(displayTxnIds); sb.append(" activeTxnIds=").append(displayTxnIds); sb.append("\" time=\"").append(time); }
@Override public String toString() { StringBuilder sb = new StringBuilder(); long start = DbLsn.makeLsn(fileNum, startOffset); long end = DbLsn.makeLsn(fileNum, endOffset); sb.append("window covers "); sb.append(DbLsn.getNoFormatString(start)).append(" to "); sb.append(DbLsn.getNoFormatString(end)); sb.append(" positioned at "); long target = DbLsn.makeLsn(fileNum, startOffset + readBuffer.position()); sb.append(DbLsn.getNoFormatString(target)); return sb.toString(); }
/** * Converts the bin/index slot, whether a singleton LN or a DIN root. * * <p>Enter/leave with bin field latched, although bin field may change. * * <p>When a singleton LN is converted, leaves with bin/index fields unchanged. * * <p>When a dup tree is converted, leaves with bin/index fields set to last inserted slot. This * is the slot of the highest key in the dup tree. */ private void convertBinSlot() { if (DEBUG) { System.out.println( "DupConvert BIN LSN " + DbLsn.getNoFormatString(bin.getLsn(index)) + " index " + index + " nEntries " + bin.getNEntries()); } /* Delete slot if LN is deleted. */ final boolean isDeleted; if (isLNDeleted(bin, index)) { deleteSlot(); return; } final Node node = bin.fetchLNOrDIN(index, CacheMode.DEFAULT); if (!node.containsDuplicates()) { if (DEBUG) { System.out.println("DupConvert BIN LN " + Key.dumpString(bin.getKey(index), 0)); } /* Fetching a non-deleted LN updates the slot key; we're done. */ assert node instanceof LN; nConverted += 1; return; } /* * Delete the slot containing the DIN before re-inserting the dup tree, * so that the DIN slot key doesn't interfere with insertions. * * The DIN is evicted and memory usage is decremented. This is not * exactly correct because we keep a local reference to the DIN until * the dup tree is converted, but we tolerate this temporary * inaccuracy. */ final byte[] binKey = bin.getKey(index); final DIN din = (DIN) node; deleteSlot(); convertDin(din, binKey); }
/** * Transfer a lock from another transaction to this one. Used for master-> replica transitions, * when a node has to transform a MasterTxn into a ReplayTxn. Another approach would be to have * this importunate ReplayTxn call lock() on the lsn, but that path is not available because we do * not have a handle on a databaseImpl. */ public void stealLockFromMasterTxn(Long lsn) { LockAttemptResult result = lockManager.stealLock(lsn, this, LockType.WRITE); /* * Assert, and if something strange happened, opt to invalidate * the environment and wipe the slate clean. */ if (!result.success) { throw EnvironmentFailureException.unexpectedState( envImpl, "Transferring from master to replica state, txn " + getId() + " was unable to transfer lock for " + DbLsn.getNoFormatString(lsn) + ", lock grant type=" + result.lockGrant); } addLock(Long.valueOf(lsn), LockType.WRITE, result.lockGrant); addLogInfo(lsn); }
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(); }
@Override public String toString() { return "lsn=" + DbLsn.getNoFormatString(lsn) + " node=" + nodeId; }
/* * 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; }
/** @return The LSN to be used for the next log entry. */ public long getEndOfLog() { return DbLsn.makeLsn(readBufferFileNum, nextUnprovenOffset); }
public long getLastValidLsn() { return DbLsn.makeLsn(window.currentFileNum(), lastValidOffset); }
/** @return The LSN to be used for the next log entry. */ public long getEndOfLog() { return DbLsn.makeLsn(window.currentFileNum(), nextUnprovenOffset); }
/** Initialize starting position to the last file with a complete header with a valid checksum. */ private void startAtLastGoodFile(Long singleFileNum) throws ChecksumException { eof = false; window.initAtFileStart(DbLsn.makeLsn(0, 0)); /* * Start at what seems like the last file. If it doesn't exist, we're * done. */ Long lastNum = ((singleFileNum != null) && (singleFileNum.longValue() >= 0)) ? singleFileNum : fileManager.getLastFileNum(); FileHandle fileHandle = null; long fileLen = 0; while ((fileHandle == null) && !eof) { if (lastNum == null) { eof = true; } else { try { try { window.initAtFileStart(DbLsn.makeLsn(lastNum, 0)); fileHandle = fileManager.getFileHandle(lastNum); /* * Check the size of this file. If it opened * successfully but only held a header or is 0 length, * backup to the next "last" file unless this is the * only file in the log. Note that an incomplete header * will end up throwing a checksum exception, but a 0 * length file will open successfully in read only * mode. */ fileLen = fileHandle.getFile().length(); if (fileLen <= FileManager.firstLogEntryOffset()) { lastNum = fileManager.getFollowingFileNum(lastNum, false); if (lastNum != null) { fileHandle.release(); fileHandle = null; } } } catch (DatabaseException e) { lastNum = attemptToMoveBadFile(e); fileHandle = null; } catch (ChecksumException e) { lastNum = attemptToMoveBadFile(e); fileHandle = null; } finally { if (fileHandle != null) { fileHandle.release(); } } } catch (IOException e) { throw new EnvironmentFailureException(envImpl, EnvironmentFailureReason.LOG_READ, e); } } } nextEntryOffset = 0; }
public long getLastValidLsn() { return DbLsn.makeLsn(readBufferFileNum, lastValidOffset); }