Files
srdb/engine_clean_test.go
bourdon 9175b98202 添加 Clean 和 Destroy 功能
主要改动:
- Engine: 添加 Clean() 和 Destroy() 方法
- Table: 添加 Clean() 和 Destroy() 方法(不持有 Database 引用)
- Database: 添加 Clean()、CleanTable()、DestroyTable()、Destroy() 方法
- 自动 flush: 添加长时间无写入自动 flush 策略(默认 30 秒)
- WebUI 优化: 优化分页查询性能

新增功能:
- Clean(): 清除数据但保留结构,Engine/Table/Database 仍可用
- Destroy(): 销毁并删除所有文件,对象不可用
- CleanTable(name): 清除指定表的数据
- DestroyTable(name): 销毁指定表并从 Database 中删除
- 自动 flush 监控: 后台定期检查,空闲时自动持久化

代码优化:
- Engine.Close(): 支持 Destroy 后调用,不会 panic
- 二级索引持久化: 在 flush 时自动持久化索引
- WebUI 分页: 预构建字段类型 map,减少 Schema 查询
- 职责分离: Table 不再持有 Database 引用

测试覆盖:
- engine_clean_test.go: Engine Clean/Destroy 测试
- table_clean_test.go: Table Clean/Destroy 测试
- database_clean_test.go: Database Clean/Destroy 测试
- database_table_ops_test.go: Database CleanTable/DestroyTable 测试
2025-10-09 01:03:22 +08:00

245 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package srdb
import (
"os"
"testing"
"time"
)
func TestEngineClean(t *testing.T) {
dir := "./test_clean_data"
defer os.RemoveAll(dir)
// 1. 创建 Engine 并插入数据
engine, err := OpenEngine(&EngineOptions{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
// 插入一些数据
for i := 0; i < 100; i++ {
err := engine.Insert(map[string]any{
"id": i,
"name": "test",
})
if err != nil {
t.Fatal(err)
}
}
// 强制 flush
engine.Flush()
time.Sleep(500 * time.Millisecond)
// 验证数据存在
stats := engine.Stats()
t.Logf("Before Clean: MemTable=%d, SST=%d, Total=%d",
stats.MemTableCount, stats.SSTCount, stats.TotalRows)
if stats.TotalRows == 0 {
t.Errorf("Expected some rows, got 0")
}
// 2. 清除数据
err = engine.Clean()
if err != nil {
t.Fatal(err)
}
// 3. 验证数据已清除
stats = engine.Stats()
t.Logf("After Clean: MemTable=%d, SST=%d, Total=%d",
stats.MemTableCount, stats.SSTCount, stats.TotalRows)
if stats.TotalRows != 0 {
t.Errorf("Expected 0 rows after clean, got %d", stats.TotalRows)
}
// 4. 验证 Engine 仍然可用
err = engine.Insert(map[string]any{
"id": 1,
"name": "after_clean",
})
if err != nil {
t.Fatal(err)
}
stats = engine.Stats()
if stats.TotalRows != 1 {
t.Errorf("Expected 1 row after insert, got %d", stats.TotalRows)
}
engine.Close()
}
func TestEngineDestroy(t *testing.T) {
dir := "./test_destroy_data"
defer os.RemoveAll(dir)
// 1. 创建 Engine 并插入数据
engine, err := OpenEngine(&EngineOptions{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
// 插入一些数据
for i := 0; i < 50; i++ {
err := engine.Insert(map[string]any{
"id": i,
"name": "test",
})
if err != nil {
t.Fatal(err)
}
}
// 验证数据存在
stats := engine.Stats()
t.Logf("Before Destroy: MemTable=%d, SST=%d, Total=%d",
stats.MemTableCount, stats.SSTCount, stats.TotalRows)
// 2. 销毁 Engine
err = engine.Destroy()
if err != nil {
t.Fatal(err)
}
// 3. 验证数据目录已删除
if _, err := os.Stat(dir); !os.IsNotExist(err) {
t.Errorf("Data directory should be deleted")
}
// 4. 验证 Engine 不可用(尝试插入会失败)
err = engine.Insert(map[string]any{
"id": 1,
"name": "after_destroy",
})
if err == nil {
t.Errorf("Insert should fail after destroy")
}
}
func TestEngineCleanWithSchema(t *testing.T) {
dir := "./test_clean_schema_data"
defer os.RemoveAll(dir)
// 定义 Schema
schema := NewSchema("test", []Field{
{Name: "id", Type: FieldTypeInt64, Indexed: true, Comment: "ID"},
{Name: "name", Type: FieldTypeString, Indexed: false, Comment: "Name"},
})
// 1. 创建 Engine 并插入数据
engine, err := OpenEngine(&EngineOptions{
Dir: dir,
Schema: schema,
})
if err != nil {
t.Fatal(err)
}
// 创建索引
err = engine.CreateIndex("id")
if err != nil {
t.Fatal(err)
}
// 插入数据
for i := 0; i < 50; i++ {
err := engine.Insert(map[string]any{
"id": int64(i),
"name": "test",
})
if err != nil {
t.Fatal(err)
}
}
// 验证索引存在
indexes := engine.ListIndexes()
if len(indexes) != 1 {
t.Errorf("Expected 1 index, got %d", len(indexes))
}
// 2. 清除数据
err = engine.Clean()
if err != nil {
t.Fatal(err)
}
// 3. 验证数据已清除但 Schema 和索引结构保留
stats := engine.Stats()
if stats.TotalRows != 0 {
t.Errorf("Expected 0 rows after clean, got %d", stats.TotalRows)
}
// 验证可以继续插入Schema 仍然有效)
err = engine.Insert(map[string]any{
"id": int64(100),
"name": "after_clean",
})
if err != nil {
t.Fatal(err)
}
engine.Close()
}
func TestEngineCleanAndReopen(t *testing.T) {
dir := "./test_clean_reopen_data"
defer os.RemoveAll(dir)
// 1. 创建 Engine 并插入数据
engine, err := OpenEngine(&EngineOptions{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
for i := 0; i < 100; i++ {
engine.Insert(map[string]any{
"id": i,
"name": "test",
})
}
// 2. 清除数据
engine.Clean()
// 3. 关闭并重新打开
engine.Close()
engine2, err := OpenEngine(&EngineOptions{
Dir: dir,
})
if err != nil {
t.Fatal(err)
}
defer engine2.Close()
// 4. 验证数据为空
stats := engine2.Stats()
if stats.TotalRows != 0 {
t.Errorf("Expected 0 rows after reopen, got %d", stats.TotalRows)
}
// 5. 验证可以插入新数据
err = engine2.Insert(map[string]any{
"id": 1,
"name": "new_data",
})
if err != nil {
t.Fatal(err)
}
stats = engine2.Stats()
if stats.TotalRows != 1 {
t.Errorf("Expected 1 row, got %d", stats.TotalRows)
}
}