mirror of
https://github.com/kemko/reproxy.git
synced 2026-01-06 10:15:47 +03:00
Bump the go-modules-updates group with 2 updates
Bumps the go-modules-updates group with 2 updates: [github.com/didip/tollbooth/v7](https://github.com/didip/tollbooth) and [golang.org/x/crypto](https://github.com/golang/crypto). Updates `github.com/didip/tollbooth/v7` from 7.0.1 to 7.0.2 - [Commits](https://github.com/didip/tollbooth/compare/v7.0.1...v7.0.2) Updates `golang.org/x/crypto` from 0.23.0 to 0.24.0 - [Commits](https://github.com/golang/crypto/compare/v0.23.0...v0.24.0) --- updated-dependencies: - dependency-name: github.com/didip/tollbooth/v7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-modules-updates - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-modules-updates ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
6
vendor/github.com/didip/tollbooth/v7/.golangci.yml
generated
vendored
6
vendor/github.com/didip/tollbooth/v7/.golangci.yml
generated
vendored
@@ -1,21 +1,17 @@
|
||||
linters:
|
||||
enable:
|
||||
- megacheck
|
||||
- revive
|
||||
- govet
|
||||
- unconvert
|
||||
- megacheck
|
||||
- structcheck
|
||||
- gas
|
||||
- gocyclo
|
||||
- dupl
|
||||
- misspell
|
||||
- unparam
|
||||
- varcheck
|
||||
- deadcode
|
||||
- unused
|
||||
- typecheck
|
||||
- ineffassign
|
||||
- varcheck
|
||||
- stylecheck
|
||||
- gochecknoinits
|
||||
- exportloopref
|
||||
|
||||
2
vendor/github.com/didip/tollbooth/v7/README.md
generated
vendored
2
vendor/github.com/didip/tollbooth/v7/README.md
generated
vendored
@@ -171,6 +171,8 @@ Sometimes, other frameworks require a little bit of shim to use Tollbooth. These
|
||||
|
||||
## My other Go libraries
|
||||
|
||||
* [ErrStack](https://github.com/didip/errstack): A small library to combine errors and also display filename and line number.
|
||||
|
||||
* [Stopwatch](https://github.com/didip/stopwatch): A small library to measure latency of things. Useful if you want to report latency data to Graphite.
|
||||
|
||||
* [LaborUnion](https://github.com/didip/laborunion): A dynamic worker pool library.
|
||||
|
||||
34
vendor/github.com/didip/tollbooth/v7/limiter/limiter.go
generated
vendored
34
vendor/github.com/didip/tollbooth/v7/limiter/limiter.go
generated
vendored
@@ -6,7 +6,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
cache "github.com/go-pkgz/expirable-cache"
|
||||
cache "github.com/go-pkgz/expirable-cache/v3"
|
||||
|
||||
"github.com/didip/tollbooth/v7/internal/time/rate"
|
||||
)
|
||||
@@ -36,9 +36,9 @@ func New(generalExpirableOptions *ExpirableOptions) *Limiter {
|
||||
lmt.generalExpirableOptions.DefaultExpirationTTL = 87600 * time.Hour
|
||||
}
|
||||
|
||||
lmt.tokenBuckets, _ = cache.NewCache(cache.TTL(lmt.generalExpirableOptions.DefaultExpirationTTL))
|
||||
lmt.tokenBuckets = cache.NewCache[string, *rate.Limiter]().WithTTL(lmt.generalExpirableOptions.DefaultExpirationTTL)
|
||||
|
||||
lmt.basicAuthUsers, _ = cache.NewCache(cache.TTL(lmt.generalExpirableOptions.DefaultExpirationTTL))
|
||||
lmt.basicAuthUsers = cache.NewCache[string, bool]().WithTTL(lmt.generalExpirableOptions.DefaultExpirationTTL)
|
||||
|
||||
return lmt
|
||||
}
|
||||
@@ -81,17 +81,17 @@ type Limiter struct {
|
||||
generalExpirableOptions *ExpirableOptions
|
||||
|
||||
// List of basic auth usernames to limit.
|
||||
basicAuthUsers cache.Cache
|
||||
basicAuthUsers cache.Cache[string, bool]
|
||||
|
||||
// Map of HTTP headers to limit.
|
||||
// Empty means skip headers checking.
|
||||
headers map[string]cache.Cache
|
||||
headers map[string]cache.Cache[string, bool]
|
||||
|
||||
// Map of Context values to limit.
|
||||
contextValues map[string]cache.Cache
|
||||
contextValues map[string]cache.Cache[string, bool]
|
||||
|
||||
// Map of limiters with TTL
|
||||
tokenBuckets cache.Cache
|
||||
tokenBuckets cache.Cache[string, *rate.Limiter]
|
||||
|
||||
// Ignore URL on the rate limiter keys
|
||||
ignoreURL bool
|
||||
@@ -261,9 +261,9 @@ func (l *Limiter) SetOnLimitReached(fn func(w http.ResponseWriter, r *http.Reque
|
||||
// ExecOnLimitReached is thread-safe way of executing after-rejection function when limit is reached.
|
||||
func (l *Limiter) ExecOnLimitReached(w http.ResponseWriter, r *http.Request) {
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
|
||||
fn := l.onLimitReached
|
||||
l.RUnlock()
|
||||
|
||||
if fn != nil {
|
||||
fn(w, r)
|
||||
}
|
||||
@@ -383,7 +383,7 @@ func (l *Limiter) DeleteExpiredTokenBuckets() {
|
||||
// SetHeaders is thread-safe way of setting map of HTTP headers to limit.
|
||||
func (l *Limiter) SetHeaders(headers map[string][]string) *Limiter {
|
||||
if l.headers == nil {
|
||||
l.headers = make(map[string]cache.Cache)
|
||||
l.headers = make(map[string]cache.Cache[string, bool])
|
||||
}
|
||||
|
||||
for header, entries := range headers {
|
||||
@@ -419,7 +419,7 @@ func (l *Limiter) SetHeader(header string, entries []string) *Limiter {
|
||||
}
|
||||
|
||||
if !found {
|
||||
existing, _ = cache.NewCache(cache.TTL(ttl))
|
||||
existing = cache.NewCache[string, bool]().WithTTL(ttl)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
@@ -450,7 +450,7 @@ func (l *Limiter) RemoveHeader(header string) *Limiter {
|
||||
}
|
||||
|
||||
l.Lock()
|
||||
l.headers[header], _ = cache.NewCache(cache.TTL(ttl))
|
||||
l.headers[header] = cache.NewCache[string, bool]().WithTTL(ttl)
|
||||
l.Unlock()
|
||||
|
||||
return l
|
||||
@@ -476,7 +476,7 @@ func (l *Limiter) RemoveHeaderEntries(header string, entriesForRemoval []string)
|
||||
// SetContextValues is thread-safe way of setting map of HTTP headers to limit.
|
||||
func (l *Limiter) SetContextValues(contextValues map[string][]string) *Limiter {
|
||||
if l.contextValues == nil {
|
||||
l.contextValues = make(map[string]cache.Cache)
|
||||
l.contextValues = make(map[string]cache.Cache[string, bool])
|
||||
}
|
||||
|
||||
for contextValue, entries := range contextValues {
|
||||
@@ -512,7 +512,7 @@ func (l *Limiter) SetContextValue(contextValue string, entries []string) *Limite
|
||||
}
|
||||
|
||||
if !found {
|
||||
existing, _ = cache.NewCache(cache.TTL(ttl))
|
||||
existing = cache.NewCache[string, bool]().WithTTL(ttl)
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
@@ -543,7 +543,7 @@ func (l *Limiter) RemoveContextValue(contextValue string) *Limiter {
|
||||
}
|
||||
|
||||
l.Lock()
|
||||
l.contextValues[contextValue], _ = cache.NewCache(cache.TTL(ttl))
|
||||
l.contextValues[contextValue] = cache.NewCache[string, bool]().WithTTL(ttl)
|
||||
l.Unlock()
|
||||
|
||||
return l
|
||||
@@ -585,7 +585,7 @@ func (l *Limiter) limitReachedWithTokenBucketTTL(key string, tokenBucketTTL time
|
||||
return false
|
||||
}
|
||||
|
||||
return !expiringMap.(*rate.Limiter).Allow()
|
||||
return !expiringMap.Allow()
|
||||
}
|
||||
|
||||
// LimitReached returns a bool indicating if the Bucket identified by key ran out of tokens.
|
||||
@@ -606,5 +606,5 @@ func (l *Limiter) Tokens(key string) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
return int(expiringMap.(*rate.Limiter).TokensAt(time.Now()))
|
||||
return int(expiringMap.TokensAt(time.Now()))
|
||||
}
|
||||
|
||||
15
vendor/github.com/go-pkgz/expirable-cache/.gitignore
generated
vendored
15
vendor/github.com/go-pkgz/expirable-cache/.gitignore
generated
vendored
@@ -1,15 +0,0 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
58
vendor/github.com/go-pkgz/expirable-cache/.golangci.yml
generated
vendored
58
vendor/github.com/go-pkgz/expirable-cache/.golangci.yml
generated
vendored
@@ -1,58 +0,0 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
gocyclo:
|
||||
min-complexity: 15
|
||||
maligned:
|
||||
suggest-new: true
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 2
|
||||
misspell:
|
||||
locale: US
|
||||
lll:
|
||||
line-length: 140
|
||||
gocritic:
|
||||
enabled-tags:
|
||||
- performance
|
||||
- style
|
||||
- experimental
|
||||
disabled-checks:
|
||||
- wrapperFunc
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- megacheck
|
||||
- revive
|
||||
- govet
|
||||
- unconvert
|
||||
- megacheck
|
||||
- structcheck
|
||||
- gas
|
||||
- gocyclo
|
||||
- dupl
|
||||
- misspell
|
||||
- unparam
|
||||
- varcheck
|
||||
- deadcode
|
||||
- typecheck
|
||||
- ineffassign
|
||||
- varcheck
|
||||
- stylecheck
|
||||
- gochecknoinits
|
||||
- exportloopref
|
||||
- gocritic
|
||||
- nakedret
|
||||
- gosimple
|
||||
- prealloc
|
||||
fast: false
|
||||
disable-all: true
|
||||
|
||||
run:
|
||||
output:
|
||||
format: tab
|
||||
skip-dirs:
|
||||
- vendor
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
70
vendor/github.com/go-pkgz/expirable-cache/README.md
generated
vendored
70
vendor/github.com/go-pkgz/expirable-cache/README.md
generated
vendored
@@ -1,70 +0,0 @@
|
||||
# expirable-cache
|
||||
|
||||
[](https://github.com/go-pkgz/expirable-cache/actions)
|
||||
[](https://coveralls.io/github/go-pkgz/expirable-cache?branch=master)
|
||||
[](https://pkg.go.dev/github.com/go-pkgz/expirable-cache?tab=doc)
|
||||
|
||||
Package cache implements expirable cache.
|
||||
|
||||
- Support LRC, LRU and TTL-based eviction.
|
||||
- Package is thread-safe and doesn't spawn any goroutines.
|
||||
- On every Set() call, cache deletes single oldest entry in case it's expired.
|
||||
- In case MaxSize is set, cache deletes the oldest entry disregarding its expiration date to maintain the size,
|
||||
either using LRC or LRU eviction.
|
||||
- In case of default TTL (10 years) and default MaxSize (0, unlimited) the cache will be truly unlimited
|
||||
and will never delete entries from itself automatically.
|
||||
|
||||
**Important**: only reliable way of not having expired entries stuck in a cache is to
|
||||
run cache.DeleteExpired periodically using [time.Ticker](https://golang.org/pkg/time/#Ticker),
|
||||
advisable period is 1/2 of TTL.
|
||||
|
||||
This cache is heavily inspired by [hashicorp/golang-lru](https://github.com/hashicorp/golang-lru) _simplelru_ implementation.
|
||||
|
||||
### Usage example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-pkgz/expirable-cache/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// make cache with short TTL and 3 max keys
|
||||
c := cache.NewCache[string, string]().WithMaxKeys(3).WithTTL(time.Millisecond * 10)
|
||||
|
||||
// set value under key1.
|
||||
// with 0 ttl (last parameter) will use cache-wide setting instead (10ms).
|
||||
c.Set("key1", "val1", 0)
|
||||
|
||||
// get value under key1
|
||||
r, ok := c.Get("key1")
|
||||
|
||||
// check for OK value, because otherwise return would be nil and
|
||||
// type conversion will panic
|
||||
if ok {
|
||||
rstr := r.(string) // convert cached value from interface{} to real type
|
||||
fmt.Printf("value before expiration is found: %v, value: %v\n", ok, rstr)
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 11)
|
||||
|
||||
// get value under key1 after key expiration
|
||||
r, ok = c.Get("key1")
|
||||
// don't convert to string as with ok == false value would be nil
|
||||
fmt.Printf("value after expiration is found: %v, value: %v\n", ok, r)
|
||||
|
||||
// set value under key2, would evict old entry because it is already expired.
|
||||
// ttl (last parameter) overrides cache-wide ttl.
|
||||
c.Set("key2", "val2", time.Minute*5)
|
||||
|
||||
fmt.Printf("%+v\n", c)
|
||||
// Output:
|
||||
// value before expiration is found: true, value: val1
|
||||
// value after expiration is found: false, value: <nil>
|
||||
// Size: 1, Stats: {Hits:1 Misses:1 Added:2 Evicted:1} (50.0%)
|
||||
}
|
||||
```
|
||||
272
vendor/github.com/go-pkgz/expirable-cache/cache.go
generated
vendored
272
vendor/github.com/go-pkgz/expirable-cache/cache.go
generated
vendored
@@ -1,272 +0,0 @@
|
||||
// Package cache implements Cache similar to hashicorp/golang-lru
|
||||
//
|
||||
// Support LRC, LRU and TTL-based eviction.
|
||||
// Package is thread-safe and doesn't spawn any goroutines.
|
||||
// On every Set() call, cache deletes single oldest entry in case it's expired.
|
||||
// In case MaxSize is set, cache deletes the oldest entry disregarding its expiration date to maintain the size,
|
||||
// either using LRC or LRU eviction.
|
||||
// In case of default TTL (10 years) and default MaxSize (0, unlimited) the cache will be truly unlimited
|
||||
// and will never delete entries from itself automatically.
|
||||
//
|
||||
// Important: only reliable way of not having expired entries stuck in a cache is to
|
||||
// run cache.DeleteExpired periodically using time.Ticker, advisable period is 1/2 of TTL.
|
||||
package cache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Cache defines cache interface
|
||||
type Cache interface {
|
||||
fmt.Stringer
|
||||
Set(key string, value interface{}, ttl time.Duration)
|
||||
Get(key string) (interface{}, bool)
|
||||
Peek(key string) (interface{}, bool)
|
||||
Keys() []string
|
||||
Len() int
|
||||
Invalidate(key string)
|
||||
InvalidateFn(fn func(key string) bool)
|
||||
RemoveOldest()
|
||||
DeleteExpired()
|
||||
Purge()
|
||||
Stat() Stats
|
||||
}
|
||||
|
||||
// Stats provides statistics for cache
|
||||
type Stats struct {
|
||||
Hits, Misses int // cache effectiveness
|
||||
Added, Evicted int // number of added and evicted records
|
||||
}
|
||||
|
||||
// cacheImpl provides Cache interface implementation.
|
||||
type cacheImpl struct {
|
||||
ttl time.Duration
|
||||
maxKeys int
|
||||
isLRU bool
|
||||
onEvicted func(key string, value interface{})
|
||||
|
||||
sync.Mutex
|
||||
stat Stats
|
||||
items map[string]*list.Element
|
||||
evictList *list.List
|
||||
}
|
||||
|
||||
// noEvictionTTL - very long ttl to prevent eviction
|
||||
const noEvictionTTL = time.Hour * 24 * 365 * 10
|
||||
|
||||
// NewCache returns a new Cache.
|
||||
// Default MaxKeys is unlimited (0).
|
||||
// Default TTL is 10 years, sane value for expirable cache is 5 minutes.
|
||||
// Default eviction mode is LRC, appropriate option allow to change it to LRU.
|
||||
func NewCache(options ...Option) (Cache, error) {
|
||||
res := cacheImpl{
|
||||
items: map[string]*list.Element{},
|
||||
evictList: list.New(),
|
||||
ttl: noEvictionTTL,
|
||||
maxKeys: 0,
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
if err := opt(&res); err != nil {
|
||||
return nil, fmt.Errorf("failed to set cache option: %w", err)
|
||||
}
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// Set key, ttl of 0 would use cache-wide TTL
|
||||
func (c *cacheImpl) Set(key string, value interface{}, ttl time.Duration) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
now := time.Now()
|
||||
if ttl == 0 {
|
||||
ttl = c.ttl
|
||||
}
|
||||
|
||||
// Check for existing item
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.evictList.MoveToFront(ent)
|
||||
ent.Value.(*cacheItem).value = value
|
||||
ent.Value.(*cacheItem).expiresAt = now.Add(ttl)
|
||||
return
|
||||
}
|
||||
|
||||
// Add new item
|
||||
ent := &cacheItem{key: key, value: value, expiresAt: now.Add(ttl)}
|
||||
entry := c.evictList.PushFront(ent)
|
||||
c.items[key] = entry
|
||||
c.stat.Added++
|
||||
|
||||
// Remove oldest entry if it is expired, only in case of non-default TTL.
|
||||
if c.ttl != noEvictionTTL || ttl != noEvictionTTL {
|
||||
c.removeOldestIfExpired()
|
||||
}
|
||||
|
||||
// Verify size not exceeded
|
||||
if c.maxKeys > 0 && len(c.items) > c.maxKeys {
|
||||
c.removeOldest()
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the key value if it's not expired
|
||||
func (c *cacheImpl) Get(key string) (interface{}, bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if ent, ok := c.items[key]; ok {
|
||||
// Expired item check
|
||||
if time.Now().After(ent.Value.(*cacheItem).expiresAt) {
|
||||
c.stat.Misses++
|
||||
return nil, false
|
||||
}
|
||||
if c.isLRU {
|
||||
c.evictList.MoveToFront(ent)
|
||||
}
|
||||
c.stat.Hits++
|
||||
return ent.Value.(*cacheItem).value, true
|
||||
}
|
||||
c.stat.Misses++
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key.
|
||||
// Works exactly the same as Get in case of LRC mode (default one).
|
||||
func (c *cacheImpl) Peek(key string) (interface{}, bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if ent, ok := c.items[key]; ok {
|
||||
// Expired item check
|
||||
if time.Now().After(ent.Value.(*cacheItem).expiresAt) {
|
||||
c.stat.Misses++
|
||||
return nil, false
|
||||
}
|
||||
c.stat.Hits++
|
||||
return ent.Value.(*cacheItem).value, true
|
||||
}
|
||||
c.stat.Misses++
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Keys returns a slice of the keys in the cache, from oldest to newest.
|
||||
func (c *cacheImpl) Keys() []string {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
return c.keys()
|
||||
}
|
||||
|
||||
// Len return count of items in cache, including expired
|
||||
func (c *cacheImpl) Len() int {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
return c.evictList.Len()
|
||||
}
|
||||
|
||||
// Invalidate key (item) from the cache
|
||||
func (c *cacheImpl) Invalidate(key string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidateFn deletes multiple keys if predicate is true
|
||||
func (c *cacheImpl) InvalidateFn(fn func(key string) bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
for key, ent := range c.items {
|
||||
if fn(key) {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveOldest remove oldest element in the cache
|
||||
func (c *cacheImpl) RemoveOldest() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.removeOldest()
|
||||
}
|
||||
|
||||
// DeleteExpired clears cache of expired items
|
||||
func (c *cacheImpl) DeleteExpired() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
for _, key := range c.keys() {
|
||||
if time.Now().After(c.items[key].Value.(*cacheItem).expiresAt) {
|
||||
c.removeElement(c.items[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Purge clears the cache completely.
|
||||
func (c *cacheImpl) Purge() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
for k, v := range c.items {
|
||||
delete(c.items, k)
|
||||
c.stat.Evicted++
|
||||
if c.onEvicted != nil {
|
||||
c.onEvicted(k, v.Value.(*cacheItem).value)
|
||||
}
|
||||
}
|
||||
c.evictList.Init()
|
||||
}
|
||||
|
||||
// Stat gets the current stats for cache
|
||||
func (c *cacheImpl) Stat() Stats {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
return c.stat
|
||||
}
|
||||
|
||||
func (c *cacheImpl) String() string {
|
||||
stats := c.Stat()
|
||||
size := c.Len()
|
||||
return fmt.Sprintf("Size: %d, Stats: %+v (%0.1f%%)", size, stats, 100*float64(stats.Hits)/float64(stats.Hits+stats.Misses))
|
||||
}
|
||||
|
||||
// Keys returns a slice of the keys in the cache, from oldest to newest. Has to be called with lock!
|
||||
func (c *cacheImpl) keys() []string {
|
||||
keys := make([]string, 0, len(c.items))
|
||||
for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
|
||||
keys = append(keys, ent.Value.(*cacheItem).key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// removeOldest removes the oldest item from the cache. Has to be called with lock!
|
||||
func (c *cacheImpl) removeOldest() {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// removeOldest removes the oldest item from the cache in case it's already expired. Has to be called with lock!
|
||||
func (c *cacheImpl) removeOldestIfExpired() {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil && time.Now().After(ent.Value.(*cacheItem).expiresAt) {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// removeElement is used to remove a given list element from the cache. Has to be called with lock!
|
||||
func (c *cacheImpl) removeElement(e *list.Element) {
|
||||
c.evictList.Remove(e)
|
||||
kv := e.Value.(*cacheItem)
|
||||
delete(c.items, kv.key)
|
||||
c.stat.Evicted++
|
||||
if c.onEvicted != nil {
|
||||
c.onEvicted(kv.key, kv.value)
|
||||
}
|
||||
}
|
||||
|
||||
// cacheItem is used to hold a value in the evictList
|
||||
type cacheItem struct {
|
||||
expiresAt time.Time
|
||||
key string
|
||||
value interface{}
|
||||
}
|
||||
40
vendor/github.com/go-pkgz/expirable-cache/options.go
generated
vendored
40
vendor/github.com/go-pkgz/expirable-cache/options.go
generated
vendored
@@ -1,40 +0,0 @@
|
||||
package cache
|
||||
|
||||
import "time"
|
||||
|
||||
// Option func type
|
||||
type Option func(lc *cacheImpl) error
|
||||
|
||||
// OnEvicted called automatically for automatically and manually deleted entries
|
||||
func OnEvicted(fn func(key string, value interface{})) Option {
|
||||
return func(lc *cacheImpl) error {
|
||||
lc.onEvicted = fn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MaxKeys functional option defines how many keys to keep.
|
||||
// By default it is 0, which means unlimited.
|
||||
func MaxKeys(max int) Option {
|
||||
return func(lc *cacheImpl) error {
|
||||
lc.maxKeys = max
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// TTL functional option defines TTL for all cache entries.
|
||||
// By default it is set to 10 years, sane option for expirable cache might be 5 minutes.
|
||||
func TTL(ttl time.Duration) Option {
|
||||
return func(lc *cacheImpl) error {
|
||||
lc.ttl = ttl
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// LRU sets cache to LRU (Least Recently Used) eviction mode.
|
||||
func LRU() Option {
|
||||
return func(lc *cacheImpl) error {
|
||||
lc.isLRU = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
371
vendor/github.com/go-pkgz/expirable-cache/v3/cache.go
generated
vendored
Normal file
371
vendor/github.com/go-pkgz/expirable-cache/v3/cache.go
generated
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
// Package cache implements Cache similar to hashicorp/golang-lru
|
||||
//
|
||||
// Support LRC, LRU and TTL-based eviction.
|
||||
// Package is thread-safe and doesn't spawn any goroutines.
|
||||
// On every Set() call, cache deletes single oldest entry in case it's expired.
|
||||
// In case MaxSize is set, cache deletes the oldest entry disregarding its expiration date to maintain the size,
|
||||
// either using LRC or LRU eviction.
|
||||
// In case of default TTL (10 years) and default MaxSize (0, unlimited) the cache will be truly unlimited
|
||||
// and will never delete entries from itself automatically.
|
||||
//
|
||||
// Important: only reliable way of not having expired entries stuck in a cache is to
|
||||
// run cache.DeleteExpired periodically using time.Ticker, advisable period is 1/2 of TTL.
|
||||
package cache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Cache defines cache interface
|
||||
type Cache[K comparable, V any] interface {
|
||||
fmt.Stringer
|
||||
options[K, V]
|
||||
Add(key K, value V) bool
|
||||
Set(key K, value V, ttl time.Duration)
|
||||
Get(key K) (V, bool)
|
||||
GetExpiration(key K) (time.Time, bool)
|
||||
GetOldest() (K, V, bool)
|
||||
Contains(key K) (ok bool)
|
||||
Peek(key K) (V, bool)
|
||||
Values() []V
|
||||
Keys() []K
|
||||
Len() int
|
||||
Remove(key K) bool
|
||||
Invalidate(key K)
|
||||
InvalidateFn(fn func(key K) bool)
|
||||
RemoveOldest() (K, V, bool)
|
||||
DeleteExpired()
|
||||
Purge()
|
||||
Resize(int) int
|
||||
Stat() Stats
|
||||
}
|
||||
|
||||
// Stats provides statistics for cache
|
||||
type Stats struct {
|
||||
Hits, Misses int // cache effectiveness
|
||||
Added, Evicted int // number of added and evicted records
|
||||
}
|
||||
|
||||
// cacheImpl provides Cache interface implementation.
|
||||
type cacheImpl[K comparable, V any] struct {
|
||||
ttl time.Duration
|
||||
maxKeys int
|
||||
isLRU bool
|
||||
onEvicted func(key K, value V)
|
||||
|
||||
sync.Mutex
|
||||
stat Stats
|
||||
items map[K]*list.Element
|
||||
evictList *list.List
|
||||
}
|
||||
|
||||
// noEvictionTTL - very long ttl to prevent eviction
|
||||
const noEvictionTTL = time.Hour * 24 * 365 * 10
|
||||
|
||||
// NewCache returns a new Cache.
|
||||
// Default MaxKeys is unlimited (0).
|
||||
// Default TTL is 10 years, sane value for expirable cache is 5 minutes.
|
||||
// Default eviction mode is LRC, appropriate option allow to change it to LRU.
|
||||
func NewCache[K comparable, V any]() Cache[K, V] {
|
||||
return &cacheImpl[K, V]{
|
||||
items: map[K]*list.Element{},
|
||||
evictList: list.New(),
|
||||
ttl: noEvictionTTL,
|
||||
maxKeys: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||
// Returns false if there was no eviction: the item was already in the cache,
|
||||
// or the size was not exceeded.
|
||||
func (c *cacheImpl[K, V]) Add(key K, value V) (evicted bool) {
|
||||
return c.addWithTTL(key, value, c.ttl)
|
||||
}
|
||||
|
||||
// Set key, ttl of 0 would use cache-wide TTL
|
||||
func (c *cacheImpl[K, V]) Set(key K, value V, ttl time.Duration) {
|
||||
c.addWithTTL(key, value, ttl)
|
||||
}
|
||||
|
||||
// Returns true if an eviction occurred.
|
||||
// Returns false if there was no eviction: the item was already in the cache,
|
||||
// or the size was not exceeded.
|
||||
func (c *cacheImpl[K, V]) addWithTTL(key K, value V, ttl time.Duration) (evicted bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
now := time.Now()
|
||||
if ttl == 0 {
|
||||
ttl = c.ttl
|
||||
}
|
||||
|
||||
// Check for existing item
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.evictList.MoveToFront(ent)
|
||||
ent.Value.(*cacheItem[K, V]).value = value
|
||||
ent.Value.(*cacheItem[K, V]).expiresAt = now.Add(ttl)
|
||||
return false
|
||||
}
|
||||
|
||||
// Add new item
|
||||
ent := &cacheItem[K, V]{key: key, value: value, expiresAt: now.Add(ttl)}
|
||||
entry := c.evictList.PushFront(ent)
|
||||
c.items[key] = entry
|
||||
c.stat.Added++
|
||||
|
||||
// Remove the oldest entry if it is expired, only in case of non-default TTL.
|
||||
if c.ttl != noEvictionTTL || ttl != noEvictionTTL {
|
||||
c.removeOldestIfExpired()
|
||||
}
|
||||
|
||||
evict := c.maxKeys > 0 && len(c.items) > c.maxKeys
|
||||
// Verify size not exceeded
|
||||
if evict {
|
||||
c.removeOldest()
|
||||
}
|
||||
return evict
|
||||
}
|
||||
|
||||
// Get returns the key value if it's not expired
|
||||
func (c *cacheImpl[K, V]) Get(key K) (V, bool) {
|
||||
def := *new(V)
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if ent, ok := c.items[key]; ok {
|
||||
// Expired item check
|
||||
if time.Now().After(ent.Value.(*cacheItem[K, V]).expiresAt) {
|
||||
c.stat.Misses++
|
||||
return ent.Value.(*cacheItem[K, V]).value, false
|
||||
}
|
||||
if c.isLRU {
|
||||
c.evictList.MoveToFront(ent)
|
||||
}
|
||||
c.stat.Hits++
|
||||
return ent.Value.(*cacheItem[K, V]).value, true
|
||||
}
|
||||
c.stat.Misses++
|
||||
return def, false
|
||||
}
|
||||
|
||||
// Contains checks if a key is in the cache, without updating the recent-ness
|
||||
// or deleting it for being stale.
|
||||
func (c *cacheImpl[K, V]) Contains(key K) (ok bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
_, ok = c.items[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Peek returns the key value (or undefined if not found) without updating the "recently used"-ness of the key.
|
||||
// Works exactly the same as Get in case of LRC mode (default one).
|
||||
func (c *cacheImpl[K, V]) Peek(key K) (V, bool) {
|
||||
def := *new(V)
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if ent, ok := c.items[key]; ok {
|
||||
// Expired item check
|
||||
if time.Now().After(ent.Value.(*cacheItem[K, V]).expiresAt) {
|
||||
c.stat.Misses++
|
||||
return ent.Value.(*cacheItem[K, V]).value, false
|
||||
}
|
||||
c.stat.Hits++
|
||||
return ent.Value.(*cacheItem[K, V]).value, true
|
||||
}
|
||||
c.stat.Misses++
|
||||
return def, false
|
||||
}
|
||||
|
||||
// GetExpiration returns the expiration time of the key. Non-existing key returns zero time.
|
||||
func (c *cacheImpl[K, V]) GetExpiration(key K) (time.Time, bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if ent, ok := c.items[key]; ok {
|
||||
return ent.Value.(*cacheItem[K, V]).expiresAt, true
|
||||
}
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// Keys returns a slice of the keys in the cache, from oldest to newest.
|
||||
func (c *cacheImpl[K, V]) Keys() []K {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
return c.keys()
|
||||
}
|
||||
|
||||
// Values returns a slice of the values in the cache, from oldest to newest.
|
||||
// Expired entries are filtered out.
|
||||
func (c *cacheImpl[K, V]) Values() []V {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
values := make([]V, 0, len(c.items))
|
||||
now := time.Now()
|
||||
for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
|
||||
if now.After(ent.Value.(*cacheItem[K, V]).expiresAt) {
|
||||
continue
|
||||
}
|
||||
values = append(values, ent.Value.(*cacheItem[K, V]).value)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// Len return count of items in cache, including expired
|
||||
func (c *cacheImpl[K, V]) Len() int {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
return c.evictList.Len()
|
||||
}
|
||||
|
||||
// Resize changes the cache size. Size of 0 means unlimited.
|
||||
func (c *cacheImpl[K, V]) Resize(size int) int {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if size <= 0 {
|
||||
c.maxKeys = 0
|
||||
return 0
|
||||
}
|
||||
diff := c.evictList.Len() - size
|
||||
if diff < 0 {
|
||||
diff = 0
|
||||
}
|
||||
for i := 0; i < diff; i++ {
|
||||
c.removeOldest()
|
||||
}
|
||||
c.maxKeys = size
|
||||
return diff
|
||||
}
|
||||
|
||||
// Invalidate key (item) from the cache
|
||||
func (c *cacheImpl[K, V]) Invalidate(key K) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidateFn deletes multiple keys if predicate is true
|
||||
func (c *cacheImpl[K, V]) InvalidateFn(fn func(key K) bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
for key, ent := range c.items {
|
||||
if fn(key) {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes the provided key from the cache, returning if the
|
||||
// key was contained.
|
||||
func (c *cacheImpl[K, V]) Remove(key K) bool {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.removeElement(ent)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveOldest remove the oldest element in the cache
|
||||
func (c *cacheImpl[K, V]) RemoveOldest() (key K, value V, ok bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if ent := c.evictList.Back(); ent != nil {
|
||||
c.removeElement(ent)
|
||||
return ent.Value.(*cacheItem[K, V]).key, ent.Value.(*cacheItem[K, V]).value, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetOldest returns the oldest entry
|
||||
func (c *cacheImpl[K, V]) GetOldest() (key K, value V, ok bool) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if ent := c.evictList.Back(); ent != nil {
|
||||
return ent.Value.(*cacheItem[K, V]).key, ent.Value.(*cacheItem[K, V]).value, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteExpired clears cache of expired items
|
||||
func (c *cacheImpl[K, V]) DeleteExpired() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
for _, key := range c.keys() {
|
||||
if time.Now().After(c.items[key].Value.(*cacheItem[K, V]).expiresAt) {
|
||||
c.removeElement(c.items[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Purge clears the cache completely.
|
||||
func (c *cacheImpl[K, V]) Purge() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
for k, v := range c.items {
|
||||
delete(c.items, k)
|
||||
c.stat.Evicted++
|
||||
if c.onEvicted != nil {
|
||||
c.onEvicted(k, v.Value.(*cacheItem[K, V]).value)
|
||||
}
|
||||
}
|
||||
c.evictList.Init()
|
||||
}
|
||||
|
||||
// Stat gets the current stats for cache
|
||||
func (c *cacheImpl[K, V]) Stat() Stats {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
return c.stat
|
||||
}
|
||||
|
||||
func (c *cacheImpl[K, V]) String() string {
|
||||
stats := c.Stat()
|
||||
size := c.Len()
|
||||
return fmt.Sprintf("Size: %d, Stats: %+v (%0.1f%%)", size, stats, 100*float64(stats.Hits)/float64(stats.Hits+stats.Misses))
|
||||
}
|
||||
|
||||
// Keys returns a slice of the keys in the cache, from oldest to newest. Has to be called with lock!
|
||||
func (c *cacheImpl[K, V]) keys() []K {
|
||||
keys := make([]K, 0, len(c.items))
|
||||
for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
|
||||
keys = append(keys, ent.Value.(*cacheItem[K, V]).key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// removeOldest removes the oldest item from the cache. Has to be called with lock!
|
||||
func (c *cacheImpl[K, V]) removeOldest() {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// removeOldest removes the oldest item from the cache in case it's already expired. Has to be called with lock!
|
||||
func (c *cacheImpl[K, V]) removeOldestIfExpired() {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil && time.Now().After(ent.Value.(*cacheItem[K, V]).expiresAt) {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// removeElement is used to remove a given list element from the cache. Has to be called with lock!
|
||||
func (c *cacheImpl[K, V]) removeElement(e *list.Element) {
|
||||
c.evictList.Remove(e)
|
||||
kv := e.Value.(*cacheItem[K, V])
|
||||
delete(c.items, kv.key)
|
||||
c.stat.Evicted++
|
||||
if c.onEvicted != nil {
|
||||
c.onEvicted(kv.key, kv.value)
|
||||
}
|
||||
}
|
||||
|
||||
// cacheItem is used to hold a value in the evictList
|
||||
type cacheItem[K comparable, V any] struct {
|
||||
expiresAt time.Time
|
||||
key K
|
||||
value V
|
||||
}
|
||||
36
vendor/github.com/go-pkgz/expirable-cache/v3/options.go
generated
vendored
Normal file
36
vendor/github.com/go-pkgz/expirable-cache/v3/options.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package cache
|
||||
|
||||
import "time"
|
||||
|
||||
type options[K comparable, V any] interface {
|
||||
WithTTL(ttl time.Duration) Cache[K, V]
|
||||
WithMaxKeys(maxKeys int) Cache[K, V]
|
||||
WithLRU() Cache[K, V]
|
||||
WithOnEvicted(fn func(key K, value V)) Cache[K, V]
|
||||
}
|
||||
|
||||
// WithTTL functional option defines TTL for all cache entries.
|
||||
// By default, it is set to 10 years, sane option for expirable cache might be 5 minutes.
|
||||
func (c *cacheImpl[K, V]) WithTTL(ttl time.Duration) Cache[K, V] {
|
||||
c.ttl = ttl
|
||||
return c
|
||||
}
|
||||
|
||||
// WithMaxKeys functional option defines how many keys to keep.
|
||||
// By default, it is 0, which means unlimited.
|
||||
func (c *cacheImpl[K, V]) WithMaxKeys(maxKeys int) Cache[K, V] {
|
||||
c.maxKeys = maxKeys
|
||||
return c
|
||||
}
|
||||
|
||||
// WithLRU sets cache to LRU (Least Recently Used) eviction mode.
|
||||
func (c *cacheImpl[K, V]) WithLRU() Cache[K, V] {
|
||||
c.isLRU = true
|
||||
return c
|
||||
}
|
||||
|
||||
// WithOnEvicted defined function which would be called automatically for automatically and manually deleted entries
|
||||
func (c *cacheImpl[K, V]) WithOnEvicted(fn func(key K, value V)) Cache[K, V] {
|
||||
c.onEvicted = fn
|
||||
return c
|
||||
}
|
||||
Reference in New Issue
Block a user