Exercise 1: Two Writes, Crash Right After Logging the Second — Possible Solution ==================================================================== THE TEST ------------------------------ heap = HeapFile(heap_path) wal = WAL(wal_path) pn1 = heap.allocate_page() pn2 = heap.allocate_page() page1 = Page(); page1.add_record(b'ROW-ONE-DATA') write_page_with_wal(wal, heap, pn1, page1) # fully completed: logged AND applied page2 = Page(); page2.add_record(b'ROW-TWO-DATA') wal.append(pn2, bytes(page2.data)) # logged only -- crash simulated here heap_after = HeapFile(heap_path) p1_after = heap_after.read_page(pn1) p2_after = heap_after.read_page(pn2) records = read_all_records(wal_path) RESULT ------------------------------ page 1 (fully completed before crash) matches intended data: True page 2 (crash happened right after logging it) matches OLD/blank data: True WAL record count after crash: 2 -- contains BOTH page 1 and page 2's updates The data file has page 1's real update applied, but page 2 is still a blank, freshly-allocated page -- the crash genuinely stopped it from being written. The WAL, however, holds records for BOTH writes, because both of them completed their own logging step before the crash occurred. WHY REAPPLYING AN ALREADY-APPLIED RECORD HAS TO BE SAFE ------------------------------ A real recovery routine reading this WAL after the crash has no way to know, just by looking at the log, that page 1's own write was already fully applied to the data file -- the log only records what WAS INTENDED, not what has or hasn't landed yet. The simplest correct recovery strategy is therefore not "figure out exactly which records still need applying" but "reapply every record in the log, in order, unconditionally" -- and for that to be safe, applying an already-applied record has to produce the identical result as not reapplying it at all. This is exactly why Chapter 2's own WAL logs the COMPLETE new page image rather than a small delta (like "add 12 bytes at this offset"). Writing the same full page a second time is idempotent -- doing it twice leaves the exact same bytes on disk as doing it once. If the log instead stored a delta ("append these 12 bytes"), reapplying page 1's own already-completed record a second time would append the same 12 bytes AGAIN, corrupting a page that was already correct. The "log the whole new page" design decision, which looked like a simplicity shortcut in Chapter 2's own opening code block, turns out to be the exact property recovery needs to work correctly without having to track precisely which records already landed. WHY THIS WORKS AS AN ANSWER ------------------------------ Confirming the WAL genuinely runs "ahead of" the data file after a crash -- holding an update the data file hasn't caught up to yet -- demonstrates the real gap Chapter 3's own recovery routine has to close, and explains, with a concrete mechanism rather than just an assertion, why full-page logging was the right design choice for making that recovery routine simple and safe.