Initial commit: SRDB - High-performance LSM-Tree database
- Core engine with MemTable, SST, WAL - B+Tree indexing for SST files - Leveled compaction strategy - Multi-table database management - Schema validation and secondary indexes - Query builder with complex conditions - Web UI with HTMX for data visualization - Command-line tools for diagnostics
This commit is contained in:
48
manifest/manifest_reader.go
Normal file
48
manifest/manifest_reader.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package manifest
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Reader MANIFEST 读取器
|
||||
type Reader struct {
|
||||
file io.Reader
|
||||
}
|
||||
|
||||
// NewReader 创建 MANIFEST 读取器
|
||||
func NewReader(file io.Reader) *Reader {
|
||||
return &Reader{
|
||||
file: file,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadEdit 读取版本变更
|
||||
func (r *Reader) ReadEdit() (*VersionEdit, error) {
|
||||
// 读取 CRC32 和 Length
|
||||
header := make([]byte, 8)
|
||||
_, err := io.ReadFull(r.file, header)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 读取长度
|
||||
length := binary.LittleEndian.Uint32(header[4:8])
|
||||
|
||||
// 读取数据
|
||||
data := make([]byte, 8+length)
|
||||
copy(data[0:8], header)
|
||||
_, err = io.ReadFull(r.file, data[8:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 解码
|
||||
edit := NewVersionEdit()
|
||||
err = edit.Decode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return edit, nil
|
||||
}
|
||||
35
manifest/manifest_writer.go
Normal file
35
manifest/manifest_writer.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package manifest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Writer MANIFEST 写入器
|
||||
type Writer struct {
|
||||
file io.Writer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewWriter 创建 MANIFEST 写入器
|
||||
func NewWriter(file io.Writer) *Writer {
|
||||
return &Writer{
|
||||
file: file,
|
||||
}
|
||||
}
|
||||
|
||||
// WriteEdit 写入版本变更
|
||||
func (w *Writer) WriteEdit(edit *VersionEdit) error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
// 编码
|
||||
data, err := edit.Encode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 写入
|
||||
_, err = w.file.Write(data)
|
||||
return err
|
||||
}
|
||||
187
manifest/version.go
Normal file
187
manifest/version.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package manifest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// FileMetadata SST 文件元数据
|
||||
type FileMetadata struct {
|
||||
FileNumber int64 // 文件编号
|
||||
Level int // 所在层级 (0-6)
|
||||
FileSize int64 // 文件大小
|
||||
MinKey int64 // 最小 key
|
||||
MaxKey int64 // 最大 key
|
||||
RowCount int64 // 行数
|
||||
}
|
||||
|
||||
const (
|
||||
NumLevels = 7 // L0-L6
|
||||
)
|
||||
|
||||
// Version 数据库的一个版本快照
|
||||
type Version struct {
|
||||
// 分层存储 SST 文件 (L0-L6)
|
||||
Levels [NumLevels][]*FileMetadata
|
||||
|
||||
// 下一个文件编号
|
||||
NextFileNumber int64
|
||||
|
||||
// 最后序列号
|
||||
LastSequence int64
|
||||
|
||||
// 版本号
|
||||
VersionNumber int64
|
||||
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewVersion 创建新版本
|
||||
func NewVersion() *Version {
|
||||
v := &Version{
|
||||
NextFileNumber: 1,
|
||||
LastSequence: 0,
|
||||
VersionNumber: 0,
|
||||
}
|
||||
// 初始化每一层
|
||||
for i := 0; i < NumLevels; i++ {
|
||||
v.Levels[i] = make([]*FileMetadata, 0)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Clone 克隆版本
|
||||
func (v *Version) Clone() *Version {
|
||||
v.mu.RLock()
|
||||
defer v.mu.RUnlock()
|
||||
|
||||
newVersion := &Version{
|
||||
NextFileNumber: v.NextFileNumber,
|
||||
LastSequence: v.LastSequence,
|
||||
VersionNumber: v.VersionNumber + 1,
|
||||
}
|
||||
|
||||
// 克隆每一层
|
||||
for level := 0; level < NumLevels; level++ {
|
||||
newVersion.Levels[level] = make([]*FileMetadata, len(v.Levels[level]))
|
||||
copy(newVersion.Levels[level], v.Levels[level])
|
||||
}
|
||||
|
||||
return newVersion
|
||||
}
|
||||
|
||||
// Apply 应用版本变更
|
||||
func (v *Version) Apply(edit *VersionEdit) {
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
|
||||
// 删除文件(按层级删除)
|
||||
if len(edit.DeletedFiles) > 0 {
|
||||
deleteSet := make(map[int64]bool)
|
||||
for _, fileNum := range edit.DeletedFiles {
|
||||
deleteSet[fileNum] = true
|
||||
}
|
||||
|
||||
// 遍历每一层,删除文件
|
||||
for level := 0; level < NumLevels; level++ {
|
||||
newFiles := make([]*FileMetadata, 0)
|
||||
deletedCount := 0
|
||||
for _, file := range v.Levels[level] {
|
||||
if !deleteSet[file.FileNumber] {
|
||||
newFiles = append(newFiles, file)
|
||||
} else {
|
||||
deletedCount++
|
||||
}
|
||||
}
|
||||
if deletedCount > 0 {
|
||||
fmt.Printf("[Version.Apply] L%d: deleted %d files\n", level, deletedCount)
|
||||
}
|
||||
v.Levels[level] = newFiles
|
||||
}
|
||||
}
|
||||
|
||||
// 添加文件(按层级添加)
|
||||
if len(edit.AddedFiles) > 0 {
|
||||
for _, file := range edit.AddedFiles {
|
||||
if file.Level >= 0 && file.Level < NumLevels {
|
||||
fmt.Printf("[Version.Apply] Adding file #%d to L%d (keys %d-%d)\n",
|
||||
file.FileNumber, file.Level, file.MinKey, file.MaxKey)
|
||||
v.Levels[file.Level] = append(v.Levels[file.Level], file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新下一个文件编号
|
||||
if edit.NextFileNumber != nil {
|
||||
v.NextFileNumber = *edit.NextFileNumber
|
||||
}
|
||||
|
||||
// 更新最后序列号
|
||||
if edit.LastSequence != nil {
|
||||
v.LastSequence = *edit.LastSequence
|
||||
}
|
||||
}
|
||||
|
||||
// GetLevel 获取指定层级的文件
|
||||
func (v *Version) GetLevel(level int) []*FileMetadata {
|
||||
v.mu.RLock()
|
||||
defer v.mu.RUnlock()
|
||||
|
||||
if level < 0 || level >= NumLevels {
|
||||
return nil
|
||||
}
|
||||
|
||||
files := make([]*FileMetadata, len(v.Levels[level]))
|
||||
copy(files, v.Levels[level])
|
||||
return files
|
||||
}
|
||||
|
||||
// GetSSTFiles 获取所有 SST 文件(副本,兼容旧接口)
|
||||
func (v *Version) GetSSTFiles() []*FileMetadata {
|
||||
v.mu.RLock()
|
||||
defer v.mu.RUnlock()
|
||||
|
||||
// 收集所有层级的文件
|
||||
allFiles := make([]*FileMetadata, 0)
|
||||
for level := 0; level < NumLevels; level++ {
|
||||
allFiles = append(allFiles, v.Levels[level]...)
|
||||
}
|
||||
return allFiles
|
||||
}
|
||||
|
||||
// GetNextFileNumber 获取下一个文件编号
|
||||
func (v *Version) GetNextFileNumber() int64 {
|
||||
v.mu.RLock()
|
||||
defer v.mu.RUnlock()
|
||||
return v.NextFileNumber
|
||||
}
|
||||
|
||||
// GetLastSequence 获取最后序列号
|
||||
func (v *Version) GetLastSequence() int64 {
|
||||
v.mu.RLock()
|
||||
defer v.mu.RUnlock()
|
||||
return v.LastSequence
|
||||
}
|
||||
|
||||
// GetFileCount 获取文件数量
|
||||
func (v *Version) GetFileCount() int {
|
||||
v.mu.RLock()
|
||||
defer v.mu.RUnlock()
|
||||
|
||||
total := 0
|
||||
for level := 0; level < NumLevels; level++ {
|
||||
total += len(v.Levels[level])
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// GetLevelFileCount 获取指定层级的文件数量
|
||||
func (v *Version) GetLevelFileCount(level int) int {
|
||||
v.mu.RLock()
|
||||
defer v.mu.RUnlock()
|
||||
|
||||
if level < 0 || level >= NumLevels {
|
||||
return 0
|
||||
}
|
||||
return len(v.Levels[level])
|
||||
}
|
||||
114
manifest/version_edit.go
Normal file
114
manifest/version_edit.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package manifest
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
)
|
||||
|
||||
// EditType 变更类型
|
||||
type EditType byte
|
||||
|
||||
const (
|
||||
EditTypeAddFile EditType = 1 // 添加文件
|
||||
EditTypeDeleteFile EditType = 2 // 删除文件
|
||||
EditTypeSetNextFile EditType = 3 // 设置下一个文件编号
|
||||
EditTypeSetLastSeq EditType = 4 // 设置最后序列号
|
||||
)
|
||||
|
||||
// VersionEdit 版本变更记录
|
||||
type VersionEdit struct {
|
||||
// 添加的文件
|
||||
AddedFiles []*FileMetadata
|
||||
|
||||
// 删除的文件(文件编号列表)
|
||||
DeletedFiles []int64
|
||||
|
||||
// 下一个文件编号
|
||||
NextFileNumber *int64
|
||||
|
||||
// 最后序列号
|
||||
LastSequence *int64
|
||||
}
|
||||
|
||||
// NewVersionEdit 创建版本变更
|
||||
func NewVersionEdit() *VersionEdit {
|
||||
return &VersionEdit{
|
||||
AddedFiles: make([]*FileMetadata, 0),
|
||||
DeletedFiles: make([]int64, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// AddFile 添加文件
|
||||
func (e *VersionEdit) AddFile(file *FileMetadata) {
|
||||
e.AddedFiles = append(e.AddedFiles, file)
|
||||
}
|
||||
|
||||
// DeleteFile 删除文件
|
||||
func (e *VersionEdit) DeleteFile(fileNumber int64) {
|
||||
e.DeletedFiles = append(e.DeletedFiles, fileNumber)
|
||||
}
|
||||
|
||||
// SetNextFileNumber 设置下一个文件编号
|
||||
func (e *VersionEdit) SetNextFileNumber(num int64) {
|
||||
e.NextFileNumber = &num
|
||||
}
|
||||
|
||||
// SetLastSequence 设置最后序列号
|
||||
func (e *VersionEdit) SetLastSequence(seq int64) {
|
||||
e.LastSequence = &seq
|
||||
}
|
||||
|
||||
// Encode 编码为字节
|
||||
func (e *VersionEdit) Encode() ([]byte, error) {
|
||||
// 使用 JSON 编码(简单实现)
|
||||
data, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 格式: CRC32(4) + Length(4) + Data
|
||||
totalLen := 8 + len(data)
|
||||
buf := make([]byte, totalLen)
|
||||
|
||||
// 计算 CRC32
|
||||
crc := crc32.ChecksumIEEE(data)
|
||||
binary.LittleEndian.PutUint32(buf[0:4], crc)
|
||||
|
||||
// 写入长度
|
||||
binary.LittleEndian.PutUint32(buf[4:8], uint32(len(data)))
|
||||
|
||||
// 写入数据
|
||||
copy(buf[8:], data)
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Decode 从字节解码
|
||||
func (e *VersionEdit) Decode(data []byte) error {
|
||||
if len(data) < 8 {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// 读取 CRC32
|
||||
crc := binary.LittleEndian.Uint32(data[0:4])
|
||||
|
||||
// 读取长度
|
||||
length := binary.LittleEndian.Uint32(data[4:8])
|
||||
|
||||
if len(data) < int(8+length) {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// 读取数据
|
||||
editData := data[8 : 8+length]
|
||||
|
||||
// 验证 CRC32
|
||||
if crc32.ChecksumIEEE(editData) != crc {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// JSON 解码
|
||||
return json.Unmarshal(editData, e)
|
||||
}
|
||||
251
manifest/version_set.go
Normal file
251
manifest/version_set.go
Normal file
@@ -0,0 +1,251 @@
|
||||
package manifest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// VersionSet 版本集合管理器
|
||||
type VersionSet struct {
|
||||
// 当前版本
|
||||
current *Version
|
||||
|
||||
// MANIFEST 文件
|
||||
manifestFile *os.File
|
||||
manifestWriter *Writer
|
||||
manifestNumber int64
|
||||
|
||||
// 下一个文件编号
|
||||
nextFileNumber atomic.Int64
|
||||
|
||||
// 最后序列号
|
||||
lastSequence atomic.Int64
|
||||
|
||||
// 目录
|
||||
dir string
|
||||
|
||||
// 锁
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewVersionSet 创建版本集合
|
||||
func NewVersionSet(dir string) (*VersionSet, error) {
|
||||
vs := &VersionSet{
|
||||
dir: dir,
|
||||
}
|
||||
|
||||
// 确保目录存在
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 读取 CURRENT 文件
|
||||
currentFile := filepath.Join(dir, "CURRENT")
|
||||
data, err := os.ReadFile(currentFile)
|
||||
|
||||
if err != nil {
|
||||
// CURRENT 不存在,创建新的 MANIFEST
|
||||
return vs, vs.createNewManifest()
|
||||
}
|
||||
|
||||
// 读取 MANIFEST 文件
|
||||
manifestName := strings.TrimSpace(string(data))
|
||||
manifestPath := filepath.Join(dir, manifestName)
|
||||
|
||||
// 恢复版本信息
|
||||
version, err := vs.recoverFromManifest(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vs.current = version
|
||||
vs.nextFileNumber.Store(version.NextFileNumber)
|
||||
vs.lastSequence.Store(version.LastSequence)
|
||||
|
||||
// 解析 MANIFEST 编号
|
||||
fmt.Sscanf(manifestName, "MANIFEST-%d", &vs.manifestNumber)
|
||||
|
||||
// 打开 MANIFEST 用于追加
|
||||
file, err := os.OpenFile(manifestPath, os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vs.manifestFile = file
|
||||
vs.manifestWriter = NewWriter(file)
|
||||
|
||||
return vs, nil
|
||||
}
|
||||
|
||||
// createNewManifest 创建新的 MANIFEST
|
||||
func (vs *VersionSet) createNewManifest() error {
|
||||
// 生成新的 MANIFEST 文件名
|
||||
vs.manifestNumber = vs.nextFileNumber.Add(1)
|
||||
manifestName := fmt.Sprintf("MANIFEST-%06d", vs.manifestNumber)
|
||||
manifestPath := filepath.Join(vs.dir, manifestName)
|
||||
|
||||
// 创建 MANIFEST 文件
|
||||
file, err := os.Create(manifestPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vs.manifestFile = file
|
||||
vs.manifestWriter = NewWriter(file)
|
||||
|
||||
// 创建初始版本
|
||||
vs.current = NewVersion()
|
||||
|
||||
// 写入初始版本
|
||||
edit := NewVersionEdit()
|
||||
nextFile := vs.manifestNumber
|
||||
edit.SetNextFileNumber(nextFile)
|
||||
lastSeq := int64(0)
|
||||
edit.SetLastSequence(lastSeq)
|
||||
|
||||
err = vs.manifestWriter.WriteEdit(edit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 同步到磁盘
|
||||
err = vs.manifestFile.Sync()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新 CURRENT 文件
|
||||
return vs.updateCurrent(manifestName)
|
||||
}
|
||||
|
||||
// recoverFromManifest 从 MANIFEST 恢复版本
|
||||
func (vs *VersionSet) recoverFromManifest(manifestPath string) (*Version, error) {
|
||||
// 打开 MANIFEST 文件
|
||||
file, err := os.Open(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
reader := NewReader(file)
|
||||
|
||||
// 创建初始版本
|
||||
version := NewVersion()
|
||||
|
||||
// 读取所有 VersionEdit
|
||||
for {
|
||||
edit, err := reader.ReadEdit()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 应用变更
|
||||
version.Apply(edit)
|
||||
}
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
// updateCurrent 更新 CURRENT 文件
|
||||
func (vs *VersionSet) updateCurrent(manifestName string) error {
|
||||
currentPath := filepath.Join(vs.dir, "CURRENT")
|
||||
tmpPath := currentPath + ".tmp"
|
||||
|
||||
// 1. 写入临时文件
|
||||
err := os.WriteFile(tmpPath, []byte(manifestName+"\n"), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 原子性重命名
|
||||
err = os.Rename(tmpPath, currentPath)
|
||||
if err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogAndApply 记录并应用版本变更
|
||||
func (vs *VersionSet) LogAndApply(edit *VersionEdit) error {
|
||||
vs.mu.Lock()
|
||||
defer vs.mu.Unlock()
|
||||
|
||||
// 1. 创建新版本
|
||||
newVersion := vs.current.Clone()
|
||||
|
||||
// 2. 应用变更
|
||||
newVersion.Apply(edit)
|
||||
|
||||
// 3. 写入 MANIFEST
|
||||
err := vs.manifestWriter.WriteEdit(edit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. 同步到磁盘
|
||||
err = vs.manifestFile.Sync()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 5. 更新当前版本
|
||||
vs.current = newVersion
|
||||
|
||||
// 6. 更新原子变量
|
||||
if edit.NextFileNumber != nil {
|
||||
vs.nextFileNumber.Store(*edit.NextFileNumber)
|
||||
}
|
||||
if edit.LastSequence != nil {
|
||||
vs.lastSequence.Store(*edit.LastSequence)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCurrent 获取当前版本
|
||||
func (vs *VersionSet) GetCurrent() *Version {
|
||||
vs.mu.RLock()
|
||||
defer vs.mu.RUnlock()
|
||||
return vs.current
|
||||
}
|
||||
|
||||
// GetNextFileNumber 获取下一个文件编号
|
||||
func (vs *VersionSet) GetNextFileNumber() int64 {
|
||||
return vs.nextFileNumber.Load()
|
||||
}
|
||||
|
||||
// AllocateFileNumber 分配文件编号
|
||||
func (vs *VersionSet) AllocateFileNumber() int64 {
|
||||
return vs.nextFileNumber.Add(1)
|
||||
}
|
||||
|
||||
// GetLastSequence 获取最后序列号
|
||||
func (vs *VersionSet) GetLastSequence() int64 {
|
||||
return vs.lastSequence.Load()
|
||||
}
|
||||
|
||||
// SetLastSequence 设置最后序列号
|
||||
func (vs *VersionSet) SetLastSequence(seq int64) {
|
||||
vs.lastSequence.Store(seq)
|
||||
}
|
||||
|
||||
// Close 关闭 VersionSet
|
||||
func (vs *VersionSet) Close() error {
|
||||
vs.mu.Lock()
|
||||
defer vs.mu.Unlock()
|
||||
|
||||
if vs.manifestFile != nil {
|
||||
return vs.manifestFile.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
220
manifest/version_set_test.go
Normal file
220
manifest/version_set_test.go
Normal file
@@ -0,0 +1,220 @@
|
||||
package manifest
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVersionSetBasic(t *testing.T) {
|
||||
dir := "./test_manifest"
|
||||
os.RemoveAll(dir)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// 创建 VersionSet
|
||||
vs, err := NewVersionSet(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewVersionSet failed: %v", err)
|
||||
}
|
||||
defer vs.Close()
|
||||
|
||||
// 检查初始状态
|
||||
version := vs.GetCurrent()
|
||||
if version.GetFileCount() != 0 {
|
||||
t.Errorf("Expected 0 files, got %d", version.GetFileCount())
|
||||
}
|
||||
|
||||
t.Log("VersionSet basic test passed!")
|
||||
}
|
||||
|
||||
func TestVersionSetAddFile(t *testing.T) {
|
||||
dir := "./test_manifest_add"
|
||||
os.RemoveAll(dir)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
vs, err := NewVersionSet(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewVersionSet failed: %v", err)
|
||||
}
|
||||
defer vs.Close()
|
||||
|
||||
// 添加文件
|
||||
edit := NewVersionEdit()
|
||||
edit.AddFile(&FileMetadata{
|
||||
FileNumber: 1,
|
||||
FileSize: 1024,
|
||||
MinKey: 1,
|
||||
MaxKey: 100,
|
||||
RowCount: 100,
|
||||
})
|
||||
|
||||
err = vs.LogAndApply(edit)
|
||||
if err != nil {
|
||||
t.Fatalf("LogAndApply failed: %v", err)
|
||||
}
|
||||
|
||||
// 检查
|
||||
version := vs.GetCurrent()
|
||||
if version.GetFileCount() != 1 {
|
||||
t.Errorf("Expected 1 file, got %d", version.GetFileCount())
|
||||
}
|
||||
|
||||
files := version.GetSSTFiles()
|
||||
if files[0].FileNumber != 1 {
|
||||
t.Errorf("Expected file number 1, got %d", files[0].FileNumber)
|
||||
}
|
||||
|
||||
t.Log("VersionSet add file test passed!")
|
||||
}
|
||||
|
||||
func TestVersionSetDeleteFile(t *testing.T) {
|
||||
dir := "./test_manifest_delete"
|
||||
os.RemoveAll(dir)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
vs, err := NewVersionSet(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewVersionSet failed: %v", err)
|
||||
}
|
||||
defer vs.Close()
|
||||
|
||||
// 添加两个文件
|
||||
edit1 := NewVersionEdit()
|
||||
edit1.AddFile(&FileMetadata{FileNumber: 1, FileSize: 1024, MinKey: 1, MaxKey: 100, RowCount: 100})
|
||||
edit1.AddFile(&FileMetadata{FileNumber: 2, FileSize: 2048, MinKey: 101, MaxKey: 200, RowCount: 100})
|
||||
vs.LogAndApply(edit1)
|
||||
|
||||
// 删除一个文件
|
||||
edit2 := NewVersionEdit()
|
||||
edit2.DeleteFile(1)
|
||||
err = vs.LogAndApply(edit2)
|
||||
if err != nil {
|
||||
t.Fatalf("LogAndApply failed: %v", err)
|
||||
}
|
||||
|
||||
// 检查
|
||||
version := vs.GetCurrent()
|
||||
if version.GetFileCount() != 1 {
|
||||
t.Errorf("Expected 1 file, got %d", version.GetFileCount())
|
||||
}
|
||||
|
||||
files := version.GetSSTFiles()
|
||||
if files[0].FileNumber != 2 {
|
||||
t.Errorf("Expected file number 2, got %d", files[0].FileNumber)
|
||||
}
|
||||
|
||||
t.Log("VersionSet delete file test passed!")
|
||||
}
|
||||
|
||||
func TestVersionSetRecover(t *testing.T) {
|
||||
dir := "./test_manifest_recover"
|
||||
os.RemoveAll(dir)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// 第一次:创建并添加文件
|
||||
vs1, err := NewVersionSet(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewVersionSet failed: %v", err)
|
||||
}
|
||||
|
||||
edit := NewVersionEdit()
|
||||
edit.AddFile(&FileMetadata{FileNumber: 1, FileSize: 1024, MinKey: 1, MaxKey: 100, RowCount: 100})
|
||||
edit.AddFile(&FileMetadata{FileNumber: 2, FileSize: 2048, MinKey: 101, MaxKey: 200, RowCount: 100})
|
||||
vs1.LogAndApply(edit)
|
||||
vs1.Close()
|
||||
|
||||
// 第二次:重新打开并恢复
|
||||
vs2, err := NewVersionSet(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewVersionSet recover failed: %v", err)
|
||||
}
|
||||
defer vs2.Close()
|
||||
|
||||
// 检查恢复的数据
|
||||
version := vs2.GetCurrent()
|
||||
if version.GetFileCount() != 2 {
|
||||
t.Errorf("Expected 2 files after recover, got %d", version.GetFileCount())
|
||||
}
|
||||
|
||||
files := version.GetSSTFiles()
|
||||
if files[0].FileNumber != 1 || files[1].FileNumber != 2 {
|
||||
t.Errorf("File numbers not correct after recover")
|
||||
}
|
||||
|
||||
t.Log("VersionSet recover test passed!")
|
||||
}
|
||||
|
||||
func TestVersionSetMultipleEdits(t *testing.T) {
|
||||
dir := "./test_manifest_multiple"
|
||||
os.RemoveAll(dir)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
vs, err := NewVersionSet(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("NewVersionSet failed: %v", err)
|
||||
}
|
||||
defer vs.Close()
|
||||
|
||||
// 多次变更
|
||||
for i := int64(1); i <= 10; i++ {
|
||||
edit := NewVersionEdit()
|
||||
edit.AddFile(&FileMetadata{
|
||||
FileNumber: i,
|
||||
FileSize: 1024 * i,
|
||||
MinKey: (i-1)*100 + 1,
|
||||
MaxKey: i * 100,
|
||||
RowCount: 100,
|
||||
})
|
||||
err = vs.LogAndApply(edit)
|
||||
if err != nil {
|
||||
t.Fatalf("LogAndApply failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 检查
|
||||
version := vs.GetCurrent()
|
||||
if version.GetFileCount() != 10 {
|
||||
t.Errorf("Expected 10 files, got %d", version.GetFileCount())
|
||||
}
|
||||
|
||||
t.Log("VersionSet multiple edits test passed!")
|
||||
}
|
||||
|
||||
func TestVersionEditEncodeDecode(t *testing.T) {
|
||||
// 创建 VersionEdit
|
||||
edit1 := NewVersionEdit()
|
||||
edit1.AddFile(&FileMetadata{FileNumber: 1, FileSize: 1024, MinKey: 1, MaxKey: 100, RowCount: 100})
|
||||
edit1.DeleteFile(2)
|
||||
nextFile := int64(10)
|
||||
edit1.SetNextFileNumber(nextFile)
|
||||
lastSeq := int64(1000)
|
||||
edit1.SetLastSequence(lastSeq)
|
||||
|
||||
// 编码
|
||||
data, err := edit1.Encode()
|
||||
if err != nil {
|
||||
t.Fatalf("Encode failed: %v", err)
|
||||
}
|
||||
|
||||
// 解码
|
||||
edit2 := NewVersionEdit()
|
||||
err = edit2.Decode(data)
|
||||
if err != nil {
|
||||
t.Fatalf("Decode failed: %v", err)
|
||||
}
|
||||
|
||||
// 检查
|
||||
if len(edit2.AddedFiles) != 1 {
|
||||
t.Errorf("Expected 1 added file, got %d", len(edit2.AddedFiles))
|
||||
}
|
||||
if len(edit2.DeletedFiles) != 1 {
|
||||
t.Errorf("Expected 1 deleted file, got %d", len(edit2.DeletedFiles))
|
||||
}
|
||||
if *edit2.NextFileNumber != 10 {
|
||||
t.Errorf("Expected NextFileNumber 10, got %d", *edit2.NextFileNumber)
|
||||
}
|
||||
if *edit2.LastSequence != 1000 {
|
||||
t.Errorf("Expected LastSequence 1000, got %d", *edit2.LastSequence)
|
||||
}
|
||||
|
||||
t.Log("VersionEdit encode/decode test passed!")
|
||||
}
|
||||
Reference in New Issue
Block a user