Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/on-pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
go-version:
- 1.22.x
- 1.23.x
- 1.24.x
os: [ ubuntu-latest ]
runs-on: ${{ matrix.os }}
steps:
Expand Down
39 changes: 32 additions & 7 deletions groupcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,31 @@ func GetGroups() []*Group {
// completes.
//
// The group name must be unique for each getter.
func NewGroup(name string, cacheBytes int64, getter Getter) *Group {
return newGroup(name, cacheBytes, getter, nil)
func NewGroup(name string, cacheBytes int64, getter Getter, opts ...Option) *Group {
group := newGroup(name, cacheBytes, getter, nil)
for _, opt := range opts {
opt.apply(group)
}
return group
}

// Option for NewGroup().
type Option interface {
apply(*Group)
}

type withPruneIntervalOption struct {
pruneInterval int
}

func (o *withPruneIntervalOption) apply(group *Group) {
group.mainCache.pruneInterval = o.pruneInterval
group.hotCache.pruneInterval = o.pruneInterval
}

// Pass prune interval option for NewGroup().
func WithPruneInterval(pruneInterval int) Option {
return &withPruneIntervalOption{pruneInterval: pruneInterval}
}

// DeregisterGroup removes group from group pool
Expand Down Expand Up @@ -608,11 +631,12 @@ var NowFunc lru.NowFunc = time.Now
// makes values always be ByteView, and counts the size of all keys and
// values.
type cache struct {
mu sync.RWMutex
nbytes int64 // of all keys and values
lru *lru.Cache
nhit, nget int64
nevict int64 // number of evictions
mu sync.RWMutex
nbytes int64 // of all keys and values
lru *lru.Cache
nhit, nget int64
nevict int64 // number of evictions
pruneInterval int
}

func (c *cache) stats() CacheStats {
Expand All @@ -638,6 +662,7 @@ func (c *cache) add(key string, value ByteView) {
c.nbytes -= int64(len(key.(string))) + int64(val.Len())
c.nevict++
},
PruneInterval: c.pruneInterval,
}
}
c.lru.Add(key, value, value.Expire())
Expand Down
26 changes: 24 additions & 2 deletions lru/lru.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,13 @@ type Cache struct {
// Defaults to time.Now()
Now NowFunc

ll *list.List
cache map[interface{}]*list.Element
// PruneInterval sets number of cache removals before internal map is pruned.
// If set to zero, no pruning is performed.
PruneInterval int

ll *list.List
cache map[interface{}]*list.Element
removeCounter int
}

// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators
Expand Down Expand Up @@ -69,6 +74,9 @@ func (c *Cache) Add(key Key, value interface{}, expire time.Time) {
if c.cache == nil {
c.cache = make(map[interface{}]*list.Element)
c.ll = list.New()
} else if c.PruneInterval > 0 && c.removeCounter >= c.PruneInterval {
c.removeCounter = 0
c.Prune()
}
if ee, ok := c.cache[key]; ok {
eee := ee.Value.(*entry)
Expand Down Expand Up @@ -131,6 +139,7 @@ func (c *Cache) removeElement(e *list.Element) {
c.ll.Remove(e)
kv := e.Value.(*entry)
delete(c.cache, kv.key)
c.removeCounter++
if c.OnEvicted != nil {
c.OnEvicted(kv.key, kv.value)
}
Expand All @@ -155,3 +164,16 @@ func (c *Cache) Clear() {
c.ll = nil
c.cache = nil
}

// Prune cache state to flush map storage from previously deleted keys.
func (c *Cache) Prune() {
if c.cache == nil {
return
}
newCache := make(map[any]*list.Element)
for k, v := range c.cache {
newCache[k] = v
delete(c.cache, k)
}
c.cache = newCache
}
Loading