Exercise 2: A Page That's Only Read, Never Written — Possible Solution ==================================================================== THE TEST ------------------------------ real_heap = HeapFile(heap_path) pn_a = real_heap.allocate_page() pn_b = real_heap.allocate_page() page_b_content = Page(); page_b_content.add_record(b'PAGE-B-DATA') real_heap.write_page(pn_b, page_b_content) # set up OUTSIDE any transaction txn = Transaction() thf = TransactionalHeapFile(real_heap, txn, 'x') _ = thf.read_page(pn_b) # only READ page B -- never written page_a = Page(); page_a.add_record(b'PAGE-A-NEW-DATA') thf.write_page(pn_a, page_a) # WRITE page A print(len(txn.undo_log)) txn.rollback({'x': real_heap}) after_b = bytes(real_heap.read_page(pn_b).data) RESULT ------------------------------ undo log entries (should only cover the WRITTEN page): 1 page B (only ever read) still has its real content after rollback: True The undo log has exactly one entry -- for page A, the page that was actually written. Page B, which the transaction only ever read, comes through rollback completely untouched, with its original content still intact. WHY READS NEVER TOUCH THE UNDO LOG AT ALL ------------------------------ Looking at TransactionalHeapFile's own two relevant methods: def read_page(self, page_num): return self.real.read_page(page_num) # no transaction involved def write_page(self, page_num, page): self.transaction.record_write(...) # THIS is what logs anything self.real.write_page(page_num, page) read_page() is a completely plain pass-through to the real heap file -- it never calls self.transaction.record_write() anywhere. Only write_page() (and allocate_page(), which itself calls write_page() internally) ever adds anything to the undo log. A page that a transaction merely reads -- to check a value, look something up, decide what to do next -- leaves no trace in the transaction at all, and rollback() has no way to even know that page was looked at, because record_write() was never called for it. WHY THIS MATTERS ------------------------------ If reads were mistakenly logged too, rollback() would be forced to "restore" pages the transaction never actually changed -- harmless in this specific test, since the restore would just write back the exact same bytes that were already there, but wasteful (every read anywhere in a transaction would cost an extra disk write during rollback) and a genuine correctness risk in a system with concurrent transactions (Chapters 5-6): overwriting a page during rollback that another transaction had legitimately modified in the meantime, just because the first transaction happened to have read it earlier, would destroy real, unrelated work. WHY THIS WORKS AS AN ANSWER ------------------------------ Confirming the undo log's own size stays at exactly 1 -- not 2 -- after one read and one write demonstrates concretely that Transaction tracks WRITES specifically, not general "pages touched," which is the correct and necessary scope for what an undo log is actually protecting.