| package cache |
|
|
| import ( |
| "context" |
| "time" |
|
|
| json "github.com/goccy/go-json" |
| "github.com/redis/go-redis/v9" |
| "github.com/router-for-me/CLIProxyAPI/v6/internal/config" |
| log "github.com/sirupsen/logrus" |
| ) |
|
|
| type redisResponseCache struct { |
| cfg config.ResponseCacheConfig |
| client redis.UniversalClient |
| prefix string |
| stats responseCacheStats |
| } |
|
|
| func newRedisResponseCache(cfg config.ResponseCacheConfig, client redis.UniversalClient) *redisResponseCache { |
| prefix := cfg.RedisKeyPrefix |
| if prefix == "" { |
| prefix = "respcache" |
| } |
| return &redisResponseCache{ |
| cfg: cfg, |
| client: client, |
| prefix: "cliproxy:" + prefix, |
| } |
| } |
|
|
| func (r *redisResponseCache) Get(ctx context.Context, key string) (*CachedResponse, bool) { |
| data, err := r.client.Get(ctx, r.formatKey(key)).Bytes() |
| if err != nil { |
| r.stats.recordMiss() |
| return nil, false |
| } |
| var entry CachedResponse |
| if err := json.Unmarshal(data, &entry); err != nil { |
| log.Warnf("response cache: unmarshal error: %v", err) |
| r.stats.recordMiss() |
| return nil, false |
| } |
| r.stats.recordHit() |
| return &entry, true |
| } |
|
|
| func (r *redisResponseCache) Set(ctx context.Context, key string, resp *CachedResponse, ttl time.Duration) error { |
| data, err := json.Marshal(resp) |
| if err != nil { |
| return err |
| } |
| return r.client.Set(ctx, r.formatKey(key), data, ttl).Err() |
| } |
|
|
| func (r *redisResponseCache) Delete(ctx context.Context, pattern string) error { |
| fullPattern := r.prefix + ":" + pattern |
| var cursor uint64 |
| for { |
| keys, next, err := r.client.Scan(ctx, cursor, fullPattern, 100).Result() |
| if err != nil { |
| return err |
| } |
| if len(keys) > 0 { |
| r.client.Del(ctx, keys...) |
| } |
| cursor = next |
| if cursor == 0 { |
| break |
| } |
| } |
| return nil |
| } |
|
|
| func (r *redisResponseCache) Stats() CacheStats { |
| return CacheStats{ |
| Hits: r.stats.hits.Load(), |
| Misses: r.stats.misses.Load(), |
| } |
| } |
|
|
| func (r *redisResponseCache) Close() error { |
| return nil |
| } |
|
|
| func (r *redisResponseCache) formatKey(key string) string { |
| return r.prefix + ":" + key |
| } |
|
|