Files
srdb/btree_test.go
bourdon 23843493b8 重构代码结构并添加完整功能
主要改动:
- 重构目录结构:合并子目录到根目录,简化项目结构
- 添加完整的查询 API:支持复杂条件查询、字段选择、游标模式
- 实现 LSM-Tree Compaction:7层结构、Score-based策略、后台异步合并
- 添加 Web UI:基于 Lit 的现代化管理界面,支持数据浏览和 Manifest 查看
- 完善文档:添加 README.md 和 examples/webui/README.md

新增功能:
- Query Builder:链式查询 API,支持 Eq/Lt/Gt/In/Between/Contains 等操作符
- Web UI 组件:srdb-app、srdb-table-list、srdb-data-view、srdb-manifest-view 等
- 列选择持久化:自动保存到 localStorage
- 刷新按钮:一键刷新当前视图
- 主题切换:深色/浅色主题支持

代码优化:
- 使用 Go 1.24 新特性:range 7、min()、maps.Copy()、slices.Sort()
- 统一组件命名:所有 Web Components 使用 srdb-* 前缀
- CSS 优化:提取共享样式,减少重复代码
- 清理遗留代码:删除未使用的方法和样式
2025-10-08 23:04:47 +08:00

156 lines
3.2 KiB
Go

package srdb
import (
"os"
"testing"
"github.com/edsrzf/mmap-go"
)
func TestBTree(t *testing.T) {
// 1. 创建测试文件
file, err := os.Create("test.sst")
if err != nil {
t.Fatal(err)
}
defer os.Remove("test.sst")
// 2. 构建 B+Tree
builder := NewBTreeBuilder(file, 256) // 从 offset 256 开始
// 添加 1000 个 key-value
for i := int64(1); i <= 1000; i++ {
dataOffset := 1000000 + i*100 // 模拟数据位置
dataSize := int32(100)
err := builder.Add(i, dataOffset, dataSize)
if err != nil {
t.Fatal(err)
}
}
// 构建
rootOffset, err := builder.Build()
if err != nil {
t.Fatal(err)
}
t.Logf("Root offset: %d", rootOffset)
// 3. 关闭并重新打开文件
file.Close()
file, err = os.Open("test.sst")
if err != nil {
t.Fatal(err)
}
defer file.Close()
// 4. mmap 映射
mmapData, err := mmap.Map(file, mmap.RDONLY, 0)
if err != nil {
t.Fatal(err)
}
defer mmapData.Unmap()
// 5. 查询测试
reader := NewBTreeReader(mmapData, rootOffset)
// 测试存在的 key
for i := int64(1); i <= 1000; i++ {
offset, size, found := reader.Get(i)
if !found {
t.Errorf("Key %d not found", i)
}
expectedOffset := 1000000 + i*100
if offset != expectedOffset {
t.Errorf("Key %d: expected offset %d, got %d", i, expectedOffset, offset)
}
if size != 100 {
t.Errorf("Key %d: expected size 100, got %d", i, size)
}
}
// 测试不存在的 key
_, _, found := reader.Get(1001)
if found {
t.Error("Key 1001 should not exist")
}
_, _, found = reader.Get(0)
if found {
t.Error("Key 0 should not exist")
}
t.Log("All tests passed!")
}
func TestBTreeSerialization(t *testing.T) {
// 测试节点序列化
leaf := NewLeafNode()
leaf.AddData(1, 1000, 100)
leaf.AddData(2, 2000, 200)
leaf.AddData(3, 3000, 300)
// 序列化
data := leaf.Marshal()
if len(data) != BTreeNodeSize {
t.Errorf("Expected size %d, got %d", BTreeNodeSize, len(data))
}
// 反序列化
leaf2 := UnmarshalBTree(data)
if leaf2 == nil {
t.Fatal("Unmarshal failed")
}
// 验证
if leaf2.NodeType != BTreeNodeTypeLeaf {
t.Error("Wrong node type")
}
if leaf2.KeyCount != 3 {
t.Errorf("Expected 3 keys, got %d", leaf2.KeyCount)
}
if len(leaf2.Keys) != 3 {
t.Errorf("Expected 3 keys, got %d", len(leaf2.Keys))
}
if leaf2.Keys[0] != 1 || leaf2.Keys[1] != 2 || leaf2.Keys[2] != 3 {
t.Error("Keys mismatch")
}
if leaf2.DataOffsets[0] != 1000 || leaf2.DataOffsets[1] != 2000 || leaf2.DataOffsets[2] != 3000 {
t.Error("Data offsets mismatch")
}
if leaf2.DataSizes[0] != 100 || leaf2.DataSizes[1] != 200 || leaf2.DataSizes[2] != 300 {
t.Error("Data sizes mismatch")
}
t.Log("Serialization test passed!")
}
func BenchmarkBTreeGet(b *testing.B) {
// 构建测试数据
file, _ := os.Create("bench.sst")
defer os.Remove("bench.sst")
builder := NewBTreeBuilder(file, 256)
for i := int64(1); i <= 100000; i++ {
builder.Add(i, i*100, 100)
}
rootOffset, _ := builder.Build()
file.Close()
// mmap
file, _ = os.Open("bench.sst")
defer file.Close()
mmapData, _ := mmap.Map(file, mmap.RDONLY, 0)
defer mmapData.Unmap()
reader := NewBTreeReader(mmapData, rootOffset)
// 性能测试
for i := 0; b.Loop(); i++ {
key := int64(i%100000 + 1)
reader.Get(key)
}
}