Files
OpenFlare/internal/repository/system_config_cache.go
T
ryan b3a55d4ab5 refactor(core): optimize performance, fix concurrency and clean up AGENTS.md design violations
- Concurrency: Added lock protection to WebSocket writes, fixed timer leaks, and prevented config cache listener context leaks.
- Performance: Added memory cache in ObservabilityBufferStore, periodic cleaning in CH Deduplicator, and buffered ZIP batch download writes.
- Design: Introduced Redis caching for OAuth session/tokens, sanitized raw DB error messages, segregated handlers and logics, and standard CAP response envelopes.
2026-06-20 09:09:02 +08:00

139 lines
3.9 KiB
Go

// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package repository
import (
"context"
"encoding/json"
"sync"
"github.com/Rain-kl/Wavelet/internal/db"
"github.com/Rain-kl/Wavelet/internal/model"
"github.com/Rain-kl/Wavelet/pkg/cache/ram"
)
const (
// SystemConfigInvalidationChannel broadcasts RAM cache eviction across nodes.
SystemConfigInvalidationChannel = "system:config_invalidation"
// SystemConfigRedisHashKey Redis Hash key,存储所有系统配置。
SystemConfigRedisHashKey = "system:system_configs"
// SystemConfigVisibleListRedisKey Redis key,缓存所有 visibility=1 的公共配置列表。
SystemConfigVisibleListRedisKey = "system:visible_configs"
systemConfigInvalidateAllToken = "*"
systemConfigRAMMaximumSize = 512
)
type systemConfigInvalidationMessage struct {
Key string `json:"key"`
}
var (
systemConfigRAMCache = ram.MustNew[string, model.SystemConfig](ram.Options{MaximumSize: systemConfigRAMMaximumSize})
systemConfigListenerOnce sync.Once
systemConfigListenerCtx context.Context
systemConfigListenerCancel context.CancelFunc
)
func ensureSystemConfigCacheListener() {
systemConfigListenerOnce.Do(startSystemConfigCacheInvalidationListener)
}
func startSystemConfigCacheInvalidationListener() {
if db.Redis == nil {
return
}
systemConfigListenerCtx, systemConfigListenerCancel = context.WithCancel(context.Background())
go func() {
pubsub := db.Redis.Subscribe(systemConfigListenerCtx, SystemConfigInvalidationChannel)
defer func() {
_ = pubsub.Close()
}()
go func() {
<-systemConfigListenerCtx.Done()
_ = pubsub.Close()
}()
for msg := range pubsub.Channel() {
var payload systemConfigInvalidationMessage
if err := json.Unmarshal([]byte(msg.Payload), &payload); err != nil {
systemConfigRAMCache.InvalidateAll()
continue
}
if payload.Key == "" || payload.Key == systemConfigInvalidateAllToken {
systemConfigRAMCache.InvalidateAll()
continue
}
systemConfigRAMCache.Invalidate(payload.Key)
}
}()
}
// StopSystemConfigCacheListener stops the Redis Pub/Sub subscription listener and resets the sync.Once guard.
func StopSystemConfigCacheListener() {
if systemConfigListenerCancel != nil {
systemConfigListenerCancel()
systemConfigListenerCancel = nil
}
systemConfigListenerOnce = sync.Once{}
}
func cloneSystemConfig(sc model.SystemConfig) model.SystemConfig {
return sc
}
func populateSystemConfigCache(ctx context.Context, sc model.SystemConfig) {
systemConfigRAMCache.Set(sc.Key, cloneSystemConfig(sc))
if db.Redis != nil {
_ = db.HSetJSON(ctx, SystemConfigRedisHashKey, sc.Key, &sc)
}
}
func publishSystemConfigRAMInvalidation(ctx context.Context, key string) {
if db.Redis == nil {
return
}
payload, err := json.Marshal(systemConfigInvalidationMessage{Key: key})
if err != nil {
return
}
_ = db.Redis.Publish(ctx, SystemConfigInvalidationChannel, payload).Err()
}
// InvalidateSystemConfigCache evicts one config key from local RAM and Redis.
func InvalidateSystemConfigCache(ctx context.Context, key string) error {
ensureSystemConfigCacheListener()
systemConfigRAMCache.Invalidate(key)
if db.Redis != nil {
if err := db.HDel(ctx, SystemConfigRedisHashKey, key); err != nil {
return err
}
}
publishSystemConfigRAMInvalidation(ctx, key)
return nil
}
// InvalidateAllSystemConfigCaches evicts all config entries from local RAM and Redis.
func InvalidateAllSystemConfigCaches(ctx context.Context) error {
ensureSystemConfigCacheListener()
systemConfigRAMCache.InvalidateAll()
if db.Redis != nil {
if err := db.Redis.Del(ctx, db.PrefixedKey(SystemConfigRedisHashKey)).Err(); err != nil {
return err
}
}
publishSystemConfigRAMInvalidation(ctx, systemConfigInvalidateAllToken)
return nil
}
// ResetSystemConfigRAMCacheForTest clears only the process-local RAM cache.
func ResetSystemConfigRAMCacheForTest() {
systemConfigRAMCache.InvalidateAll()
}