- 添加 Import Map 支持 Lit 和本地模块的简洁导入 - 创建统一的 API 管理模块 (common/api.js) - 重命名 styles/ 为 common/ 目录 - 修复分页时列选择被重置的问题 - 将 app.js 重命名为 main.js - 所有导入路径使用 ~ 别名映射
615 lines
12 KiB
Go
615 lines
12 KiB
Go
package srdb
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"fmt"
|
|
"hash/crc32"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
)
|
|
|
|
// FileMetadata SST 文件元数据
|
|
type FileMetadata struct {
|
|
FileNumber int64 // 文件编号
|
|
Level int // 所在层级 (0-3)
|
|
FileSize int64 // 文件大小
|
|
MinKey int64 // 最小 key
|
|
MaxKey int64 // 最大 key
|
|
RowCount int64 // 行数
|
|
}
|
|
|
|
const (
|
|
NumLevels = 4 // L0-L3
|
|
)
|
|
|
|
// Version 数据库的一个版本快照
|
|
type Version struct {
|
|
// 分层存储 SST 文件 (L0-L3)
|
|
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 := range NumLevels {
|
|
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 := range NumLevels {
|
|
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 := range NumLevels {
|
|
newFiles := make([]*FileMetadata, 0)
|
|
for _, file := range v.Levels[level] {
|
|
if !deleteSet[file.FileNumber] {
|
|
newFiles = append(newFiles, file)
|
|
}
|
|
}
|
|
v.Levels[level] = newFiles
|
|
}
|
|
}
|
|
|
|
// 添加文件(按层级添加)
|
|
if len(edit.AddedFiles) > 0 {
|
|
for _, file := range edit.AddedFiles {
|
|
if file.Level >= 0 && file.Level < NumLevels {
|
|
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 := range NumLevels {
|
|
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 := range NumLevels {
|
|
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])
|
|
}
|
|
|
|
// ManifestReader MANIFEST 读取器
|
|
type ManifestReader struct {
|
|
file io.Reader
|
|
}
|
|
|
|
// NewManifestReader 创建 MANIFEST 读取器
|
|
func NewManifestReader(file io.Reader) *ManifestReader {
|
|
return &ManifestReader{
|
|
file: file,
|
|
}
|
|
}
|
|
|
|
// ReadEdit 读取版本变更
|
|
func (r *ManifestReader) 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
|
|
}
|
|
|
|
// ManifestWriter MANIFEST 写入器
|
|
type ManifestWriter struct {
|
|
file io.Writer
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// NewManifestWriter 创建 MANIFEST 写入器
|
|
func NewManifestWriter(file io.Writer) *ManifestWriter {
|
|
return &ManifestWriter{
|
|
file: file,
|
|
}
|
|
}
|
|
|
|
// WriteEdit 写入版本变更
|
|
func (w *ManifestWriter) 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
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
// VersionSet 版本集合管理器
|
|
type VersionSet struct {
|
|
// 当前版本
|
|
current *Version
|
|
|
|
// MANIFEST 文件
|
|
manifestFile *os.File
|
|
manifestWriter *ManifestWriter
|
|
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 = NewManifestWriter(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 = NewManifestWriter(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 := NewManifestReader(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 {
|
|
// 使用 CAS 循环确保只在新值更大时更新,避免并发回退
|
|
for {
|
|
old := vs.nextFileNumber.Load()
|
|
if *edit.NextFileNumber <= old {
|
|
break // 新值不大于当前值,不更新
|
|
}
|
|
if vs.nextFileNumber.CompareAndSwap(old, *edit.NextFileNumber) {
|
|
break // 更新成功
|
|
}
|
|
// CAS 失败,重试
|
|
}
|
|
}
|
|
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
|
|
}
|