Class WALManager
- java.lang.Object
-
- edu.caltech.nanodb.storage.writeahead.WALManager
-
public class WALManager extends java.lang.Object
This class manages the write-ahead logs of the database. There are methods to write the different kinds of log records, and also to perform recovery based on the contents of the write-ahead logs. Note that many details of transaction coordination are handled by the Transaction Manager, such as syncing the write-ahead log, forcing the WAL out to a specific log sequence number, and so forth.
The actual details of the write-ahead log format are in the package Javadocs:
edu.caltech.nanodb.storage.writeahead
.Some of the
writeXXXX()
methods require explicit transaction details, while others retrieve the transaction state from thread-local storage. The main difference is that methods that require explicit transaction details are needed during recovery processing, when transaction state is dictated by the log file, not what is in thread-local storage.Thread Safety
This class is written to be thread-safe when invoked from multiple transaction threads. Operations that involve reading logs may overlap with each other, since they will always read log entries that have been written earlier by the same thread, and those entries will not change. Operations that involve writing logs always write each log-entry atomically so that individual records cannot be interleaved with each other; but, the records from two transactions may of course be interleaved.
Recovery processing is written to occur atomically, but the reality is that recovery processing always occurs from a single thread when the database starts up, so no thread-safety is required.
-
-
Field Summary
Fields Modifier and Type Field Description private BufferManager
bufferManager
private LogSequenceNumber
firstLSN
This object holds the log sequence number of the first write-ahead log record where recovery would need to start from.private java.lang.Object
guard
A simple object for synchronizing on, so that the WAL manager will be thread-safe.private static org.apache.logging.log4j.Logger
logger
A logging object for reporting anything interesting that happens.static int
MAX_WAL_FILE_NUMBER
Maximum file number for a write-ahead log file.private static int
MAX_WAL_FILE_SIZE
Maximum size of a write-ahead log file is 10MB.private LogSequenceNumber
nextLSN
This object holds the log sequence number where the next write-ahead log record will be written.static int
OFFSET_FIRST_RECORD
This is the file-offset of the first log entry in a WAL file.private static int
OFFSET_PREV_FILE_END
This is the file-offset just past the last byte written in the previous WAL file, or 0 for the first WAL file.private StorageManager
storageManager
private static java.lang.String
WAL_FILENAME_PATTERN
Write-ahead log files follow this pattern.
-
Constructor Summary
Constructors Constructor Description WALManager(StorageManager storageManager)
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description private void
applyRedo(WALRecordType type, DBFileReader walReader, DBPage dbPage, int numSegments)
This helper function writes a sequence of redo-segments from anWALRecordType.UPDATE_PAGE
orWALRecordType.UPDATE_PAGE_REDO_ONLY
record.private byte[]
applyUndoAndGenRedoOnlyData(DBFileReader walReader, DBPage dbPage, int numSegments)
This helper method uses aWALRecordType.UPDATE_PAGE
record to undo changes to a data page, and at the same time the method generates the data that must go into a corresponding redo-only WAL record.static LogSequenceNumber
computeNextLSN(int fileNo, int fileOffset)
This static helper function takes the file number of a WAL file, and the offset in the WAL file where the next write-ahead log record would go if the WAL file can hold more data, and then creates a newLogSequenceNumber
object, wrapping to the next file if necessary.private DBFile
createWALFile(int fileNo)
This helper method creates a brand new write-ahead log file using the Storage Manager, generating a suitable filename and passing the appropriate arguments to the Storage Manager.RecoveryInfo
doRecovery(LogSequenceNumber storedFirstLSN, LogSequenceNumber storedNextLSN)
Performs recovery processing starting at the specified log sequence number, and returns the LSN where the next recovery process should start from.LogSequenceNumber
getFirstLSN()
LogSequenceNumber
getNextLSN()
static java.lang.String
getWALFileName(int fileNo)
This static helper method simply takes a WAL file number and translates it into a corresponding filename based on that number.private DBFileReader
getWALFileReader(LogSequenceNumber lsn, DBFileReader reader)
This method opens the WAL file specified in the passed-in Log Sequence Number, wraps it with aDBFileReader
so that it can be read from, and then seeks to the specified file offset.private DBFileWriter
getWALFileWriter(LogSequenceNumber lsn)
This method opens the WAL file specified in the passed-in Log Sequence Number, wraps it with aDBFileWriter
so that it can be read and written, and then seeks to the specified file offset.private DBFile
openWALFile(int fileNo)
This helper method opens an existing write-ahead log file using the Storage Manager, generating a suitable filename and passing the appropriate arguments to the Storage Manager.private void
performRedo(RecoveryInfo recoveryInfo)
This helper function performs redo processing using the write-ahead log.private void
performUndo(RecoveryInfo recoveryInfo)
This helper function performs undo processing using the write-ahead log.void
rollbackTransaction()
This method performs the operations necessary to rollback the current transaction from the database.private LogSequenceNumber
writeRedoOnlyUpdatePageRecord(int transactionID, LogSequenceNumber prevLSN, DBPage dbPage, int numSegments, byte[] changes)
This method writes a redo-only update-page record to the write-ahead log, including only redo details.private LogSequenceNumber
writeRedoOnlyUpdatePageRecord(DBPage dbPage, int numSegments, byte[] changes)
This method writes a redo-only update-page record to the write-ahead log, including only redo details.LogSequenceNumber
writeTxnRecord(WALRecordType type)
This function writes a transaction demarcation record (WALRecordType.START_TXN
,WALRecordType.COMMIT_TXN
, orWALRecordType.ABORT_TXN
) to the write-ahead log.private LogSequenceNumber
writeTxnRecord(WALRecordType type, int transactionID, LogSequenceNumber prevLSN)
This function writes a transaction demarcation record (WALRecordType.START_TXN
,WALRecordType.COMMIT_TXN
, orWALRecordType.ABORT_TXN
) to the write-ahead log.void
writeUpdatePageRecord(DBPage dbPage)
This method writes an update-page record to the write-ahead log, including both undo and redo details.
-
-
-
Field Detail
-
logger
private static org.apache.logging.log4j.Logger logger
A logging object for reporting anything interesting that happens.
-
WAL_FILENAME_PATTERN
private static final java.lang.String WAL_FILENAME_PATTERN
Write-ahead log files follow this pattern.- See Also:
- Constant Field Values
-
MAX_WAL_FILE_NUMBER
public static final int MAX_WAL_FILE_NUMBER
Maximum file number for a write-ahead log file.- See Also:
- Constant Field Values
-
MAX_WAL_FILE_SIZE
private static final int MAX_WAL_FILE_SIZE
Maximum size of a write-ahead log file is 10MB. When the current WAL file reaches this size, it is closed and a new WAL file is created with the next increasing file number.- See Also:
- Constant Field Values
- To Do:
- Make this a configuration propery
-
OFFSET_PREV_FILE_END
private static final int OFFSET_PREV_FILE_END
This is the file-offset just past the last byte written in the previous WAL file, or 0 for the first WAL file. The value is an unsigned 32-bit integer.- See Also:
- Constant Field Values
-
OFFSET_FIRST_RECORD
public static final int OFFSET_FIRST_RECORD
This is the file-offset of the first log entry in a WAL file.- See Also:
- Constant Field Values
-
storageManager
private StorageManager storageManager
-
bufferManager
private BufferManager bufferManager
-
firstLSN
private LogSequenceNumber firstLSN
This object holds the log sequence number of the first write-ahead log record where recovery would need to start from.
-
nextLSN
private LogSequenceNumber nextLSN
This object holds the log sequence number where the next write-ahead log record will be written.
-
guard
private final java.lang.Object guard
A simple object for synchronizing on, so that the WAL manager will be thread-safe.
-
-
Constructor Detail
-
WALManager
public WALManager(StorageManager storageManager)
-
-
Method Detail
-
getWALFileName
public static java.lang.String getWALFileName(int fileNo)
This static helper method simply takes a WAL file number and translates it into a corresponding filename based on that number.- Parameters:
fileNo
- the WAL file number to get the filename for- Returns:
- the string file-name for the corresponding WAL file
-
createWALFile
private DBFile createWALFile(int fileNo)
This helper method creates a brand new write-ahead log file using the Storage Manager, generating a suitable filename and passing the appropriate arguments to the Storage Manager.- Parameters:
fileNo
- the number of the WAL file to create- Returns:
- a
DBFile
for the newly created and opened WAL file - Throws:
StorageException
- if the file cannot be created for some reason
-
openWALFile
private DBFile openWALFile(int fileNo)
This helper method opens an existing write-ahead log file using the Storage Manager, generating a suitable filename and passing the appropriate arguments to the Storage Manager. Of course, if the file has already been opened, the Storage Manager will return theDBFile
from its cache.- Parameters:
fileNo
- the number of the WAL file to open- Returns:
- a
DBFile
for the opened WAL file - Throws:
StorageException
- if the file cannot be opened for some reason, such as if the file doesn't exist, or if permissions are incorrect
-
getFirstLSN
public LogSequenceNumber getFirstLSN()
-
getNextLSN
public LogSequenceNumber getNextLSN()
-
doRecovery
public RecoveryInfo doRecovery(LogSequenceNumber storedFirstLSN, LogSequenceNumber storedNextLSN)
Performs recovery processing starting at the specified log sequence number, and returns the LSN where the next recovery process should start from.
This method is not thread-safe. It is expected that when recovery processing occurs, nothing else will be happening in the database system.
This function is thread-safe merely for the sake of completeness, since it will always be called from a single thread of execution at start-up. Its approach to thread-safety is simple: the
guard
lock is held for the entirety of the recovery operation.- Parameters:
storedFirstLSN
- the location of the write-ahead log record where recovery should start fromstoredNextLSN
- the location in the write-ahead log that is just past the last valid log record in the WAL- Returns:
- the new location where recovery should start from the next time recovery processing is performed
- Throws:
StorageException
- if an IO error occurs during recovery processing
-
performRedo
private void performRedo(RecoveryInfo recoveryInfo)
This helper function performs redo processing using the write-ahead log. As the log is traversed, the RecoveryInfo object is also updated with important redo/undo information.
This method is not thread-safe. It is expected that when recovery processing occurs, nothing else will be happening in the database system.
- Parameters:
recoveryInfo
- the object used to track information about specific transactions during recovery processing. This object will be passed toperformUndo(edu.caltech.nanodb.storage.writeahead.RecoveryInfo)
.
-
performUndo
private void performUndo(RecoveryInfo recoveryInfo)
This helper function performs undo processing using the write-ahead log. As the log is traversed backwards, the RecoveryInfo object is also updated with important redo/undo information.
This method is not thread-safe. It is expected that when recovery processing occurs, nothing else will be happening in the database system.
- Parameters:
recoveryInfo
- the object used to track information about specific transactions during recovery processing. This object is populated by a previous call toperformRedo(edu.caltech.nanodb.storage.writeahead.RecoveryInfo)
.
-
computeNextLSN
public static LogSequenceNumber computeNextLSN(int fileNo, int fileOffset)
This static helper function takes the file number of a WAL file, and the offset in the WAL file where the next write-ahead log record would go if the WAL file can hold more data, and then creates a newLogSequenceNumber
object, wrapping to the next file if necessary.- Parameters:
fileNo
- the number of the current WAL filefileOffset
- the offset of where the next write-ahead log record would go, in the absence of wrapping to the next file- Returns:
- a
LogSequenceNumber
object that takes wrapping into account - Design Note:
- This is a method on
WALManager
and not onLogSequenceNumber
since the calculation must also consider the format of WAL files as well as their size limit. The class simply doesn't have that information.
-
getWALFileWriter
private DBFileWriter getWALFileWriter(LogSequenceNumber lsn)
This method opens the WAL file specified in the passed-in Log Sequence Number, wraps it with a
DBFileWriter
so that it can be read and written, and then seeks to the specified file offset.Because we are writing, the file may not yet exist. In that case, this method will also create a new WAL file for the specified file number, initializing it with the proper values, and then seeking to the location where the first WAL record may be written.
By itself, this method is not thread-safe. However, it is definitely called concurrently from multiple threads by code within the
WALManager
. It is expected that callers of this method have acquired theguard
monitor before calling this method.- Parameters:
lsn
- The log sequence number specifying the WAL file and the offset in the WAL file to go to.- Returns:
- the WAL file, with the file position moved to the specified offset.
- Throws:
StorageException
- if the corresponding WAL file cannot be opened, or if some other IO error occurs.
-
getWALFileReader
private DBFileReader getWALFileReader(LogSequenceNumber lsn, DBFileReader reader)
This method opens the WAL file specified in the passed-in Log Sequence Number, wraps it with a
DBFileReader
so that it can be read from, and then seeks to the specified file offset. Since we are reading, the expectation is that the file already exists.By itself, this method is not thread-safe. However, it is definitely called concurrently from multiple threads by code within the
WALManager
. It is expected that callers of this method have acquired theguard
monitor before calling this method.- Parameters:
lsn
- The log sequence number specifying the WAL file and the offset in the WAL file to go to.- Returns:
- the WAL file, with the file position moved to the specified offset.
-
writeTxnRecord
private LogSequenceNumber writeTxnRecord(WALRecordType type, int transactionID, LogSequenceNumber prevLSN)
This function writes a transaction demarcation record (WALRecordType.START_TXN
,WALRecordType.COMMIT_TXN
, orWALRecordType.ABORT_TXN
) to the write-ahead log. The transaction state is passed explicitly so that this method can be used both during recovery processing and during normal operation. The alternate methodwriteTxnRecord(WALRecordType)
retrieves the transaction state from thread-local storage, and should be used during normal operation. (This is also why this method is markedprivate
access.)- Parameters:
type
- The type of the transaction demarcation to write, one of the valuesWALRecordType.START_TXN
,WALRecordType.COMMIT_TXN
, orWALRecordType.ABORT_TXN
.transactionID
- the transaction ID that the WAL record is forprevLSN
- the log sequence number of the transaction's immediately previous WAL record, if the record type is either a commit or abort record.- Returns:
- the Log Sequence Number of the WAL record that was written
- Throws:
java.lang.IllegalArgumentException
- if type is null, or if it isn't one of the valuesWALRecordType.START_TXN
,WALRecordType.COMMIT_TXN
, orWALRecordType.ABORT_TXN
.
-
writeTxnRecord
public LogSequenceNumber writeTxnRecord(WALRecordType type)
This function writes a transaction demarcation record (WALRecordType.START_TXN
,WALRecordType.COMMIT_TXN
, orWALRecordType.ABORT_TXN
) to the write-ahead log. The transaction state is retrieved from thread-local storage so that it doesn't need to be passed.- Parameters:
type
- The type of the transaction demarcation to write, one of the valuesWALRecordType.START_TXN
,WALRecordType.COMMIT_TXN
, orWALRecordType.ABORT_TXN
.- Returns:
- the Log Sequence Number of the WAL record that was written
- Throws:
java.lang.IllegalArgumentException
- iftype
isnull
, or if it isn't one of the valuesWALRecordType.START_TXN
,WALRecordType.COMMIT_TXN
, orWALRecordType.ABORT_TXN
.
-
writeUpdatePageRecord
public void writeUpdatePageRecord(DBPage dbPage)
This method writes an update-page record to the write-ahead log, including both undo and redo details.- Parameters:
dbPage
- The data page whose changes are to be recorded in the log.- Throws:
java.lang.IllegalArgumentException
- if dbPage is null, or if it shows no updates.
-
applyRedo
private void applyRedo(WALRecordType type, DBFileReader walReader, DBPage dbPage, int numSegments)
This helper function writes a sequence of redo-segments from anWALRecordType.UPDATE_PAGE
orWALRecordType.UPDATE_PAGE_REDO_ONLY
record. Note that thewalReader
argument is expected to be positioned at the start of the segments containing the old and new versions of the page data (or just the new versions, for redo-only records). Additionally, the reader position will be advanced by this method.- Parameters:
type
- The record type, eitherWALRecordType.UPDATE_PAGE
orWALRecordType.UPDATE_PAGE_REDO_ONLY
.walReader
- A reader positioned at the start of the redo/undo data to apply to the data page. This method will advance the reader's position past this redo/undo data.dbPage
- the page that the redo should be applied tonumSegments
- the number of segments containing redo[/undo] data; this value is expected to already be unpacked from the log record
-
applyUndoAndGenRedoOnlyData
private byte[] applyUndoAndGenRedoOnlyData(DBFileReader walReader, DBPage dbPage, int numSegments)
This helper method uses aWALRecordType.UPDATE_PAGE
record to undo changes to a data page, and at the same time the method generates the data that must go into a corresponding redo-only WAL record.- Parameters:
walReader
- A reader positioned at the start of the redo/undo data to apply to the data page. This method will advance the reader's position past this redo/undo data.dbPage
- the data page that undo operations should be applied tonumSegments
- the number of segments in the redo/undo data- Returns:
- a byte-array containing the same number of segments as the original update record, with only the data necessary for the redo-only record.
-
writeRedoOnlyUpdatePageRecord
private LogSequenceNumber writeRedoOnlyUpdatePageRecord(int transactionID, LogSequenceNumber prevLSN, DBPage dbPage, int numSegments, byte[] changes)
This method writes a redo-only update-page record to the write-ahead log, including only redo details. The transaction state is passed explicitly so that this method can be used during recovery processing. The alternate methodwriteRedoOnlyUpdatePageRecord(DBPage, int, byte[])
retrieves the transaction state from thread-local storage, and should be used during normal operation.- Parameters:
transactionID
- the transaction ID that the WAL record is for.prevLSN
- the log sequence number of the transaction's immediately previous WAL record.dbPage
- The data page whose changes are to be recorded in the log.numSegments
- The number of segments in the change-data to record.changes
- The actual changes themselves, serialized to a byte array.- Returns:
- the Log Sequence Number of the WAL record that was written
- Throws:
java.lang.IllegalArgumentException
- ifdbPage
isnull
, or ifchanges
isnull
.
-
writeRedoOnlyUpdatePageRecord
private LogSequenceNumber writeRedoOnlyUpdatePageRecord(DBPage dbPage, int numSegments, byte[] changes)
This method writes a redo-only update-page record to the write-ahead log, including only redo details. The transaction state is taken from thread-local storage; this method should be used during normal operation. The alternate methodwriteRedoOnlyUpdatePageRecord(int, LogSequenceNumber, DBPage, int, byte[])
should be used during recovery processing when transaction state is specifically known.- Parameters:
dbPage
- The data page whose changes are to be recorded in the log.numSegments
- The number of segments in the change-data to record.changes
- The actual changes themselves, serialized to a byte array.- Returns:
- the Log Sequence Number of the WAL record that was written
- Throws:
java.lang.IllegalArgumentException
- if dbPage is null, or if changes is null.
-
rollbackTransaction
public void rollbackTransaction()
This method performs the operations necessary to rollback the current transaction from the database. The transaction details are taken from the transaction state stored in thread-local storage. This method is not used during recovery processing; theperformUndo(edu.caltech.nanodb.storage.writeahead.RecoveryInfo)
method is used to rollback all incomplete transactions in the logs.
-
-