@@ -10,10 +10,8 @@ import (
10
10
"time"
11
11
"unsafe"
12
12
13
- "github.com/cornelk/hashmap"
14
13
"github.com/kpango/fastime"
15
14
"github.com/zeebo/xxh3"
16
- "golang.org/x/sync/singleflight"
17
15
)
18
16
19
17
type (
@@ -64,10 +62,9 @@ type (
64
62
expire int64
65
63
l uint64
66
64
cancel atomic.Pointer [context.CancelFunc ]
67
- expGroup singleflight.Group
68
65
expChan chan string
69
66
expFunc func (context.Context , string )
70
- shards [slen ]* hashmap. Map [string , value [V ]]
67
+ shards [slen ]* Map [string , * value [V ]]
71
68
}
72
69
73
70
value [V any ] struct {
@@ -101,23 +98,19 @@ func New[V any](opts ...Option[V]) Gache[V] {
101
98
return g
102
99
}
103
100
104
- func newMap [V any ]() (m * hashmap.Map [string , value [V ]]) {
105
- m = hashmap .New [string , value [V ]]()
106
- m .SetHasher (func (k string ) uintptr {
107
- return uintptr (xxh3 .HashString (k ))
108
- })
109
- return m
101
+ func newMap [V any ]() (m * Map [string , * value [V ]]) {
102
+ return new (Map [string , * value [V ]])
110
103
}
111
104
112
- func getShardID (key string ) uint64 {
105
+ func getShardID (key string ) ( id uint64 ) {
113
106
if len (key ) > 128 {
114
107
return xxh3 .HashString (key [:128 ]) & mask
115
108
}
116
109
return xxh3 .HashString (key ) & mask
117
110
}
118
111
119
112
// isValid checks expiration of value
120
- func (v * value [V ]) isValid () bool {
113
+ func (v * value [V ]) isValid () ( valid bool ) {
121
114
return v .expire <= 0 || fastime .UnixNanoNow () <= v .expire
122
115
}
123
116
@@ -170,8 +163,14 @@ func (g *gache[V]) StartExpired(ctx context.Context, dur time.Duration) Gache[V]
170
163
// ToMap returns All Cache Key-Value sync.Map
171
164
func (g * gache [V ]) ToMap (ctx context.Context ) * sync.Map {
172
165
m := new (sync.Map )
173
- g .Range (ctx , func (key string , val V , exp int64 ) bool {
174
- go m .Store (key , val )
166
+ var wg sync.WaitGroup
167
+ defer wg .Wait ()
168
+ g .Range (ctx , func (key string , val V , exp int64 ) (ok bool ) {
169
+ wg .Add (1 )
170
+ go func () {
171
+ m .Store (key , val )
172
+ wg .Done ()
173
+ }()
175
174
return true
176
175
})
177
176
@@ -182,7 +181,7 @@ func (g *gache[V]) ToMap(ctx context.Context) *sync.Map {
182
181
func (g * gache [V ]) ToRawMap (ctx context.Context ) map [string ]V {
183
182
m := make (map [string ]V , g .Len ())
184
183
mu := new (sync.Mutex )
185
- g .Range (ctx , func (key string , val V , exp int64 ) bool {
184
+ g .Range (ctx , func (key string , val V , exp int64 ) ( ok bool ) {
186
185
mu .Lock ()
187
186
m [key ] = val
188
187
mu .Unlock ()
@@ -192,30 +191,29 @@ func (g *gache[V]) ToRawMap(ctx context.Context) map[string]V {
192
191
}
193
192
194
193
// get returns value & exists from key
195
- func (g * gache [V ]) get (key string ) (V , int64 , bool ) {
196
- var val V
197
- v , ok : = g .shards [getShardID (key )].Get (key )
194
+ func (g * gache [V ]) get (key string ) (v V , expire int64 , ok bool ) {
195
+ var val * value [ V ]
196
+ val , ok = g .shards [getShardID (key )].Load (key )
198
197
if ! ok {
199
- return val , 0 , false
198
+ return v , 0 , false
200
199
}
201
200
202
- if v .isValid () {
203
- val = v .val
204
- return val , v .expire , true
201
+ if val .isValid () {
202
+ return val .val , val .expire , true
205
203
}
206
204
207
205
g .expiration (key )
208
- return val , v .expire , false
206
+ return v , val .expire , false
209
207
}
210
208
211
209
// Get returns value & exists from key
212
- func (g * gache [V ]) Get (key string ) (V , bool ) {
213
- v , _ , ok : = g .get (key )
210
+ func (g * gache [V ]) Get (key string ) (v V , ok bool ) {
211
+ v , _ , ok = g .get (key )
214
212
return v , ok
215
213
}
216
214
217
215
// GetWithExpire returns value & expire & exists from key
218
- func (g * gache [V ]) GetWithExpire (key string ) (V , int64 , bool ) {
216
+ func (g * gache [V ]) GetWithExpire (key string ) (v V , expire int64 , ok bool ) {
219
217
return g .get (key )
220
218
}
221
219
@@ -224,11 +222,13 @@ func (g *gache[V]) set(key string, val V, expire int64) {
224
222
if expire > 0 {
225
223
expire = fastime .UnixNanoNow () + expire
226
224
}
227
- atomic .AddUint64 (& g .l , 1 )
228
- g .shards [getShardID (key )].Set (key , value [V ]{
225
+ _ , loaded := g .shards [getShardID (key )].Swap (key , & value [V ]{
229
226
expire : expire ,
230
227
val : val ,
231
228
})
229
+ if ! loaded {
230
+ atomic .AddUint64 (& g .l , 1 )
231
+ }
232
232
}
233
233
234
234
// SetWithExpire sets key-value & expiration to Gache
@@ -243,41 +243,39 @@ func (g *gache[V]) Set(key string, val V) {
243
243
244
244
// Delete deletes value from Gache using key
245
245
func (g * gache [V ]) Delete (key string ) (loaded bool ) {
246
- atomic .AddUint64 (& g .l , ^ uint64 (0 ))
247
- return g .shards [getShardID (key )].Del (key )
246
+ _ , loaded = g .shards [getShardID (key )].LoadAndDelete (key )
247
+ if loaded {
248
+ atomic .AddUint64 (& g .l , ^ uint64 (0 ))
249
+ }
250
+ return
248
251
}
249
252
250
253
func (g * gache [V ]) expiration (key string ) {
251
- g .expGroup .Do (key , func () (interface {}, error ) {
252
- g .Delete (key )
253
- if g .expFuncEnabled {
254
- g .expChan <- key
255
- }
256
- return nil , nil
257
- })
254
+ g .Delete (key )
255
+ if g .expFuncEnabled {
256
+ g .expChan <- key
257
+ }
258
258
}
259
259
260
260
// DeleteExpired deletes expired value from Gache it can be cancel using context
261
- func (g * gache [V ]) DeleteExpired (ctx context.Context ) uint64 {
262
- wg := new (sync.WaitGroup )
263
- var rows uint64
261
+ func (g * gache [V ]) DeleteExpired (ctx context.Context ) (rows uint64 ) {
262
+ var wg sync.WaitGroup
264
263
for i := range g .shards {
265
264
wg .Add (1 )
266
265
go func (c context.Context , idx int ) {
267
- g .shards [idx ].Range (func (k string , v value [V ]) bool {
268
- select {
269
- case <- c .Done ():
270
- return false
271
- default :
266
+ defer wg .Done ()
267
+ select {
268
+ case <- c .Done ():
269
+ return
270
+ default :
271
+ g .shards [idx ].Range (func (k string , v * value [V ]) (ok bool ) {
272
272
if ! v .isValid () {
273
273
g .expiration (k )
274
274
atomic .AddUint64 (& rows , 1 )
275
- runtime .Gosched ()
276
275
}
277
276
return true
278
- }
279
- })
280
- wg .Done ()
277
+ })
278
+ }
281
279
}(ctx , i )
282
280
}
283
281
wg .Wait ()
@@ -290,20 +288,19 @@ func (g *gache[V]) Range(ctx context.Context, f func(string, V, int64) bool) Gac
290
288
for i := range g .shards {
291
289
wg .Add (1 )
292
290
go func (c context.Context , idx int ) {
293
- g .shards [idx ].Range (func (k string , v value [V ]) bool {
294
- select {
295
- case <- c .Done ():
296
- return false
297
- default :
291
+ defer wg .Done ()
292
+ select {
293
+ case <- c .Done ():
294
+ return
295
+ default :
296
+ g .shards [idx ].Range (func (k string , v * value [V ]) (ok bool ) {
298
297
if v .isValid () {
299
298
return f (k , v .val , v .expire )
300
299
}
301
- runtime .Gosched ()
302
300
g .expiration (k )
303
301
return true
304
- }
305
- })
306
- wg .Done ()
302
+ })
303
+ }
307
304
}(ctx , i )
308
305
}
309
306
wg .Wait ()
@@ -318,16 +315,7 @@ func (g *gache[V]) Len() int {
318
315
319
316
// Write writes all cached data to writer
320
317
func (g * gache [V ]) Write (ctx context.Context , w io.Writer ) error {
321
- mu := new (sync.Mutex )
322
- m := make (map [string ]V , g .Len ())
323
-
324
- g .Range (ctx , func (key string , val V , exp int64 ) bool {
325
- gob .Register (val )
326
- mu .Lock ()
327
- m [key ] = val
328
- mu .Unlock ()
329
- return true
330
- })
318
+ m := g .ToRawMap (ctx )
331
319
gob .Register (map [string ]V {})
332
320
return gob .NewEncoder (w ).Encode (& m )
333
321
}
0 commit comments