use of org.apache.derby.iapi.store.raw.xact.TransactionId in project derby by apache.
the class Scan method getNextRecordBackward.
/**
* 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);
// the number of bytes actually read
int readAmount;
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) {
// 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);
}
}
// no more log record
return null;
}
// 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();
// happens to avoid any recovery issues.
if (curpos == LogToFile.LOG_FILE_HEADER_SIZE)
continue;
}
scan.seek(curpos - 4);
// get the length after the log record
int recordLength = scan.readInt();
// 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, checkLength, recordLength, inst, 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 + ")");
}
// nothing more can be read. Else check scan limit
if (currentInstant < stopAt && stopAt != LogCounter.INVALID_LOG_INSTANT) {
currentInstant = LogCounter.INVALID_LOG_INSTANT;
// we went past the stopAt
return null;
}
byte[] data = input.getData();
if (data.length < recordLength) {
// make a new array of sufficient size and reset the arrary
// in the input stream
data = new byte[recordLength];
input.setData(data);
}
// and decrypting the record.
if (logFactory.databaseEncrypted()) {
scan.readFully(data, 0, recordLength);
int len = logFactory.decrypt(data, 0, recordLength, data, 0);
if (SanityManager.DEBUG)
SanityManager.ASSERT(len == recordLength);
input.setLimit(0, recordLength);
} else // no need to decrypt, only get the group and tid if we filter
{
if (groupmask == 0 && tranId == null) {
// no filter, get the whole thing
scan.readFully(data, 0, recordLength);
input.setLimit(0, recordLength);
} else {
// Read only enough so that group and the tran id is in
// the data buffer. Group is stored as compressed int
// and tran id is stored as who knows what. read min
// of peekAmount or recordLength
readAmount = (recordLength > peekAmount) ? peekAmount : recordLength;
// in the data buffer, we now have enough to peek
scan.readFully(data, 0, readAmount);
input.setLimit(0, readAmount);
}
}
lr = (LogRecord) input.readObject();
// during backward scans. They are used only in forwardscan during recovery.
if (lr.isChecksum()) {
candidate = false;
} else if (groupmask != 0 || tranId != null) {
// skip the checksum log records
if (lr.isChecksum())
candidate = false;
if (candidate && groupmask != 0 && (groupmask & lr.group()) == 0)
// no match, throw this log record out
candidate = false;
if (candidate && tranId != null) {
TransactionId tid = lr.getTransactionId();
if (// nomatch
!tid.equals(tranId))
// throw this log record out
candidate = false;
}
// decryption.
if (candidate && !logFactory.databaseEncrypted()) {
// read the rest of the log into the buffer
if (SanityManager.DEBUG)
SanityManager.ASSERT(readAmount > 0);
if (readAmount < recordLength) {
// Need to remember where we are because the log
// record may have read part of it off the input
// stream already and that position is lost when we
// set limit again.
int inputPosition = input.getPosition();
scan.readFully(data, readAmount, recordLength - readAmount);
input.setLimit(0, recordLength);
input.setPosition(inputPosition);
}
}
}
// go back to the start of the log record so that the next time
// this method is called, it is positioned right past the last byte
// of the record.
curpos = recordStartPosition;
scan.seek(curpos);
} while (candidate == false);
return lr;
}
use of org.apache.derby.iapi.store.raw.xact.TransactionId in project derby by apache.
the class FileLogger method logAndUndo.
/**
* Writes out a compensation log record to the log stream, and call its
* doMe method to undo the change of a previous log operation.
*
* <P>MT - Not needed. A transaction must be single threaded thru undo, each
* RawTransaction has its own logger, therefore no need to synchronize.
* The RawTransaction must handle synchronizing with multiple threads
* during rollback.
*
* @param xact the transaction logging the change
* @param compensation the compensation log operation
* @param undoInstant the log instant of the operation that is to be
* rolled back
* @param in optional data input for the compenastion doMe method
*
* @return the instant in the log that can be used to identify the log
* record
*
* @exception StandardException Derby Standard error policy
*/
public LogInstant logAndUndo(RawTransaction xact, Compensation compensation, LogInstant undoInstant, LimitObjectInput in) throws StandardException {
boolean inUserCode = false;
try {
logOutputBuffer.reset();
TransactionId transactionId = xact.getId();
// write out the log header with the operation embedded
logRecord.setValue(transactionId, compensation);
inUserCode = true;
logicalOut.writeObject(logRecord);
inUserCode = false;
// write out the undoInstant
logicalOut.writeLong(((LogCounter) undoInstant).getValueAsLong());
// in this implemetaion, there is no optional data for the
// compensation operation. Optional data for the rollback comes
// from the undoable operation - and is passed into this call.
int completeLength = logOutputBuffer.getPosition();
long instant = 0;
if (logFactory.databaseEncrypted()) {
// we must pad the encryption data to be multiple of block
// size, which is logFactory.getEncryptionBlockSize()
int encryptedLength = completeLength;
if ((encryptedLength % logFactory.getEncryptionBlockSize()) != 0)
encryptedLength = encryptedLength + logFactory.getEncryptionBlockSize() - (encryptedLength % logFactory.getEncryptionBlockSize());
if (encryptionBuffer == null || encryptionBuffer.length < encryptedLength)
encryptionBuffer = new byte[encryptedLength];
System.arraycopy(logOutputBuffer.getByteArray(), 0, encryptionBuffer, 0, completeLength);
// do not bother to clear out the padding area
int len = logFactory.encrypt(encryptionBuffer, 0, encryptedLength, encryptionBuffer, 0);
if (SanityManager.DEBUG)
SanityManager.ASSERT(len == encryptedLength, "encrypted log buffer length != log buffer len");
instant = logFactory.appendLogRecord(encryptionBuffer, 0, encryptedLength, null, 0, 0);
} else {
instant = logFactory.appendLogRecord(logOutputBuffer.getByteArray(), 0, completeLength, null, 0, 0);
}
LogInstant logInstant = new LogCounter(instant);
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG)) {
SanityManager.DEBUG(LogToFile.DBG_FLAG, "Write CLR: Xact: " + transactionId.toString() + "clrinstant: " + logInstant.toString() + " undoinstant " + undoInstant + "\n");
}
}
try {
// in and dataLength contains optional data that was written
// to the log during a previous call to logAndDo.
compensation.doMe(xact, logInstant, in);
} catch (StandardException se) {
throw logFactory.markCorrupt(StandardException.newException(SQLState.LOG_DO_ME_FAIL, se, compensation));
} catch (IOException ioe) {
throw logFactory.markCorrupt(StandardException.newException(SQLState.LOG_DO_ME_FAIL, ioe, compensation));
}
return logInstant;
} catch (IOException ioe) {
if (inUserCode) {
throw StandardException.newException(SQLState.LOG_WRITE_LOG_RECORD, ioe, compensation);
} else {
throw StandardException.newException(SQLState.LOG_BUFFER_FULL, ioe, compensation);
}
}
}
use of org.apache.derby.iapi.store.raw.xact.TransactionId in project derby by apache.
the class TransactionTable method getMostRecentPreparedRecoveredXact.
/**
* Get the most recently added transaction that says it is prepared during
* recovery the transaction table and make the passed in transaction
* assume its identity. This routine turns off the isRecovery() state
* <B> Should only be used in recovery handle prepare after undo !! </B>
*
* <P>MT - unsafe, caller is recovery, which is single threaded.
*/
/**
* Get the most recent recovered prepared transaction.
* <p>
* Get the most recently added transaction that says it is prepared during
* recovery the transaction table and make the passed in transaction
* assume its identity.
* <p>
* This routine, unlike the redo and rollback getMostRecent*() routines
* expects a brand new transaction to be passed in. If a candidate
* transaction is found, then upon return the transaction table will
* be altered such that the old entry no longer exists, and a new entry
* will exist pointing to the transaction passed in. The new entry will
* look the same as if the prepared transaction had been created during
* runtime rather than recovery.
*
* <B> Should only be used in recovery handle prepare after undo !! </B>
*
* <P>MT - unsafe, caller is recovery, which is single threaded.
*
* @return true if a candidate transaction has been found. false if no
* prepared/recovery transactions found in the table.
*
* @param tran Newly allocated transaction to add to link to a entry.
*/
public boolean getMostRecentPreparedRecoveredXact(RawTransaction tran) {
TransactionTableEntry found_ent = null;
if (!trans.isEmpty()) {
TransactionId id = null;
GlobalTransactionId gid = null;
for (TransactionTableEntry ent : trans.values()) {
if (ent != null && ent.isRecovery() && ent.isPrepared()) {
// try to locate the most recent one
if (id == null || XactId.compare(id, ent.getXid()) < 0) {
found_ent = ent;
id = ent.getXid();
gid = ent.getGid();
}
}
}
if (SanityManager.DEBUG) {
if (found_ent == null) {
// be non-recover, prepared global transactions.
for (TransactionTableEntry ent : trans.values()) {
if (XactId.compare(ent.getXid(), tran.getId()) != 0) {
SanityManager.ASSERT(!ent.isRecovery() && ent.isPrepared());
SanityManager.ASSERT(ent.getGid() != null);
}
}
}
}
if (found_ent != null) {
// At this point there are 2 tt entries of interest:
// new_ent - the read only transaction entry that was
// created when we allocated a new transaction.
// We will just throw this one away after
// assuming the identity of the global xact.
// found_ent
// - the entry of the transaction that we are going
// to take over.
TransactionTableEntry new_ent = trans.remove(tran.getId());
// At this point only the found_ent should be in the table.
if (SanityManager.DEBUG) {
SanityManager.ASSERT(findTransactionEntry(id) == found_ent);
}
((Xact) tran).assumeGlobalXactIdentity(found_ent);
// transform this recovery entry, into a runtime entry.
found_ent.unsetRecoveryStatus();
}
}
return (found_ent != null);
}
use of org.apache.derby.iapi.store.raw.xact.TransactionId in project derby by apache.
the class TransactionTable method add.
void add(Xact xact, boolean exclude) {
TransactionId id = xact.getId();
TransactionTableEntry newEntry = new TransactionTableEntry(xact, id, 0, exclude ? TransactionTableEntry.EXCLUDE : 0);
synchronized (this) {
Object oldEntry = trans.put(id, newEntry);
if (SanityManager.DEBUG) {
SanityManager.ASSERT(oldEntry == null, "Trying to add a transaction that's already " + "in the transaction table");
if (SanityManager.DEBUG_ON("TranTrace")) {
SanityManager.DEBUG("TranTrace", "adding transaction " + id);
SanityManager.showTrace(new Throwable("TranTrace"));
}
}
}
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON("memoryLeakTrace")) {
if (trans.size() > 50)
System.out.println("memoryLeakTrace:TransactionTable " + trans.size());
}
}
}
use of org.apache.derby.iapi.store.raw.xact.TransactionId in project derby by apache.
the class TransactionTableEntry method getTransactionIdString.
/**
* Methods of TransactionInfo
*/
public String getTransactionIdString() {
if (SanityManager.DEBUG) {
SanityManager.ASSERT(!recovery, "trying to display recovery transaction");
SanityManager.ASSERT(myxact != null, "my xact is null");
SanityManager.ASSERT(isClone, "Should only call method on a clone");
}
TransactionId t = myxact.getIdNoCheck();
return (t == null) ? "CLOSED" : t.toString();
}
Aggregations