|
|
@@ -4,28 +4,27 @@ import (
|
|
|
"fmt"
|
|
|
)
|
|
|
|
|
|
-// Transaction 事务管理器(极简版)
|
|
|
+// Transaction 事务管理器
|
|
|
//
|
|
|
// 核心思想:
|
|
|
-// - 事务内操作正常写 BufferPool(标记 dirty)
|
|
|
-// - Commit 时收集脏页 + WAL + fsync 落盘(保证持久化)
|
|
|
-// - Rollback 时仅标记回滚,实际回滚依赖 WAL 恢复机制
|
|
|
-//
|
|
|
-// 注意:
|
|
|
-// - 这是简化实现,适合单线程场景
|
|
|
-// - 多线程并发事务需要更复杂的隔离机制
|
|
|
+// - 事务开始时,记录所有被修改页的原始数据(快照)
|
|
|
+// - 事务内操作正常修改 BufferPool
|
|
|
+// - Commit 时将修改后的脏页写盘(WAL + fsync)
|
|
|
+// - Rollback 时用原始数据快照覆盖 BufferPool 中的脏页(立即生效)
|
|
|
type Transaction struct {
|
|
|
- db *DB
|
|
|
- committed bool
|
|
|
- rolledBack bool
|
|
|
+ db *DB
|
|
|
+ originalPages map[uint32][]byte // pageID → 原始数据快照(用于 Rollback)
|
|
|
+ committed bool
|
|
|
+ rolledBack bool
|
|
|
}
|
|
|
|
|
|
// BeginTransaction 开启新事务
|
|
|
func (db *DB) BeginTransaction() *Transaction {
|
|
|
txn := &Transaction{
|
|
|
- db: db,
|
|
|
- committed: false,
|
|
|
- rolledBack: false,
|
|
|
+ db: db,
|
|
|
+ originalPages: make(map[uint32][]byte),
|
|
|
+ committed: false,
|
|
|
+ rolledBack: false,
|
|
|
}
|
|
|
|
|
|
// 设置当前活跃事务
|
|
|
@@ -34,6 +33,23 @@ func (db *DB) BeginTransaction() *Transaction {
|
|
|
return txn
|
|
|
}
|
|
|
|
|
|
+// cacheOriginalPage 缓存页的原始数据(首次修改时调用)
|
|
|
+func (txn *Transaction) cacheOriginalPage(pageID uint32) error {
|
|
|
+ // 如果已经缓存过,不需要重复缓存
|
|
|
+ if _, exists := txn.originalPages[pageID]; exists {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从磁盘读取原始数据
|
|
|
+ buf := make([]byte, PageSize)
|
|
|
+ if err := txn.db.file.readPageDisk(pageID, buf); err != nil {
|
|
|
+ return fmt.Errorf("failed to read page %d for rollback cache: %v", pageID, err)
|
|
|
+ }
|
|
|
+
|
|
|
+ txn.originalPages[pageID] = buf
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
// Commit 提交事务
|
|
|
// 1. 收集所有 BufferPool 脏页
|
|
|
// 2. 写入 WAL
|
|
|
@@ -75,13 +91,8 @@ func (txn *Transaction) Commit() error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-// Rollback 回滚事务(简化版)
|
|
|
-// 当前实现:仅标记回滚,不清除 BufferPool
|
|
|
-// 实际回滚依赖:
|
|
|
-// 1. 下次打开数据库时的 WAL 恢复机制
|
|
|
-// 2. 或者手动调用 db.RecoverFromWAL()
|
|
|
-//
|
|
|
-// TODO: 未来可以实现立即回滚(从磁盘 reload 脏页)
|
|
|
+// Rollback 回滚事务
|
|
|
+// 用原始数据快照覆盖 BufferPool 中的脏页(立即生效)
|
|
|
func (txn *Transaction) Rollback() error {
|
|
|
if txn.committed {
|
|
|
return fmt.Errorf("transaction already committed, cannot rollback")
|
|
|
@@ -90,24 +101,22 @@ func (txn *Transaction) Rollback() error {
|
|
|
return fmt.Errorf("transaction already rolled back")
|
|
|
}
|
|
|
|
|
|
- // 标记回滚
|
|
|
+ // 用原始数据覆盖所有被修改的页
|
|
|
+ for pageID, originalData := range txn.originalPages {
|
|
|
+ // 直接写回 BufferPool(绕过 WAL)
|
|
|
+ if err := txn.db.file.WritePage(pageID, originalData); err != nil {
|
|
|
+ return fmt.Errorf("failed to rollback page %d: %v", pageID, err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清理事务状态
|
|
|
txn.rolledBack = true
|
|
|
txn.db.currentTxn = nil
|
|
|
-
|
|
|
- // 注意:这里没有清除 BufferPool 中的脏页
|
|
|
- // 如果需要立即回滚,可以调用 txn.rollbackBufferPool()
|
|
|
+ txn.originalPages = nil
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-// rollbackBufferPool 从磁盘重新加载所有脏页(可选实现)
|
|
|
-// 这个方法可以在 Rollback 时调用,实现立即回滚
|
|
|
-// TODO: 需要 File 层提供 GetDirtyPages() 方法
|
|
|
-func (txn *Transaction) rollbackBufferPool() error {
|
|
|
- // TODO: 实现立即回滚逻辑
|
|
|
- return fmt.Errorf("rollbackBufferPool not yet implemented")
|
|
|
-}
|
|
|
-
|
|
|
// IsCommitted 检查事务是否已提交
|
|
|
func (txn *Transaction) IsCommitted() bool {
|
|
|
return txn.committed
|