LogHandlepublic class LogHandle extends Object A class containing attributes of an open log file. |
Fields Summary |
---|
static final int | BUFFERBuffer the data and return (minimal overhead); | static final int | FORCEFlush and force the data to permanent storage before returning (high
overhead) - ie to physically write the record. | static final int | TAIL_NOT_INCLUSIVEDon't include tail LSN | static final int | TAIL_INCLUSIVEInclude tail LSN | static final int | START_CKPTStart-of-checkpoint record - internally generated by &damjo. | static final int | INDV_CKPTCheckpoint record from an individual registered module | static final int | END_CKPTEnd-of-checkpoint record - internally generated by &damjo | static final int | NEW_JRNLRecord of a newly opened journal | static final int | RECORD_TYPE_MAXUpper limit for user specified record type value. | static final int | MARKERRecord type written to local system logs to indicate the position
corresponding to the start of the last successful checkpoint. | static final int | LINKThe record type written to at the end of an extent to signify that the
log record is a link record (a dummy log record). | static final int | MAX_NUMBER_EXTENTSThe maximum number of extents which can be created for a log file. | static final int | CONTROL_FORCE_INTERVALNumber of log write operations which will be performed before forcing
the control data to permanent storage | static final int | MAX_EXTENT_SIZEThis determines the size of the largest log record which can be written. | static final int | CUSHION_SIZEThis is the size of the cushion file used to find if the log is
short on space. | static final int | NAME_LENGTHThe length of the name assigned to a logfile. This is restricted to
8 to support the FAT file system. | static final int | MAX_NAMESThe maximum number of names available to be assigned for logfiles.
The name is made up of LOG_FILENAME_PREFIX which is 5 characters
followed by a 3 digit hex extension. | static final int | FILENAME_PREFIX_LENThe length of the fixed filename prefix used when allocating new
log file names. | static final int | EXTENT_TABLE_SIZEThe number of entries in each log file descriptor. It is used for
performance reason, so we can get to the extent descriptor quickly. | static final int | CALLBACK_REASON_SOSThis is the reason why we are calling the calling back function. | static final int | RESTART_OFFSET_1The offset in the control file for the first restart data record. | static final int | RESTART_OFFSET_2The offset in the control file for the second restart data record. | static final int | MAX_RECORD_SIZEThis is the maximum size of a log record. | static final int | MAX_RESTART_SIZEThe maximum size of a restart record | static final int | CONTROL_FILE_SIZEThe size of a control file which is allocated at open time. | static final int | ALLOCATE_SIZEThe size of a chunk to allocate from the disk space | LogHandle | blockValid | int | restartDataLength | int | recordsWritten | int | chunkRemaining | int | activeRestartVersion | LogUpcallTarget | upcallTarget | ArrayList | cursors | boolean | cushionExists | boolean | upcallInProgress | Hashtable | extentTable | String | logFileName | LogFileHandle | logFileHandle | LogControlDescriptor | logControlDescriptor | LogControl | logControl |
Constructors Summary |
---|
LogHandle(LogControl control, String logName, LogFileHandle controlFH, LogUpcallTarget upcall)Creates a LogHandle object for the given log instance.
// Initialise instance members.
logFileName = new String(logName);
upcallTarget = upcall;
logControl = control;
logFileHandle = controlFH;
blockValid = this;
logControlDescriptor = new LogControlDescriptor();
cursors = new ArrayList();
extentTable = new Hashtable(EXTENT_TABLE_SIZE);
|
Methods Summary |
---|
static final int | alternateRestart(int restart)Returns the alternate restart number.
return (restart == 1) ? 2 : 1;
| synchronized void | checkLSN(LogLSN chkLSN)Ensures that the log is written up to the given point.
// Check BlockValid field in Log_FileDescriptor block pointed to
// by logHandle parameter, and ensure it is valid
// IF not valid Log_FileDescriptor
// Return LOG_INVALID_FILE_DESCRIPTOR
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
// IF not LogInitialised
// Return LOG_NOT_INITIALISED
if( !logControl.logInitialised )
throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
// IF ReadOnly log
// Return LOG_READ_ONLY_ACCESS
if( logControl.logReadOnly )
throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);
// IF the lsn parameter is equal to LOG_HEAD_LSN
// Copy head LSN from Log_FileDescriptor into lsn
// ELSE IF the lsn parameter is equal to LOG_TAIL_LSN
// Copy tail LSN from Log_FileDescriptor into lsn
// ELSE Copy lsn parameter into lsn
LogLSN lsn;
if( chkLSN.equals(LogLSN.HEAD_LSN) )
lsn = new LogLSN(logControlDescriptor.headLSN);
else if( chkLSN.equals(LogLSN.TAIL_LSN) )
lsn = new LogLSN(logControlDescriptor.tailLSN);
else
lsn = new LogLSN(chkLSN);
// IF lsn value is less than log tail LSN
// Return LOG_SUCCESS
if( lsn.lessThan(logControlDescriptor.tailLSN) ) {
return;
}
// IF log file is empty (log head = LOG_NULL_LSN)
// Unlock the Log_FileDescriptor latch
// Return LOG_SUCCESS
if( logControlDescriptor.headLSN.isNULL() ) {
return;
}
// IF lsn value is greater than log head LSN
// Copy head LSN from Log_FileDescriptor into lsn parameter
if( lsn.greaterThan(logControlDescriptor.headLSN) )
lsn.copy(logControlDescriptor.headLSN);
// Determine the extent which contains the record to be forced (this is
// derived from the 'extent' part of the lsn parameter) - remember this
// as LAST_EXTENT
int lastExtent = lsn.extent;
// Determine the extent which contains the log tail LSN, remember this
// as FIRST_EXTENT
int firstExtent = logControlDescriptor.tailLSN.extent;
// Now force each of the extent files (FIRST_EXTENT to LAST_EXTENT
// inclusive)
for( int extent = firstExtent; extent <= lastExtent; extent++ ) {
// IF extent is currently open (Log_ExtentDescriptor block exists)
// IF the Written flag in the Log_ExtentDescriptor is TRUE
// Issue FSYNC for extent file
// IF not successful allow the error to pass to the caller.
// ELSE
// Set 'extent written' flag to FALSE
LogExtent logEDP = (LogExtent)extentTable.get(new Integer(extent));
if( logEDP != null &&
logEDP.writtenSinceLastForce ) {
logEDP.fileHandle.fileSync();
logEDP.writtenSinceLastForce = false;
}
}
// IF 'extent' part of head LSN is same as LAST_EXTENT
// Force the Log_ControlDescriptor structure to the control file
// by issuing WRITE (implied sync)
// IF not successful allow the error to pass to the caller.
// ELSE
// Don't force control data, since we do not want control data to
// be 'ahead' of extent data
// The following block of code will no longer be executed as a result
// of a performance suggestion. This reduces the number of occasions
// when the control data is forced to disk
/*
if( logControlDescriptor.headLSN.extent == lastExtent )
writeControlFile();
*/
| static void | checkRestart(LogFileHandle fileHandle, int restartNumber, int[] restartInfo)Checks restart record information.
This internal method does not need to be synchronized.
// Initialise callers output parameters
restartInfo[0] = 0; // length
restartInfo[1] = 0; // time stamp
// Calculate the offsets within control file for both restart records
// Use LSEEK to move to the first record and issue a READ for its
// Log_RestartDescriptor block
byte[] restartBytes = new byte[LogRestartDescriptor.SIZEOF];
int offset = restartPosition(restartNumber);
fileHandle.fileSeek(offset,LogFileHandle.SEEK_ABSOLUTE);
int bytesRead = fileHandle.fileRead(restartBytes);
LogRestartDescriptor logRD = new LogRestartDescriptor(restartBytes,0);
// IF the READ is successful and it return the restart data
if( bytesRead > 0 ) {
// Check that the RestartValid value in the
// Log_RestartDescriptor block matches the record offset
// IF it matches
// Use LSEEK to move to the end of the restart data and read
// in the matching Log_RestartDescriptor block
if( logRD.restartValid == restartPosition(restartNumber) ) {
fileHandle.fileSeek(logRD.restartDataLength,
LogFileHandle.SEEK_RELATIVE);
bytesRead = fileHandle.fileRead(restartBytes);
LogRestartDescriptor logRDEnd = new LogRestartDescriptor(restartBytes,0);
// Check that the two Log_RestartDescriptor blocks are the same
// IF they are identical
// Assume the first version of restart data is valid
// Remember the timestamp contained in the block
if( logRD.equals(logRDEnd) ) {
restartInfo[0] = logRD.restartDataLength;
restartInfo[1] = logRD.timeStamp;
}
else
throw new LogException(null,LogException.LOG_CORRUPTED,1);
}
}
| void | cleanUpExtents()Removes all extent information from the log file.
This internal method does not need to be synchronized.
Enumeration extents = extentTable.elements();
while( extents.hasMoreElements() ) {
LogExtent logEDP = (LogExtent)extents.nextElement();
extentTable.remove(new Integer(logEDP.extentNumber));
logEDP.doFinalize();
}
extentTable = null;
| synchronized void | closeCursor(LogCursor cursor)Closes the cursor.
// Check BlockValid field in Log_CursorDescriptor block and
// ensure it is valid
// IF not valid Log_CursorDescriptor
// Return LOG_INVALID_CURSOR
if( cursor == null || cursor.blockValid != cursor )
throw new LogException(null,LogException.LOG_INVALID_CURSOR,1);
// Check BlockValid field in Log_FileDescriptor block pointed to
// by field in Log_CursorDescriptor block and ensure it is valid
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_CURSOR,2);
// IF not LogInitialised
// Return LOG_NOT_INITIALISED
if( !logControl.logInitialised )
throw new LogException(null,LogException.LOG_NOT_INITIALISED,3);
// Now we know the blocks are valid.
// Remove the Log_CursorDescriptor block from the chain hung off
// the Log_FileDescriptor (CursorDescriptorHead)
cursors.remove(cursor);
// Deallocate the Log_CursorDescriptor block
// cursor.finalize();
| synchronized void | closeFile(boolean deleteFile)Closes (and optionally deletes) the log file.
// Check BlockValid field in Log_FileDescriptor block and
// ensure it is valid
// IF not valid Log_FileDescriptor
// Return LOG_INVALID_FILE_DESCRIPTOR
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
// IF not LogInitialised
// Return LOG_NOT_INITIALISED
if( !logControl.logInitialised )
throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
// Set the block valid to NULL
blockValid = null;
// LOOP for each of the 16 elements in the log file's extent hash table
boolean forced = false;
Enumeration extents = extentTable.elements();
while( extents.hasMoreElements() ) {
LogExtent logEDP = (LogExtent)extents.nextElement();
// IF extent has been written since last force
// Issue FSYNC for the extent's file descriptor
// IF not successful
// Return LOG_WRITE_FAILURE
if( logEDP.writtenSinceLastForce ) {
logEDP.fileHandle.fileSync();
logEDP.writtenSinceLastForce = false;
forced = true;
}
// Issue a close for the extent file.
// Allow any error to pass to the caller.
logEDP.fileHandle.fileClose();
// If deletion of the logfile was requested, delete it.
//Start IASRI 4720539
if( deleteFile ){
//if( !logEDP.file.delete() )
final LogExtent tmplogEDP = logEDP;
Boolean isdeleted = (Boolean) java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run(){
return new Boolean(tmplogEDP.file.delete());
}
}
);
if(!isdeleted.booleanValue())
throw new LogException(null,LogException.LOG_CLOSE_FAILURE,6);
}
//End IASRI 4720539
// Address next block in chain
// Clear the signature in the Log_ExtentDescriptor block
// Deallocate the Log_ExtentDescriptor block
extentTable.remove(new Integer(logEDP.extentNumber));
logEDP.doFinalize();
}
// IF any log extents were forced (FSYNC'ed)
// WRITE the Log_ControlDescriptor block to the control file (with
// implied sync)
// IF not successful allow the error to pass to the caller.
// Return LOG_WRITE_FAILURE
if( forced && !logControl.logReadOnly )
writeControlFile();
// Issue CLOSE for the control file
// IF not successful allow the error to pass to the caller.
logFileHandle.fileClose();
// logFileHandle.finalize();
// If deletion of the logfile was requested, delete it's
// control File and the cushion file.
if( deleteFile ) {
// Delete the control file.
// Start IASRI 4720539
//if( !logControl.controlFile.delete() )
Boolean isdeleted = (Boolean) java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run(){
return new Boolean(logControl.controlFile.delete());
}
}
);
if( !isdeleted.booleanValue() )
throw new LogException(null,LogException.LOG_CLOSE_FAILURE,7);
// End IASRI 4720539
freeCushion();
// Finally remove the directory.
// Start IASRI 4720539
//LogControl.directory(logFileName,logControl.directoryPath).delete();
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run(){
LogControl.directory(logFileName,logControl.directoryPath).delete();
return null;
}
}
);
// End IASRI 4720539
}
// Unchain the Log_FileDescriptor block from the RCA chain
// the latch will be unset and terminated by Log_RemoveFileDescriptor
logControl.removeFile(this);
| void | dump()Dumps the state of the object.
LogExtent logEDP; // Extent file descriptor
LogCursor logCuDP; // ptr to cursor descriptor
// Check that the LogHandle passed points to a genuine File
// Descriptor block using the BlockValid field.
// IF the block is not genuine
// Return LOG_INVALID_FILE_DESCRIPTOR
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
// LOOP for each of the elements in the log file's extent hash table
Enumeration extents = extentTable.elements();
while( extents.hasMoreElements() ) {
logEDP = (LogExtent)extents.nextElement();
}
java.util.Iterator curs = cursors.iterator();
while( curs.hasNext() ) {
logCuDP = (LogCursor)curs.next();
}
| private void | freeCushion()Frees the cushion file.
This internal method does not need to be synchronized.
// If the cushion file exists, remove it.
if( cushionExists ) {
// Delete the cushion file.
// Start IASRI 4720539
//logControl.cushionFile.delete();
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run(){
logControl.cushionFile.delete();
return null;
}
}
);
// End IASRI 4720539
cushionExists = false;
}
| void | freeFileStorage(LogLSN tailLSN)Frees file storage for the file.
This internal method does not need to be synchronized.
// Using the extent containing the tail LSN, calculate the number of
// bytes up to but not including the log tail record
// Zero this space by issuing an FCLEAR request for the extent file
// IF not successful
// Unlock the Log_FileDescriptor mutex
// Return LOG_WRITE_FAILURE
int bytesToClear = tailLSN.offset;
if( bytesToClear == 0 ) {
return;
}
// Build LSN which will cause Log_PositionFilePointer to
// position the file pointer at the start of the extent file
LogLSN startOfExtent = new LogLSN(tailLSN.extent,0);
LogExtent logEDP = positionFilePointer(startOfExtent,0,LogExtent.ACCESSTYPE_UNKNOWN);
// Write the change to permanent storage via an FSYNC request
logEDP.fileHandle.fileSync();
| final java.lang.String | logFileName()Returns the log file name.
return logFileName;
| synchronized LogCursor | openCursor(LogLSN startLSN, LogLSN endLSN)Opens a cursor on the log file.
// Check BlockValid field in Log_FileDescriptor block and
// ensure it is valid
// IF not valid Log_FileDescriptor
// Return LOG_INVALID_FILE_DESCRIPTOR
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
// IF not LogInitialised
// Return LOG_NOT_INITIALISED
if( !logControl.logInitialised )
throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
// Allocate a Log_CursorDescriptor block
// IF allocate fails
// Return LOG_INSUFFICIENT_MEMORY
LogCursor cursor = new LogCursor(logControl,this,startLSN,endLSN);
if( cursor == null ) {
throw new LogException(null,LogException.LOG_INSUFFICIENT_MEMORY,4);
}
// Add the Log_CursorDescriptor block to the chain of similar blocks
// hung off the LogFileDescriptor (anchor is CursorDescriptorHead)
cursors.add(cursor);
return cursor;
| LogExtent | openExtent(int extent)Opens the given extent.
This internal method does not need to be synchronized.
// Build the extent file name from the CurrentLSN parameter
File extentFile = logControl.extentFile(logFileName,LogExtent.modExtent(extent));
// Issue an OPEN request for the file
// IF not successful (rc == -1 and not EOF)
// Return LOG_OPEN_FAILURE
int openOptions = LogFileHandle.OPEN_RDWR | LogFileHandle.OPEN_CREAT;
if( logControl.logReadOnly )
openOptions = LogFileHandle.OPEN_RDONLY;
LogFileHandle extentFH = new LogFileHandle(extentFile,openOptions);
// Allocate a Log_ExtentDescriptor block and initialise it
LogExtent logEDP = new LogExtent(extent,extentFH,extentFile);
if( logEDP == null ) {
extentFH.finalize();
throw new LogException(null,LogException.LOG_INSUFFICIENT_MEMORY,2);
}
// Use the already hashed extent number to find the position in the
// hash table and add it to the chain
extentTable.put(new Integer(extent),logEDP);
logEDP.blockValid = logEDP;
return logEDP;
| LogExtent | positionFilePointer(LogLSN currentLSN, int extra, int accessType)Positions the file pointer to the given position in the log.
This internal method does not need to be synchronized.
boolean extentJustOpened = false; // Remember open operation
// Run the extent chain to see if extent file is already open
LogExtent extent = (LogExtent)extentTable.get(new Integer(currentLSN.extent));
// Open the extent file if it was not found in the extent chain
if( extent == null ) {
// Open the new extent file
extent = openExtent(currentLSN.extent);
extentJustOpened = true;
}
// If the current cursor position for the extent is not in the
// required position, seek to the correct position
if( extent.cursorPosition != currentLSN.offset + extra ||
extent.lastAccess != accessType ) {
// lseek to the correct offset in the open extent file
// if the last cursor position is unknown or the distance
// of the seek from the start of the file is closer to the
// required position than the current position do a seek
// from the start of the file, otherwise do a seek from the
// current position.
int seekDist = ((currentLSN.offset + extra) > extent.cursorPosition) ?
(currentLSN.offset + extra - extent.cursorPosition) :
(extent.cursorPosition - currentLSN.offset - extra);
try {
if( extent.lastAccess == LogExtent.ACCESSTYPE_UNKNOWN ||
currentLSN.offset + extra < seekDist )
extent.fileHandle.fileSeek(currentLSN.offset+extra,LogFileHandle.SEEK_ABSOLUTE);
else
extent.fileHandle.fileSeek(currentLSN.offset+extra-extent.cursorPosition,LogFileHandle.SEEK_RELATIVE);
} catch( LogException le ) {
if( extentJustOpened ) {
extentTable.remove(new Integer(currentLSN.extent));
extent.doFinalize();
}
throw new LogException(null,LogException.LOG_READ_FAILURE,3);
}
extent.cursorPosition = currentLSN.offset + extra;
extent.lastAccess = accessType;
}
// Return the file descriptor for the extent file
return extent;
| synchronized byte[] | readRecord(LogLSN readLSN, int[] type)Reads a record from the log.
// Check BlockValid field in Log_FileDescriptor block pointed to
// by logHandle parameter, and ensure it is valid
// IF not valid Log_FileDescriptor
// Return LOG_INVALID_FILE_DESCRIPTOR
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
// IF not LogInitialised
// Return LOG_NOT_INITIALISED
if( !logControl.logInitialised )
throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
// IF the log file is empty (head LSN equal to LOG_NULL_LSN)
// Unlock the log file latch
// Return LOG_INVALID_LSN
if( logControlDescriptor.headLSN.isNULL() )
throw new LogException(null,LogException.LOG_INVALID_LSN,3);
// IF the lsn specified is LOG_HEAD_LSN or LOG_TAIL_LSN
// substitute the current head or tail LSN from the
// Log_ControlDescriptor structure
// ELSE
// Ensure that the lsn specified is <= current head LSN and
// >= current tail LSN
// IF lsn does not pass these checks
// Unlock the log file latch
// Return LOG_INVALID_LSN
LogLSN lsn;
if( readLSN.equals(LogLSN.HEAD_LSN) )
lsn = logControlDescriptor.headLSN;
else if( readLSN.equals(LogLSN.TAIL_LSN) )
lsn = logControlDescriptor.tailLSN;
else if( readLSN.lessThan(logControlDescriptor.tailLSN) ||
readLSN.greaterThan(logControlDescriptor.headLSN) )
throw new LogException(null,LogException.LOG_INVALID_LSN,4);
else
lsn = readLSN;
// Position the file pointer to the LSN specified
// IF not successful allow the error to pass to the caller.
LogExtent logEDP = positionFilePointer(lsn,0,LogExtent.ACCESSTYPE_READ);
// Issue a READ for the log header record
// IF the READ was not successful
// Unlock the log file latch
// Return LOG_READ_FAILURE
byte[] headerBytes = new byte[LogRecordHeader.SIZEOF];
int bytesRead = 0;
try {
bytesRead = logEDP.fileHandle.fileRead(headerBytes);
} catch( LogException le ) {
logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN;
throw new LogException(null,le.errorCode,6);
}
LogRecordHeader logRH = new LogRecordHeader(headerBytes,0);
logEDP.cursorPosition += bytesRead;
// Check the record type is not a LOG_LINK_RECORD_TYPE &&
// the LSN in the header record is same as lsn parameter
// IF either test fails
// Unlock the log file latch
// Return LOG_INVALID_LSN
if( logRH.recordType == LINK ||
!logRH.currentLSN.equals(lsn) )
throw new LogException(null,LogException.LOG_INVALID_LSN,7);
// Set up a 2-element iovec array to enable the log record data and record
// ending to be read into a separate buffers
// Issue a READV request for the extent file, passing the iovec array as
// an input parameter
// IF the READV was not successful
// Unlock the log file latch
// Return LOG_READ_FAILURE
byte[][] readVect = new byte[2][];
readVect[0] = new byte[logRH.recordLength];
readVect[1] = new byte[LogRecordEnding.SIZEOF];
try {
bytesRead = logEDP.fileHandle.readVector(readVect);
} catch( LogException le ) {
logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN;
throw new LogException(null,le.errorCode,9);
}
LogRecordEnding logRE = new LogRecordEnding(readVect[1],0);
logEDP.cursorPosition += bytesRead;
// IF the LSN contained in the record ending != lsn parameter
// Unlock the log file latch
// Return LOG_CORRUPTED
if( !logRE.currentLSN.equals(lsn) )
throw new LogException(null,LogException.LOG_CORRUPTED,10);
// Copy the returned number of bytes into the recordLengthP parameter and
// the record type value into the recordTypeP parameter.
type[0] = logRH.recordType;
return readVect[0];
| synchronized byte[] | readRestart()Reads the restart record.
// Check BlockValid field in Log_FileDescriptor block pointed to
// by logHandle parameter, and ensure it is valid
// IF not valid Log_FileDescriptor
// Return LOG_INVALID_FILE_DESCRIPTOR
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
// IF not LogInitialised
// Return LOG_NOT_INITIALISED
if( !logControl.logInitialised )
throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
// Check BlockValid field in Log_FileDescriptor block pointed to
// by logHandle parameter, and ensure it is still valid
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,3);
// IF there is no restart data (restart length in Log_FileDescriptor
// block is zero)
// Return LOG_NO_RESTART_RECORD
if( restartDataLength == 0 )
return new byte[0];
// Use the ActiveRestartVersion field in the Log_FileDescriptor block
// to find out which restart record is currently the active one and
// determine its offset within the control file
// Use LSEEK to move the file pointer to the start of the restart record
// Allow any error to pass to the caller.
int restartOffset = restartPosition(activeRestartVersion);
logFileHandle.fileSeek(restartOffset,LogFileHandle.SEEK_ABSOLUTE);
// Initialise an iovec array with the first element containing details of
// a Log_RestartDescriptor block, the second containing details of
// the callers buffer (bufferP and restart data length) and the third also
// pointing to a Log_RestartDescriptor block
byte[][] readVect = new byte[3][];
readVect[0] = new byte[LogRestartDescriptor.SIZEOF];
readVect[1] = new byte[restartDataLength];
readVect[2] = new byte[LogRestartDescriptor.SIZEOF];
// Issue a READV for the restart data
// IF not successful let the error pass to the caller.
int bytesRead = logFileHandle.readVector(readVect);
LogRestartDescriptor logRD = new LogRestartDescriptor(readVect[0],0);
LogRestartDescriptor logRDEnd = new LogRestartDescriptor(readVect[2],0);
// IF the offset value stored in the returned Log_RestartDescriptor
// block is not equal to the offset of the record just read OR
// the length held in the Log_RestartDescriptor block is not equal to
// the restart data length held in the Log_FileDescriptor block OR
// the first Log_RestartDescriptor block is not equal to the second
// Return LOG_CORRUPTED
if( logRD.restartValid != restartOffset ||
logRD.restartDataLength != restartDataLength ||
!logRD.equals(logRDEnd) )
throw new LogException(null,LogException.LOG_CORRUPTED,7);
// Copy the restart data length from Log_RestartDescriptor block into
// the callers recordLengthP parameter
// Return LOG_SUCCESS
return readVect[1];
| static final int | restartPosition(int restart)Returns the offset of the specified restart number.
return (restart == 1) ? RESTART_OFFSET_1 : RESTART_OFFSET_2;
| void | restoreCushion(boolean callUpcall)Restores the cushion file
This internal method does not need to be synchronized.
// IF the LOG_CUSHION_SIZE > 0
if( CUSHION_SIZE > 0 ) {
// if the cushion file already exists, set the flag in the
// File Descriptor block to say so.
if( !logControl.cushionFile.exists() ) {
LogFileHandle cushionFH; // Cushion file descriptor
int openOptions = LogFileHandle.OPEN_RDWR |
LogFileHandle.OPEN_CREAT |
LogFileHandle.OPEN_SYNC;
// Create an empty log file as a storage cushion
// Issue OPEN request for $REGIONDIR/log/cushion
// IF OPEN fails
// Call function whose address is stored in UpcallFunction,
// passing a reason value of LOG_CALLBACK_REASON_SOS
// Unlock the Log_ProcessSharedLock
// Return
try {
cushionFH = new LogFileHandle(logControl.cushionFile,openOptions);
} catch( LogException le ) {
if( callUpcall && !upcallInProgress ) {
upcallInProgress = true;
upcallTarget.upcall(CALLBACK_REASON_SOS);
upcallInProgress = false;
}
throw new LogException(null,LogException.LOG_OPEN_FAILURE,3);
}
// Use Log_AllocFileStorage to create a file the
// size LOG_CUSHION_SIZE
// IF Log_AllocFileStorage fails
// Call function whose address is stored in UpcallFunction,
// passing a reason value of LOG_CALLBACK_REASON_SOS
// CLOSE & Unlink the cushion file
// Unlock the Log_ProcessSharedLock
// Return
try {
cushionFH.allocFileStorage(CUSHION_SIZE);
} catch( LogException le ) {
cushionFH.finalize();
// Start IASRI 4720539
//logControl.cushionFile.delete();
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run(){
logControl.cushionFile.delete();
return null;
}
}
);
// End IASRI 4720539
if( callUpcall && !upcallInProgress ) {
upcallInProgress = true;
upcallTarget.upcall(CALLBACK_REASON_SOS);
upcallInProgress = false;
}
cushionExists = false;
throw new LogException(null,LogException.LOG_OPEN_FAILURE,4);
}
// CLOSE the cushion file
cushionFH.finalize();
}
cushionExists = true;
}
| synchronized void | truncate(LogLSN truncLSN, int inclusive)Truncates the log at the given point.
// Check BlockValid field in Log_FileDescriptor block and
// ensure it is valid
// IF not valid Log_FileDescriptor
// Return LOG_INVALID_FILE_DESCRIPTOR
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
// IF not LogInitialised
// Return LOG_NOT_INITIALISED
if( !logControl.logInitialised )
throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
// IF ReadOnly log
// Return LOG_READ_ONLY_ACCESS
if( logControl.logReadOnly )
throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);
// IF the log file is empty (head LSN = LOG_NULL_LSN) &&
// the lsn value specified is not equal to LOG_HEAD_LSN
// Unlock the Log_FileDescriptor latch
// Return LOG_NEW_TAIL_TOO_HIGH
if( logControlDescriptor.headLSN.isNULL() ) {
if( truncLSN.equals(LogLSN.HEAD_LSN) ) {
return;
} else
throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_HIGH,6);
}
// IF the lsn parameter is equal to the symbolic LOG_HEAD_LSN or
// the lsn parameter is equal to the actual log head LSN
// Copy head LSN from Log_FileDescriptor into lsn
// Remember that head of log is being truncated
// ELSE IF the lsn parameter is equal to LOG_TAIL_LSN
// Copy tail LSN from Log_FileDescriptor into lsn
// ELSE Copy lsn parameter into lsn
LogLSN lsn;
boolean truncateHead = false;
if( truncLSN.equals(LogLSN.HEAD_LSN) ||
truncLSN.equals(logControlDescriptor.headLSN) ) {
lsn = new LogLSN(logControlDescriptor.headLSN);
truncateHead = true;
} else if( truncLSN.equals(LogLSN.TAIL_LSN) )
lsn = new LogLSN(logControlDescriptor.tailLSN);
else
lsn = new LogLSN(truncLSN);
// Check the lsn parameter to ensure it is within the range of log records
// IF lsn < log tail LSN (in Log_FileDescriptor)
// Unlock the Log_FileDescriptor latch
// Return LOG_NEW_TAIL_TOO_LOW
// ELSE
// IF lsn > log head LSN
// Unlock the Log_FileDescriptor latch
// Return LOG_NEW_TAIL_TOO_HIGH
if( lsn.lessThan(logControlDescriptor.tailLSN) )
throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_LOW,7);
else if( lsn.greaterThan(logControlDescriptor.headLSN) )
throw new LogException(null,LogException.LOG_NEW_TAIL_TOO_HIGH,8);
// IF log head is being truncated &&
// inclusive parameter = LOG_TAIL_NOT_INCLUSIVE
// Set Truncation record to the lsn specified (head LSN)
// and the New Tail LSN to the next lsn;
// ELSE
// set truncation record and new log tail LSN depending
// on whether or not LOG_TAIL_INCLUSIVE was set. Either way the
// record pointed to by the current lsn must be read first
LogLSN truncationRecord;
LogLSN newTailRecord;
boolean truncLastExtent = false;
if( truncateHead &&
inclusive == TAIL_NOT_INCLUSIVE ) {
truncationRecord = new LogLSN(lsn);
newTailRecord = new LogLSN(logControlDescriptor.nextLSN);
} else {
// IF inclusive parameter = LOG_TAIL_INCLUSIVE and
// lsn parameter = log tail LSN (in Log_FileDescriptor)
// (then there is nothing to truncate)
// Unlock the Log_FileDescriptor latch
// Return LOG_SUCCESS
if( inclusive == TAIL_INCLUSIVE &&
lsn.equals(logControlDescriptor.tailLSN) ) {
return;
}
// Call Log_PositionFilePointer to position file pointer at the
// start of the record specified by the lsn parameter
// Allow any error to pass to the caller.
LogExtent logEDP = positionFilePointer(lsn,0,LogExtent.ACCESSTYPE_READ);
// Issue READ for the log record header
// IF not successful return LOG_READ_ERROR
byte[] headerBytes = new byte[LogRecordHeader.SIZEOF];
int bytesRead = 0;
try {
bytesRead = logEDP.fileHandle.fileRead(headerBytes);
} catch( LogException le ) {
logEDP.lastAccess = LogExtent.ACCESSTYPE_UNKNOWN;
throw new LogException(null,LogException.LOG_READ_FAILURE,11);
}
logEDP.cursorPosition += bytesRead;
LogRecordHeader recordHeader = new LogRecordHeader(headerBytes,0);
// Check that retrieved record is not an extent link record
// IF it is
// Unlock the Log_FileDescriptor latch
// Return LOG_INVALID_TAIL
if( recordHeader.recordType == LINK )
throw new LogException(null,LogException.LOG_INVALID_TAIL,12);
// Now set truncation record, and new tail LSN according to whether
// or not LOG_TAIL_INCLUSIVE was specified
if( inclusive == TAIL_INCLUSIVE ) {
// The specified LSN is to be retained in the logfile so
// set the truncation record to the previous LSN and the
// new tail to the specified LSN
truncationRecord = new LogLSN(recordHeader.previousLSN);
newTailRecord = new LogLSN(lsn);
// IF the current LSN is the first record in an extent file
// Remember that previous extent file is to be truncated
if( lsn.offset == 0 )
truncLastExtent = true;
} else {
// The specified LSN is to be truncated from the logfile so
// set the truncation record to the specified LSN and the
// new tail to the next LSN
truncationRecord = new LogLSN(lsn);
newTailRecord = new LogLSN(recordHeader.nextLSN);
}
}
// Now that the true truncation point in the log file is known, work out
// how many extent files (if any) can be unlinked
// - Set first_extent to extent number from log tail LSN
// - Set last_extent to extent number from truncation point LSN
int firstExtent = logControlDescriptor.tailLSN.extent;
int lastExtent = truncationRecord.extent;
// IF log head is being truncated &&
// inclusive parameter = LOG_TAIL_NOT_INCLUSIVE
// Set log tail LSN to current log head LSN
// Set log head LSN in Log_ControlDescriptor structure to LOG_NULL_LSN
// ELSE
// Set log tail LSN in Log_ControlDescriptor structure
// to truncation point LSN
if( truncateHead &&
inclusive == TAIL_NOT_INCLUSIVE ) {
logControlDescriptor.tailLSN.copy(newTailRecord);
logControlDescriptor.headLSN.copy(LogLSN.NULL_LSN);
} else
logControlDescriptor.tailLSN.copy(newTailRecord);
// Write (and implicitly sync) the Log_ControlDescriptor structure
// to the control file. Allow any error to pass to the caller.
writeControlFile();
// Now unlink any extent files no longer required
// This involves processing each of the extent files in the range
// FirstExtent to LastExtent-1.
// Note: If the TruncationRecord is a link record (last in the extent
// file), then the LastExtent must also be processed.
if( truncLastExtent )
lastExtent++;
for( int extent = firstExtent; extent <= lastExtent-1; extent++ ) {
// IF extent is currently open
// Issue CLOSE for extent file
// IF not successful allow the error to pass to the caller.
LogExtent logEDP = (LogExtent)extentTable.get(new Integer(extent));
if( logEDP != null )
logEDP.fileHandle.fileClose();
// Issue UNLINK for extent file
// IF not successful
// Return LOG_CLOSE_FAILURE
//Start IASRI 4720539
//if( !logEDP.file.delete() )
final LogExtent tmplogEDP = logEDP;
Boolean isdeleted = (Boolean) java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run(){
return new Boolean(tmplogEDP.file.delete());
}
}
);
if(!isdeleted.booleanValue())
throw new LogException(null,LogException.LOG_CLOSE_FAILURE,15);
//End IASRI 4720539
// Unchain the Log_ExtentDescriptor block, set its BlockValid
// field to binary zeroes and deallocate it.
extentTable.remove(new Integer(extent));
logEDP.doFinalize();
}
// If the cushion file does not exist and at least one extents has
// just been removed, now is a good time to try and restore the
// cushion file.
// Call RestoreCushion but specify that the upcall should not
// be called if the restore fails, as it should already have
// been called when the cushion was first freed.
if( !cushionExists &&
firstExtent <= lastExtent - 1 )
restoreCushion(false);
// Call the platform specific SUPOS_LOG_FREE_FILE_STORAGE macro to
// release any unwanted areas of the extent file containing the TAIL LSN
// Allow any error to pass to the caller.
if( logControlDescriptor.tailLSN.offset > 0 )
freeFileStorage(logControlDescriptor.tailLSN);
// If the log head has been set to 00000000.00000000, then ensure that
// the next record which is written to the log causes the log control
// data to be forced. Otherwise, should a crash occur AFTER writing the
// record and BEFORE writing the control file, when the log is re-opened
// during restart, it will be assumed that the log is empty and no
// scanning for records 'beyond the end of the log' will take place.
if( logControlDescriptor.headLSN.isNULL() )
recordsWritten = CONTROL_FORCE_INTERVAL;
| void | writeControlFile()Writes the control file.
This internal method does not need to be synchronized.
// BUGFIX (Ram J) This fixes the log corruption problem.
// The log extents have to be forced every time control
// information is written. If not, there is a chance that
// the control information (particularly headLSN) will go
// inconsistent. The extent log that the headLSN in controlFile
// points to, may not exist in the extent log, if it is
// not forced. So, if JTS crashes, during recovery/reconstruction
// there will be no log in the extents corresponding to the
// headLSN stored in the control file. The fix is to force
// all the dirty extents, everytime the control information
// is updated.
Enumeration extents = extentTable.elements();
while (extents.hasMoreElements()) {
LogExtent nextEDP = (LogExtent) extents.nextElement();
if (nextEDP.writtenSinceLastForce) {
try {
nextEDP.fileHandle.fileSync();
nextEDP.writtenSinceLastForce = false;
} catch (LogException le) {
throw new LogException(null,
LogException.LOG_ERROR_FORCING_LOG,
14);
}
}
}
// Move the file pointer to the beginning of the control file
logFileHandle.fileSeek(0,LogFileHandle.SEEK_ABSOLUTE);
// Write out the control data to the control file
byte[] controlBytes = new byte[LogControlDescriptor.SIZEOF];
logControlDescriptor.toBytes(controlBytes,0);
int bytesWritten = logFileHandle.fileWrite(controlBytes);
| synchronized LogLSN | writeRecord(byte[] record, int recordType, int writeMode)Writes a record to the log.
// Check BlockValid field in Log_FileDescriptor block pointed to
// by logHandle parameter, and ensure it is valid
// IF not valid Log_FileDescriptor
// Return LOG_INVALID_FILE_DESCRIPTOR
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
// IF not LogInitialised
// Return LOG_NOT_INITIALISED
if( !logControl.logInitialised )
throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
// IF ReadOnly log
// Return LOG_READ_ONLY_ACCESS
if( logControl.logReadOnly )
throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);
// Sanity check the recordType and writeMode parameters
if( recordType > RECORD_TYPE_MAX )
throw new LogException(null,LogException.LOG_INVALID_RECORDTYPE,5);
if( writeMode != FORCE && writeMode != BUFFER )
throw new LogException(null,LogException.LOG_INVALID_WRITEMODE,6);
// Calculate the total size of the log record by totalling size of all
// input buffers together with the record header and record ending
int recordSize = record.length + LogRecordHeader.SIZEOF + LogRecordEnding.SIZEOF;
// IF the log record data is greater than LOG_MAX_LOG_RECORD_SIZE
// Unlock the log file latch
// Return LOG_RECORD_TOO_LARGE
if( recordSize > MAX_RECORD_SIZE )
throw new LogException(null,LogException.LOG_RECORD_TOO_LARGE,7);
// Calculate the remaining space in the current extent by subtracting
// (log head LSN's offset + 2*LOG_HEADER_SIZE + LOG_ENDING_SIZE) from
// LOG_MAX_EXTENT_SIZE
int remainingSpace = MAX_EXTENT_SIZE - ( logControlDescriptor.nextLSN.offset
+ 2*LogRecordHeader.SIZEOF
+ LogRecordEnding.SIZEOF );
// Position the file pointer to the next free location
// NOTE: either the record or a link record will be wrote here
// Set the WORKING extent descriptor to the returned value
//
// IF an error occurs let it go to the caller.
LogExtent logEDP = positionFilePointer(logControlDescriptor.nextLSN,0,LogExtent.ACCESSTYPE_WRITE);
// IF not enough space in current extent
if( remainingSpace < recordSize ) {
LogRecordHeader link = new LogRecordHeader();
// Calculate the number of the next (new) extent
// Calculate LSN of first record in the new extent.
// Test that the new extent number has not wrapped to become negative;
// if it has, throw an exception.
int nextExtent = logControlDescriptor.headLSN.extent+1;
if( nextExtent < 0 )
throw new LogException(null,LogException.LOG_WRITE_FAILURE,8);
// If the new extent file is already open, there is nothing we can do but
// fail. We cannot run the short-on-storage upcall to try to free the
// extent as the upcall needs to write information to the offending extent.
if( extentTable.containsKey(new Integer(LogExtent.modExtent(nextExtent))) )
throw new LogException(null,LogException.LOG_WRITE_FAILURE,9);
// Create link record containing
// - the LSN of the link record (i.e. its own LSN)
// - the LSN of the previous log record (log head LSN from
// Log_FileDescriptor block)
// - the LSN of the next log record (this is the LSN of the
// first record in new extent file
link.recordType = LINK;
link.previousLSN = new LogLSN(logControlDescriptor.headLSN);
link.currentLSN = new LogLSN(logControlDescriptor.nextLSN);
link.nextLSN = new LogLSN(nextExtent,0);
// Move a file pointer to the next record position
LogExtent nextEDP = positionFilePointer(link.nextLSN,0,LogExtent.ACCESSTYPE_WRITE);
// Issue WRITE to add link record to the 'full' extent file
// IF the WRITE fails
// Close the new extent file
// Unchain its extent descriptor block from the hash table
// Deallocate the extent descriptor block
// Unlock the log file latch
// Return LOG_WRITE_FAILURE
byte[] linkBytes = new byte[link.SIZEOF];
link.toBytes(linkBytes,0);
int bytesWritten = 0;
try {
bytesWritten = logEDP.fileHandle.fileWrite(linkBytes);
} catch( LogException le ) {
extentTable.remove(new Integer(logControlDescriptor.headLSN.extent));
nextEDP.doFinalize();
throw new LogException(null,LogException.LOG_WRITE_FAILURE,10);
}
// Set its 'extent written' flag to TRUE
logEDP.writtenSinceLastForce = true;
logEDP.cursorPosition += bytesWritten;
// Update the head LSN value in the Log_FileDescriptor block
// with the LSN of the link record
// Update the next LSN value in the Log_FileDescriptor block
// with the LSN of the first block in the new extent
logControlDescriptor.headLSN.copy(link.currentLSN);
logControlDescriptor.nextLSN.copy(link.nextLSN);
// Set the WORKING extent descriptor to the new/next extent
logEDP = nextEDP;
// Set the ChunkRemaining to Zero
chunkRemaining = 0;
}
// Use the offset value from the next LSN to calculate the next free offset
// in the extent file
// Calculate the 'next free' LSN
LogLSN nextFree = new LogLSN(logControlDescriptor.nextLSN.extent,
logControlDescriptor.nextLSN.offset + recordSize);
// Build the record header, initialising with
// - log record type (recordType passed as input parameter)
// - log record length (cumulative length of all data buffers)
// - the LSN of the previous log record (PreviousRecord; log head LSN from
// Log_FileDescriptor block)
// - the LSN of the next log record (NextRecord; the 'next free' LSN value)
// - the LSN of the record about to be written (ThisRecord)
LogRecordHeader logRH = new LogRecordHeader();
logRH.recordType = recordType;
logRH.recordLength = record.length;
logRH.nextLSN = nextFree;
logRH.previousLSN = new LogLSN(logControlDescriptor.headLSN);
logRH.currentLSN = new LogLSN(logControlDescriptor.nextLSN);
// Build the record ending, initialising with
// the LSN of the record about to be written (ThisRecord)
LogRecordEnding logRE = new LogRecordEnding();
logRE.currentLSN = logRH.currentLSN;
// Initialise an array of iovec structures ready for a WRITEV request
// (an iovec structure specifies the base address and length of an area in
// memory from which data should be written)
// - set the first element to point to the record header, set iovCount=1
// - LOOP for each buffer in recordPtrList
// initialise next iovec element with its address and length
// increment iovCount
// ENDLOOP
// - set the next element to point to the record ending, increment iovCount
byte[] writeBytes = new byte[LogRecordHeader.SIZEOF+record.length+LogRecordEnding.SIZEOF];
logRH.toBytes(writeBytes,0);
System.arraycopy(record,0,writeBytes,LogRecordHeader.SIZEOF,record.length);
logRE.toBytes(writeBytes,LogRecordHeader.SIZEOF+record.length);
// IF there is enough space in current chunk
// Decrease ChunkRemaining by RecordSize
boolean cushionFreed = false;
if( chunkRemaining > recordSize )
chunkRemaining -= recordSize;
else {
// CALCULATE the size of disk space to grab
int grabSize = chunkRemaining + ALLOCATE_SIZE;
// IF there is NOT enough space in current extent
// Set the Grab size to be the size of the remaining extent
if( grabSize + logControlDescriptor.nextLSN.offset > MAX_EXTENT_SIZE )
grabSize = MAX_EXTENT_SIZE - logControlDescriptor.nextLSN.offset;
// Set the Allocate success flag to FALSE;
boolean allocateSuccess = false;
do {
// ALLOCATE the Grab size of disk space
// IF successful
// Set AllocateSuccess to TRUE
// BREAK
try {
logEDP.fileHandle.allocFileStorage(grabSize);
} catch( LogException le ) {
// IF the request fails due to lack of storage, i.e.
// ENOSPC - insufficient space left in file system or
// EDQUOT - user or group disk block quota reached
// Call the Log_FreeCushion routine
// IF there was no cushion to free
// Unlock the log file latch
// Return LOG_NO_SPACE
// Move the File pointer back to it's original offset
// ELSE
// EXIT LOOP with 'Allocate unsuccessful' status
if( le.errorCode == LogException.LOG_NO_SPACE ) {
if( cushionExists ) {
freeCushion();
cushionFreed = true;
} else {
if( cushionFreed )
restoreCushion(false);
throw new LogException(null,LogException.LOG_NO_SPACE,11);
}
try {
logEDP = positionFilePointer(logControlDescriptor.nextLSN,0,LogExtent.ACCESSTYPE_WRITE);
} catch( Throwable e ) {};
}
else
allocateSuccess = false;
}
allocateSuccess = true;
}
while( !allocateSuccess );
// IF allocate failed
// Unlock the log file latch
// Return LOG_WRITE_FAILURE
if( !allocateSuccess )
throw new LogException(null,LogException.LOG_WRITE_FAILURE,12);
// SET ChunkRemaining to the Grabbed size - RecordSize
chunkRemaining = grabSize - recordSize;
}
// Issue a WRITEV request to the extent file, specifying the iovec array
// and iovCount as input
// IF write failed return the error.
int bytesWritten = logEDP.fileHandle.fileWrite(writeBytes);
// Set 'extent written' flag to TRUE
logEDP.writtenSinceLastForce = true;
logEDP.cursorPosition += bytesWritten;
// IF LOG_FORCE was specified
// LOOP through each extent chain in the hash table
// IF 'extent written' flag is TRUE
// Issue FSYNC for extent file descriptor
// IF not successful
// Unlock the log file latch
// Return LOG_ERROR_FORCING_LOG
// Set 'extent written' flag to FALSE
// ENDLOOP
if( writeMode == FORCE ) {
Enumeration extents = extentTable.elements();
while( extents.hasMoreElements() ) {
LogExtent nextEDP = (LogExtent)extents.nextElement();
if( nextEDP.writtenSinceLastForce )
try {
nextEDP.fileHandle.fileSync();
nextEDP.writtenSinceLastForce = false;
} catch( LogException le ) {
throw new LogException(null,LogException.LOG_ERROR_FORCING_LOG,14);
}
}
}
// Update the head LSN and 'next free' LSN in the Log_FileDescriptor
// block
logControlDescriptor.headLSN.copy(logRH.currentLSN);
logControlDescriptor.nextLSN.copy(logRH.nextLSN);
// Increment the RecordsWritten counter in Log_FileDescriptor block
recordsWritten++;
// IF RecordsWritten = LOG_CONTROL_FORCE_INTERVAL or LOG_FORCE was specified
// Write the Log_ControlDescriptor structure (embedded in the
// Log_FileDescriptor block out to the control file (implied sync)
// IF not successful let the error pass to the caller.
// Reset the RecordsWritten counter to zero
// IF LogCushionOK is FALSE
// Call RestoreLogCushion Routine
if( recordsWritten >= CONTROL_FORCE_INTERVAL ) {
writeControlFile();
recordsWritten = 0;
}
if( cushionFreed )
restoreCushion(true);
// Return the written LSN as the result of the write operation.
LogLSN result = new LogLSN(logRH.currentLSN);
return result;
| synchronized void | writeRestart(byte[] buffer)Writes the restart record.
// Check BlockValid field in Log_FileDescriptor block pointed to
// by logHandle parameter, and ensure it is valid
// IF not valid Log_FileDescriptor
// Return LOG_INVALID_FILE_DESCRIPTOR
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,1);
// IF not LogInitialised
// Return LOG_NOT_INITIALISED
if( !logControl.logInitialised )
throw new LogException(null,LogException.LOG_NOT_INITIALISED,2);
// IF ReadOnly log
// Return LOG_READ_ONLY_ACCESS
if( logControl.logReadOnly )
throw new LogException(null,LogException.LOG_READ_ONLY_ACCESS,3);
// IF the bufferLength parameter is greater than LOG_MAX_RESTART_RECORD_SIZE
// Return LOG_RECORD_TOO_LARGE
if( buffer.length > MAX_RESTART_SIZE )
throw new LogException(null,LogException.LOG_RECORD_TOO_LARGE,4);
// Check BlockValid field in Log_FileDescriptor block pointed to
// by logHandle parameter, and ensure it is still valid
if( blockValid != this )
throw new LogException(null,LogException.LOG_INVALID_FILE_DESCRIPTOR,5);
// Use the value in ActiveRestartVersion field showing which is the active
// to determine which is the alternate restart record
// Use LSEEK to move the file pointer to its offset
int alternate = alternateRestart(activeRestartVersion);
int restartOffset = restartPosition(alternate);
logFileHandle.fileSeek(restartOffset,LogFileHandle.SEEK_ABSOLUTE);
// Initialise a Log_RestartDescriptor block with
// - the current file pointer offset (copied into RestartValid field)
// - the length of the restart data (DataLength field)
// - a timestamp obtained from the seconds field of a gettimer call
LogRestartDescriptor logRD = new LogRestartDescriptor();
logRD.restartDataLength = buffer.length;
logRD.timeStamp = (int)new Date().getTime();
logRD.restartValid = restartOffset;
// Set up a 3-element iovec array with the first element 'containing'
// the Log_RestartDescriptor block, the second, the supplied
// restart data and the third, the Log_RestartDescriptor block again.
byte[] writeBytes = new byte[LogRestartDescriptor.SIZEOF*2+buffer.length];
logRD.toBytes(writeBytes,0);
System.arraycopy(buffer,0,writeBytes,LogRestartDescriptor.SIZEOF,buffer.length);
logRD.toBytes(writeBytes,LogRestartDescriptor.SIZEOF+buffer.length);
// Issue a WRITEV request to copy the restart data to the control file
// IF successful
// Data has now been written to permanent storage, so update
// RestartDataLength field in Log_FileDescriptor with bufferLength
// and indicate (value 1 or 2) in ActiveRestartVersion field that the
// alternate has now become the active
// Return LOG_SUCCESS
// ELSE let the error pass to the caller.
int bytesWritten = logFileHandle.fileWrite(writeBytes);
activeRestartVersion = alternate;
|
|