| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 |
- package core
- import (
- "encoding/binary"
- "testing"
- "unsafe"
- "git.ycl.pub/yl32940/TsDB2/DB/core/types"
- )
- // TestRecoverWALRefreshesTableMeta 回归: Open 必须先做 WAL 恢复、再载入表元数据。
- //
- // 背景 (wal_crash_chaos 压测实锤): 旧顺序 tablePage.Init → RecoverWAL 会让内存
- // Table 对象持有【恢复前】的表信息页 (TotalRows/堆链指针/RowSize), 恢复重放的
- // 新元数据只到磁盘 —— 内存与磁盘分叉: 行数统计错误, 堆链指针过期后后续写入
- // 按过期结构分配 = 损坏。
- func TestRecoverWALRefreshesTableMeta(t *testing.T) {
- // 建库: 一张表, 不提交任何行 (TotalRows=0)
- path := t.TempDir() + "/meta_refresh.db"
- db, err := Open(path)
- if err != nil {
- t.Fatalf("建库: %v", err)
- }
- db.SetAutoCommitDelay(0)
- tbl, err := db.CreateTable("t", []types.FieldInfo{
- {FieldType: types.FieldType_Uint64, FieldName: []byte("id")},
- {FieldType: types.FieldType_Uint64, FieldName: []byte("c")},
- {FieldType: types.FieldType_String, FieldName: []byte("p")},
- })
- if err != nil {
- t.Fatalf("建表: %v", err)
- }
- if _, err := tbl.CreateIndex("pk", 2, []byte{0}); err != nil {
- t.Fatalf("建索引: %v", err)
- }
- tablePID := tbl.TablePageID()
- startOff := tbl.TableStartOffset() // 表信息结构在表页内的偏移 (页头之后)
- if err := db.Close(); err != nil {
- t.Fatalf("关闭: %v", err)
- }
- // 伪造一条已提交 WAL: 把表信息页的 TotalRows 改成 4242 后整页镜像入链
- ff, err := OpenFile(path)
- if err != nil {
- t.Fatalf("裸开: %v", err)
- }
- walPID := nextFreePage(t, ff)
- pageBuf := make([]byte, PageSize)
- if err := ff.readPageDisk(tablePID, pageBuf); err != nil {
- t.Fatalf("读表页: %v", err)
- }
- // TotalRows 页内偏移 = 表信息结构起始偏移 + 结构内偏移 (与载入路径同口径)
- trOff := startOff + uint32(unsafe.Offsetof(TableInfo{}.TotalRows))
- before := binary.LittleEndian.Uint64(pageBuf[trOff:])
- binary.LittleEndian.PutUint64(pageBuf[trOff:], 4242)
- // 与真实写端一致: 整页镜像只存有效前缀 (表页 useSize 远小于页容量)
- useSize := binary.LittleEndian.Uint32(pageBuf[16:])
- entry := encodeWALEntry(tablePID, WALOpFullPage, pageBuf[:useSize], 0)
- first := buildWALFirstPage(t, walPID, 0, 1, entry)
- publishWALChain(t, ff, first)
- _ = ff.Close()
- // 重开: 恢复必须先于表元数据载入, 引擎读到的 TotalRows 必须是恢复后的 4242
- db2, err := Open(path)
- if err != nil {
- t.Fatalf("重开: %v", err)
- }
- defer db2.Close()
- tbl2, ok := db2.FindTable("t")
- if !ok {
- t.Fatal("表丢失")
- }
- if got := tbl2.TotalRows(); got != 4242 {
- t.Fatalf("恢复后的表元数据必须对引擎可见: TotalRows=%d, 想要 4242 (恢复前=%d)", got, before)
- }
- }
|