Scenario Setup:
- Pages: P, P
- Transactions: T, T, T
- Initial State: Both P and P on disk have some initial data. Their
PageLSNon disk is . The buffer pool is initially empty/clean.
Simulated Log Sequence (Normal Operation + Crash):
Assume the following log records are written sequentially to stable storage. We also note the state of the relevant data structures (in memory) as things progress before the crash.
- LSN :
Update(T1, P1, PrevLSN=0, Redo=Op1, Undo=UndoOp1)- Effect: T1 modifies P1 in the buffer pool.
- TxnTable:
{ T1: {Status: Running, LastLSN: $10$} } - DPT:
{ P1: {RecLSN: $10$} }(P1 becomes dirty)
- LSN :
Update(T2, P2, PrevLSN=0, Redo=Op2, Undo=UndoOp2)- Effect: T2 modifies P2 in the buffer pool.
- TxnTable:
{ T1: {Status: Running, LastLSN: $10$}, T2: {Status: Running, LastLSN: $20$} } - DPT:
{ P1: {RecLSN: $10$}, P2: {RecLSN: $20$} }(P2 becomes dirty)
- LSN :
Update(T1, P2, PrevLSN=10, Redo=Op3, Undo=UndoOp3)- Effect: T1 modifies P2 in the buffer pool.
- TxnTable:
{ T1: {Status: Running, LastLSN: $30$}, T2: {Status: Running, LastLSN: $20$} } - DPT:
{ P1: {RecLSN: $10$}, P2: {RecLSN: $20$} }(P2 already dirty,RecLSNdoesn’t change)
- LSN :
BeginCheckpoint - LSN :
EndCheckpoint( ActiveTxn=[T1(LastLSN=$30$), T2(LastLSN=$20$)], DirtyPages=[P1(RecLSN=$10$), P2(RecLSN=$20$)] )- Effect: Checkpoint written, recording the current TxnTable and DPT state.
- LSN :
Update(T3, P1, PrevLSN=0, Redo=Op4, Undo=UndoOp4)- Effect: T3 starts and modifies P1 in the buffer pool.
- TxnTable:
{ T1: {Status: Running, LastLSN: $30$}, T2: {Status: Running, LastLSN: $20$}, T3: {Status: Running, LastLSN: $60$} } - DPT:
{ P1: {RecLSN: $10$}, P2: {RecLSN: $20$} }(P1 already dirty,RecLSNdoesn’t change)
- LSN :
Commit(T2)- Effect: T2 successfully commits. Its log records (LSN , ) are stable.
- TxnTable:
{ T1: {Status: Running, LastLSN: $30$}, T2: {Status: Committed, LastLSN: $20$}, T3: {Status: Running, LastLSN: $60$} }
- LSN :
Update(T1, P1, PrevLSN=30, Redo=Op5, Undo=UndoOp5)- Effect: T1 modifies P1 again.
- TxnTable:
{ T1: {Status: Running, LastLSN: $80$}, T2: {Status: Committed, LastLSN: $20$}, T3: {Status: Running, LastLSN: $60$} } - DPT:
{ P1: {RecLSN: $10$}, P2: {RecLSN: $20$} }
- CRASH! Assume the system crashes now. All log records up to LSN are safe on disk. Assume no pages were flushed to disk after the checkpoint (so P and P on disk still have
PageLSN = 0).
Recovery Process:
Phase 1: Analysis
- Find Checkpoint: Locate the last complete checkpoint (LSN - ).
- Load State: Initialize the TxnTable and DPT from the
EndCheckpointrecord (LSN ):- TxnTable:
{ T1: {Status: Running, LastLSN: $30$}, T2: {Status: Running, LastLSN: $20$} } - DPT:
{ P1: {RecLSN: $10$}, P2: {RecLSN: $20$} }
- TxnTable:
- Scan Forward from Checkpoint (LSN ):
- LSN :
Update(T3, P1, ...)- Add T3 to TxnTable:
{ ..., T3: {Status: Running, LastLSN: $60$} } - P1 already in DPT, no change to DPT.
- Add T3 to TxnTable:
- LSN :
Commit(T2)- Update T2 status in TxnTable:
{ ..., T2: {Status: Committed, LastLSN: $20$}, ... }
- Update T2 status in TxnTable:
- LSN :
Update(T1, P1, ...)- Update T1 LastLSN in TxnTable:
{ T1: {Status: Running, LastLSN: $80$}, ... } - P1 already in DPT, no change to DPT.
- Update T1 LastLSN in TxnTable:
- LSN :
- End of Log: Scan finishes.
- Analysis Outcome:
- Final TxnTable:
{ T1: {Status: Running, LastLSN: $80$}, T2: {Status: Committed, LastLSN: $20$}, T3: {Status: Running, LastLSN: $60$} } - Final DPT:
{ P1: {RecLSN: $10$}, P2: {RecLSN: $20$} } - Loser Transactions: T1, T3 (Status is ‘Running’).
redoLSN:min(RecLSN values in DPT)=min() = .
- Final TxnTable:
Phase 2: Redo (“Repeating History”)
- Start Scan: Begin scanning the log forward from `redoLSN = .
- Process Log Records:
- LSN :
Update(T1, P1, ...)- Is P in DPT? Yes. Is
RecLSN(P)= LSN ? Yes. - Fetch P (Disk
PageLSN=). IsPageLSN() < LSN()? Yes. - Redo Op1 on P (in buffer). Set P’s buffer
PageLSN= .
- Is P in DPT? Yes. Is
- LSN :
Update(T2, P2, ...)- Is P in DPT? Yes. Is
RecLSN(P)= LSN ? Yes. - Fetch P (Disk
PageLSN=). IsPageLSN() < LSN()? Yes. - Redo Op2 on P (in buffer). Set P’s buffer
PageLSN= .
- Is P in DPT? Yes. Is
- LSN :
Update(T1, P2, ...)- Is P in DPT? Yes. Is
RecLSN(P)= LSN ? Yes. - Fetch P (Buffer
PageLSN=). IsPageLSN() < LSN()? Yes. - Redo Op3 on P (in buffer). Set P’s buffer
PageLSN= .
- Is P in DPT? Yes. Is
- LSN :
BeginCheckpoint- Skip. - LSN :
EndCheckpoint- Skip. - LSN :
Update(T3, P1, ...)- Is P in DPT? Yes. Is
RecLSN(P)= LSN ? Yes. - Fetch P (Buffer
PageLSN=). IsPageLSN() < LSN()? Yes. - Redo Op4 on P (in buffer). Set P’s buffer
PageLSN= .
- Is P in DPT? Yes. Is
- LSN :
Commit(T2)- Skip (no data modification). - LSN :
Update(T1, P1, ...)- Is P in DPT? Yes. Is
RecLSN(P)= LSN ? Yes. - Fetch P (Buffer
PageLSN=). IsPageLSN() < LSN()? Yes. - Redo Op5 on P (in buffer). Set P’s buffer
PageLSN= .
- Is P in DPT? Yes. Is
- LSN :
- End of Log: Redo phase finishes.
- Redo Outcome: The buffer pool now reflects the exact state at the moment of crash.
- P contains effects of Op1 (LSN ), Op4 (LSN ), Op5 (LSN ). Buffer
PageLSN= . - P contains effects of Op2 (LSN ), Op3 (LSN ). Buffer
PageLSN= .
- P contains effects of Op1 (LSN ), Op4 (LSN ), Op5 (LSN ). Buffer
Phase 3: Undo
- Identify Losers: From Analysis, losers are T1 and T3.
- Initialize
ToUndo: GetLastLSNfor each loser:ToUndo= { (from T1), (from T3) }. - Scan Backward: Start processing from
max() = .- Process LSN :
Update(T1, P1, PrevLSN=30, ...)- Is LSN in
ToUndo? Yes. - Write CLR: Append
CLR(T1, P1, UndoNextLSN=30, Redo=None, Undo=UndoOp5)to log. Let’s say this gets LSN . (Log LSN must be stable). - Apply Undo: Perform UndoOp5 on P in the buffer.
- Set PageLSN: Set P’s buffer
PageLSN= (the LSN of the CLR). - Update
ToUndo: Remove . AddUndoNextLSN() toToUndo. ToUndo= { }. Next LSN =max() = .
- Is LSN in
- Process LSN :
Commit(T2)- Skip (not inToUndo). - Process LSN :
Update(T3, P1, PrevLSN=0, ...)- Is LSN in
ToUndo? Yes. - Write CLR: Append
CLR(T3, P1, UndoNextLSN=0, Redo=None, Undo=UndoOp4)to log. Let’s say this gets LSN . (Log LSN must be stable). - Apply Undo: Perform UndoOp4 on P in the buffer.
- Set PageLSN: Set P’s buffer
PageLSN= . - Update
ToUndo: Remove . AddUndoNextLSN() (i.e., add nothing). ToUndo= { }. Next LSN = .
- Is LSN in
- Process LSN :
EndCheckpoint- Skip. - Process LSN :
BeginCheckpoint- Skip. - Process LSN :
Update(T1, P2, PrevLSN=10, ...)- Is LSN in
ToUndo? Yes. - Write CLR: Append
CLR(T1, P2, UndoNextLSN=10, Redo=None, Undo=UndoOp3)to log. Let’s say this gets LSN . (Log LSN must be stable). - Apply Undo: Perform UndoOp3 on P in the buffer.
- Set PageLSN: Set P’s buffer
PageLSN= . - Update
ToUndo: Remove . AddUndoNextLSN() toToUndo. ToUndo= { }. Next LSN = .
- Is LSN in
- Process LSN :
Update(T2, P2, ...)- Skip (T2 is not a loser). - Process LSN :
Update(T1, P1, PrevLSN=0, ...)- Is LSN in
ToUndo? Yes. - Write CLR: Append
CLR(T1, P1, UndoNextLSN=0, Redo=None, Undo=UndoOp1)to log. Let’s say this gets LSN . (Log LSN must be stable). - Apply Undo: Perform UndoOp1 on P in the buffer.
- Set PageLSN: Set P’s buffer
PageLSN= . - Update
ToUndo: Remove . AddUndoNextLSN(). ToUndo= {}.
- Is LSN in
- Process LSN :
ToUndois Empty: The main backward scan is complete.- Write Abort Records: Append
Abort(T1)andAbort(T3)log records (e.g., LSN and LSN ). - Undo Outcome:
- All effects of loser transactions T1 and T3 have been undone.
- P is back to its initial state (UndoOp4 undid Op4, UndoOp5 undid Op5, UndoOp1 undid Op1). Its
PageLSNis . - P now only contains the effects of the committed transaction T2 (Op2 from LSN ). UndoOp3 undid Op3 (from LSN ). Its
PageLSNis . - The database is now consistent, reflecting only the committed work of T2.
Recovery is complete. The database can now accept new transactions. Note how CLRs ensured that undo operations were logged and wouldn’t be re-attempted incorrectly if a crash happened during the Undo phase itself.