Skip to content

Commit b8e414e

Browse files
wanghaolong613hailazCopilot
authored
fix(os/gcache): defaultcache lazy init (gogf#4468)
defaultcache更改为懒加载,在用户使用redis缓存时,避免了程序启动时不必要的初始化开销。 <img width="2638" height="806" alt="image" src="https://github.com/user-attachments/assets/96bb0097-8463-4303-971c-ee1a9ef671a6" /> --------- Co-authored-by: hailaz <739476267@qq.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 08c34b5 commit b8e414e

7 files changed

Lines changed: 109 additions & 45 deletions

File tree

contrib/registry/zookeeper/zookeeper.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
"github.com/gogf/gf/v2/net/gsvc"
1818
)
1919

20-
var _ gsvc.Registry = &Registry{}
20+
var _ gsvc.Registry = (*Registry)(nil)
2121

2222
// Content for custom service Marshal/Unmarshal.
2323
type Content struct {

net/ghttp/ghttp_z_unit_feature_middleware_basic_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -821,7 +821,7 @@ type testTracerProvider struct {
821821
noop.TracerProvider
822822
}
823823

824-
var _ trace.TracerProvider = &testTracerProvider{}
824+
var _ trace.TracerProvider = (*testTracerProvider)(nil)
825825

826826
func (*testTracerProvider) Tracer(_ string, _ ...trace.TracerOption) trace.Tracer {
827827
return noop.NewTracerProvider().Tracer("")

os/gcache/gcache.go

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package gcache
1111

1212
import (
1313
"context"
14+
"sync"
1415
"time"
1516

1617
"github.com/gogf/gf/v2/container/gvar"
@@ -22,23 +23,25 @@ type Func = func(ctx context.Context) (value any, err error)
2223
// DurationNoExpire represents the cache key-value pair that never expires.
2324
const DurationNoExpire = time.Duration(0)
2425

25-
// Default cache object.
26-
var defaultCache = New()
26+
// defaultCache returns the lazily-initialized default cache instance using sync.OnceValue.
27+
var defaultCache = sync.OnceValue(func() *Cache {
28+
return New()
29+
})
2730

2831
// Set sets cache with `key`-`value` pair, which is expired after `duration`.
2932
//
3033
// It does not expire if `duration` == 0.
3134
// It deletes the keys of `data` if `duration` < 0 or given `value` is nil.
3235
func Set(ctx context.Context, key any, value any, duration time.Duration) error {
33-
return defaultCache.Set(ctx, key, value, duration)
36+
return defaultCache().Set(ctx, key, value, duration)
3437
}
3538

3639
// SetMap batch sets cache with key-value pairs by `data` map, which is expired after `duration`.
3740
//
3841
// It does not expire if `duration` == 0.
3942
// It deletes the keys of `data` if `duration` < 0 or given `value` is nil.
4043
func SetMap(ctx context.Context, data map[any]any, duration time.Duration) error {
41-
return defaultCache.SetMap(ctx, data, duration)
44+
return defaultCache().SetMap(ctx, data, duration)
4245
}
4346

4447
// SetIfNotExist sets cache with `key`-`value` pair which is expired after `duration`
@@ -48,7 +51,7 @@ func SetMap(ctx context.Context, data map[any]any, duration time.Duration) error
4851
// It does not expire if `duration` == 0.
4952
// It deletes the `key` if `duration` < 0 or given `value` is nil.
5053
func SetIfNotExist(ctx context.Context, key any, value any, duration time.Duration) (bool, error) {
51-
return defaultCache.SetIfNotExist(ctx, key, value, duration)
54+
return defaultCache().SetIfNotExist(ctx, key, value, duration)
5255
}
5356

5457
// SetIfNotExistFunc sets `key` with result of function `f` and returns true
@@ -60,7 +63,7 @@ func SetIfNotExist(ctx context.Context, key any, value any, duration time.Durati
6063
// It does not expire if `duration` == 0.
6164
// It deletes the `key` if `duration` < 0 or given `value` is nil.
6265
func SetIfNotExistFunc(ctx context.Context, key any, f Func, duration time.Duration) (bool, error) {
63-
return defaultCache.SetIfNotExistFunc(ctx, key, f, duration)
66+
return defaultCache().SetIfNotExistFunc(ctx, key, f, duration)
6467
}
6568

6669
// SetIfNotExistFuncLock sets `key` with result of function `f` and returns true
@@ -72,14 +75,14 @@ func SetIfNotExistFunc(ctx context.Context, key any, f Func, duration time.Durat
7275
// Note that it differs from function `SetIfNotExistFunc` is that the function `f` is executed within
7376
// writing mutex lock for concurrent safety purpose.
7477
func SetIfNotExistFuncLock(ctx context.Context, key any, f Func, duration time.Duration) (bool, error) {
75-
return defaultCache.SetIfNotExistFuncLock(ctx, key, f, duration)
78+
return defaultCache().SetIfNotExistFuncLock(ctx, key, f, duration)
7679
}
7780

7881
// Get retrieves and returns the associated value of given `key`.
7982
// It returns nil if it does not exist, or its value is nil, or it's expired.
8083
// If you would like to check if the `key` exists in the cache, it's better using function Contains.
8184
func Get(ctx context.Context, key any) (*gvar.Var, error) {
82-
return defaultCache.Get(ctx, key)
85+
return defaultCache().Get(ctx, key)
8386
}
8487

8588
// GetOrSet retrieves and returns the value of `key`, or sets `key`-`value` pair and
@@ -90,7 +93,7 @@ func Get(ctx context.Context, key any) (*gvar.Var, error) {
9093
// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing
9194
// if `value` is a function and the function result is nil.
9295
func GetOrSet(ctx context.Context, key any, value any, duration time.Duration) (*gvar.Var, error) {
93-
return defaultCache.GetOrSet(ctx, key, value, duration)
96+
return defaultCache().GetOrSet(ctx, key, value, duration)
9497
}
9598

9699
// GetOrSetFunc retrieves and returns the value of `key`, or sets `key` with result of
@@ -101,7 +104,7 @@ func GetOrSet(ctx context.Context, key any, value any, duration time.Duration) (
101104
// It deletes the `key` if `duration` < 0 or given `value` is nil, but it does nothing
102105
// if `value` is a function and the function result is nil.
103106
func GetOrSetFunc(ctx context.Context, key any, f Func, duration time.Duration) (*gvar.Var, error) {
104-
return defaultCache.GetOrSetFunc(ctx, key, f, duration)
107+
return defaultCache().GetOrSetFunc(ctx, key, f, duration)
105108
}
106109

107110
// GetOrSetFuncLock retrieves and returns the value of `key`, or sets `key` with result of
@@ -115,12 +118,12 @@ func GetOrSetFunc(ctx context.Context, key any, f Func, duration time.Duration)
115118
// Note that it differs from function `GetOrSetFunc` is that the function `f` is executed within
116119
// writing mutex lock for concurrent safety purpose.
117120
func GetOrSetFuncLock(ctx context.Context, key any, f Func, duration time.Duration) (*gvar.Var, error) {
118-
return defaultCache.GetOrSetFuncLock(ctx, key, f, duration)
121+
return defaultCache().GetOrSetFuncLock(ctx, key, f, duration)
119122
}
120123

121124
// Contains checks and returns true if `key` exists in the cache, or else returns false.
122125
func Contains(ctx context.Context, key any) (bool, error) {
123-
return defaultCache.Contains(ctx, key)
126+
return defaultCache().Contains(ctx, key)
124127
}
125128

126129
// GetExpire retrieves and returns the expiration of `key` in the cache.
@@ -129,18 +132,18 @@ func Contains(ctx context.Context, key any) (bool, error) {
129132
// It returns 0 if the `key` does not expire.
130133
// It returns -1 if the `key` does not exist in the cache.
131134
func GetExpire(ctx context.Context, key any) (time.Duration, error) {
132-
return defaultCache.GetExpire(ctx, key)
135+
return defaultCache().GetExpire(ctx, key)
133136
}
134137

135138
// Remove deletes one or more keys from cache, and returns its value.
136139
// If multiple keys are given, it returns the value of the last deleted item.
137140
func Remove(ctx context.Context, keys ...any) (value *gvar.Var, err error) {
138-
return defaultCache.Remove(ctx, keys...)
141+
return defaultCache().Remove(ctx, keys...)
139142
}
140143

141144
// Removes deletes `keys` in the cache.
142145
func Removes(ctx context.Context, keys []any) error {
143-
return defaultCache.Removes(ctx, keys)
146+
return defaultCache().Removes(ctx, keys)
144147
}
145148

146149
// Update updates the value of `key` without changing its expiration and returns the old value.
@@ -149,95 +152,95 @@ func Removes(ctx context.Context, keys []any) error {
149152
// It deletes the `key` if given `value` is nil.
150153
// It does nothing if `key` does not exist in the cache.
151154
func Update(ctx context.Context, key any, value any) (oldValue *gvar.Var, exist bool, err error) {
152-
return defaultCache.Update(ctx, key, value)
155+
return defaultCache().Update(ctx, key, value)
153156
}
154157

155158
// UpdateExpire updates the expiration of `key` and returns the old expiration duration value.
156159
//
157160
// It returns -1 and does nothing if the `key` does not exist in the cache.
158161
// It deletes the `key` if `duration` < 0.
159162
func UpdateExpire(ctx context.Context, key any, duration time.Duration) (oldDuration time.Duration, err error) {
160-
return defaultCache.UpdateExpire(ctx, key, duration)
163+
return defaultCache().UpdateExpire(ctx, key, duration)
161164
}
162165

163166
// Size returns the number of items in the cache.
164167
func Size(ctx context.Context) (int, error) {
165-
return defaultCache.Size(ctx)
168+
return defaultCache().Size(ctx)
166169
}
167170

168171
// Data returns a copy of all key-value pairs in the cache as map type.
169172
// Note that this function may lead lots of memory usage, you can implement this function
170173
// if necessary.
171174
func Data(ctx context.Context) (map[any]any, error) {
172-
return defaultCache.Data(ctx)
175+
return defaultCache().Data(ctx)
173176
}
174177

175178
// Keys returns all keys in the cache as slice.
176179
func Keys(ctx context.Context) ([]any, error) {
177-
return defaultCache.Keys(ctx)
180+
return defaultCache().Keys(ctx)
178181
}
179182

180183
// KeyStrings returns all keys in the cache as string slice.
181184
func KeyStrings(ctx context.Context) ([]string, error) {
182-
return defaultCache.KeyStrings(ctx)
185+
return defaultCache().KeyStrings(ctx)
183186
}
184187

185188
// Values returns all values in the cache as slice.
186189
func Values(ctx context.Context) ([]any, error) {
187-
return defaultCache.Values(ctx)
190+
return defaultCache().Values(ctx)
188191
}
189192

190193
// MustGet acts like Get, but it panics if any error occurs.
191194
func MustGet(ctx context.Context, key any) *gvar.Var {
192-
return defaultCache.MustGet(ctx, key)
195+
return defaultCache().MustGet(ctx, key)
193196
}
194197

195198
// MustGetOrSet acts like GetOrSet, but it panics if any error occurs.
196199
func MustGetOrSet(ctx context.Context, key any, value any, duration time.Duration) *gvar.Var {
197-
return defaultCache.MustGetOrSet(ctx, key, value, duration)
200+
return defaultCache().MustGetOrSet(ctx, key, value, duration)
198201
}
199202

200203
// MustGetOrSetFunc acts like GetOrSetFunc, but it panics if any error occurs.
201204
func MustGetOrSetFunc(ctx context.Context, key any, f Func, duration time.Duration) *gvar.Var {
202-
return defaultCache.MustGetOrSetFunc(ctx, key, f, duration)
205+
return defaultCache().MustGetOrSetFunc(ctx, key, f, duration)
203206
}
204207

205208
// MustGetOrSetFuncLock acts like GetOrSetFuncLock, but it panics if any error occurs.
206209
func MustGetOrSetFuncLock(ctx context.Context, key any, f Func, duration time.Duration) *gvar.Var {
207-
return defaultCache.MustGetOrSetFuncLock(ctx, key, f, duration)
210+
return defaultCache().MustGetOrSetFuncLock(ctx, key, f, duration)
208211
}
209212

210213
// MustContains acts like Contains, but it panics if any error occurs.
211214
func MustContains(ctx context.Context, key any) bool {
212-
return defaultCache.MustContains(ctx, key)
215+
return defaultCache().MustContains(ctx, key)
213216
}
214217

215218
// MustGetExpire acts like GetExpire, but it panics if any error occurs.
216219
func MustGetExpire(ctx context.Context, key any) time.Duration {
217-
return defaultCache.MustGetExpire(ctx, key)
220+
return defaultCache().MustGetExpire(ctx, key)
218221
}
219222

220223
// MustSize acts like Size, but it panics if any error occurs.
221224
func MustSize(ctx context.Context) int {
222-
return defaultCache.MustSize(ctx)
225+
return defaultCache().MustSize(ctx)
223226
}
224227

225228
// MustData acts like Data, but it panics if any error occurs.
226229
func MustData(ctx context.Context) map[any]any {
227-
return defaultCache.MustData(ctx)
230+
return defaultCache().MustData(ctx)
228231
}
229232

230233
// MustKeys acts like Keys, but it panics if any error occurs.
231234
func MustKeys(ctx context.Context) []any {
232-
return defaultCache.MustKeys(ctx)
235+
return defaultCache().MustKeys(ctx)
233236
}
234237

235238
// MustKeyStrings acts like KeyStrings, but it panics if any error occurs.
236239
func MustKeyStrings(ctx context.Context) []string {
237-
return defaultCache.MustKeyStrings(ctx)
240+
return defaultCache().MustKeyStrings(ctx)
238241
}
239242

240243
// MustValues acts like Values, but it panics if any error occurs.
241244
func MustValues(ctx context.Context) []any {
242-
return defaultCache.MustValues(ctx)
245+
return defaultCache().MustValues(ctx)
243246
}

os/gcache/gcache_adapter_memory.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ type AdapterMemory struct {
2929
closed *gtype.Bool // closed controls the cache closed or not.
3030
}
3131

32+
var _ Adapter = (*AdapterMemory)(nil)
33+
3234
// Internal event item.
3335
type adapterMemoryEvent struct {
3436
k any // Key.

os/gcache/gcache_adapter_redis.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ type AdapterRedis struct {
2020
redis *gredis.Redis
2121
}
2222

23-
// NewAdapterRedis creates and returns a new memory cache object.
23+
var _ Adapter = (*AdapterRedis)(nil)
24+
25+
// NewAdapterRedis creates and returns a new Redis cache adapter.
2426
func NewAdapterRedis(redis *gredis.Redis) *AdapterRedis {
2527
return &AdapterRedis{
2628
redis: redis,

os/gcache/gcache_z_bench_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ package gcache_test
1010

1111
import (
1212
"context"
13+
"sync"
1314
"testing"
15+
"time"
1416

1517
"github.com/gogf/gf/v2/os/gcache"
1618
)
@@ -79,3 +81,65 @@ func Benchmark_CacheLruRemove(b *testing.B) {
7981
}
8082
})
8183
}
84+
85+
var oldDefaultCache = gcache.New()
86+
var newDefaultCache = sync.OnceValue(func() *gcache.Cache {
87+
return gcache.New()
88+
})
89+
90+
func BenchmarkOldImplementation(b *testing.B) {
91+
ctx := context.Background()
92+
b.ResetTimer()
93+
for i := 0; i < b.N; i++ {
94+
_ = oldDefaultCache.Set(ctx, "key", "value", time.Minute)
95+
}
96+
}
97+
98+
func BenchmarkNewImplementation(b *testing.B) {
99+
ctx := context.Background()
100+
newDefaultCache()
101+
b.ResetTimer()
102+
for i := 0; i < b.N; i++ {
103+
_ = newDefaultCache().Set(ctx, "key", "value", time.Minute)
104+
}
105+
}
106+
107+
func BenchmarkOldGet(b *testing.B) {
108+
ctx := context.Background()
109+
oldDefaultCache.Set(ctx, "test_key", "test_value", time.Minute)
110+
111+
b.ResetTimer()
112+
for i := 0; i < b.N; i++ {
113+
_, _ = oldDefaultCache.Get(ctx, "test_key")
114+
}
115+
}
116+
117+
func BenchmarkNewGet(b *testing.B) {
118+
ctx := context.Background()
119+
newDefaultCache().Set(ctx, "test_key", "test_value", time.Minute)
120+
121+
b.ResetTimer()
122+
for i := 0; i < b.N; i++ {
123+
_, _ = newDefaultCache().Get(ctx, "test_key")
124+
}
125+
}
126+
127+
func BenchmarkOldConcurrent(b *testing.B) {
128+
ctx := context.Background()
129+
130+
b.RunParallel(func(pb *testing.PB) {
131+
for pb.Next() {
132+
_ = oldDefaultCache.Set(ctx, "key", "value", time.Minute)
133+
}
134+
})
135+
}
136+
137+
func BenchmarkNewConcurrent(b *testing.B) {
138+
ctx := context.Background()
139+
140+
b.RunParallel(func(pb *testing.PB) {
141+
for pb.Next() {
142+
_ = newDefaultCache().Set(ctx, "key", "value", time.Minute)
143+
}
144+
})
145+
}

os/gcache/gcache_z_unit_test.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -453,22 +453,15 @@ func TestCache_SetConcurrency(t *testing.T) {
453453
})
454454
}
455455
}()
456-
select {
457-
case <-time.After(2 * time.Second):
458-
// t.Log("first part end")
459-
}
460-
456+
time.Sleep(2 * time.Second)
461457
go func() {
462458
for {
463459
pool.Add(ctx, func(ctx context.Context) {
464460
cache.SetIfNotExist(ctx, 1, nil, 10)
465461
})
466462
}
467463
}()
468-
select {
469-
case <-time.After(2 * time.Second):
470-
// t.Log("second part end")
471-
}
464+
time.Sleep(2 * time.Second)
472465
})
473466
}
474467

0 commit comments

Comments
 (0)