Exercise 1: Two Simultaneous Crashes, Two Independent Recoveries — Possible Solution ==================================================================== THE TEST ------------------------------ cat = FixedDurableCatalog(tmp) cat.create_table('customers', [('id','INTEGER'),('name','TEXT')]) cat.create_table('products', [('id','INTEGER'),('name','TEXT'),('stock','INTEGER')]) # ... insert one real committed row into each table ... # simulate a crash affecting BOTH tables: log a write, never apply it, for EACH cust_page = Page(); cust_page.add_record(encode_row(cat.schemas['customers'], [2, 'Bob'])) cat.wals['customers'].append(cat.real_heaps['customers'].num_pages(), bytes(cust_page.data)) prod_page = Page(); prod_page.add_record(encode_row(cat.schemas['products'], [11, 'Gadget', 50])) cat.wals['products'].append(cat.real_heaps['products'].num_pages(), bytes(prod_page.data)) fresh_cust = HeapFile(os.path.join(tmp, 'customers.heap')) fresh_prod = HeapFile(os.path.join(tmp, 'products.heap')) recover(fresh_cust, os.path.join(tmp, 'customers.wal')) recover(fresh_prod, os.path.join(tmp, 'products.wal')) RESULT ------------------------------ recovered customers: [[1, 'Alice'], [2, 'Bob']] recovered products: [[10, 'Widget', 100], [11, 'Gadget', 50]] Both tables recover correctly and completely -- customers gets back Alice (already committed) plus Bob (recovered from the crash), and products gets back Widget (already committed) plus Gadget (recovered). Neither table's own data shows any trace of the other. WHY RECOVERING TWO TABLES AT ONCE IS SAFE ------------------------------ recover(heap_file, wal_path) only ever touches the ONE heap_file and ONE wal_path it's given. Calling it once for customers/customers.wal and once for products/products.wal means each call reads a real, physically SEPARATE file on disk -- there's no shared state between the two calls at all, not even a shared Python object. This is the direct payoff of Step 7's own fix: because each table's WAL only ever contains that table's own records (bare page_num values that are only meaningful within that one table's own heap file), running recover() for customers can never accidentally pick up a byte that belongs to products, and vice versa. WHY THIS WOULD HAVE FAILED BEFORE THE FIX ------------------------------ With the ORIGINAL, buggy shared-WAL design from Steps 6-7, both of these crash-simulation writes would have landed in the SAME catalog.wal file, both using small page_num values (0 or 1, since both tables are nearly empty) -- genuinely ambiguous, exactly like the original bug. A single recover() call against that one shared WAL, applied to either heap file, would have replayed records meant for the OTHER table onto it, corrupting data exactly as Step 7's own finding demonstrated. WHY THIS WORKS AS AN ANSWER ------------------------------ Deliberately crashing TWO tables at once, rather than one at a time as the main capstone body did, stress-tests the fix under the condition most likely to expose a remaining bug -- simultaneous recovery activity across multiple tables -- and confirms the per-table WAL design holds up cleanly under it.