重构:简化查询接口

- RecordQuery.QueryOldest 和 QueryNewest 不再接收 startIdx/endIdx 参数
- 查询方法返回纯 Record 列表,状态判断移到调用方
- TopicProcessor 的查询方法负责添加状态信息
- 更新所有测试文件以适配新接口
This commit is contained in:
2025-10-04 00:10:14 +08:00
parent a421ca1d85
commit 5c028a55b3
8 changed files with 168 additions and 208 deletions

View File

@@ -5,6 +5,8 @@ import (
"fmt"
"io"
"os"
"sync"
"time"
)
const (
@@ -19,16 +21,27 @@ const (
// IndexEntrySize 每条索引条目大小(字节)
IndexEntrySize = 8 // Offset(8)
// DefaultSyncInterval 默认同步间隔
DefaultSyncInterval = 1 * time.Second
// DefaultSyncBatch 默认同步批次大小(累积多少条记录后同步)
DefaultSyncBatch = 100
)
// RecordIndex 记录索引管理器
type RecordIndex struct {
logPath string // 日志文件路径
indexPath string // 索引文件路径
offsets []int64 // 内存中的偏移索引
magic uint32 // 魔数,用于识别索引文件
version uint32 // 版本号
indexFile *os.File // 索引文件句柄(用于追加写入)
logPath string // 日志文件路径
indexPath string // 索引文件路径
offsets []int64 // 内存中的偏移索引
magic uint32 // 魔数,用于识别索引文件
version uint32 // 版本号
indexFile *os.File // 索引文件句柄(用于追加写入)
syncInterval time.Duration // 同步时间间隔
syncBatch int // 同步批次大小
lastSync time.Time // 上次同步时间
dirtyCount int // 未同步的记录数
mu sync.Mutex // 保护并发访问
}
// NewRecordIndex 创建或加载记录索引
@@ -37,11 +50,14 @@ func NewRecordIndex(logPath string) (*RecordIndex, error) {
indexPath := logPath + ".idx"
ri := &RecordIndex{
logPath: logPath,
indexPath: indexPath,
offsets: make([]int64, 0, 1024),
magic: IndexMagic,
version: IndexVersion,
logPath: logPath,
indexPath: indexPath,
offsets: make([]int64, 0, 1024),
magic: IndexMagic,
version: IndexVersion,
syncInterval: DefaultSyncInterval,
syncBatch: DefaultSyncBatch,
lastSync: time.Now(),
}
// 启动时总是从日志文件重建索引
@@ -136,6 +152,9 @@ func (ri *RecordIndex) save() error {
// Append 追加一条索引(当写入新记录时调用)
func (ri *RecordIndex) Append(offset int64) error {
ri.mu.Lock()
defer ri.mu.Unlock()
// 追加到索引文件(先写文件,后更新内存)
entryBuf := make([]byte, IndexEntrySize)
binary.LittleEndian.PutUint64(entryBuf, uint64(offset))
@@ -146,11 +165,15 @@ func (ri *RecordIndex) Append(offset int64) error {
// 更新内存索引
ri.offsets = append(ri.offsets, offset)
ri.dirtyCount++
// 同步索引文件
// TODO 这里太频繁了
if err := ri.indexFile.Sync(); err != nil {
return fmt.Errorf("sync index file: %w", err)
// 批量同步:达到批次大小或时间间隔后才同步
if ri.dirtyCount >= ri.syncBatch || time.Since(ri.lastSync) >= ri.syncInterval {
if err := ri.indexFile.Sync(); err != nil {
return fmt.Errorf("sync index file: %w", err)
}
ri.lastSync = time.Now()
ri.dirtyCount = 0
}
return nil
@@ -197,18 +220,35 @@ func (ri *RecordIndex) LastOffset() int64 {
return ri.offsets[len(ri.offsets)-1]
}
// Flush 强制同步未写入的数据到磁盘
func (ri *RecordIndex) Flush() error {
ri.mu.Lock()
defer ri.mu.Unlock()
if ri.indexFile != nil && ri.dirtyCount > 0 {
if err := ri.indexFile.Sync(); err != nil {
return fmt.Errorf("flush index file: %w", err)
}
ri.lastSync = time.Now()
ri.dirtyCount = 0
}
return nil
}
// Close 关闭索引文件
func (ri *RecordIndex) Close() error {
// 关闭前确保所有数据已同步
if err := ri.Flush(); err != nil {
return err
}
if ri.indexFile != nil {
return ri.indexFile.Close()
}
return nil
}
// Sync 同步索引文件到磁盘
// Sync 同步索引文件到磁盘(立即同步,不考虑批量策略)
func (ri *RecordIndex) Sync() error {
if ri.indexFile != nil {
return ri.indexFile.Sync()
}
return nil
return ri.Flush()
}