Files
seqlog/example/topic_processor_example.go
bourdon de39339620 重构:统一使用索引(Index)替代位置(Position)进行状态判断
## 主要变更

### 架构改进
- 明确索引(Index)与偏移(Offset)的职责分离
  - Index: 记录序号(逻辑概念),用于状态判断
  - Offset: 文件字节位置(物理概念),仅用于 I/O 操作

### API 变更
- 删除所有 Position 相关方法:
  - `LogCursor.StartPos()/EndPos()`
  - `LogTailer.GetStartPos()/GetEndPos()`
  - `TopicProcessor.GetProcessingPosition()/GetReadPosition()`
  - `Seqlog.GetProcessingPosition()/GetReadPosition()`

- 新增索引方法:
  - `LogCursor.StartIndex()/EndIndex()`
  - `LogTailer.GetStartIndex()/GetEndIndex()`
  - `TopicProcessor.GetProcessingIndex()/GetReadIndex()`
  - `Seqlog.GetProcessingIndex()/GetReadIndex()`
  - `Seqlog.GetProcessor()` - 获取 processor 实例以访问 Index

### 查询接口变更
- `RecordQuery.QueryOldest(startIndex, count, startIdx, endIdx)` - 使用索引参数
- `RecordQuery.QueryNewest(endIndex, count, startIdx, endIdx)` - 使用索引参数
- `RecordQuery.QueryAt(position, direction, count, startIdx, endIdx)` - startIdx/endIdx 用于状态判断

### 性能优化
- 状态判断改用整数比较,不再需要计算偏移量
- 减少不必要的索引到偏移的转换
- 只在实际文件 I/O 时才获取 offset

### 测试更新
- 更新所有测试用例使用新的 Index API
- 更新示例代码(topic_processor_example.go, webapp/main.go)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-03 23:50:53 +08:00

117 lines
3.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"fmt"
"log"
"log/slog"
"code.tczkiot.com/seqlog"
)
func main() {
// ===== TopicProcessor 作为聚合器使用 =====
fmt.Println("=== TopicProcessor 聚合器示例 ===\n")
// 创建 TopicProcessor提供空 handler
logger := slog.Default()
tp, err := seqlog.NewTopicProcessor("test_seqlog", "app", logger, &seqlog.TopicConfig{
Handler: func(rec *seqlog.Record) error {
return nil // 示例中不需要处理
},
})
if err != nil {
log.Fatalf("创建 TopicProcessor 失败: %v", err)
}
// ===== 1. 写入数据 =====
fmt.Println("1. 写入数据:")
for i := 1; i <= 5; i++ {
data := fmt.Sprintf("消息 #%d", i)
offset, err := tp.Write([]byte(data))
if err != nil {
log.Fatal(err)
}
fmt.Printf(" 写入成功: offset=%d, data=%s\n", offset, data)
}
fmt.Println()
// ===== 2. 获取记录总数 =====
fmt.Println("2. 查询记录总数:")
count := tp.GetRecordCount()
fmt.Printf(" 总共 %d 条记录\n\n", count)
// ===== 3. 获取索引 =====
fmt.Println("3. 使用索引:")
index := tp.Index()
fmt.Printf(" 索引记录数: %d\n", index.Count())
fmt.Printf(" 最后偏移: %d\n\n", index.LastOffset())
// ===== 4. 使用查询器查询 =====
fmt.Println("4. 查询记录:")
// 查询最老的 3 条记录(从索引 0 开始)
oldest, err := tp.QueryOldest(0, 3)
if err != nil {
log.Fatal(err)
}
fmt.Println(" 查询最老的 3 条:")
for i, rws := range oldest {
fmt.Printf(" [%d] 状态=%s, 数据=%s\n", i, rws.Status, string(rws.Record.Data))
}
// 查询最新的 2 条记录(从最后一条开始)
totalCount := tp.GetRecordCount()
newest, err := tp.QueryNewest(totalCount-1, 2)
if err != nil {
log.Fatal(err)
}
fmt.Println(" 查询最新的 2 条:")
for i, rws := range newest {
fmt.Printf(" [%d] 状态=%s, 数据=%s\n", i, rws.Status, string(rws.Record.Data))
}
fmt.Println()
// ===== 5. 使用游标读取 =====
fmt.Println("5. 使用游标读取:")
cursor, err := tp.Cursor()
if err != nil {
log.Fatal(err)
}
defer cursor.Close()
// 读取 3 条记录
records, err := cursor.NextRange(3)
if err != nil {
log.Fatal(err)
}
fmt.Printf(" 读取了 %d 条记录:\n", len(records))
for i, rec := range records {
fmt.Printf(" [%d] %s\n", i, string(rec.Data))
}
// 提交游标位置
cursor.Commit()
fmt.Printf(" 游标位置: start=%d, end=%d\n\n", cursor.StartIndex(), cursor.EndIndex())
// ===== 6. 继续写入 =====
fmt.Println("6. 继续写入:")
for i := 6; i <= 8; i++ {
data := fmt.Sprintf("消息 #%d", i)
offset, _ := tp.Write([]byte(data))
fmt.Printf(" 写入成功: offset=%d, data=%s\n", offset, data)
}
fmt.Println()
// ===== 7. 再次查询总数 =====
fmt.Println("7. 更新后的记录总数:")
count = tp.GetRecordCount()
fmt.Printf(" 总共 %d 条记录\n\n", count)
// ===== 8. 获取统计信息 =====
fmt.Println("8. 统计信息:")
stats := tp.GetStats()
fmt.Printf(" 写入: %d 条, %d 字节\n", stats.WriteCount, stats.WriteBytes)
fmt.Println("\n=== 所有示例完成 ===")
}