txn_begin_boundary_test.go 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. package core
  2. // 事务起始边界收敛的回归测试。
  3. //
  4. // 背景 (database/sql 驱动变异测试实锤): 自动提交挂起的未落盘插入 → BEGIN →
  5. // 事务内插入 → ROLLBACK → 再插入 —— "load failed"。根因: 回滚按 touched 丢弃
  6. // 页缓存时, 把这些页里【事务开始前】的未提交修改一起丢掉了, 而未被 touched 的
  7. // IndexMeta 页 (仍缓存着脏副本) 指向已被丢弃的树页 —— meta 指树、树已没了。
  8. // 修复: BeginTransaction 先把未提交脏页提交掉 (BEGIN = 此前工作已提交的边界)。
  9. import (
  10. "testing"
  11. "git.ycl.pub/yl32940/TsDB2/DB/core/page"
  12. "git.ycl.pub/yl32940/TsDB2/DB/core/types"
  13. )
  14. func TestBeginTransactionConvergesPendingWrites(t *testing.T) {
  15. // 与驱动同款环境: 自动提交默认开 (1s 防抖), 不手动收敛
  16. db, err := Open(t.TempDir() + "/begin_boundary.db")
  17. if err != nil {
  18. t.Fatal(err)
  19. }
  20. defer db.Close()
  21. tbl, err := db.CreateTable("t", []types.FieldInfo{
  22. {FieldType: types.FieldType_Int64, FieldName: []byte("id")},
  23. {FieldType: types.FieldType_Int64, FieldName: []byte("v")},
  24. })
  25. if err != nil {
  26. t.Fatal(err)
  27. }
  28. if _, err := tbl.CreateIndex("pk", page.IndexTypePrimary, []byte{0}); err != nil {
  29. t.Fatal(err)
  30. }
  31. // 自动提交挂起的插入 (不落盘)
  32. if _, err := tbl.InsertRowWithID(1, []types.FieldValue{
  33. {FieldName: []byte("id"), Value: int64(1)},
  34. {FieldName: []byte("v"), Value: int64(10)},
  35. }); err != nil {
  36. t.Fatal(err)
  37. }
  38. // BEGIN (收敛未提交修改) → 事务内插入 → ROLLBACK
  39. tx := db.BeginTransaction()
  40. if _, err := tbl.InsertRowWithID(2, []types.FieldValue{
  41. {FieldName: []byte("id"), Value: int64(2)},
  42. {FieldName: []byte("v"), Value: int64(20)},
  43. }); err != nil {
  44. t.Fatal(err)
  45. }
  46. if err := tx.Rollback(); err != nil {
  47. t.Fatal(err)
  48. }
  49. // 事务前的行必须还在 (Begin 时已收敛), 事务内的行必须没了
  50. if _, err := tbl.FindByPK(1); err != nil {
  51. t.Fatalf("事务前的行丢失: %v", err)
  52. }
  53. if _, err := tbl.FindByPK(2); err == nil {
  54. t.Fatalf("事务内的行未被回滚")
  55. }
  56. // 修复前的炸点: 回滚后再插入必须成功
  57. if _, err := tbl.InsertRowWithID(3, []types.FieldValue{
  58. {FieldName: []byte("id"), Value: int64(3)},
  59. {FieldName: []byte("v"), Value: int64(30)},
  60. }); err != nil {
  61. t.Fatalf("回滚后再插入: %v", err)
  62. }
  63. if _, err := tbl.FindByPK(3); err != nil {
  64. t.Fatalf("新插入不可见: %v", err)
  65. }
  66. }