Skip to content

Commit c8ab2a2

Browse files
committed
feat(core): type-safe backend creation, safer item pooling, and cleanup around expiration handling
hypercache: refactor New into helpers and call resolveBackend without explicit type params; avoid returning expired items to the pool in Get/GetWithInfo/GetOrSet/GetMultiple to prevent zeroing live references while still coalescing expiration triggers cache/item: zero Item before returning to pool to avoid retaining large values across reuses spelling: add “backpressure” to cspell dictionary Context: preserves public API and behavior, improves memory hygiene and correctness for expired-item paths, and keeps backend construction strictly typed.
1 parent de43b99 commit c8ab2a2

3 files changed

Lines changed: 11 additions & 10 deletions

File tree

cspell.config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ ignorePaths: []
33
dictionaryDefinitions: []
44
dictionaries: []
55
words:
6+
- backpressure
67
- benchmem
78
- benchtime
89
- cacheerrors

hypercache.go

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func NewInMemoryWithDefaults(capacity int) (*HyperCache[backend.InMemory], error
106106
// - The stats collector is set to the HistogramStatsCollector stats collector.
107107
func New[T backend.IBackendConstrain](bm *BackendManager, config *Config[T]) (*HyperCache[T], error) {
108108
// Resolve typed backend from registry
109-
backendTyped, err := resolveBackend[T](bm, config)
109+
backendTyped, err := resolveBackend(bm, config)
110110
if err != nil {
111111
return nil, err
112112
}
@@ -535,8 +535,7 @@ func (hyperCache *HyperCache[T]) Get(ctx context.Context, key string) (any, bool
535535

536536
// Check if the item has expired, if so, trigger the expiration loop
537537
if item.Expired() {
538-
// Return the item to the pool and non-blocking trigger of expiration loop
539-
hyperCache.itemPoolManager.Put(item)
538+
// Non-blocking trigger of expiration loop (do not return to pool yet; backend still holds it)
540539
// Coalesced/debounced trigger
541540
hyperCache.triggerExpiration()
542541

@@ -559,8 +558,7 @@ func (hyperCache *HyperCache[T]) GetWithInfo(ctx context.Context, key string) (*
559558

560559
// Check if the item has expired, if so, trigger the expiration loop
561560
if item.Expired() {
562-
// Return the item to the pool and non-blocking trigger of expiration loop
563-
hyperCache.itemPoolManager.Put(item)
561+
// Non-blocking trigger of expiration loop; don't return to pool here
564562
// Coalesced/debounced trigger
565563
hyperCache.triggerExpiration()
566564

@@ -580,8 +578,7 @@ func (hyperCache *HyperCache[T]) GetOrSet(ctx context.Context, key string, value
580578
if item, ok := hyperCache.backend.Get(ctx, key); ok {
581579
// Check if the item has expired
582580
if item.Expired() {
583-
// Return the item to the pool and non-blocking trigger of expiration loop
584-
hyperCache.itemPoolManager.Put(item)
581+
// Non-blocking trigger of expiration loop; don't pool here to avoid zeroing live refs
585582
// Coalesced/debounced trigger
586583
hyperCache.triggerExpiration()
587584

@@ -659,9 +656,7 @@ func (hyperCache *HyperCache[T]) GetMultiple(ctx context.Context, keys ...string
659656

660657
// Check if the item has expired
661658
if item.Expired() {
662-
// Put the item back in the pool
663-
hyperCache.itemPoolManager.Put(item)
664-
// Treat expired items as not found per API semantics
659+
// Treat expired items as not found per API semantics; don't pool here to avoid zeroing live refs
665660
failed[key] = sentinel.ErrKeyNotFound
666661
// Coalesced/debounced trigger of the expiration loop via channel
667662
hyperCache.triggerExpiration()

pkg/cache/item.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ func (m *ItemPoolManager) Get() *Item {
4343

4444
// Put returns an Item to the pool.
4545
func (m *ItemPoolManager) Put(item *Item) {
46+
if item == nil {
47+
return
48+
}
49+
// Zero the struct to avoid retaining large references across pool reuses
50+
*item = Item{}
4651
m.pool.Put(item)
4752
}
4853

0 commit comments

Comments
 (0)