summaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/golang-lru/v2/2q.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/golang-lru/v2/2q.go')
-rw-r--r--vendor/github.com/hashicorp/golang-lru/v2/2q.go267
1 files changed, 0 insertions, 267 deletions
diff --git a/vendor/github.com/hashicorp/golang-lru/v2/2q.go b/vendor/github.com/hashicorp/golang-lru/v2/2q.go
deleted file mode 100644
index 8c95252b6..000000000
--- a/vendor/github.com/hashicorp/golang-lru/v2/2q.go
+++ /dev/null
@@ -1,267 +0,0 @@
-// Copyright (c) HashiCorp, Inc.
-// SPDX-License-Identifier: MPL-2.0
-
-package lru
-
-import (
- "errors"
- "sync"
-
- "github.com/hashicorp/golang-lru/v2/simplelru"
-)
-
-const (
- // Default2QRecentRatio is the ratio of the 2Q cache dedicated
- // to recently added entries that have only been accessed once.
- Default2QRecentRatio = 0.25
-
- // Default2QGhostEntries is the default ratio of ghost
- // entries kept to track entries recently evicted
- Default2QGhostEntries = 0.50
-)
-
-// TwoQueueCache is a thread-safe fixed size 2Q cache.
-// 2Q is an enhancement over the standard LRU cache
-// in that it tracks both frequently and recently used
-// entries separately. This avoids a burst in access to new
-// entries from evicting frequently used entries. It adds some
-// additional tracking overhead to the standard LRU cache, and is
-// computationally about 2x the cost, and adds some metadata over
-// head. The ARCCache is similar, but does not require setting any
-// parameters.
-type TwoQueueCache[K comparable, V any] struct {
- size int
- recentSize int
- recentRatio float64
- ghostRatio float64
-
- recent simplelru.LRUCache[K, V]
- frequent simplelru.LRUCache[K, V]
- recentEvict simplelru.LRUCache[K, struct{}]
- lock sync.RWMutex
-}
-
-// New2Q creates a new TwoQueueCache using the default
-// values for the parameters.
-func New2Q[K comparable, V any](size int) (*TwoQueueCache[K, V], error) {
- return New2QParams[K, V](size, Default2QRecentRatio, Default2QGhostEntries)
-}
-
-// New2QParams creates a new TwoQueueCache using the provided
-// parameter values.
-func New2QParams[K comparable, V any](size int, recentRatio, ghostRatio float64) (*TwoQueueCache[K, V], error) {
- if size <= 0 {
- return nil, errors.New("invalid size")
- }
- if recentRatio < 0.0 || recentRatio > 1.0 {
- return nil, errors.New("invalid recent ratio")
- }
- if ghostRatio < 0.0 || ghostRatio > 1.0 {
- return nil, errors.New("invalid ghost ratio")
- }
-
- // Determine the sub-sizes
- recentSize := int(float64(size) * recentRatio)
- evictSize := int(float64(size) * ghostRatio)
-
- // Allocate the LRUs
- recent, err := simplelru.NewLRU[K, V](size, nil)
- if err != nil {
- return nil, err
- }
- frequent, err := simplelru.NewLRU[K, V](size, nil)
- if err != nil {
- return nil, err
- }
- recentEvict, err := simplelru.NewLRU[K, struct{}](evictSize, nil)
- if err != nil {
- return nil, err
- }
-
- // Initialize the cache
- c := &TwoQueueCache[K, V]{
- size: size,
- recentSize: recentSize,
- recentRatio: recentRatio,
- ghostRatio: ghostRatio,
- recent: recent,
- frequent: frequent,
- recentEvict: recentEvict,
- }
- return c, nil
-}
-
-// Get looks up a key's value from the cache.
-func (c *TwoQueueCache[K, V]) Get(key K) (value V, ok bool) {
- c.lock.Lock()
- defer c.lock.Unlock()
-
- // Check if this is a frequent value
- if val, ok := c.frequent.Get(key); ok {
- return val, ok
- }
-
- // If the value is contained in recent, then we
- // promote it to frequent
- if val, ok := c.recent.Peek(key); ok {
- c.recent.Remove(key)
- c.frequent.Add(key, val)
- return val, ok
- }
-
- // No hit
- return
-}
-
-// Add adds a value to the cache.
-func (c *TwoQueueCache[K, V]) Add(key K, value V) {
- c.lock.Lock()
- defer c.lock.Unlock()
-
- // Check if the value is frequently used already,
- // and just update the value
- if c.frequent.Contains(key) {
- c.frequent.Add(key, value)
- return
- }
-
- // Check if the value is recently used, and promote
- // the value into the frequent list
- if c.recent.Contains(key) {
- c.recent.Remove(key)
- c.frequent.Add(key, value)
- return
- }
-
- // If the value was recently evicted, add it to the
- // frequently used list
- if c.recentEvict.Contains(key) {
- c.ensureSpace(true)
- c.recentEvict.Remove(key)
- c.frequent.Add(key, value)
- return
- }
-
- // Add to the recently seen list
- c.ensureSpace(false)
- c.recent.Add(key, value)
-}
-
-// ensureSpace is used to ensure we have space in the cache
-func (c *TwoQueueCache[K, V]) ensureSpace(recentEvict bool) {
- // If we have space, nothing to do
- recentLen := c.recent.Len()
- freqLen := c.frequent.Len()
- if recentLen+freqLen < c.size {
- return
- }
-
- // If the recent buffer is larger than
- // the target, evict from there
- if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) {
- k, _, _ := c.recent.RemoveOldest()
- c.recentEvict.Add(k, struct{}{})
- return
- }
-
- // Remove from the frequent list otherwise
- c.frequent.RemoveOldest()
-}
-
-// Len returns the number of items in the cache.
-func (c *TwoQueueCache[K, V]) Len() int {
- c.lock.RLock()
- defer c.lock.RUnlock()
- return c.recent.Len() + c.frequent.Len()
-}
-
-// Resize changes the cache size.
-func (c *TwoQueueCache[K, V]) Resize(size int) (evicted int) {
- c.lock.Lock()
- defer c.lock.Unlock()
-
- // Recalculate the sub-sizes
- recentSize := int(float64(size) * c.recentRatio)
- evictSize := int(float64(size) * c.ghostRatio)
- c.size = size
- c.recentSize = recentSize
-
- // ensureSpace
- diff := c.recent.Len() + c.frequent.Len() - size
- if diff < 0 {
- diff = 0
- }
- for i := 0; i < diff; i++ {
- c.ensureSpace(true)
- }
-
- // Reallocate the LRUs
- c.recent.Resize(size)
- c.frequent.Resize(size)
- c.recentEvict.Resize(evictSize)
-
- return diff
-}
-
-// Keys returns a slice of the keys in the cache.
-// The frequently used keys are first in the returned slice.
-func (c *TwoQueueCache[K, V]) Keys() []K {
- c.lock.RLock()
- defer c.lock.RUnlock()
- k1 := c.frequent.Keys()
- k2 := c.recent.Keys()
- return append(k1, k2...)
-}
-
-// Values returns a slice of the values in the cache.
-// The frequently used values are first in the returned slice.
-func (c *TwoQueueCache[K, V]) Values() []V {
- c.lock.RLock()
- defer c.lock.RUnlock()
- v1 := c.frequent.Values()
- v2 := c.recent.Values()
- return append(v1, v2...)
-}
-
-// Remove removes the provided key from the cache.
-func (c *TwoQueueCache[K, V]) Remove(key K) {
- c.lock.Lock()
- defer c.lock.Unlock()
- if c.frequent.Remove(key) {
- return
- }
- if c.recent.Remove(key) {
- return
- }
- if c.recentEvict.Remove(key) {
- return
- }
-}
-
-// Purge is used to completely clear the cache.
-func (c *TwoQueueCache[K, V]) Purge() {
- c.lock.Lock()
- defer c.lock.Unlock()
- c.recent.Purge()
- c.frequent.Purge()
- c.recentEvict.Purge()
-}
-
-// Contains is used to check if the cache contains a key
-// without updating recency or frequency.
-func (c *TwoQueueCache[K, V]) Contains(key K) bool {
- c.lock.RLock()
- defer c.lock.RUnlock()
- return c.frequent.Contains(key) || c.recent.Contains(key)
-}
-
-// Peek is used to inspect the cache value of a key
-// without updating recency or frequency.
-func (c *TwoQueueCache[K, V]) Peek(key K) (value V, ok bool) {
- c.lock.RLock()
- defer c.lock.RUnlock()
- if val, ok := c.frequent.Peek(key); ok {
- return val, ok
- }
- return c.recent.Peek(key)
-}