- 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
285 lines
5.6 KiB
Go
285 lines
5.6 KiB
Go
package sst
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"sort"
|
||
"strings"
|
||
"sync"
|
||
)
|
||
|
||
// Manager SST 文件管理器
|
||
type Manager struct {
|
||
dir string
|
||
readers []*Reader
|
||
mu sync.RWMutex
|
||
}
|
||
|
||
// NewManager 创建 SST 管理器
|
||
func NewManager(dir string) (*Manager, error) {
|
||
// 确保目录存在
|
||
err := os.MkdirAll(dir, 0755)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
mgr := &Manager{
|
||
dir: dir,
|
||
readers: make([]*Reader, 0),
|
||
}
|
||
|
||
// 恢复现有的 SST 文件
|
||
err = mgr.recover()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return mgr, nil
|
||
}
|
||
|
||
// recover 恢复现有的 SST 文件
|
||
func (m *Manager) recover() error {
|
||
// 查找所有 SST 文件
|
||
files, err := filepath.Glob(filepath.Join(m.dir, "*.sst"))
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
for _, file := range files {
|
||
// 跳过索引文件
|
||
filename := filepath.Base(file)
|
||
if strings.HasPrefix(filename, "idx_") {
|
||
continue
|
||
}
|
||
|
||
// 打开 SST Reader
|
||
reader, err := NewReader(file)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
m.readers = append(m.readers, reader)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// CreateSST 创建新的 SST 文件
|
||
// fileNumber: 文件编号(由 VersionSet 分配)
|
||
func (m *Manager) CreateSST(fileNumber int64, rows []*Row) (*Reader, error) {
|
||
return m.CreateSSTWithLevel(fileNumber, rows, 0) // 默认创建到 L0
|
||
}
|
||
|
||
// CreateSSTWithLevel 创建新的 SST 文件到指定层级
|
||
// fileNumber: 文件编号(由 VersionSet 分配)
|
||
func (m *Manager) CreateSSTWithLevel(fileNumber int64, rows []*Row, level int) (*Reader, error) {
|
||
m.mu.Lock()
|
||
defer m.mu.Unlock()
|
||
|
||
sstPath := filepath.Join(m.dir, fmt.Sprintf("%06d.sst", fileNumber))
|
||
|
||
// 创建文件
|
||
file, err := os.Create(sstPath)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
writer := NewWriter(file)
|
||
|
||
// 写入所有行
|
||
for _, row := range rows {
|
||
err = writer.Add(row)
|
||
if err != nil {
|
||
file.Close()
|
||
os.Remove(sstPath)
|
||
return nil, err
|
||
}
|
||
}
|
||
|
||
// 完成写入
|
||
err = writer.Finish()
|
||
if err != nil {
|
||
file.Close()
|
||
os.Remove(sstPath)
|
||
return nil, err
|
||
}
|
||
|
||
file.Close()
|
||
|
||
// 打开 SST Reader
|
||
reader, err := NewReader(sstPath)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 添加到 readers 列表
|
||
m.readers = append(m.readers, reader)
|
||
|
||
return reader, nil
|
||
}
|
||
|
||
// Get 从所有 SST 文件中查找数据
|
||
func (m *Manager) Get(seq int64) (*Row, error) {
|
||
m.mu.RLock()
|
||
defer m.mu.RUnlock()
|
||
|
||
// 从后往前查找(新的文件优先)
|
||
for i := len(m.readers) - 1; i >= 0; i-- {
|
||
reader := m.readers[i]
|
||
row, err := reader.Get(seq)
|
||
if err == nil {
|
||
return row, nil
|
||
}
|
||
}
|
||
|
||
return nil, fmt.Errorf("key not found: %d", seq)
|
||
}
|
||
|
||
// GetReaders 获取所有 Readers(用于扫描)
|
||
func (m *Manager) GetReaders() []*Reader {
|
||
m.mu.RLock()
|
||
defer m.mu.RUnlock()
|
||
|
||
// 返回副本
|
||
readers := make([]*Reader, len(m.readers))
|
||
copy(readers, m.readers)
|
||
return readers
|
||
}
|
||
|
||
// GetMaxSeq 获取所有 SST 中的最大 seq
|
||
func (m *Manager) GetMaxSeq() int64 {
|
||
m.mu.RLock()
|
||
defer m.mu.RUnlock()
|
||
|
||
maxSeq := int64(0)
|
||
for _, reader := range m.readers {
|
||
header := reader.GetHeader()
|
||
if header.MaxKey > maxSeq {
|
||
maxSeq = header.MaxKey
|
||
}
|
||
}
|
||
|
||
return maxSeq
|
||
}
|
||
|
||
// Count 获取 SST 文件数量
|
||
func (m *Manager) Count() int {
|
||
m.mu.RLock()
|
||
defer m.mu.RUnlock()
|
||
|
||
return len(m.readers)
|
||
}
|
||
|
||
// ListFiles 列出所有 SST 文件
|
||
func (m *Manager) ListFiles() []string {
|
||
m.mu.RLock()
|
||
defer m.mu.RUnlock()
|
||
|
||
files := make([]string, 0, len(m.readers))
|
||
for _, reader := range m.readers {
|
||
files = append(files, reader.path)
|
||
}
|
||
|
||
return files
|
||
}
|
||
|
||
// CompactionConfig Compaction 配置
|
||
// 已废弃:请使用 compaction 包中的 Manager
|
||
type CompactionConfig struct {
|
||
Threshold int // 触发阈值(SST 文件数量)
|
||
BatchSize int // 每次合并的文件数量
|
||
}
|
||
|
||
// DefaultCompactionConfig 默认配置
|
||
// 已废弃:请使用 compaction 包中的 Manager
|
||
var DefaultCompactionConfig = CompactionConfig{
|
||
Threshold: 10,
|
||
BatchSize: 10,
|
||
}
|
||
|
||
// ShouldCompact 检查是否需要 Compaction
|
||
// 已废弃:请使用 compaction 包中的 Manager
|
||
func (m *Manager) ShouldCompact(config CompactionConfig) bool {
|
||
m.mu.RLock()
|
||
defer m.mu.RUnlock()
|
||
|
||
return len(m.readers) > config.Threshold
|
||
}
|
||
|
||
// Compact 执行 Compaction
|
||
// 已废弃:请使用 compaction 包中的 Manager
|
||
// 注意:此方法已不再维护,不应在新代码中使用
|
||
func (m *Manager) Compact(config CompactionConfig) error {
|
||
// 此方法已废弃,不再实现
|
||
return fmt.Errorf("Compact is deprecated, please use compaction.Manager")
|
||
}
|
||
|
||
// sortRows 按 seq 排序
|
||
func sortRows(rows []*Row) {
|
||
sort.Slice(rows, func(i, j int) bool {
|
||
return rows[i].Seq < rows[j].Seq
|
||
})
|
||
}
|
||
|
||
// Delete 删除指定的 SST 文件(预留接口)
|
||
func (m *Manager) Delete(fileNumber int64) error {
|
||
m.mu.Lock()
|
||
defer m.mu.Unlock()
|
||
|
||
sstPath := filepath.Join(m.dir, fmt.Sprintf("%06d.sst", fileNumber))
|
||
return os.Remove(sstPath)
|
||
}
|
||
|
||
// Close 关闭所有 SST Readers
|
||
func (m *Manager) Close() error {
|
||
m.mu.Lock()
|
||
defer m.mu.Unlock()
|
||
|
||
for _, reader := range m.readers {
|
||
reader.Close()
|
||
}
|
||
|
||
m.readers = nil
|
||
return nil
|
||
}
|
||
|
||
// Stats 统计信息
|
||
type Stats struct {
|
||
FileCount int
|
||
TotalSize int64
|
||
MinSeq int64
|
||
MaxSeq int64
|
||
}
|
||
|
||
// GetStats 获取统计信息
|
||
func (m *Manager) GetStats() *Stats {
|
||
m.mu.RLock()
|
||
defer m.mu.RUnlock()
|
||
|
||
stats := &Stats{
|
||
FileCount: len(m.readers),
|
||
MinSeq: -1,
|
||
MaxSeq: -1,
|
||
}
|
||
|
||
for _, reader := range m.readers {
|
||
header := reader.GetHeader()
|
||
|
||
if stats.MinSeq == -1 || header.MinKey < stats.MinSeq {
|
||
stats.MinSeq = header.MinKey
|
||
}
|
||
|
||
if stats.MaxSeq == -1 || header.MaxKey > stats.MaxSeq {
|
||
stats.MaxSeq = header.MaxKey
|
||
}
|
||
|
||
// 获取文件大小
|
||
if stat, err := os.Stat(reader.path); err == nil {
|
||
stats.TotalSize += stat.Size()
|
||
}
|
||
}
|
||
|
||
return stats
|
||
}
|