mirror of
https://github.com/DNSCrypt/dnscrypt-proxy.git
synced 2025-03-04 02:14:40 +01:00
Cache plugin: replace ARC cache with SIEVE
This commit is contained in:
parent
63f8d9b30d
commit
f2484f5bd5
19 changed files with 434 additions and 845 deletions
|
@ -6,8 +6,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
lru "github.com/hashicorp/golang-lru"
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
sieve "github.com/opencoff/go-sieve"
|
||||||
)
|
)
|
||||||
|
|
||||||
const StaleResponseTTL = 30 * time.Second
|
const StaleResponseTTL = 30 * time.Second
|
||||||
|
@ -19,7 +19,7 @@ type CachedResponse struct {
|
||||||
|
|
||||||
type CachedResponses struct {
|
type CachedResponses struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
cache *lru.ARCCache
|
cache *sieve.Sieve[[32]byte, CachedResponse]
|
||||||
}
|
}
|
||||||
|
|
||||||
var cachedResponses CachedResponses
|
var cachedResponses CachedResponses
|
||||||
|
@ -75,12 +75,11 @@ func (plugin *PluginCache) Eval(pluginsState *PluginsState, msg *dns.Msg) error
|
||||||
cachedResponses.RUnlock()
|
cachedResponses.RUnlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cachedAny, ok := cachedResponses.cache.Get(cacheKey)
|
cached, ok := cachedResponses.cache.Get(cacheKey)
|
||||||
if !ok {
|
if !ok {
|
||||||
cachedResponses.RUnlock()
|
cachedResponses.RUnlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cached := cachedAny.(CachedResponse)
|
|
||||||
expiration := cached.expiration
|
expiration := cached.expiration
|
||||||
synth := cached.msg.Copy()
|
synth := cached.msg.Copy()
|
||||||
cachedResponses.RUnlock()
|
cachedResponses.RUnlock()
|
||||||
|
@ -151,8 +150,8 @@ func (plugin *PluginCacheResponse) Eval(pluginsState *PluginsState, msg *dns.Msg
|
||||||
cachedResponses.Lock()
|
cachedResponses.Lock()
|
||||||
if cachedResponses.cache == nil {
|
if cachedResponses.cache == nil {
|
||||||
var err error
|
var err error
|
||||||
cachedResponses.cache, err = lru.NewARC(pluginsState.cacheSize)
|
cachedResponses.cache = sieve.New[[32]byte, CachedResponse](pluginsState.cacheSize)
|
||||||
if err != nil {
|
if cachedResponses.cache == nil {
|
||||||
cachedResponses.Unlock()
|
cachedResponses.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
3
go.mod
3
go.mod
|
@ -8,7 +8,6 @@ require (
|
||||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||||
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185
|
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185
|
||||||
github.com/hashicorp/go-immutable-radix v1.3.1
|
github.com/hashicorp/go-immutable-radix v1.3.1
|
||||||
github.com/hashicorp/golang-lru v1.0.2
|
|
||||||
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb
|
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb
|
||||||
github.com/jedisct1/dlog v0.0.0-20230811132706-443b333ff1b3
|
github.com/jedisct1/dlog v0.0.0-20230811132706-443b333ff1b3
|
||||||
github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e
|
github.com/jedisct1/go-clocksmith v0.0.0-20230211133011-392c1afea73e
|
||||||
|
@ -19,6 +18,7 @@ require (
|
||||||
github.com/k-sone/critbitgo v1.4.0
|
github.com/k-sone/critbitgo v1.4.0
|
||||||
github.com/kardianos/service v1.2.2
|
github.com/kardianos/service v1.2.2
|
||||||
github.com/miekg/dns v1.1.58
|
github.com/miekg/dns v1.1.58
|
||||||
|
github.com/opencoff/go-sieve v0.2.1
|
||||||
github.com/powerman/check v1.7.0
|
github.com/powerman/check v1.7.0
|
||||||
github.com/quic-go/quic-go v0.41.0
|
github.com/quic-go/quic-go v0.41.0
|
||||||
golang.org/x/crypto v0.18.0
|
golang.org/x/crypto v0.18.0
|
||||||
|
@ -33,6 +33,7 @@ require (
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
|
||||||
github.com/hashicorp/go-syslog v1.0.0 // indirect
|
github.com/hashicorp/go-syslog v1.0.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
|
5
go.sum
5
go.sum
|
@ -32,9 +32,8 @@ github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwM
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
|
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
|
||||||
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
|
||||||
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM=
|
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM=
|
||||||
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E=
|
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
@ -62,6 +61,8 @@ github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
||||||
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||||
|
github.com/opencoff/go-sieve v0.2.1 h1:5Pv6rd3zRquNmXcYHFndjVoolTgcv0ua2XTdMQ+gw0M=
|
||||||
|
github.com/opencoff/go-sieve v0.2.1/go.mod h1:CndxLpW4R8fDq04XfBSCOZ+qWwDCcxjfUJbr0GPqWHY=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|
23
vendor/github.com/hashicorp/golang-lru/.gitignore
generated
vendored
23
vendor/github.com/hashicorp/golang-lru/.gitignore
generated
vendored
|
@ -1,23 +0,0 @@
|
||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
*.test
|
|
30
vendor/github.com/hashicorp/golang-lru/.golangci.yml
generated
vendored
30
vendor/github.com/hashicorp/golang-lru/.golangci.yml
generated
vendored
|
@ -1,30 +0,0 @@
|
||||||
linters:
|
|
||||||
enable:
|
|
||||||
- megacheck
|
|
||||||
- revive
|
|
||||||
- govet
|
|
||||||
- unconvert
|
|
||||||
- megacheck
|
|
||||||
- gas
|
|
||||||
- gocyclo
|
|
||||||
- dupl
|
|
||||||
- misspell
|
|
||||||
- unparam
|
|
||||||
- unused
|
|
||||||
- typecheck
|
|
||||||
- ineffassign
|
|
||||||
- stylecheck
|
|
||||||
- exportloopref
|
|
||||||
- gocritic
|
|
||||||
- nakedret
|
|
||||||
- gosimple
|
|
||||||
- prealloc
|
|
||||||
fast: false
|
|
||||||
disable-all: true
|
|
||||||
|
|
||||||
issues:
|
|
||||||
exclude-rules:
|
|
||||||
- path: _test\.go
|
|
||||||
linters:
|
|
||||||
- dupl
|
|
||||||
exclude-use-default: false
|
|
222
vendor/github.com/hashicorp/golang-lru/2q.go
generated
vendored
222
vendor/github.com/hashicorp/golang-lru/2q.go
generated
vendored
|
@ -1,222 +0,0 @@
|
||||||
package lru
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/hashicorp/golang-lru/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 struct {
|
|
||||||
size int
|
|
||||||
recentSize int
|
|
||||||
|
|
||||||
recent simplelru.LRUCache
|
|
||||||
frequent simplelru.LRUCache
|
|
||||||
recentEvict simplelru.LRUCache
|
|
||||||
lock sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// New2Q creates a new TwoQueueCache using the default
|
|
||||||
// values for the parameters.
|
|
||||||
func New2Q(size int) (*TwoQueueCache, error) {
|
|
||||||
return New2QParams(size, Default2QRecentRatio, Default2QGhostEntries)
|
|
||||||
}
|
|
||||||
|
|
||||||
// New2QParams creates a new TwoQueueCache using the provided
|
|
||||||
// parameter values.
|
|
||||||
func New2QParams(size int, recentRatio, ghostRatio float64) (*TwoQueueCache, error) {
|
|
||||||
if size <= 0 {
|
|
||||||
return nil, fmt.Errorf("invalid size")
|
|
||||||
}
|
|
||||||
if recentRatio < 0.0 || recentRatio > 1.0 {
|
|
||||||
return nil, fmt.Errorf("invalid recent ratio")
|
|
||||||
}
|
|
||||||
if ghostRatio < 0.0 || ghostRatio > 1.0 {
|
|
||||||
return nil, fmt.Errorf("invalid ghost ratio")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the sub-sizes
|
|
||||||
recentSize := int(float64(size) * recentRatio)
|
|
||||||
evictSize := int(float64(size) * ghostRatio)
|
|
||||||
|
|
||||||
// Allocate the LRUs
|
|
||||||
recent, err := simplelru.NewLRU(size, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
frequent, err := simplelru.NewLRU(size, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
recentEvict, err := simplelru.NewLRU(evictSize, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the cache
|
|
||||||
c := &TwoQueueCache{
|
|
||||||
size: size,
|
|
||||||
recentSize: recentSize,
|
|
||||||
recent: recent,
|
|
||||||
frequent: frequent,
|
|
||||||
recentEvict: recentEvict,
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get looks up a key's value from the cache.
|
|
||||||
func (c *TwoQueueCache) Get(key interface{}) (value interface{}, 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 nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds a value to the cache.
|
|
||||||
func (c *TwoQueueCache) Add(key, value interface{}) {
|
|
||||||
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) 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, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from the frequent list otherwise
|
|
||||||
c.frequent.RemoveOldest()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of items in the cache.
|
|
||||||
func (c *TwoQueueCache) Len() int {
|
|
||||||
c.lock.RLock()
|
|
||||||
defer c.lock.RUnlock()
|
|
||||||
return c.recent.Len() + c.frequent.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys returns a slice of the keys in the cache.
|
|
||||||
// The frequently used keys are first in the returned slice.
|
|
||||||
func (c *TwoQueueCache) Keys() []interface{} {
|
|
||||||
c.lock.RLock()
|
|
||||||
defer c.lock.RUnlock()
|
|
||||||
k1 := c.frequent.Keys()
|
|
||||||
k2 := c.recent.Keys()
|
|
||||||
return append(k1, k2...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes the provided key from the cache.
|
|
||||||
func (c *TwoQueueCache) Remove(key interface{}) {
|
|
||||||
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) 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) Contains(key interface{}) 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) Peek(key interface{}) (value interface{}, 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)
|
|
||||||
}
|
|
2
vendor/github.com/hashicorp/golang-lru/LICENSE
generated
vendored
2
vendor/github.com/hashicorp/golang-lru/LICENSE
generated
vendored
|
@ -1,5 +1,3 @@
|
||||||
Copyright (c) 2014 HashiCorp, Inc.
|
|
||||||
|
|
||||||
Mozilla Public License, version 2.0
|
Mozilla Public License, version 2.0
|
||||||
|
|
||||||
1. Definitions
|
1. Definitions
|
||||||
|
|
7
vendor/github.com/hashicorp/golang-lru/README.md
generated
vendored
7
vendor/github.com/hashicorp/golang-lru/README.md
generated
vendored
|
@ -1,7 +0,0 @@
|
||||||
golang-lru
|
|
||||||
==========
|
|
||||||
|
|
||||||
Please upgrade to github.com/hashicorp/golang-lru/v2 for all new code as v1 will
|
|
||||||
not be updated anymore. The v2 version supports generics and is faster; old code
|
|
||||||
can specify a specific tag, e.g. github.com/hashicorp/golang-lru/v1.0.2 for
|
|
||||||
backwards compatibility.
|
|
256
vendor/github.com/hashicorp/golang-lru/arc.go
generated
vendored
256
vendor/github.com/hashicorp/golang-lru/arc.go
generated
vendored
|
@ -1,256 +0,0 @@
|
||||||
package lru
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/hashicorp/golang-lru/simplelru"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ARCCache is a thread-safe fixed size Adaptive Replacement Cache (ARC).
|
|
||||||
// ARC is an enhancement over the standard LRU cache in that tracks both
|
|
||||||
// frequency and recency of use. This avoids a burst in access to new
|
|
||||||
// entries from evicting the frequently used older entries. It adds some
|
|
||||||
// additional tracking overhead to a standard LRU cache, computationally
|
|
||||||
// it is roughly 2x the cost, and the extra memory overhead is linear
|
|
||||||
// with the size of the cache. ARC has been patented by IBM, but is
|
|
||||||
// similar to the TwoQueueCache (2Q) which requires setting parameters.
|
|
||||||
type ARCCache struct {
|
|
||||||
size int // Size is the total capacity of the cache
|
|
||||||
p int // P is the dynamic preference towards T1 or T2
|
|
||||||
|
|
||||||
t1 simplelru.LRUCache // T1 is the LRU for recently accessed items
|
|
||||||
b1 simplelru.LRUCache // B1 is the LRU for evictions from t1
|
|
||||||
|
|
||||||
t2 simplelru.LRUCache // T2 is the LRU for frequently accessed items
|
|
||||||
b2 simplelru.LRUCache // B2 is the LRU for evictions from t2
|
|
||||||
|
|
||||||
lock sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewARC creates an ARC of the given size
|
|
||||||
func NewARC(size int) (*ARCCache, error) {
|
|
||||||
// Create the sub LRUs
|
|
||||||
b1, err := simplelru.NewLRU(size, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
b2, err := simplelru.NewLRU(size, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
t1, err := simplelru.NewLRU(size, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
t2, err := simplelru.NewLRU(size, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the ARC
|
|
||||||
c := &ARCCache{
|
|
||||||
size: size,
|
|
||||||
p: 0,
|
|
||||||
t1: t1,
|
|
||||||
b1: b1,
|
|
||||||
t2: t2,
|
|
||||||
b2: b2,
|
|
||||||
}
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get looks up a key's value from the cache.
|
|
||||||
func (c *ARCCache) Get(key interface{}) (value interface{}, ok bool) {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
// If the value is contained in T1 (recent), then
|
|
||||||
// promote it to T2 (frequent)
|
|
||||||
if val, ok := c.t1.Peek(key); ok {
|
|
||||||
c.t1.Remove(key)
|
|
||||||
c.t2.Add(key, val)
|
|
||||||
return val, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the value is contained in T2 (frequent)
|
|
||||||
if val, ok := c.t2.Get(key); ok {
|
|
||||||
return val, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// No hit
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds a value to the cache.
|
|
||||||
func (c *ARCCache) Add(key, value interface{}) {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
|
|
||||||
// Check if the value is contained in T1 (recent), and potentially
|
|
||||||
// promote it to frequent T2
|
|
||||||
if c.t1.Contains(key) {
|
|
||||||
c.t1.Remove(key)
|
|
||||||
c.t2.Add(key, value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the value is already in T2 (frequent) and update it
|
|
||||||
if c.t2.Contains(key) {
|
|
||||||
c.t2.Add(key, value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this value was recently evicted as part of the
|
|
||||||
// recently used list
|
|
||||||
if c.b1.Contains(key) {
|
|
||||||
// T1 set is too small, increase P appropriately
|
|
||||||
delta := 1
|
|
||||||
b1Len := c.b1.Len()
|
|
||||||
b2Len := c.b2.Len()
|
|
||||||
if b2Len > b1Len {
|
|
||||||
delta = b2Len / b1Len
|
|
||||||
}
|
|
||||||
if c.p+delta >= c.size {
|
|
||||||
c.p = c.size
|
|
||||||
} else {
|
|
||||||
c.p += delta
|
|
||||||
}
|
|
||||||
|
|
||||||
// Potentially need to make room in the cache
|
|
||||||
if c.t1.Len()+c.t2.Len() >= c.size {
|
|
||||||
c.replace(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from B1
|
|
||||||
c.b1.Remove(key)
|
|
||||||
|
|
||||||
// Add the key to the frequently used list
|
|
||||||
c.t2.Add(key, value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this value was recently evicted as part of the
|
|
||||||
// frequently used list
|
|
||||||
if c.b2.Contains(key) {
|
|
||||||
// T2 set is too small, decrease P appropriately
|
|
||||||
delta := 1
|
|
||||||
b1Len := c.b1.Len()
|
|
||||||
b2Len := c.b2.Len()
|
|
||||||
if b1Len > b2Len {
|
|
||||||
delta = b1Len / b2Len
|
|
||||||
}
|
|
||||||
if delta >= c.p {
|
|
||||||
c.p = 0
|
|
||||||
} else {
|
|
||||||
c.p -= delta
|
|
||||||
}
|
|
||||||
|
|
||||||
// Potentially need to make room in the cache
|
|
||||||
if c.t1.Len()+c.t2.Len() >= c.size {
|
|
||||||
c.replace(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from B2
|
|
||||||
c.b2.Remove(key)
|
|
||||||
|
|
||||||
// Add the key to the frequently used list
|
|
||||||
c.t2.Add(key, value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Potentially need to make room in the cache
|
|
||||||
if c.t1.Len()+c.t2.Len() >= c.size {
|
|
||||||
c.replace(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep the size of the ghost buffers trim
|
|
||||||
if c.b1.Len() > c.size-c.p {
|
|
||||||
c.b1.RemoveOldest()
|
|
||||||
}
|
|
||||||
if c.b2.Len() > c.p {
|
|
||||||
c.b2.RemoveOldest()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to the recently seen list
|
|
||||||
c.t1.Add(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace is used to adaptively evict from either T1 or T2
|
|
||||||
// based on the current learned value of P
|
|
||||||
func (c *ARCCache) replace(b2ContainsKey bool) {
|
|
||||||
t1Len := c.t1.Len()
|
|
||||||
if t1Len > 0 && (t1Len > c.p || (t1Len == c.p && b2ContainsKey)) {
|
|
||||||
k, _, ok := c.t1.RemoveOldest()
|
|
||||||
if ok {
|
|
||||||
c.b1.Add(k, nil)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
k, _, ok := c.t2.RemoveOldest()
|
|
||||||
if ok {
|
|
||||||
c.b2.Add(k, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of cached entries
|
|
||||||
func (c *ARCCache) Len() int {
|
|
||||||
c.lock.RLock()
|
|
||||||
defer c.lock.RUnlock()
|
|
||||||
return c.t1.Len() + c.t2.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys returns all the cached keys
|
|
||||||
func (c *ARCCache) Keys() []interface{} {
|
|
||||||
c.lock.RLock()
|
|
||||||
defer c.lock.RUnlock()
|
|
||||||
k1 := c.t1.Keys()
|
|
||||||
k2 := c.t2.Keys()
|
|
||||||
return append(k1, k2...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove is used to purge a key from the cache
|
|
||||||
func (c *ARCCache) Remove(key interface{}) {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
if c.t1.Remove(key) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c.t2.Remove(key) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c.b1.Remove(key) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if c.b2.Remove(key) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Purge is used to clear the cache
|
|
||||||
func (c *ARCCache) Purge() {
|
|
||||||
c.lock.Lock()
|
|
||||||
defer c.lock.Unlock()
|
|
||||||
c.t1.Purge()
|
|
||||||
c.t2.Purge()
|
|
||||||
c.b1.Purge()
|
|
||||||
c.b2.Purge()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains is used to check if the cache contains a key
|
|
||||||
// without updating recency or frequency.
|
|
||||||
func (c *ARCCache) Contains(key interface{}) bool {
|
|
||||||
c.lock.RLock()
|
|
||||||
defer c.lock.RUnlock()
|
|
||||||
return c.t1.Contains(key) || c.t2.Contains(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Peek is used to inspect the cache value of a key
|
|
||||||
// without updating recency or frequency.
|
|
||||||
func (c *ARCCache) Peek(key interface{}) (value interface{}, ok bool) {
|
|
||||||
c.lock.RLock()
|
|
||||||
defer c.lock.RUnlock()
|
|
||||||
if val, ok := c.t1.Peek(key); ok {
|
|
||||||
return val, ok
|
|
||||||
}
|
|
||||||
return c.t2.Peek(key)
|
|
||||||
}
|
|
21
vendor/github.com/hashicorp/golang-lru/doc.go
generated
vendored
21
vendor/github.com/hashicorp/golang-lru/doc.go
generated
vendored
|
@ -1,21 +0,0 @@
|
||||||
// Package lru provides three different LRU caches of varying sophistication.
|
|
||||||
//
|
|
||||||
// Cache is a simple LRU cache. It is based on the
|
|
||||||
// LRU implementation in groupcache:
|
|
||||||
// https://github.com/golang/groupcache/tree/master/lru
|
|
||||||
//
|
|
||||||
// TwoQueueCache tracks frequently used and recently used entries separately.
|
|
||||||
// This avoids a burst of accesses from taking out frequently used entries,
|
|
||||||
// at the cost of about 2x computational overhead and some extra bookkeeping.
|
|
||||||
//
|
|
||||||
// ARCCache is an adaptive replacement cache. It tracks recent evictions as
|
|
||||||
// well as recent usage in both the frequent and recent caches. Its
|
|
||||||
// computational overhead is comparable to TwoQueueCache, but the memory
|
|
||||||
// overhead is linear with the size of the cache.
|
|
||||||
//
|
|
||||||
// ARC has been patented by IBM, so do not use it if that is problematic for
|
|
||||||
// your program.
|
|
||||||
//
|
|
||||||
// All caches in this package take locks while operating, and are therefore
|
|
||||||
// thread-safe for consumers.
|
|
||||||
package lru
|
|
231
vendor/github.com/hashicorp/golang-lru/lru.go
generated
vendored
231
vendor/github.com/hashicorp/golang-lru/lru.go
generated
vendored
|
@ -1,231 +0,0 @@
|
||||||
package lru
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/hashicorp/golang-lru/simplelru"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// DefaultEvictedBufferSize defines the default buffer size to store evicted key/val
|
|
||||||
DefaultEvictedBufferSize = 16
|
|
||||||
)
|
|
||||||
|
|
||||||
// Cache is a thread-safe fixed size LRU cache.
|
|
||||||
type Cache struct {
|
|
||||||
lru *simplelru.LRU
|
|
||||||
evictedKeys, evictedVals []interface{}
|
|
||||||
onEvictedCB func(k, v interface{})
|
|
||||||
lock sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates an LRU of the given size.
|
|
||||||
func New(size int) (*Cache, error) {
|
|
||||||
return NewWithEvict(size, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWithEvict constructs a fixed size cache with the given eviction
|
|
||||||
// callback.
|
|
||||||
func NewWithEvict(size int, onEvicted func(key, value interface{})) (c *Cache, err error) {
|
|
||||||
// create a cache with default settings
|
|
||||||
c = &Cache{
|
|
||||||
onEvictedCB: onEvicted,
|
|
||||||
}
|
|
||||||
if onEvicted != nil {
|
|
||||||
c.initEvictBuffers()
|
|
||||||
onEvicted = c.onEvicted
|
|
||||||
}
|
|
||||||
c.lru, err = simplelru.NewLRU(size, onEvicted)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) initEvictBuffers() {
|
|
||||||
c.evictedKeys = make([]interface{}, 0, DefaultEvictedBufferSize)
|
|
||||||
c.evictedVals = make([]interface{}, 0, DefaultEvictedBufferSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// onEvicted save evicted key/val and sent in externally registered callback
|
|
||||||
// outside of critical section
|
|
||||||
func (c *Cache) onEvicted(k, v interface{}) {
|
|
||||||
c.evictedKeys = append(c.evictedKeys, k)
|
|
||||||
c.evictedVals = append(c.evictedVals, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Purge is used to completely clear the cache.
|
|
||||||
func (c *Cache) Purge() {
|
|
||||||
var ks, vs []interface{}
|
|
||||||
c.lock.Lock()
|
|
||||||
c.lru.Purge()
|
|
||||||
if c.onEvictedCB != nil && len(c.evictedKeys) > 0 {
|
|
||||||
ks, vs = c.evictedKeys, c.evictedVals
|
|
||||||
c.initEvictBuffers()
|
|
||||||
}
|
|
||||||
c.lock.Unlock()
|
|
||||||
// invoke callback outside of critical section
|
|
||||||
if c.onEvictedCB != nil {
|
|
||||||
for i := 0; i < len(ks); i++ {
|
|
||||||
c.onEvictedCB(ks[i], vs[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
|
||||||
func (c *Cache) Add(key, value interface{}) (evicted bool) {
|
|
||||||
var k, v interface{}
|
|
||||||
c.lock.Lock()
|
|
||||||
evicted = c.lru.Add(key, value)
|
|
||||||
if c.onEvictedCB != nil && evicted {
|
|
||||||
k, v = c.evictedKeys[0], c.evictedVals[0]
|
|
||||||
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
|
|
||||||
}
|
|
||||||
c.lock.Unlock()
|
|
||||||
if c.onEvictedCB != nil && evicted {
|
|
||||||
c.onEvictedCB(k, v)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get looks up a key's value from the cache.
|
|
||||||
func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
|
|
||||||
c.lock.Lock()
|
|
||||||
value, ok = c.lru.Get(key)
|
|
||||||
c.lock.Unlock()
|
|
||||||
return value, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains checks if a key is in the cache, without updating the
|
|
||||||
// recent-ness or deleting it for being stale.
|
|
||||||
func (c *Cache) Contains(key interface{}) bool {
|
|
||||||
c.lock.RLock()
|
|
||||||
containKey := c.lru.Contains(key)
|
|
||||||
c.lock.RUnlock()
|
|
||||||
return containKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Peek returns the key value (or undefined if not found) without updating
|
|
||||||
// the "recently used"-ness of the key.
|
|
||||||
func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
|
|
||||||
c.lock.RLock()
|
|
||||||
value, ok = c.lru.Peek(key)
|
|
||||||
c.lock.RUnlock()
|
|
||||||
return value, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainsOrAdd checks if a key is in the cache without updating the
|
|
||||||
// recent-ness or deleting it for being stale, and if not, adds the value.
|
|
||||||
// Returns whether found and whether an eviction occurred.
|
|
||||||
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
|
|
||||||
var k, v interface{}
|
|
||||||
c.lock.Lock()
|
|
||||||
if c.lru.Contains(key) {
|
|
||||||
c.lock.Unlock()
|
|
||||||
return true, false
|
|
||||||
}
|
|
||||||
evicted = c.lru.Add(key, value)
|
|
||||||
if c.onEvictedCB != nil && evicted {
|
|
||||||
k, v = c.evictedKeys[0], c.evictedVals[0]
|
|
||||||
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
|
|
||||||
}
|
|
||||||
c.lock.Unlock()
|
|
||||||
if c.onEvictedCB != nil && evicted {
|
|
||||||
c.onEvictedCB(k, v)
|
|
||||||
}
|
|
||||||
return false, evicted
|
|
||||||
}
|
|
||||||
|
|
||||||
// PeekOrAdd checks if a key is in the cache without updating the
|
|
||||||
// recent-ness or deleting it for being stale, and if not, adds the value.
|
|
||||||
// Returns whether found and whether an eviction occurred.
|
|
||||||
func (c *Cache) PeekOrAdd(key, value interface{}) (previous interface{}, ok, evicted bool) {
|
|
||||||
var k, v interface{}
|
|
||||||
c.lock.Lock()
|
|
||||||
previous, ok = c.lru.Peek(key)
|
|
||||||
if ok {
|
|
||||||
c.lock.Unlock()
|
|
||||||
return previous, true, false
|
|
||||||
}
|
|
||||||
evicted = c.lru.Add(key, value)
|
|
||||||
if c.onEvictedCB != nil && evicted {
|
|
||||||
k, v = c.evictedKeys[0], c.evictedVals[0]
|
|
||||||
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
|
|
||||||
}
|
|
||||||
c.lock.Unlock()
|
|
||||||
if c.onEvictedCB != nil && evicted {
|
|
||||||
c.onEvictedCB(k, v)
|
|
||||||
}
|
|
||||||
return nil, false, evicted
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes the provided key from the cache.
|
|
||||||
func (c *Cache) Remove(key interface{}) (present bool) {
|
|
||||||
var k, v interface{}
|
|
||||||
c.lock.Lock()
|
|
||||||
present = c.lru.Remove(key)
|
|
||||||
if c.onEvictedCB != nil && present {
|
|
||||||
k, v = c.evictedKeys[0], c.evictedVals[0]
|
|
||||||
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
|
|
||||||
}
|
|
||||||
c.lock.Unlock()
|
|
||||||
if c.onEvictedCB != nil && present {
|
|
||||||
c.onEvictedCB(k, v)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resize changes the cache size.
|
|
||||||
func (c *Cache) Resize(size int) (evicted int) {
|
|
||||||
var ks, vs []interface{}
|
|
||||||
c.lock.Lock()
|
|
||||||
evicted = c.lru.Resize(size)
|
|
||||||
if c.onEvictedCB != nil && evicted > 0 {
|
|
||||||
ks, vs = c.evictedKeys, c.evictedVals
|
|
||||||
c.initEvictBuffers()
|
|
||||||
}
|
|
||||||
c.lock.Unlock()
|
|
||||||
if c.onEvictedCB != nil && evicted > 0 {
|
|
||||||
for i := 0; i < len(ks); i++ {
|
|
||||||
c.onEvictedCB(ks[i], vs[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return evicted
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveOldest removes the oldest item from the cache.
|
|
||||||
func (c *Cache) RemoveOldest() (key, value interface{}, ok bool) {
|
|
||||||
var k, v interface{}
|
|
||||||
c.lock.Lock()
|
|
||||||
key, value, ok = c.lru.RemoveOldest()
|
|
||||||
if c.onEvictedCB != nil && ok {
|
|
||||||
k, v = c.evictedKeys[0], c.evictedVals[0]
|
|
||||||
c.evictedKeys, c.evictedVals = c.evictedKeys[:0], c.evictedVals[:0]
|
|
||||||
}
|
|
||||||
c.lock.Unlock()
|
|
||||||
if c.onEvictedCB != nil && ok {
|
|
||||||
c.onEvictedCB(k, v)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOldest returns the oldest entry
|
|
||||||
func (c *Cache) GetOldest() (key, value interface{}, ok bool) {
|
|
||||||
c.lock.RLock()
|
|
||||||
key, value, ok = c.lru.GetOldest()
|
|
||||||
c.lock.RUnlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keys returns a slice of the keys in the cache, from oldest to newest.
|
|
||||||
func (c *Cache) Keys() []interface{} {
|
|
||||||
c.lock.RLock()
|
|
||||||
keys := c.lru.Keys()
|
|
||||||
c.lock.RUnlock()
|
|
||||||
return keys
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of items in the cache.
|
|
||||||
func (c *Cache) Len() int {
|
|
||||||
c.lock.RLock()
|
|
||||||
length := c.lru.Len()
|
|
||||||
c.lock.RUnlock()
|
|
||||||
return length
|
|
||||||
}
|
|
22
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
generated
vendored
22
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
generated
vendored
|
@ -25,7 +25,7 @@ type entry struct {
|
||||||
// NewLRU constructs an LRU of the given size
|
// NewLRU constructs an LRU of the given size
|
||||||
func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
|
func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
|
||||||
if size <= 0 {
|
if size <= 0 {
|
||||||
return nil, errors.New("must provide a positive size")
|
return nil, errors.New("Must provide a positive size")
|
||||||
}
|
}
|
||||||
c := &LRU{
|
c := &LRU{
|
||||||
size: size,
|
size: size,
|
||||||
|
@ -73,9 +73,6 @@ func (c *LRU) Add(key, value interface{}) (evicted bool) {
|
||||||
func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
|
func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
|
||||||
if ent, ok := c.items[key]; ok {
|
if ent, ok := c.items[key]; ok {
|
||||||
c.evictList.MoveToFront(ent)
|
c.evictList.MoveToFront(ent)
|
||||||
if ent.Value.(*entry) == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return ent.Value.(*entry).value, true
|
return ent.Value.(*entry).value, true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -109,7 +106,7 @@ func (c *LRU) Remove(key interface{}) (present bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveOldest removes the oldest item from the cache.
|
// RemoveOldest removes the oldest item from the cache.
|
||||||
func (c *LRU) RemoveOldest() (key, value interface{}, ok bool) {
|
func (c *LRU) RemoveOldest() (key interface{}, value interface{}, ok bool) {
|
||||||
ent := c.evictList.Back()
|
ent := c.evictList.Back()
|
||||||
if ent != nil {
|
if ent != nil {
|
||||||
c.removeElement(ent)
|
c.removeElement(ent)
|
||||||
|
@ -120,7 +117,7 @@ func (c *LRU) RemoveOldest() (key, value interface{}, ok bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOldest returns the oldest entry
|
// GetOldest returns the oldest entry
|
||||||
func (c *LRU) GetOldest() (key, value interface{}, ok bool) {
|
func (c *LRU) GetOldest() (key interface{}, value interface{}, ok bool) {
|
||||||
ent := c.evictList.Back()
|
ent := c.evictList.Back()
|
||||||
if ent != nil {
|
if ent != nil {
|
||||||
kv := ent.Value.(*entry)
|
kv := ent.Value.(*entry)
|
||||||
|
@ -145,19 +142,6 @@ func (c *LRU) Len() int {
|
||||||
return c.evictList.Len()
|
return c.evictList.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resize changes the cache size.
|
|
||||||
func (c *LRU) Resize(size int) (evicted int) {
|
|
||||||
diff := c.Len() - size
|
|
||||||
if diff < 0 {
|
|
||||||
diff = 0
|
|
||||||
}
|
|
||||||
for i := 0; i < diff; i++ {
|
|
||||||
c.removeOldest()
|
|
||||||
}
|
|
||||||
c.size = size
|
|
||||||
return diff
|
|
||||||
}
|
|
||||||
|
|
||||||
// removeOldest removes the oldest item from the cache.
|
// removeOldest removes the oldest item from the cache.
|
||||||
func (c *LRU) removeOldest() {
|
func (c *LRU) removeOldest() {
|
||||||
ent := c.evictList.Back()
|
ent := c.evictList.Back()
|
||||||
|
|
8
vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go
generated
vendored
8
vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go
generated
vendored
|
@ -1,4 +1,3 @@
|
||||||
// Package simplelru provides simple LRU implementation based on build-in container/list.
|
|
||||||
package simplelru
|
package simplelru
|
||||||
|
|
||||||
// LRUCache is the interface for simple LRU cache.
|
// LRUCache is the interface for simple LRU cache.
|
||||||
|
@ -11,7 +10,7 @@ type LRUCache interface {
|
||||||
// updates the "recently used"-ness of the key. #value, isFound
|
// updates the "recently used"-ness of the key. #value, isFound
|
||||||
Get(key interface{}) (value interface{}, ok bool)
|
Get(key interface{}) (value interface{}, ok bool)
|
||||||
|
|
||||||
// Checks if a key exists in cache without updating the recent-ness.
|
// Check if a key exsists in cache without updating the recent-ness.
|
||||||
Contains(key interface{}) (ok bool)
|
Contains(key interface{}) (ok bool)
|
||||||
|
|
||||||
// Returns key's value without updating the "recently used"-ness of the key.
|
// Returns key's value without updating the "recently used"-ness of the key.
|
||||||
|
@ -32,9 +31,6 @@ type LRUCache interface {
|
||||||
// Returns the number of items in the cache.
|
// Returns the number of items in the cache.
|
||||||
Len() int
|
Len() int
|
||||||
|
|
||||||
// Clears all cache entries.
|
// Clear all cache entries
|
||||||
Purge()
|
Purge()
|
||||||
|
|
||||||
// Resizes cache, returning number evicted
|
|
||||||
Resize(int) int
|
|
||||||
}
|
}
|
||||||
|
|
16
vendor/github.com/hashicorp/golang-lru/testing.go
generated
vendored
16
vendor/github.com/hashicorp/golang-lru/testing.go
generated
vendored
|
@ -1,16 +0,0 @@
|
||||||
package lru
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"math"
|
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getRand(tb testing.TB) int64 {
|
|
||||||
out, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64))
|
|
||||||
if err != nil {
|
|
||||||
tb.Fatal(err)
|
|
||||||
}
|
|
||||||
return out.Int64()
|
|
||||||
}
|
|
47
vendor/github.com/opencoff/go-sieve/.gitignore
generated
vendored
Normal file
47
vendor/github.com/opencoff/go-sieve/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
|
||||||
|
bin/*
|
||||||
|
.*.sw?
|
||||||
|
.idea
|
||||||
|
logs/*
|
||||||
|
|
||||||
|
# gg ignores
|
||||||
|
vendor/src/*
|
||||||
|
vendor/pkg/*
|
||||||
|
servers.iml
|
||||||
|
*.DS_Store
|
||||||
|
|
||||||
|
# vagrant ignores
|
||||||
|
tools/vagrant/.vagrant
|
||||||
|
tools/vagrant/adsrv-conf/.frontend
|
||||||
|
tools/vagrant/adsrv-conf/.bidder
|
||||||
|
tools/vagrant/adsrv-conf/.transcoder
|
||||||
|
tools/vagrant/redis-cluster-conf/7777/nodes.conf
|
||||||
|
tools/vagrant/redis-cluster-conf/7778/nodes.conf
|
||||||
|
tools/vagrant/redis-cluster-conf/7779/nodes.conf
|
||||||
|
*.aof
|
||||||
|
*.rdb
|
||||||
|
*.deb
|
24
vendor/github.com/opencoff/go-sieve/LICENSE
generated
vendored
Normal file
24
vendor/github.com/opencoff/go-sieve/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2024, Sudhi Herle
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
9
vendor/github.com/opencoff/go-sieve/README.md
generated
vendored
Normal file
9
vendor/github.com/opencoff/go-sieve/README.md
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# go-sieve - SIEVE is simpler than LRU
|
||||||
|
|
||||||
|
## What is it?
|
||||||
|
`go-sieve` is golang implementation of the [SIEVE](https://yazhuozhang.com/assets/pdf/nsdi24-sieve.pdf)
|
||||||
|
cache eviction algorithm.
|
||||||
|
|
||||||
|
This implementation closely follows the paper's pseudo-code - but
|
||||||
|
uses golang generics to provide an ergonomic interface.
|
||||||
|
|
334
vendor/github.com/opencoff/go-sieve/sieve.go
generated
vendored
Normal file
334
vendor/github.com/opencoff/go-sieve/sieve.go
generated
vendored
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
// sieve.go - SIEVE - a simple and efficient cache
|
||||||
|
//
|
||||||
|
// (c) 2024 Sudhi Herle <sudhi@herle.net>
|
||||||
|
//
|
||||||
|
// Copyright 2024- Sudhi Herle <sw-at-herle-dot-net>
|
||||||
|
// License: BSD-2-Clause
|
||||||
|
//
|
||||||
|
// If you need a commercial license for this work, please contact
|
||||||
|
// the author.
|
||||||
|
//
|
||||||
|
// This software does not come with any express or implied
|
||||||
|
// warranty; it is provided "as is". No claim is made to its
|
||||||
|
// suitability for any purpose.
|
||||||
|
|
||||||
|
// This is golang implementation of the SIEVE cache eviction algorithm
|
||||||
|
// The original paper is:
|
||||||
|
// https://yazhuozhang.com/assets/pdf/nsdi24-sieve.pdf
|
||||||
|
//
|
||||||
|
// This implementation closely follows the paper - but uses golang generics
|
||||||
|
// for an ergonomic interface.
|
||||||
|
|
||||||
|
// Package sieve implements the SIEVE cache eviction algorithm.
|
||||||
|
// SIEVE stands in contrast to other eviction algorithms like LRU, 2Q, ARC
|
||||||
|
// with its simplicity. The original paper is in:
|
||||||
|
// https://yazhuozhang.com/assets/pdf/nsdi24-sieve.pdf
|
||||||
|
//
|
||||||
|
// SIEVE is built on a FIFO queue - with an extra pointer (called "hand") in
|
||||||
|
// the paper. This "hand" plays a crucial role in determining who to evict
|
||||||
|
// next.
|
||||||
|
package sieve
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
// node contains the <key, val> tuple as a node in a linked list.
|
||||||
|
type node[K comparable, V any] struct {
|
||||||
|
sync.Mutex
|
||||||
|
key K
|
||||||
|
val V
|
||||||
|
visited atomic.Bool
|
||||||
|
next *node[K, V]
|
||||||
|
prev *node[K, V]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sieve represents a cache mapping the key of type 'K' with
|
||||||
|
// a value of type 'V'. The type 'K' must implement the
|
||||||
|
// comparable trait. An instance of Sieve has a fixed max capacity;
|
||||||
|
// new additions to the cache beyond the capacity will cause cache
|
||||||
|
// eviction of other entries - as determined by the SIEVE algorithm.
|
||||||
|
type Sieve[K comparable, V any] struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
cache *syncMap[K, *node[K, V]]
|
||||||
|
head *node[K, V]
|
||||||
|
tail *node[K, V]
|
||||||
|
hand *node[K, V]
|
||||||
|
size int
|
||||||
|
capacity int
|
||||||
|
|
||||||
|
pool *syncPool[node[K, V]]
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new cache of size 'capacity' mapping key 'K' to value 'V'
|
||||||
|
func New[K comparable, V any](capacity int) *Sieve[K, V] {
|
||||||
|
s := &Sieve[K, V]{
|
||||||
|
cache: newSyncMap[K, *node[K, V]](),
|
||||||
|
capacity: capacity,
|
||||||
|
pool: newSyncPool[node[K, V]](),
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get fetches the value for a given key in the cache.
|
||||||
|
// It returns true if the key is in the cache, false otherwise.
|
||||||
|
// The zero value for 'V' is returned when key is not in the cache.
|
||||||
|
func (s *Sieve[K, V]) Get(key K) (V, bool) {
|
||||||
|
|
||||||
|
if v, ok := s.cache.Get(key); ok {
|
||||||
|
v.visited.Store(true)
|
||||||
|
return v.val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
var x V
|
||||||
|
return x, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a new element to the cache or overwrite one if it exists
|
||||||
|
// Return true if we replaced, false otherwise
|
||||||
|
func (s *Sieve[K, V]) Add(key K, val V) bool {
|
||||||
|
|
||||||
|
if v, ok := s.cache.Get(key); ok {
|
||||||
|
v.visited.Store(true)
|
||||||
|
v.Lock()
|
||||||
|
v.val = val
|
||||||
|
v.Unlock()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
s.add(key, val)
|
||||||
|
s.mu.Unlock()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Probe adds <key, val> if not present in the cache.
|
||||||
|
// Returns:
|
||||||
|
//
|
||||||
|
// <cached-val, true> when key is present in the cache
|
||||||
|
// <val, false> when key is not present in the cache
|
||||||
|
func (s *Sieve[K, V]) Probe(key K, val V) (V, bool) {
|
||||||
|
|
||||||
|
if v, ok := s.cache.Get(key); ok {
|
||||||
|
v.visited.Store(true)
|
||||||
|
return v.val, true
|
||||||
|
}
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
s.add(key, val)
|
||||||
|
s.mu.Unlock()
|
||||||
|
return val, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the named key from the cache
|
||||||
|
// It returns true if the item was in the cache and false otherwise
|
||||||
|
func (s *Sieve[K, V]) Delete(key K) bool {
|
||||||
|
|
||||||
|
if v, ok := s.cache.Del(key); ok {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.remove(v)
|
||||||
|
s.mu.Unlock()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge resets the cache
|
||||||
|
func (s *Sieve[K, V]) Purge() {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.cache = newSyncMap[K, *node[K, V]]()
|
||||||
|
s.head = nil
|
||||||
|
s.tail = nil
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the current cache utilization
|
||||||
|
func (s *Sieve[K, V]) Len() int {
|
||||||
|
return s.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cap returns the max cache capacity
|
||||||
|
func (s *Sieve[K, V]) Cap() int {
|
||||||
|
return s.capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a string description of the sieve cache
|
||||||
|
func (s *Sieve[K, V]) String() string {
|
||||||
|
s.mu.Lock()
|
||||||
|
m := s.desc()
|
||||||
|
s.mu.Unlock()
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump dumps all the cache contents as a newline delimited
|
||||||
|
// string.
|
||||||
|
func (s *Sieve[K, V]) Dump() string {
|
||||||
|
var b strings.Builder
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
b.WriteString(s.desc())
|
||||||
|
b.WriteRune('\n')
|
||||||
|
for n := s.head; n != nil; n = n.next {
|
||||||
|
h := " "
|
||||||
|
if n == s.hand {
|
||||||
|
h = ">>"
|
||||||
|
}
|
||||||
|
b.WriteString(fmt.Sprintf("%svisited=%v, key=%v, val=%v\n", h, n.visited.Load(), n.key, n.val))
|
||||||
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- internal methods --
|
||||||
|
|
||||||
|
// add a new tuple to the cache and evict as necessary
|
||||||
|
// caller must hold lock.
|
||||||
|
func (s *Sieve[K, V]) add(key K, val V) {
|
||||||
|
// cache miss; we evict and fnd a new node
|
||||||
|
if s.size == s.capacity {
|
||||||
|
s.evict()
|
||||||
|
}
|
||||||
|
|
||||||
|
n := s.newNode(key, val)
|
||||||
|
|
||||||
|
// Eviction is guaranteed to remove one node; so this should never happen.
|
||||||
|
if n == nil {
|
||||||
|
msg := fmt.Sprintf("%T: add <%v>: objpool empty after eviction", s, key)
|
||||||
|
panic(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.cache.Put(key, n)
|
||||||
|
|
||||||
|
// insert at the head of the list
|
||||||
|
n.next = s.head
|
||||||
|
n.prev = nil
|
||||||
|
if s.head != nil {
|
||||||
|
s.head.prev = n
|
||||||
|
}
|
||||||
|
s.head = n
|
||||||
|
if s.tail == nil {
|
||||||
|
s.tail = n
|
||||||
|
}
|
||||||
|
|
||||||
|
s.size += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// evict an item from the cache.
|
||||||
|
// NB: Caller must hold the lock
|
||||||
|
func (s *Sieve[K, V]) evict() {
|
||||||
|
hand := s.hand
|
||||||
|
if hand == nil {
|
||||||
|
hand = s.tail
|
||||||
|
}
|
||||||
|
|
||||||
|
for hand != nil {
|
||||||
|
if !hand.visited.Load() {
|
||||||
|
s.cache.Del(hand.key)
|
||||||
|
s.remove(hand)
|
||||||
|
s.hand = hand.prev
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hand.visited.Store(false)
|
||||||
|
hand = hand.prev
|
||||||
|
// wrap around and start again
|
||||||
|
if hand == nil {
|
||||||
|
hand = s.tail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.hand = hand
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sieve[K, V]) remove(n *node[K, V]) {
|
||||||
|
s.size -= 1
|
||||||
|
|
||||||
|
// remove node from list
|
||||||
|
if n.prev != nil {
|
||||||
|
n.prev.next = n.next
|
||||||
|
} else {
|
||||||
|
s.head = n.next
|
||||||
|
}
|
||||||
|
if n.next != nil {
|
||||||
|
n.next.prev = n.prev
|
||||||
|
} else {
|
||||||
|
s.tail = n.prev
|
||||||
|
}
|
||||||
|
|
||||||
|
s.pool.Put(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sieve[K, V]) newNode(key K, val V) *node[K, V] {
|
||||||
|
n := s.pool.Get()
|
||||||
|
n.key, n.val = key, val
|
||||||
|
n.next, n.prev = nil, nil
|
||||||
|
n.visited.Store(false)
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// desc describes the properties of the sieve
|
||||||
|
func (s *Sieve[K, V]) desc() string {
|
||||||
|
m := fmt.Sprintf("cache<%T>: size %d, cap %d, head=%p, tail=%p, hand=%p",
|
||||||
|
s, s.size, s.capacity, s.head, s.tail, s.hand)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic sync.Pool
|
||||||
|
type syncPool[T any] struct {
|
||||||
|
pool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSyncPool[T any]() *syncPool[T] {
|
||||||
|
p := &syncPool[T]{
|
||||||
|
pool: sync.Pool{
|
||||||
|
New: func() any { return new(T) },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *syncPool[T]) Get() *T {
|
||||||
|
p := s.pool.Get()
|
||||||
|
return p.(*T)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *syncPool[T]) Put(n *T) {
|
||||||
|
s.pool.Put(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generic sync.Map
|
||||||
|
type syncMap[K comparable, V any] struct {
|
||||||
|
m sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSyncMap[K comparable, V any]() *syncMap[K, V] {
|
||||||
|
m := syncMap[K, V]{}
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *syncMap[K, V]) Get(key K) (V, bool) {
|
||||||
|
v, ok := m.m.Load(key)
|
||||||
|
if ok {
|
||||||
|
return v.(V), true
|
||||||
|
}
|
||||||
|
|
||||||
|
var z V
|
||||||
|
return z, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *syncMap[K, V]) Put(key K, val V) {
|
||||||
|
m.m.Store(key, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *syncMap[K, V]) Del(key K) (V, bool) {
|
||||||
|
x, ok := m.m.LoadAndDelete(key)
|
||||||
|
if ok {
|
||||||
|
return x.(V), true
|
||||||
|
}
|
||||||
|
|
||||||
|
var z V
|
||||||
|
return z, false
|
||||||
|
}
|
8
vendor/modules.txt
vendored
8
vendor/modules.txt
vendored
|
@ -34,9 +34,8 @@ github.com/hashicorp/go-immutable-radix
|
||||||
# github.com/hashicorp/go-syslog v1.0.0
|
# github.com/hashicorp/go-syslog v1.0.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/hashicorp/go-syslog
|
github.com/hashicorp/go-syslog
|
||||||
# github.com/hashicorp/golang-lru v1.0.2
|
# github.com/hashicorp/golang-lru v0.5.0
|
||||||
## explicit; go 1.12
|
## explicit
|
||||||
github.com/hashicorp/golang-lru
|
|
||||||
github.com/hashicorp/golang-lru/simplelru
|
github.com/hashicorp/golang-lru/simplelru
|
||||||
# github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb
|
# github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
|
@ -87,6 +86,9 @@ github.com/onsi/ginkgo/v2/internal/interrupt_handler
|
||||||
github.com/onsi/ginkgo/v2/internal/parallel_support
|
github.com/onsi/ginkgo/v2/internal/parallel_support
|
||||||
github.com/onsi/ginkgo/v2/reporters
|
github.com/onsi/ginkgo/v2/reporters
|
||||||
github.com/onsi/ginkgo/v2/types
|
github.com/onsi/ginkgo/v2/types
|
||||||
|
# github.com/opencoff/go-sieve v0.2.1
|
||||||
|
## explicit; go 1.21.1
|
||||||
|
github.com/opencoff/go-sieve
|
||||||
# github.com/pkg/errors v0.9.1
|
# github.com/pkg/errors v0.9.1
|
||||||
## explicit
|
## explicit
|
||||||
github.com/pkg/errors
|
github.com/pkg/errors
|
||||||
|
|
Loading…
Add table
Reference in a new issue