feat(cache): 实现 RoutingCache 和 StatsBuffer 优化数据库写入
- 新增 RoutingCache 组件,使用 sync.Map 缓存 Provider 和 Model - 新增 StatsBuffer 组件,使用 sync.Map + atomic.Int64 缓冲统计数据 - 扩展 StatsRepository.BatchUpdate 支持批量增量更新 - 改造 RoutingService/StatsService/ProviderService/ModelService 集成缓存 - 更新 usage-statistics spec,新增 routing-cache 和 stats-buffer spec - 新增单元测试覆盖缓存命中/失效/并发场景
This commit is contained in:
134
backend/internal/service/routing_cache.go
Normal file
134
backend/internal/service/routing_cache.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"nex/backend/internal/domain"
|
||||
"nex/backend/internal/repository"
|
||||
)
|
||||
|
||||
type RoutingCache struct {
|
||||
providers sync.Map
|
||||
models sync.Map
|
||||
|
||||
modelRepo repository.ModelRepository
|
||||
providerRepo repository.ProviderRepository
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func NewRoutingCache(
|
||||
modelRepo repository.ModelRepository,
|
||||
providerRepo repository.ProviderRepository,
|
||||
logger *zap.Logger,
|
||||
) *RoutingCache {
|
||||
return &RoutingCache{
|
||||
modelRepo: modelRepo,
|
||||
providerRepo: providerRepo,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RoutingCache) GetProvider(id string) (*domain.Provider, error) {
|
||||
if v, ok := c.providers.Load(id); ok {
|
||||
return v.(*domain.Provider), nil
|
||||
}
|
||||
|
||||
provider, err := c.providerRepo.GetByID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v, ok := c.providers.Load(id); ok {
|
||||
return v.(*domain.Provider), nil
|
||||
}
|
||||
|
||||
c.providers.Store(id, provider)
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
func (c *RoutingCache) GetModel(providerID, modelName string) (*domain.Model, error) {
|
||||
key := providerID + "/" + modelName
|
||||
|
||||
if v, ok := c.models.Load(key); ok {
|
||||
return v.(*domain.Model), nil
|
||||
}
|
||||
|
||||
model, err := c.modelRepo.FindByProviderAndModelName(providerID, modelName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if v, ok := c.models.Load(key); ok {
|
||||
return v.(*domain.Model), nil
|
||||
}
|
||||
|
||||
c.models.Store(key, model)
|
||||
return model, nil
|
||||
}
|
||||
|
||||
func (c *RoutingCache) SetProvider(provider *domain.Provider) {
|
||||
c.providers.Store(provider.ID, provider)
|
||||
}
|
||||
|
||||
func (c *RoutingCache) SetModel(model *domain.Model) {
|
||||
key := model.ProviderID + "/" + model.ModelName
|
||||
c.models.Store(key, model)
|
||||
}
|
||||
|
||||
func (c *RoutingCache) InvalidateProvider(id string) {
|
||||
c.providers.Delete(id)
|
||||
c.invalidateModelsByProvider(id)
|
||||
c.logger.Debug("Provider 缓存失效", zap.String("provider_id", id))
|
||||
}
|
||||
|
||||
func (c *RoutingCache) InvalidateModel(providerID, modelName string) {
|
||||
key := providerID + "/" + modelName
|
||||
c.models.Delete(key)
|
||||
c.logger.Debug("Model 缓存失效",
|
||||
zap.String("provider_id", providerID),
|
||||
zap.String("model_name", modelName))
|
||||
}
|
||||
|
||||
func (c *RoutingCache) invalidateModelsByProvider(providerID string) {
|
||||
prefix := providerID + "/"
|
||||
count := 0
|
||||
c.models.Range(func(key, value interface{}) bool {
|
||||
if strings.HasPrefix(key.(string), prefix) {
|
||||
c.models.Delete(key)
|
||||
count++
|
||||
}
|
||||
return true
|
||||
})
|
||||
if count > 0 {
|
||||
c.logger.Debug("清除 Provider 相关 Model 缓存",
|
||||
zap.String("provider_id", providerID),
|
||||
zap.Int("count", count))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RoutingCache) Preload() error {
|
||||
providers, err := c.providerRepo.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range providers {
|
||||
c.providers.Store(providers[i].ID, &providers[i])
|
||||
}
|
||||
|
||||
models, err := c.modelRepo.List("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range models {
|
||||
key := models[i].ProviderID + "/" + models[i].ModelName
|
||||
c.models.Store(key, &models[i])
|
||||
}
|
||||
|
||||
c.logger.Info("缓存预热完成",
|
||||
zap.Int("providers", len(providers)),
|
||||
zap.Int("models", len(models)))
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user