- 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
252 lines
4.8 KiB
Go
252 lines
4.8 KiB
Go
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
|
|
}
|