Skip to content

Commit 38b805a

Browse files
committed
perf: remove per-call goroutines on expired items\n\nAvoid spawning goroutines in Get/GetWithInfo/GetOrSet/GetMultiple; trigger expiration via non-blocking channel send to batch work in the background loop. Reduces overhead under load while preserving behavior.
1 parent 1f92bcd commit 38b805a

1 file changed

Lines changed: 23 additions & 14 deletions

File tree

hypercache.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -399,11 +399,13 @@ func (hyperCache *HyperCache[T]) Get(ctx context.Context, key string) (any, bool
399399

400400
// Check if the item has expired, if so, trigger the expiration loop
401401
if item.Expired() {
402-
go func() {
403-
hyperCache.itemPoolManager.Put(item)
402+
// Return the item to the pool and non-blocking trigger of expiration loop
403+
hyperCache.itemPoolManager.Put(item)
404404

405-
hyperCache.expirationTriggerCh <- true
406-
}()
405+
select {
406+
case hyperCache.expirationTriggerCh <- true:
407+
default:
408+
}
407409

408410
return nil, false
409411
}
@@ -424,11 +426,13 @@ func (hyperCache *HyperCache[T]) GetWithInfo(ctx context.Context, key string) (*
424426

425427
// Check if the item has expired, if so, trigger the expiration loop
426428
if item.Expired() {
427-
go func() {
428-
hyperCache.itemPoolManager.Put(item)
429+
// Return the item to the pool and non-blocking trigger of expiration loop
430+
hyperCache.itemPoolManager.Put(item)
429431

430-
hyperCache.expirationTriggerCh <- true
431-
}()
432+
select {
433+
case hyperCache.expirationTriggerCh <- true:
434+
default:
435+
}
432436

433437
return nil, false
434438
}
@@ -446,11 +450,13 @@ func (hyperCache *HyperCache[T]) GetOrSet(ctx context.Context, key string, value
446450
if item, ok := hyperCache.backend.Get(ctx, key); ok {
447451
// Check if the item has expired
448452
if item.Expired() {
449-
go func() {
450-
hyperCache.itemPoolManager.Put(item)
453+
// Return the item to the pool and non-blocking trigger of expiration loop
454+
hyperCache.itemPoolManager.Put(item)
451455

452-
hyperCache.expirationTriggerCh <- true
453-
}()
456+
select {
457+
case hyperCache.expirationTriggerCh <- true:
458+
default:
459+
}
454460

455461
return nil, sentinel.ErrKeyExpired
456462
}
@@ -530,8 +536,11 @@ func (hyperCache *HyperCache[T]) GetMultiple(ctx context.Context, keys ...string
530536
hyperCache.itemPoolManager.Put(item)
531537
// Treat expired items as not found per API semantics
532538
failed[key] = sentinel.ErrKeyNotFound
533-
// Trigger the expiration loop
534-
go hyperCache.expirationLoop(ctx)
539+
// Non-blocking trigger of the expiration loop via channel
540+
select {
541+
case hyperCache.expirationTriggerCh <- true:
542+
default:
543+
}
535544
} else {
536545
item.Touch() // Update the last access time and access count
537546
// Add the item to the result map

0 commit comments

Comments
 (0)