Currently: Building reliable systems out of unreliable components. We're working on implementing transactions which provide:
Atomicity
Isolation
So far: Have a poorly-performing version of atomicity via shadow copies.
Today: Logging, which will give us reasonable performance for atomicity. Logging also works when we have multiple concurrent transactions, even though for today we're not thinking about concurrency.
Motivating Example
begin
// T1
A = 100
B = 50
commit
// At commit: A=100; B=50
begin
// T2
A = A - 20
B = B + 20
commit
// At commit: A=80; B=70
begin
// T3
A = A + 30
—CRASH—
Problem: A = 110, but T3 didn't commit. We need to revert.
Basic Idea
Keep a log of all changes and whether a transaction commits or aborts.
Every transaction gets a unique ID.
UPDATE records include old an new values of a variable.
COMMIT records specify that transaction committed..
What if we crash during recovery? No worries; recover() is idempotent. Can do it repeatedly.
How to write:
Log before install, not the other way; otherwise, can't recover from a crash in between the two writes.
This is write-ahead logging.
Performance of Log + Cell Storage
Writes: Okay, but now we write to disk twice instead of once.
Reads: Fast.
Recovery: Bad. Have to scan the entire log.
Improving Performance
Improve writes: Use a (volatile) cache.
Reads go to cache first, writes go to cache and are eventually flushed to cell storage.
Problem: After crash, there may be updates that didn't make it to cell storage (were in cache but not flushed).
Also could be updates in cell storage that need to be undone, but we had that problem before.
Solution: We need a redo phase in addition to an undo phase in our recovery.
Improving recovery:
Problem: Recovery takes longer and longer as the log grows.
Solution: Truncate the log.
How?
Assuming no pending actions:
Flush all cached updates to cell storage.
Write a CHECKPOINT record.
Truncate the log prior to the CHECKPOINT record.
Usually amounts to deleting a file.
With pending actions, delete before the checkpoint and earliest undecided record.
ABORT records
Can be used to help recovery and skip undo-ing aborted transaction. Not necessary for correctness—can always just pretend we crashed—but can help.
What about Un-undo-able Actions?
What if our transaction fires a missile and then aborts?
Typically: Wait for software that controls the action to commit and then take the action, but have a special way to detect whether the action has/will happened.
Summary
Logging is a general technique for achieving atomicity.
Writes are fast, reads can be fast with cell storage.
Need to log before installing (write-ahead), and need a recovery process.
Tomorrow is recitation: Logging for file systems.
Now: We're good with atomicity.
In fact, logging will work fine with concurrent transactions; the problem will be figuring out which steps we can actually run in parallel.