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:
206
wal/manager.go
Normal file
206
wal/manager.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package wal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Manager WAL 管理器,管理多个 WAL 文件
|
||||
type Manager struct {
|
||||
dir string
|
||||
currentWAL *WAL
|
||||
currentNumber int64
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewManager 创建 WAL 管理器
|
||||
func NewManager(dir string) (*Manager, error) {
|
||||
// 确保目录存在
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 读取当前 WAL 编号
|
||||
number, err := readCurrentNumber(dir)
|
||||
if err != nil {
|
||||
// 如果读取失败,从 1 开始
|
||||
number = 1
|
||||
}
|
||||
|
||||
// 打开当前 WAL
|
||||
walPath := filepath.Join(dir, fmt.Sprintf("%06d.wal", number))
|
||||
wal, err := Open(walPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 保存当前编号
|
||||
err = saveCurrentNumber(dir, number)
|
||||
if err != nil {
|
||||
wal.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Manager{
|
||||
dir: dir,
|
||||
currentWAL: wal,
|
||||
currentNumber: number,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Append 追加记录到当前 WAL
|
||||
func (m *Manager) Append(entry *Entry) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
return m.currentWAL.Append(entry)
|
||||
}
|
||||
|
||||
// Sync 同步当前 WAL 到磁盘
|
||||
func (m *Manager) Sync() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
return m.currentWAL.Sync()
|
||||
}
|
||||
|
||||
// Rotate 切换到新的 WAL 文件
|
||||
func (m *Manager) Rotate() (int64, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// 记录旧的 WAL 编号
|
||||
oldNumber := m.currentNumber
|
||||
|
||||
// 关闭当前 WAL
|
||||
err := m.currentWAL.Close()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 创建新 WAL
|
||||
m.currentNumber++
|
||||
walPath := filepath.Join(m.dir, fmt.Sprintf("%06d.wal", m.currentNumber))
|
||||
wal, err := Open(walPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
m.currentWAL = wal
|
||||
|
||||
// 更新 CURRENT 文件
|
||||
err = saveCurrentNumber(m.dir, m.currentNumber)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return oldNumber, nil
|
||||
}
|
||||
|
||||
// Delete 删除指定的 WAL 文件
|
||||
func (m *Manager) Delete(number int64) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
walPath := filepath.Join(m.dir, fmt.Sprintf("%06d.wal", number))
|
||||
return os.Remove(walPath)
|
||||
}
|
||||
|
||||
// GetCurrentNumber 获取当前 WAL 编号
|
||||
func (m *Manager) GetCurrentNumber() int64 {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
return m.currentNumber
|
||||
}
|
||||
|
||||
// RecoverAll 恢复所有 WAL 文件
|
||||
func (m *Manager) RecoverAll() ([]*Entry, error) {
|
||||
// 查找所有 WAL 文件
|
||||
pattern := filepath.Join(m.dir, "*.wal")
|
||||
files, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 按文件名排序(确保按时间顺序)
|
||||
sort.Strings(files)
|
||||
|
||||
var allEntries []*Entry
|
||||
|
||||
// 依次读取每个 WAL
|
||||
for _, file := range files {
|
||||
reader, err := NewReader(file)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
entries, err := reader.Read()
|
||||
reader.Close()
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
allEntries = append(allEntries, entries...)
|
||||
}
|
||||
|
||||
return allEntries, nil
|
||||
}
|
||||
|
||||
// ListWALFiles 列出所有 WAL 文件
|
||||
func (m *Manager) ListWALFiles() ([]string, error) {
|
||||
pattern := filepath.Join(m.dir, "*.wal")
|
||||
files, err := filepath.Glob(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Strings(files)
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// Close 关闭 WAL 管理器
|
||||
func (m *Manager) Close() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.currentWAL != nil {
|
||||
return m.currentWAL.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// readCurrentNumber 读取当前 WAL 编号
|
||||
func readCurrentNumber(dir string) (int64, error) {
|
||||
currentPath := filepath.Join(dir, "CURRENT")
|
||||
data, err := os.ReadFile(currentPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
number, err := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return number, nil
|
||||
}
|
||||
|
||||
// saveCurrentNumber 保存当前 WAL 编号
|
||||
func saveCurrentNumber(dir string, number int64) error {
|
||||
currentPath := filepath.Join(dir, "CURRENT")
|
||||
data := []byte(fmt.Sprintf("%d\n", number))
|
||||
return os.WriteFile(currentPath, data, 0644)
|
||||
}
|
||||
Reference in New Issue
Block a user