transaction.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package core
  2. import (
  3. "fmt"
  4. )
  5. // Transaction 事务管理器
  6. //
  7. // 核心思想:
  8. // - 事务开始时,记录所有被修改行的原始数据和新数据
  9. // - 事务内操作正常修改 BufferPool
  10. // - Commit 时将行级数据写入 WAL(节省空间)
  11. // - Rollback 时用原始数据覆盖 BufferPool 中的脏页(立即生效)
  12. type Transaction struct {
  13. db *DB
  14. originalPages map[uint32][]byte // pageID → 原始页数据快照(用于 Rollback)
  15. rowChanges map[uint32]struct{ original, new []byte } // pageID → {原始行, 新行}(用于 WAL)
  16. committed bool
  17. rolledBack bool
  18. }
  19. // BeginTransaction 开启新事务
  20. func (db *DB) BeginTransaction() *Transaction {
  21. txn := &Transaction{
  22. db: db,
  23. originalPages: make(map[uint32][]byte),
  24. rowChanges: make(map[uint32]struct{ original, new []byte }),
  25. committed: false,
  26. rolledBack: false,
  27. }
  28. // 设置当前活跃事务
  29. db.currentTxn = txn
  30. return txn
  31. }
  32. // cacheOriginalPage 缓存页的原始数据(首次修改时调用)
  33. func (txn *Transaction) cacheOriginalPage(pageID uint32) error {
  34. // 如果已经缓存过,不需要重复缓存
  35. if _, exists := txn.originalPages[pageID]; exists {
  36. return nil
  37. }
  38. // 从磁盘读取原始数据
  39. buf := make([]byte, PageSize)
  40. if err := txn.db.file.readPageDisk(pageID, buf); err != nil {
  41. return fmt.Errorf("failed to read page %d for rollback cache: %v", pageID, err)
  42. }
  43. txn.originalPages[pageID] = buf
  44. return nil
  45. }
  46. // RecordRowChange 记录行级变化(用于 WAL)
  47. // originalRow: 原始行数据
  48. // newRow: 新行数据
  49. func (txn *Transaction) RecordRowChange(pageID uint32, originalRow []byte, newRow []byte) {
  50. txn.rowChanges[pageID] = struct{ original, new []byte }{
  51. original: originalRow,
  52. new: newRow,
  53. }
  54. }
  55. // Commit 提交事务
  56. // 1. 收集所有 BufferPool 脏页
  57. // 2. 写入 WAL
  58. // 3. ⭐ 关键:fsync 确保落盘 ⭐
  59. // 4. 回收 WAL 页
  60. // 5. 清理原始数据快照(释放内存)
  61. func (txn *Transaction) Commit() error {
  62. if txn.committed {
  63. return fmt.Errorf("transaction already committed")
  64. }
  65. if txn.rolledBack {
  66. return fmt.Errorf("transaction already rolled back")
  67. }
  68. // 1. 创建 WAL 事务
  69. wal := txn.db.BeginTxn()
  70. // 2. 记录行级数据到 WAL(如果有的话)
  71. if len(txn.rowChanges) > 0 {
  72. for pageID, change := range txn.rowChanges {
  73. wal.RecordRowLevel(pageID, change.original, change.new)
  74. }
  75. } else {
  76. // Fallback: 整页模式
  77. dirty := txn.db.file.CollectDirty()
  78. if len(dirty) == 0 {
  79. txn.committed = true
  80. txn.db.currentTxn = nil
  81. txn.originalPages = nil
  82. txn.rowChanges = nil
  83. return nil // 无脏页,直接返回
  84. }
  85. for _, d := range dirty {
  86. wal.RecordPage(d.PageID, d.Data)
  87. }
  88. }
  89. // 4. ⭐ 关键:Commit 会触发 fsync,确保落盘 ⭐
  90. if err := wal.Commit(); err != nil {
  91. return fmt.Errorf("WAL commit failed: %v", err)
  92. }
  93. // 5. 清理事务状态 + 释放原始数据快照
  94. txn.committed = true
  95. txn.db.currentTxn = nil
  96. txn.originalPages = nil // ← 提交成功,原始数据不再需要
  97. txn.rowChanges = nil // ← 清理行级变化
  98. return nil
  99. }
  100. // Rollback 回滚事务
  101. // 用原始数据快照覆盖 BufferPool 中的脏页(立即生效)
  102. // 回滚后清理所有缓存数据(释放内存)
  103. func (txn *Transaction) Rollback() error {
  104. if txn.committed {
  105. return fmt.Errorf("transaction already committed, cannot rollback")
  106. }
  107. if txn.rolledBack {
  108. return fmt.Errorf("transaction already rolled back")
  109. }
  110. // 用原始数据覆盖所有被修改的页
  111. for pageID, originalData := range txn.originalPages {
  112. // 直接写回 BufferPool(绕过 WAL)
  113. if err := txn.db.file.WritePage(pageID, originalData); err != nil {
  114. return fmt.Errorf("failed to rollback page %d: %v", pageID, err)
  115. }
  116. }
  117. // 清理事务状态 + 释放所有缓存数据
  118. txn.rolledBack = true
  119. txn.db.currentTxn = nil
  120. txn.originalPages = nil // ← 回滚成功,原始数据和修改数据都不再需要
  121. txn.rowChanges = nil // ← 清理行级变化
  122. return nil
  123. }
  124. // IsCommitted 检查事务是否已提交
  125. func (txn *Transaction) IsCommitted() bool {
  126. return txn.committed
  127. }
  128. // IsRolledBack 检查事务是否已回滚
  129. func (txn *Transaction) IsRolledBack() bool {
  130. return txn.rolledBack
  131. }