The Repair Class
The physical log contains copies of only those sections of the data file that have changed. To restore data from the physical log, in the case of a failure, you open the log and apply each entry to the data file. The PhysicalLog class constructor opens the physical log, and its readEntry() method lets you read PhysicalLogEntry objects.
All of this is carried out by a class called Repair (see Listing 3). Repair also has a main() method, which means you can run it from the command line to repair a data file after a crash. In Listing 3, look especially at the first method, repair(), which does the actual repair.
It's safe to call Repair.repair() on a file every time you start your application. It will first check to see if the file actually needs to be repaired. If it does, it repairs it. It can tell whether or not a file needs to be repaired simply by checking to see if there is a physical log for that file lying around. If there is, then the file didn't checkpoint successfully the last time the program was run, and is therefore corrupt. If there is no physical log, then the file was checkpointed properly, and no repair is necessary.
(Author's Note: Actually, this explanation isn't precisely correct. If the checkpointing process completes, but the program crashes before it deletes the physical log, then repair will think it's damaged even though it isn't. This scenario is very unlikely but it can happen. Fortunately, the only cost of repairing an already-repaired file is a little bit of wasted time.)
Crashes can happen during regular writing, but they can also occur during checkpointing or the repair process. Luckily, this doesn't complicate things for us much: we can just repair the file in the normal way.
You'll also notice that there's a flag called repairing in CKPTFile. When Repair is repairing a datafile, it does this by writing to it, which of course generates yet another physical log. If repairing is set to true, then this file is given a different name, so that it doesn't conflict with the real physical log, which is needed to actually repair the file.
Putting it All Together
We've now seen all of the parts of our system, but it's important to see the big picture. Figure 8 shows the entire process of writing to the datafile, both for successful checkpoints and for crashes.
|Figure 8. The Big Picture: This flowchart shows each of the cases that we have to deal with, including successful writes, interrupted writes, and interrupted repairs.|
There are a couple of options you can set in the code:
PhysicalLog.justArchive. This option tells the system what to do with physical logs once they are no longer needed after a checkpoint. If it is set to false, they are deleted. If it is set to true, the files are renamed and kept. This can be useful because a full set of physical logs contains the entire history of the changes made to the datafile.
PhysicalLog.safely. This option determines whether or not PhysicalLog flushes its data after each write. Setting this to true turns the flushing on, which is safer but much slower. Note that this doesn't control the flushing of the datafile (which happens during checkpointing) but of the physical log. If you go the unsafe route (false) your data will survive a program crash but may not survive a powerful failure. If you go the safe route (true), your data will survive either situation.