diff --git a/os/gcache/gcache_adapter_memory_data.go b/os/gcache/gcache_adapter_memory_data.go index ddd0c5fae86..8e0037036f0 100644 --- a/os/gcache/gcache_adapter_memory_data.go +++ b/os/gcache/gcache_adapter_memory_data.go @@ -182,12 +182,13 @@ func (d *memoryData) SetMap(data map[interface{}]interface{}, expireTime int64) } func (d *memoryData) SetWithLock(ctx context.Context, key interface{}, value interface{}, expireTimestamp int64) (interface{}, error) { - d.mu.Lock() - defer d.mu.Unlock() var ( err error ) - if v, ok := d.data[key]; ok && !v.IsExpired() { + d.mu.Lock() + v, ok := d.data[key] + d.mu.Unlock() + if ok && !v.IsExpired() { return v.v, nil } f, ok := value.(Func) @@ -203,7 +204,9 @@ func (d *memoryData) SetWithLock(ctx context.Context, key interface{}, value int return nil, nil } } + d.mu.Lock() d.data[key] = memoryDataItem{v: value, e: expireTimestamp} + d.mu.Unlock() return value, nil } diff --git a/os/gcache/gcache_z_unit_issue_test.go b/os/gcache/gcache_z_unit_issue_test.go new file mode 100644 index 00000000000..67bf1630182 --- /dev/null +++ b/os/gcache/gcache_z_unit_issue_test.go @@ -0,0 +1,89 @@ +package gcache_test + +import ( + "context" + "testing" + "time" + + "github.com/gogf/gf/v2/os/gcache" + "github.com/gogf/gf/v2/test/gtest" +) + +// https://github.com/gogf/gf/issues/4145 +func Test_Issue4145(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var ( + ctx = context.Background() + cache = gcache.New() + cacheKey1 = "GetTest-1" + cacheKey2 = "GetTest2-1" + cacheValue = "123456789" + ) + + // 定义需要测试的闭包函数 + getTestCached := func(ctx context.Context) (*string, error) { + v, err := cache.GetOrSetFuncLock(ctx, cacheKey1, func(ctx context.Context) (interface{}, error) { + str := cacheValue + return &str, nil + }, 1*time.Minute) + + if err != nil { + return nil, err + } + + var res *string + if err := v.Struct(&res); err != nil { + return nil, err + } + return res, nil + } + + getTest2Cached := func(ctx context.Context) (*string, error) { + v, err := cache.GetOrSetFuncLock(ctx, cacheKey2, func(ctx context.Context) (interface{}, error) { + // 内部调用 getTestCached + return getTestCached(ctx) + }, 1*time.Minute) + + if err != nil { + return nil, err + } + + var res *string + if err := v.Struct(&res); err != nil { + return nil, err + } + return res, nil + } + + // 测试用例 + // 第一次获取应该走实际逻辑 + value, err := getTestCached(ctx) + t.AssertNil(err) + t.Assert(*value, cacheValue) + + // 第二次获取应该走缓存 + v, err := cache.Get(ctx, cacheKey1) + t.AssertNil(err) + t.Assert(v, cacheValue) + + // 测试嵌套缓存调用 + value, err = getTest2Cached(ctx) + t.AssertNil(err) + t.Assert(*value, cacheValue) + + // 验证二级缓存 + v, err = cache.Get(ctx, cacheKey2) + t.AssertNil(err) + t.Assert(v, cacheValue) + + // 清理所有缓存 + _, err = cache.Remove(ctx, cacheKey1, cacheKey2) + t.AssertNil(err) + + // 验证清理结果 + v1, _ := cache.Get(ctx, cacheKey1) + v2, _ := cache.Get(ctx, cacheKey2) + t.Assert(v1, nil) + t.Assert(v2, nil) + }) +}