I changed the class Transaction
to keep track of the active transactions. When a new tx is
created, its id is added to the list, and when the tx finishes (commit
or rollback), its id is removed from the list. The constructor
also decides when to write an NQCKPT record into the log, calling the new RecoveryMgr method checkpoint.
I added the new class NQCheckpointRecord. The first constructor takes the list of active transactions as its argument, and the method writeToLog writes them to the log. The second constructor (the one that takes a BasicLogRecord argument) reads an integer denoting the number of txs in the list, and then reads that many integers. Those values are used by the recover method. Some people assumed that they could keep the list in a static variable, and just use it as needed. However, recovery occurs after a system crash, which means that that variable's contents will have disappeared. You need to use the log.
I changed RecoveryMgr as described in the assignment. Two parts of the recover method needed changing. The first part is that you needed to add a section dealing with an NQCKPT record. The code will get the tx list from the record (which got it from the log), and remove any completed txs from that list. If the list becomes empty, you can break out of the loop and stop. Most people got that. But what happens if the list is not empty? The recovery needs to continue reading the log until the START record for all of the txs on the list have been encountered. In other words, you need to add a section to the loop dealing with a START record. Each time you encounter a START, you remove it from the list and check for empty. Very few people thought to do that part.
Changes to LogRecord and LogRecordIterator were necessary, but they were trivial and everyone got them.