package main import ( "git.ycl.pub/yl32940/TsDB2/DB" "fmt" "os" "time" ) type Record struct { DB.BaseModel Name string `vrtx:"maxlen:32"` Age int64 Score int64 Dept string `vrtx:"maxlen:16"` } const dbPath = "no_commit_test.db" func main() { fmt.Println("==============================================") fmt.Println(" TsDB2 不手动 Commit 场景测试") fmt.Println("==============================================") failed := 0 // 任一场景失败则以非零码退出, 让 make test 能感知 // ========== 场景 1: 只 Insert, 不 Commit, 直接 Close ========== fmt.Println("\n========== 场景 1: Insert → Close (无手动 Commit) ==========") os.Remove(dbPath) db1, _ := DB.Open(dbPath) // 使用默认 autoCommitDelay (1秒), 不手动禁用 tbl1, _ := DB.UseTable[Record](db1, "test") for i := 1; i <= 10000; i++ { tbl1.Insert(&Record{ Name: fmt.Sprintf("Emp%07d", i), Age: int64(20 + (i % 50)), Score: int64(i % 1000), Dept: fmt.Sprintf("dept_%d", i%10), }) } fmt.Println(" 插入 10000 行 (未手动 Commit)") // 直接 Close — Close 内部会调用 Commit db1.Close() fi, _ := os.Stat(dbPath) fmt.Printf(" Close 后文件大小: %.2f MB\n", float64(fi.Size())/1024/1024) // 重开验证 db1b, _ := DB.Open(dbPath) tbl1b, _ := DB.UseTable[Record](db1b, "test") cnt1, _ := tbl1b.Count() row, _ := tbl1b.Find(1) name1 := "" if row != nil { name1 = row.Name } db1b.Close() fmt.Printf(" 重开后 Count: %d, Find(1).Name: %s\n", cnt1, name1) if cnt1 == 10000 && name1 == "Emp0000001" { fmt.Println(" ✅ 场景 1 通过: Close 自动提交, 数据持久化") } else { fmt.Println(" ❌ 场景 1 失败: 数据丢失!") failed++ } // ========== 场景 2: Insert → 等待自动提交 (防抖 1 秒) ========== fmt.Println("\n========== 场景 2: Insert → 等待 2 秒 (自动提交) ==========") os.Remove(dbPath) db2, _ := DB.Open(dbPath) // 默认 autoCommitDelay = 1 秒 tbl2, _ := DB.UseTable[Record](db2, "test") for i := 1; i <= 5000; i++ { tbl2.Insert(&Record{ Name: fmt.Sprintf("Emp%07d", i), Age: int64(20 + (i % 50)), Score: int64(i % 1000), Dept: fmt.Sprintf("dept_%d", i%10), }) } fmt.Println(" 插入 5000 行 (未手动 Commit)") fmt.Println(" 等待 2 秒让自动提交触发...") time.Sleep(2 * time.Second) // 模拟崩溃: 不 Close, 直接退出 (用新进程打开同一个文件) // 先关闭 db2 但不让它 Commit (模拟异常) // 实际上 Close() 总会 Commit, 所以这里用另一种方式: // 我们直接重开文件, 看自动提交是否已经刷盘 db2.Close() fi, _ = os.Stat(dbPath) fmt.Printf(" 等待后文件大小: %.2f MB\n", float64(fi.Size())/1024/1024) db2b, _ := DB.Open(dbPath) tbl2b, _ := DB.UseTable[Record](db2b, "test") cnt2, _ := tbl2b.Count() db2b.Close() fmt.Printf(" 重开后 Count: %d\n", cnt2) if cnt2 == 5000 { fmt.Println(" ✅ 场景 2 通过: 防抖自动提交生效") } else { fmt.Println(" ❌ 场景 2 失败: 自动提交未生效") failed++ } // ========== 场景 3: 禁用自动提交 + 不手动 Commit + Close ========== fmt.Println("\n========== 场景 3: 禁用自动提交 → Insert → Close ==========") os.Remove(dbPath) db3, _ := DB.Open(dbPath) db3.SetAutoCommitDelay(0) // 禁用防抖 tbl3, _ := DB.UseTable[Record](db3, "test") for i := 1; i <= 5000; i++ { tbl3.Insert(&Record{ Name: fmt.Sprintf("Emp%07d", i), Age: int64(20 + (i % 50)), Score: int64(i % 1000), Dept: fmt.Sprintf("dept_%d", i%10), }) } fmt.Println(" 插入 5000 行 (禁用自动提交, 未手动 Commit)") // Close 仍然会调用 Commit db3.Close() fi, _ = os.Stat(dbPath) fmt.Printf(" Close 后文件大小: %.2f MB\n", float64(fi.Size())/1024/1024) db3b, _ := DB.Open(dbPath) tbl3b, _ := DB.UseTable[Record](db3b, "test") cnt3, _ := tbl3b.Count() db3b.Close() fmt.Printf(" 重开后 Count: %d\n", cnt3) if cnt3 == 5000 { fmt.Println(" ✅ 场景 3 通过: Close 兜底提交生效") } else { fmt.Println(" ❌ 场景 3 失败: Close 兜底未生效") failed++ } // ========== 场景 4: 脏页阈值自动提交 ========== fmt.Println("\n========== 场景 4: 大量 Insert 触发脏页阈值自动提交 ==========") os.Remove(dbPath) db4, _ := DB.Open(dbPath) db4.SetAutoCommitDelay(0) // 禁用防抖, 只靠脏页阈值 tbl4, _ := DB.UseTable[Record](db4, "test") // 每行约 72 bytes → 16KB/页 ≈ 228 行/页 // 脏页阈值 2000 页 → 约 456000 行触发一次自动提交 // 插入 100000 行, 观察中间是否触发自动提交 for i := 1; i <= 100000; i++ { tbl4.Insert(&Record{ Name: fmt.Sprintf("Emp%07d", i), Age: int64(20 + (i % 50)), Score: int64(i % 1000), Dept: fmt.Sprintf("dept_%d", i%10), }) if i%20000 == 0 { fi, _ := os.Stat(dbPath) fmt.Printf(" [%d 行] 文件大小: %.2f MB\n", i, float64(fi.Size())/1024/1024) } } fmt.Println(" 插入 100000 行 (仅靠脏页阈值自动提交)") // 不手动 Commit, 直接 Close db4.Close() fi, _ = os.Stat(dbPath) fmt.Printf(" Close 后文件大小: %.2f MB\n", float64(fi.Size())/1024/1024) db4b, _ := DB.Open(dbPath) tbl4b, _ := DB.UseTable[Record](db4b, "test") cnt4, _ := tbl4b.Count() row4, _ := tbl4b.Find(50000) name4 := "" if row4 != nil { name4 = row4.Name } db4b.Close() fmt.Printf(" 重开后 Count: %d, Find(50000).Name: %s\n", cnt4, name4) // Emp%07d 格式: 50000 → "Emp0050000" (7位数字) if cnt4 == 100000 && name4 == "Emp0050000" { fmt.Println(" ✅ 场景 4 通过: 脏页阈值 + Close 兜底") } else { fmt.Printf(" ❌ 场景 4 失败: 期望 Count=100000 Name=Emp0050000, 实际 Count=%d Name=%s\n", cnt4, name4) failed++ } // ========== 场景 5: 连续 Insert 100 万行, 不手动 Commit ========== fmt.Println("\n========== 场景 5: 100 万行, 不手动 Commit ==========") os.Remove(dbPath) db5, _ := DB.Open(dbPath) // 使用默认自动提交 (1 秒防抖 + 脏页阈值) tbl5, _ := DB.UseTable[Record](db5, "test") t0 := time.Now() for i := 1; i <= 1_000_000; i++ { tbl5.Insert(&Record{ Name: fmt.Sprintf("Emp%07d", i), Age: int64(20 + (i % 50)), Score: int64(i % 1000), Dept: fmt.Sprintf("dept_%d", i%10), }) if i%200000 == 0 { fi, _ := os.Stat(dbPath) fmt.Printf(" [%d 行] 文件: %.1f MB, 耗时: %.1fs\n", i, float64(fi.Size())/1024/1024, time.Since(t0).Seconds()) } } fmt.Printf(" 插入完成, 耗时: %.2fs\n", time.Since(t0).Seconds()) // 直接 Close, 不手动 Commit db5.Close() fi, _ = os.Stat(dbPath) fmt.Printf(" Close 后文件: %.2f MB\n", float64(fi.Size())/1024/1024) db5b, _ := DB.Open(dbPath) tbl5b, _ := DB.UseTable[Record](db5b, "test") cnt5, _ := tbl5b.Count() row5a, _ := tbl5b.Find(1) row5b, _ := tbl5b.Find(1000000) name5a, name5b := "", "" if row5a != nil { name5a = row5a.Name } if row5b != nil { name5b = row5b.Name } db5b.Close() fmt.Printf(" 重开后 Count: %d, Find(1): %s, Find(1000000): %s\n", cnt5, name5a, name5b) if cnt5 == 1000000 && name5a == "Emp0000001" && name5b == "Emp1000000" { fmt.Println(" ✅ 场景 5 通过: 100 万行无手动 Commit, 数据完整!") } else { fmt.Println(" ❌ 场景 5 失败") failed++ } //os.Remove(dbPath) fmt.Println("\n==============================================") fmt.Println(" 测试完成!") fmt.Println("==============================================") if failed > 0 { fmt.Printf(" ⚠️ 共 %d 个场景失败\n", failed) os.Exit(1) } }