| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950 |
- package cache
- import (
- "context"
- "fwh-toolbox-server/cache/redisx"
- "go.uber.org/zap"
- )
- // GetOrSetStale: 如果缓存存在但 TTL 小于 threshold,则返回旧值并后台刷新
- func GetOrSetStale[T any](
- ctx context.Context,
- key string,
- loader LoaderFunc[T],
- opts ...Option,
- ) (*T, error) {
- o := defaultOptions()
- for _, op := range opts {
- op(o)
- }
- full := fullKey(o.prefix, key)
- // 1) 尝试读取缓存并检查 TTL
- var out T
- if err := redisx.GetJSON(ctx, full, &out); err == nil {
- // cache exists => check remaining TTL
- ttl, err := redisx.TTL(ctx, full)
- if err != nil {
- // 无法获取 TTL 则直接返回缓存值
- return &out, nil
- }
- if ttl <= 0 || ttl > o.staleThreshold {
- // TTL 足够 -> 直接返回
- return &out, nil
- }
- // TTL <= threshold -> 视为 stale:返回旧值并后台刷新
- go func() {
- // Use GetOrSet with lock disabled? We want one worker to refresh: use lockEnabled true
- _, err := GetOrSet(ctx, key, loader, append(opts, WithAsync(false))...)
- if err != nil {
- zap.L().Error("cachex: background refresh failed", zap.String("full", full), zap.String("err", err.Error()))
- }
- }()
- return &out, nil
- }
- // 未命中则走普通 GetOrSet(带锁)
- return GetOrSet(ctx, key, loader, opts...)
- }
|