deepseek修改版
This commit is contained in:
Vendored
+17
-7
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/EthanCodeCraft/xlgo-core/logger"
|
||||
@@ -164,16 +165,25 @@ type CacheManager struct {
|
||||
svc CacheService
|
||||
}
|
||||
|
||||
// DefaultCache 默认缓存管理器,包级 facade 代理到它。
|
||||
var DefaultCache = NewCacheManager()
|
||||
// defaultCachePtr 全局默认缓存管理器(CK3 修复:atomic.Pointer 替代裸指针)。
|
||||
var defaultCachePtr atomic.Pointer[CacheManager]
|
||||
|
||||
func init() {
|
||||
defaultCachePtr.Store(NewCacheManager())
|
||||
}
|
||||
|
||||
// NewCacheManager 创建缓存管理器实例。
|
||||
func NewCacheManager() *CacheManager { return &CacheManager{} }
|
||||
|
||||
// SetDefaultCacheManager 提升指定 CacheManager 为全局默认。
|
||||
// GetDefaultCache 返回全局默认缓存管理器(并发安全,CK3 修复)。
|
||||
func GetDefaultCache() *CacheManager {
|
||||
return defaultCachePtr.Load()
|
||||
}
|
||||
|
||||
// SetDefaultCacheManager 提升指定 CacheManager 为全局默认(atomic 置换,并发安全)。
|
||||
func SetDefaultCacheManager(m *CacheManager) {
|
||||
if m != nil {
|
||||
DefaultCache = m
|
||||
defaultCachePtr.Store(m)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,14 +211,14 @@ func (m *CacheManager) Get() CacheService {
|
||||
return m.svc
|
||||
}
|
||||
|
||||
// --- 包级 facade(代理到 DefaultCache,兼容存量) ---
|
||||
// --- 包级 facade(代理到 GetDefaultCache(),CK3 修复) ---
|
||||
|
||||
// Init 初始化全局缓存实例
|
||||
func Init() {
|
||||
DefaultCache.Init()
|
||||
GetDefaultCache().Init()
|
||||
}
|
||||
|
||||
// GetCache 获取全局缓存实例
|
||||
func GetCache() CacheService {
|
||||
return DefaultCache.Get()
|
||||
return GetDefaultCache().Get()
|
||||
}
|
||||
|
||||
Vendored
+27
-13
@@ -9,9 +9,10 @@ import (
|
||||
"github.com/EthanCodeCraft/xlgo-core/config"
|
||||
)
|
||||
|
||||
// KeyBuilder 缓存键名构建器
|
||||
// KeyBuilder 缓存键名构建器(CK2 修复:mu 保护并发访问)。
|
||||
// 使用场景: 多个小项目共用一台 Redis 服务器,每个项目设置不同前缀
|
||||
type KeyBuilder struct {
|
||||
mu sync.Mutex
|
||||
prefix string // 项目/站点前缀,如 "site_a"
|
||||
_separator string // 分隔符,默认 ":"
|
||||
_cacheType string // 缓存类型标识,如 "cache"
|
||||
@@ -57,10 +58,12 @@ func NewKeyBuilder(opts ...KeyBuilderOption) *KeyBuilder {
|
||||
return kb
|
||||
}
|
||||
|
||||
// Build 构建完整键名
|
||||
// Build 构建完整键名(CK2 修复:mu 读保护)。
|
||||
// 格式: {cacheType}{separator}{prefix}{separator}{key}
|
||||
// 示例: kb.Build("user:1") -> "cache_site_a_user:1"
|
||||
func (kb *KeyBuilder) Build(key string) string {
|
||||
kb.mu.Lock()
|
||||
defer kb.mu.Unlock()
|
||||
parts := []string{}
|
||||
if kb._cacheType != "" {
|
||||
parts = append(parts, kb._cacheType)
|
||||
@@ -72,9 +75,11 @@ func (kb *KeyBuilder) Build(key string) string {
|
||||
return strings.Join(parts, kb._separator)
|
||||
}
|
||||
|
||||
// BuildTemp 构建临时缓存键名(带过期时间)
|
||||
// BuildTemp 构建临时缓存键名(CK2 修复:mu 读保护)。
|
||||
// 格式: "temp{separator}{prefix}{separator}{key}"
|
||||
func (kb *KeyBuilder) BuildTemp(key string) string {
|
||||
kb.mu.Lock()
|
||||
defer kb.mu.Unlock()
|
||||
parts := []string{"temp"}
|
||||
if kb.prefix != "" {
|
||||
parts = append(parts, kb.prefix)
|
||||
@@ -83,9 +88,11 @@ func (kb *KeyBuilder) BuildTemp(key string) string {
|
||||
return strings.Join(parts, kb._separator)
|
||||
}
|
||||
|
||||
// BuildPerm 构建永久缓存键名(不带过期时间)
|
||||
// BuildPerm 构建永久缓存键名(CK2 修复:mu 读保护)。
|
||||
// 格式: "perm{separator}{prefix}{separator}{key}"
|
||||
func (kb *KeyBuilder) BuildPerm(key string) string {
|
||||
kb.mu.Lock()
|
||||
defer kb.mu.Unlock()
|
||||
parts := []string{"perm"}
|
||||
if kb.prefix != "" {
|
||||
parts = append(parts, kb.prefix)
|
||||
@@ -94,9 +101,11 @@ func (kb *KeyBuilder) BuildPerm(key string) string {
|
||||
return strings.Join(parts, kb._separator)
|
||||
}
|
||||
|
||||
// BuildLock 构建分布式锁键名
|
||||
// BuildLock 构建分布式锁键名(CK2 修复:mu 读保护)。
|
||||
// 格式: "lock{separator}{prefix}{separator}{key}"
|
||||
func (kb *KeyBuilder) BuildLock(key string) string {
|
||||
kb.mu.Lock()
|
||||
defer kb.mu.Unlock()
|
||||
parts := []string{"lock"}
|
||||
if kb.prefix != "" {
|
||||
parts = append(parts, kb.prefix)
|
||||
@@ -105,9 +114,11 @@ func (kb *KeyBuilder) BuildLock(key string) string {
|
||||
return strings.Join(parts, kb._separator)
|
||||
}
|
||||
|
||||
// BuildCounter 构建计数器键名
|
||||
// BuildCounter 构建计数器键名(CK2 修复:mu 读保护)。
|
||||
// 格式: "counter{separator}{prefix}{separator}{key}"
|
||||
func (kb *KeyBuilder) BuildCounter(key string) string {
|
||||
kb.mu.Lock()
|
||||
defer kb.mu.Unlock()
|
||||
parts := []string{"counter"}
|
||||
if kb.prefix != "" {
|
||||
parts = append(parts, kb.prefix)
|
||||
@@ -116,9 +127,11 @@ func (kb *KeyBuilder) BuildCounter(key string) string {
|
||||
return strings.Join(parts, kb._separator)
|
||||
}
|
||||
|
||||
// BuildSession 构建会话键名
|
||||
// BuildSession 构建会话键名(CK2 修复:mu 读保护)。
|
||||
// 格式: "session{separator}{prefix}{separator}{key}"
|
||||
func (kb *KeyBuilder) BuildSession(key string) string {
|
||||
kb.mu.Lock()
|
||||
defer kb.mu.Unlock()
|
||||
parts := []string{"session"}
|
||||
if kb.prefix != "" {
|
||||
parts = append(parts, kb.prefix)
|
||||
@@ -127,22 +140,23 @@ func (kb *KeyBuilder) BuildSession(key string) string {
|
||||
return strings.Join(parts, kb._separator)
|
||||
}
|
||||
|
||||
// BuildPattern 构建匹配模式(用于 SCAN/Keys)
|
||||
// BuildPattern 构建匹配模式(用于 SCAN/Keys)(CK2 修复:mu 读保护)。
|
||||
// 示例: kb.BuildPattern("user:*") -> "cache_site_a_user:*"
|
||||
func (kb *KeyBuilder) BuildPattern(pattern string) string {
|
||||
return kb.Build(pattern)
|
||||
}
|
||||
|
||||
// GetPrefix 获取当前前缀
|
||||
// GetPrefix 获取当前前缀(CK2 修复:mu 读保护)。
|
||||
func (kb *KeyBuilder) GetPrefix() string {
|
||||
kb.mu.Lock()
|
||||
defer kb.mu.Unlock()
|
||||
return kb.prefix
|
||||
}
|
||||
|
||||
// SetPrefix 动态设置前缀。
|
||||
//
|
||||
// 注意(M13):KeyBuilder 实例非并发安全——并发 Build 与 SetPrefix 会竞争 prefix。
|
||||
// 全局构建器主要在启动期配置,运行期改前缀请自行加锁或重建实例。
|
||||
// SetPrefix 动态设置前缀(CK2 修复:mu 写保护,并发安全)。
|
||||
func (kb *KeyBuilder) SetPrefix(prefix string) *KeyBuilder {
|
||||
kb.mu.Lock()
|
||||
defer kb.mu.Unlock()
|
||||
kb.prefix = prefix
|
||||
return kb
|
||||
}
|
||||
|
||||
Vendored
+42
-28
@@ -82,14 +82,15 @@ end
|
||||
// 参数: key 锁名称,ttl 锁定时长
|
||||
// 返回: LockToken 用于后续解锁或续期
|
||||
func NewLock(ctx context.Context, key string, ttl time.Duration) (*LockToken, error) {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return nil, ErrRedisNotReady
|
||||
}
|
||||
|
||||
token := utils.UUID()
|
||||
ttlMs := int64(ttl / time.Millisecond)
|
||||
|
||||
result, err := database.RedisClient.Eval(ctx, lockScript, []string{key}, token, ttlMs).Result()
|
||||
result, err := rdb.Eval(ctx, lockScript, []string{key}, token, ttlMs).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -117,7 +118,8 @@ func Lock(ctx context.Context, key string, ttl time.Duration) (bool, error) {
|
||||
|
||||
// Unlock 安全释放锁
|
||||
func Unlock(ctx context.Context, token *LockToken) error {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return ErrRedisNotReady
|
||||
}
|
||||
|
||||
@@ -125,7 +127,7 @@ func Unlock(ctx context.Context, token *LockToken) error {
|
||||
return ErrLockNotHeld
|
||||
}
|
||||
|
||||
result, err := database.RedisClient.Eval(ctx, unlockScript, []string{token.Key}, token.Token).Result()
|
||||
result, err := rdb.Eval(ctx, unlockScript, []string{token.Key}, token.Token).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -144,16 +146,18 @@ func Unlock(ctx context.Context, token *LockToken) error {
|
||||
// UnlockByKey 按键名释放锁(不安全,仅用于旧代码兼容)
|
||||
// 注意: 此函数不检查 Token,任何客户端都能释放锁
|
||||
func UnlockByKey(ctx context.Context, key string) error {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return nil
|
||||
}
|
||||
return database.RedisClient.Del(ctx, key).Err()
|
||||
return rdb.Del(ctx, key).Err()
|
||||
}
|
||||
|
||||
// ExtendLock 续期锁
|
||||
// 参数: token 锁令牌,ttl 新的过期时间
|
||||
func ExtendLock(ctx context.Context, token *LockToken, ttl time.Duration) error {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return ErrRedisNotReady
|
||||
}
|
||||
|
||||
@@ -163,7 +167,7 @@ func ExtendLock(ctx context.Context, token *LockToken, ttl time.Duration) error
|
||||
|
||||
ttlMs := int64(ttl / time.Millisecond)
|
||||
|
||||
result, err := database.RedisClient.Eval(ctx, extendScript, []string{token.Key}, token.Token, ttlMs).Result()
|
||||
result, err := rdb.Eval(ctx, extendScript, []string{token.Key}, token.Token, ttlMs).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -281,83 +285,93 @@ func WithLockAutoExtend(ctx context.Context, key string, initialTTL time.Duratio
|
||||
|
||||
// IsLocked 检查锁是否被占用(不获取锁)
|
||||
func IsLocked(ctx context.Context, key string) (bool, error) {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return false, nil
|
||||
}
|
||||
return database.RedisClient.Exists(ctx, key).Val() > 0, nil
|
||||
return rdb.Exists(ctx, key).Val() > 0, nil
|
||||
}
|
||||
|
||||
// GetLockTTL 获取锁的剩余过期时间
|
||||
func GetLockTTL(ctx context.Context, key string) (time.Duration, error) {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return database.RedisClient.TTL(ctx, key).Result()
|
||||
return rdb.TTL(ctx, key).Result()
|
||||
}
|
||||
|
||||
// ForceUnlock 强制释放锁(危险操作,仅用于管理场景)
|
||||
// 注意: 此函数不检查 Token,强制删除锁
|
||||
func ForceUnlock(ctx context.Context, key string) error {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return nil
|
||||
}
|
||||
return database.RedisClient.Del(ctx, key).Err()
|
||||
return rdb.Del(ctx, key).Err()
|
||||
}
|
||||
|
||||
// ===== 计数器操作 =====
|
||||
|
||||
// Incr 自增计数器
|
||||
func Incr(ctx context.Context, key string) (int64, error) {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return database.RedisClient.Incr(ctx, key).Result()
|
||||
return rdb.Incr(ctx, key).Result()
|
||||
}
|
||||
|
||||
// IncrBy 指定增量自增
|
||||
func IncrBy(ctx context.Context, key string, value int64) (int64, error) {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return database.RedisClient.IncrBy(ctx, key, value).Result()
|
||||
return rdb.IncrBy(ctx, key, value).Result()
|
||||
}
|
||||
|
||||
// Decr 自减计数器
|
||||
func Decr(ctx context.Context, key string) (int64, error) {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return database.RedisClient.Decr(ctx, key).Result()
|
||||
return rdb.Decr(ctx, key).Result()
|
||||
}
|
||||
|
||||
// GetTTL 获取键的剩余过期时间
|
||||
func GetTTL(ctx context.Context, key string) (time.Duration, error) {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return database.RedisClient.TTL(ctx, key).Result()
|
||||
return rdb.TTL(ctx, key).Result()
|
||||
}
|
||||
|
||||
// SetExpire 设置键的过期时间
|
||||
func SetExpire(ctx context.Context, key string, ttl time.Duration) (bool, error) {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return false, nil
|
||||
}
|
||||
return database.RedisClient.Expire(ctx, key, ttl).Result()
|
||||
return rdb.Expire(ctx, key, ttl).Result()
|
||||
}
|
||||
|
||||
// GetRaw 获取原始字符串值(不反序列化)
|
||||
func GetRaw(ctx context.Context, key string) (string, error) {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return "", nil
|
||||
}
|
||||
return database.RedisClient.Get(ctx, key).Result()
|
||||
return rdb.Get(ctx, key).Result()
|
||||
}
|
||||
|
||||
// SetRaw 设置原始值(不序列化)
|
||||
func SetRaw(ctx context.Context, key string, value string, ttl time.Duration) error {
|
||||
if database.RedisClient == nil {
|
||||
rdb := database.GetRedis()
|
||||
if rdb == nil {
|
||||
return nil
|
||||
}
|
||||
return database.RedisClient.Set(ctx, key, value, ttl).Err()
|
||||
return rdb.Set(ctx, key, value, ttl).Err()
|
||||
}
|
||||
Vendored
+4
-5
@@ -14,16 +14,15 @@ import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// setupMiniRedis 启动一个 miniredis 实例并把它接到 database.RedisClient,返回清理函数。
|
||||
// setupMiniRedis 启动一个 miniredis 实例并把它注入 database 内部,返回清理函数。
|
||||
func setupMiniRedis(t *testing.T) *miniredis.Miniredis {
|
||||
t.Helper()
|
||||
mr := miniredis.RunT(t)
|
||||
client := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
||||
t.Cleanup(func() { _ = client.Close() })
|
||||
// 保存旧值,测试结束恢复,避免污染其他测试。
|
||||
old := database.RedisClient
|
||||
database.RedisClient = client
|
||||
t.Cleanup(func() { database.RedisClient = old })
|
||||
// 通过 SetTestRedisClient 注入 miniredis 客户端到 database 包的内部 redisClient
|
||||
database.SetTestRedisClient(client)
|
||||
t.Cleanup(func() { database.SetTestRedisClient(nil) })
|
||||
return mr
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user