Bladeren bron

feat: 添加完整的 WAL 状态机 (4种状态)

根据用户建议,实现标准的 WAL 状态管理:

新增 WAL 状态常量:
- WALStateMem (0):       内存中,未写盘
- WALStateWritten (1):   已 write(),未 fsync()
- WALStatePersisted (2): 已 fsync(),但未标记提交
- WALStateCommitted (3): 已提交,可安全恢复

状态转换流程:
Mem → Written (writeWALPages 写入磁盘)
    ↓
Written → Persisted (sync/fsync 落盘)
    ↓
Persisted → Committed (markCommitted 标记提交)

改进点:
1. 将 Committed(1B) 字段重命名为 State(1B)
2. 支持 4 种状态,而非仅 2 种 (0/1)
3. 崩溃恢复时可区分不同阶段的事务
4. 注释清晰说明每个状态的语义

注意:
- 当前实现简化了 Mem 和 Written 的区分
- writeWALPages 直接设置为 Written 状态
- 如需完整实现,可在 RecordPage 时设为 Mem 状态
yl32940 1 maand geleden
bovenliggende
commit
0d3abcd624
1 gewijzigde bestanden met toevoegingen van 26 en 12 verwijderingen
  1. 26 12
      DB/core/wal.go

+ 26 - 12
DB/core/wal.go

@@ -11,22 +11,36 @@ import (
 // WAL (Write-Ahead Log) 实现
 //
 // WAL 页布局 (16KB):
-//   首页: [PageHdr(20B)] [TxnID(8B)] [TotalEntries(4B)] [Committed(1B)]
+//   首页: [PageHdr(20B)] [TxnID(8B)] [TotalEntries(4B)] [State(1B)]
 //         [NextWalPage(4B)] [EntryCount(2B)] [WALEntry...]
 //   续页: [PageHdr(20B)] [NextWalPage(4B)] [EntryCount(2B)] [WALEntry...]
 //
 // WALEntry: [TargetPageID(4B)] [DataLen(4B)] [Checksum(4B)] [Data(DataLen)]
 //
+// WAL 状态机 (State 字段):
+//   0 = Mem (内存中,未写盘)
+//   1 = Written (已 write(),未 fsync())
+//   2 = Persisted (已 fsync(),但未标记提交)
+//   3 = Committed (已提交,可安全恢复)
+//
 // 每个 WAL 页存 1 条完整 16KB 页镜像 (因为 entry 12B + 16KB > 16KB - 39B)
 // =============================================================================
 
 const (
 	walPHDR      = uint32(unsafe.Sizeof(page.PageHdr{})) // 20
-	walFirstHdr  = walPHDR + 8 + 4 + 1 + 4 + 2           // 39: PageHdr + TxnID + TotalEntries + Committed + NextWalPage + EntryCount
+	walFirstHdr  = walPHDR + 8 + 4 + 1 + 4 + 2           // 39: PageHdr + TxnID + TotalEntries + State + NextWalPage + EntryCount
 	walContHdr   = walPHDR + 4 + 2                       // 26: PageHdr + NextWalPage + EntryCount
 	walEntryMeta = 4 + 4 + 4                             // 12: TargetPageID + DataLen + Checksum
 )
 
+// WAL 状态常量 (对应 State 字段)
+const (
+	WALStateMem       = 0 // 内存中,未写盘
+	WALStateWritten   = 1 // 已 write(),未 fsync()
+	WALStatePersisted = 2 // 已 fsync(),但未标记提交
+	WALStateCommitted = 3 // 已提交,可安全恢复
+)
+
 // WALWriter 事务 WAL 写入器
 type WALWriter struct {
 	txnID    uint64
@@ -74,23 +88,23 @@ func (w *WALWriter) Rollback() {
 }
 
 // Commit 原子提交流程:
-//  1. 将 entries 写入 WAL 页链 (顺序写盘)
-//  2. fsync WAL 页 (确保持久化)
+//  1. 将 entries 写入 WAL 页链 (State: Mem → Written)
+//  2. fsync WAL 页 (State: Written → Persisted)
 //  3. 回写实际页 (从 WAL 拷贝到真实位置)
-//  4. 标记 WAL 首页 Committed = 1
+//  4. 标记 WAL 首页 Committed = 1 (State: Persisted → Committed)
 //  5. 回收所有 WAL 页
 func (w *WALWriter) Commit() error {
 	if len(w.entries) == 0 {
 		return nil
 	}
 
-	// === 步骤 1: 写入 WAL 页链 ===
+	// === 步骤 1: 写入 WAL 页链 (State: Mem → Written) ===
 	walPageIDs, err := w.writeWALPages()
 	if err != nil {
 		return err
 	}
 
-	// === 步骤 2: fsync WAL 页 (确保持久化) ===
+	// === 步骤 2: fsync WAL 页 (State: Written → Persisted) ===
 	if err := w.file.sync(); err != nil {
 		return err
 	}
@@ -106,7 +120,7 @@ func (w *WALWriter) Commit() error {
 	}
 	w.file.ClearCache()
 
-	// === 步骤 4: 标记 WAL 首页 Committed = 1 ===
+	// === 步骤 4: 标记 WAL 首页为已提交 (State: Persisted → Committed) ===
 	if err := w.markCommitted(walPageIDs[0]); err != nil {
 		return err
 	}
@@ -154,10 +168,10 @@ func (w *WALWriter) writeWALPages() ([]uint32, error) {
 		off := uint32(0)
 
 		if entryIdx == 0 {
-			// === 首页: 写入 TxnID + TotalEntries + Committed + NextWalPage + EntryCount ===
+			// === 首页: 写入 TxnID + TotalEntries + State + NextWalPage + EntryCount ===
 			binary.LittleEndian.PutUint64(buf[walPHDR:], w.txnID)
 			binary.LittleEndian.PutUint32(buf[walPHDR+8:], uint32(len(w.entries)))
-			buf[walPHDR+12] = 0                                // Committed = 0 (进行中)
+			buf[walPHDR+12] = WALStateWritten                        // State = Written (已写盘,未 fsync)
 			binary.LittleEndian.PutUint32(buf[walPHDR+13:], 0) // NextWalPage (稍后填)
 			binary.LittleEndian.PutUint16(buf[walPHDR+17:], 0) // EntryCount (稍后填)
 
@@ -236,13 +250,13 @@ func (w *WALWriter) writeWALPages() ([]uint32, error) {
 	return pageIDs, nil
 }
 
-// markCommitted 标记 WAL 首页为已提交
+// markCommitted 标记 WAL 首页为已提交 (State: Persisted → Committed)
 func (w *WALWriter) markCommitted(firstPageID uint32) error {
 	buf := make([]byte, PageSize)
 	if err := w.file.ReadPage(firstPageID, buf); err != nil {
 		return err
 	}
-	buf[walPHDR+12] = 1 // Committed = 1
+	buf[walPHDR+12] = WALStateCommitted // State = Committed
 	return w.file.WritePageDirect(firstPageID, buf)
 }