package core // 事务起始边界收敛的回归测试。 // // 背景 (database/sql 驱动变异测试实锤): 自动提交挂起的未落盘插入 → BEGIN → // 事务内插入 → ROLLBACK → 再插入 —— "load failed"。根因: 回滚按 touched 丢弃 // 页缓存时, 把这些页里【事务开始前】的未提交修改一起丢掉了, 而未被 touched 的 // IndexMeta 页 (仍缓存着脏副本) 指向已被丢弃的树页 —— meta 指树、树已没了。 // 修复: BeginTransaction 先把未提交脏页提交掉 (BEGIN = 此前工作已提交的边界)。 import ( "testing" "git.ycl.pub/yl32940/TsDB2/DB/core/page" "git.ycl.pub/yl32940/TsDB2/DB/core/types" ) func TestBeginTransactionConvergesPendingWrites(t *testing.T) { // 与驱动同款环境: 自动提交默认开 (1s 防抖), 不手动收敛 db, err := Open(t.TempDir() + "/begin_boundary.db") if err != nil { t.Fatal(err) } defer db.Close() tbl, err := db.CreateTable("t", []types.FieldInfo{ {FieldType: types.FieldType_Int64, FieldName: []byte("id")}, {FieldType: types.FieldType_Int64, FieldName: []byte("v")}, }) if err != nil { t.Fatal(err) } if _, err := tbl.CreateIndex("pk", page.IndexTypePrimary, []byte{0}); err != nil { t.Fatal(err) } // 自动提交挂起的插入 (不落盘) if _, err := tbl.InsertRowWithID(1, []types.FieldValue{ {FieldName: []byte("id"), Value: int64(1)}, {FieldName: []byte("v"), Value: int64(10)}, }); err != nil { t.Fatal(err) } // BEGIN (收敛未提交修改) → 事务内插入 → ROLLBACK tx := db.BeginTransaction() if _, err := tbl.InsertRowWithID(2, []types.FieldValue{ {FieldName: []byte("id"), Value: int64(2)}, {FieldName: []byte("v"), Value: int64(20)}, }); err != nil { t.Fatal(err) } if err := tx.Rollback(); err != nil { t.Fatal(err) } // 事务前的行必须还在 (Begin 时已收敛), 事务内的行必须没了 if _, err := tbl.FindByPK(1); err != nil { t.Fatalf("事务前的行丢失: %v", err) } if _, err := tbl.FindByPK(2); err == nil { t.Fatalf("事务内的行未被回滚") } // 修复前的炸点: 回滚后再插入必须成功 if _, err := tbl.InsertRowWithID(3, []types.FieldValue{ {FieldName: []byte("id"), Value: int64(3)}, {FieldName: []byte("v"), Value: int64(30)}, }); err != nil { t.Fatalf("回滚后再插入: %v", err) } if _, err := tbl.FindByPK(3); err != nil { t.Fatalf("新插入不可见: %v", err) } }