Files
smartmate/backend/kafka/admin.go
Losita 959049db42 Version: 0.4.9.dev.260309
feat: 🗄️ 新增自动建表功能

* 新增项目启动时自动建表能力,减少手动初始化数据库步骤
* 解决 `agent_chat` 与 `chat_history` 结构体互相持有对方结构体用于 `preload` 导致的循环依赖问题
* 修复因结构体互相依赖引发的建表失败问题,保证数据库初始化流程稳定

feat: 🐳 Docker Compose 引入 Kafka 分区自动初始化

* 更新 `docker-compose` 配置,引入 Kafka partition 自动初始化脚本
* 保证服务启动后 Topic 即具备可用 partition,实现开箱即用
* 修复转移环境后 MySQL 等容器数据无法持久化的问题,统一改为使用命名卷进行数据持久化

docs: 📚 补充 Outbox + Kafka 持久化链路注释

* 为 Outbox + Kafka 消息持久化链路补充详细代码注释
* 提升异步消息链路的可读性与维护性
* 当前代码 Review 进度约 50%

undo: ⚠️ Kafka 初始化阶段出现消息短暂堆积

* 初次初始化项目时观察到消息在 Kafka 中短暂堆积现象
* 后续被消费者一次性消费且未再次复现
* 已在生产者启动、消费者启动以及消息消费流程中增加控制台日志输出,降低系统黑箱程度
* 后续若条件允许将进一步排查该现象的触发原因
2026-03-09 23:25:25 +08:00

79 lines
1.8 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 kafka
import (
"context"
"errors"
"fmt"
"time"
segmentkafka "github.com/segmentio/kafka-go"
)
// WaitTopicReady 在指定超时时间内等待 Kafka topic 可用。
// 背景:初次部署时 broker 可能已启动,但 topic/partition 还没就绪。
// 这里启动前先探测,可减少“应用已启动但实际无法消费”的静默窗口。
func WaitTopicReady(parent context.Context, brokers []string, topic string, timeout time.Duration) error {
if len(brokers) == 0 {
return errors.New("kafka brokers is empty")
}
if topic == "" {
return errors.New("kafka topic is empty")
}
if timeout <= 0 {
timeout = 30 * time.Second
}
ctx, cancel := context.WithTimeout(parent, timeout)
defer cancel()
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
var lastErr error
for {
if err := probeTopic(ctx, brokers, topic); err == nil {
return nil
} else {
lastErr = err
}
select {
case <-ctx.Done():
if lastErr != nil {
return fmt.Errorf("wait topic ready timeout, topic=%s: %w", topic, lastErr)
}
return fmt.Errorf("wait topic ready timeout, topic=%s", topic)
case <-ticker.C:
}
}
}
// probeTopic 轮询所有 broker只要任一 broker 能读到 topic 分区信息即视为就绪。
func probeTopic(ctx context.Context, brokers []string, topic string) error {
var lastErr error
for _, broker := range brokers {
conn, err := segmentkafka.DialContext(ctx, "tcp", broker)
if err != nil {
lastErr = err
continue
}
partitions, readErr := conn.ReadPartitions(topic)
_ = conn.Close()
if readErr != nil {
lastErr = readErr
continue
}
if len(partitions) == 0 {
lastErr = fmt.Errorf("topic %s has no partitions yet", topic)
continue
}
return nil
}
if lastErr != nil {
return lastErr
}
return errors.New("unable to probe topic readiness")
}