mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-10-31 22:40:01 +00:00
[bugfix] update go-cache to v3.3.3 (#1778)
* update go-cache to v3.3.1 Signed-off-by: kim <grufwub@gmail.com> * bump go-cache again Signed-off-by: kim <grufwub@gmail.com> * remove accidentally comitted build-script changes Signed-off-by: kim <grufwub@gmail.com> * now v3.3.3 ... Signed-off-by: kim <grufwub@gmail.com> --------- Signed-off-by: kim <grufwub@gmail.com>
This commit is contained in:
parent
514eb8c83e
commit
2b7c815ed6
6 changed files with 466 additions and 240 deletions
2
go.mod
2
go.mod
|
@ -5,7 +5,7 @@ go 1.20
|
||||||
require (
|
require (
|
||||||
codeberg.org/gruf/go-bytesize v1.0.2
|
codeberg.org/gruf/go-bytesize v1.0.2
|
||||||
codeberg.org/gruf/go-byteutil v1.1.2
|
codeberg.org/gruf/go-byteutil v1.1.2
|
||||||
codeberg.org/gruf/go-cache/v3 v3.3.0
|
codeberg.org/gruf/go-cache/v3 v3.3.3
|
||||||
codeberg.org/gruf/go-debug v1.3.0
|
codeberg.org/gruf/go-debug v1.3.0
|
||||||
codeberg.org/gruf/go-errors/v2 v2.2.0
|
codeberg.org/gruf/go-errors/v2 v2.2.0
|
||||||
codeberg.org/gruf/go-fastcopy v1.1.2
|
codeberg.org/gruf/go-fastcopy v1.1.2
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -49,8 +49,8 @@ codeberg.org/gruf/go-bytesize v1.0.2/go.mod h1:n/GU8HzL9f3UNp/mUKyr1qVmTlj7+xacp
|
||||||
codeberg.org/gruf/go-byteutil v1.0.0/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU=
|
codeberg.org/gruf/go-byteutil v1.0.0/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU=
|
||||||
codeberg.org/gruf/go-byteutil v1.1.2 h1:TQLZtTxTNca9xEfDIndmo7nBYxeS94nrv/9DS3Nk5Tw=
|
codeberg.org/gruf/go-byteutil v1.1.2 h1:TQLZtTxTNca9xEfDIndmo7nBYxeS94nrv/9DS3Nk5Tw=
|
||||||
codeberg.org/gruf/go-byteutil v1.1.2/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU=
|
codeberg.org/gruf/go-byteutil v1.1.2/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU=
|
||||||
codeberg.org/gruf/go-cache/v3 v3.3.0 h1:Bor75j4MYJIDqH22/aQvmwA7hMqBOzDOWdSQz25Lq+8=
|
codeberg.org/gruf/go-cache/v3 v3.3.3 h1:CzOFg6JV+typ8Jst1rrYDbZjxEV7bUxKggkbfN5Y79o=
|
||||||
codeberg.org/gruf/go-cache/v3 v3.3.0/go.mod h1:pTeVPEb9DshXUkd8Dg76UcsLpU6EC/tXQ2qb+JrmxEc=
|
codeberg.org/gruf/go-cache/v3 v3.3.3/go.mod h1:pTeVPEb9DshXUkd8Dg76UcsLpU6EC/tXQ2qb+JrmxEc=
|
||||||
codeberg.org/gruf/go-debug v1.3.0 h1:PIRxQiWUFKtGOGZFdZ3Y0pqyfI0Xr87j224IYe2snZs=
|
codeberg.org/gruf/go-debug v1.3.0 h1:PIRxQiWUFKtGOGZFdZ3Y0pqyfI0Xr87j224IYe2snZs=
|
||||||
codeberg.org/gruf/go-debug v1.3.0/go.mod h1:N+vSy9uJBQgpQcJUqjctvqFz7tBHJf+S/PIjLILzpLg=
|
codeberg.org/gruf/go-debug v1.3.0/go.mod h1:N+vSy9uJBQgpQcJUqjctvqFz7tBHJf+S/PIjLILzpLg=
|
||||||
codeberg.org/gruf/go-errors/v2 v2.0.0/go.mod h1:ZRhbdhvgoUA3Yw6e56kd9Ox984RrvbEFC2pOXyHDJP4=
|
codeberg.org/gruf/go-errors/v2 v2.0.0/go.mod h1:ZRhbdhvgoUA3Yw6e56kd9Ox984RrvbEFC2pOXyHDJP4=
|
||||||
|
|
7
vendor/codeberg.org/gruf/go-cache/v3/cache.go
generated
vendored
7
vendor/codeberg.org/gruf/go-cache/v3/cache.go
generated
vendored
|
@ -15,10 +15,10 @@ type Cache[Key comparable, Value any] interface {
|
||||||
Stop() bool
|
Stop() bool
|
||||||
|
|
||||||
// SetEvictionCallback sets the eviction callback to the provided hook.
|
// SetEvictionCallback sets the eviction callback to the provided hook.
|
||||||
SetEvictionCallback(hook func(*ttlcache.Entry[Key, Value]))
|
SetEvictionCallback(hook func(Key, Value))
|
||||||
|
|
||||||
// SetInvalidateCallback sets the invalidate callback to the provided hook.
|
// SetInvalidateCallback sets the invalidate callback to the provided hook.
|
||||||
SetInvalidateCallback(hook func(*ttlcache.Entry[Key, Value]))
|
SetInvalidateCallback(hook func(Key, Value))
|
||||||
|
|
||||||
// SetTTL sets the cache item TTL. Update can be specified to force updates of existing items in the cache, this will simply add the change in TTL to their current expiry time.
|
// SetTTL sets the cache item TTL. Update can be specified to force updates of existing items in the cache, this will simply add the change in TTL to their current expiry time.
|
||||||
SetTTL(ttl time.Duration, update bool)
|
SetTTL(ttl time.Duration, update bool)
|
||||||
|
@ -44,6 +44,9 @@ type Cache[Key comparable, Value any] interface {
|
||||||
// Invalidate deletes a value from the cache, calling the invalidate callback.
|
// Invalidate deletes a value from the cache, calling the invalidate callback.
|
||||||
Invalidate(key Key) bool
|
Invalidate(key Key) bool
|
||||||
|
|
||||||
|
// InvalidateAll is equivalent to multiple Invalidate calls.
|
||||||
|
InvalidateAll(keys ...Key) bool
|
||||||
|
|
||||||
// Clear empties the cache, calling the invalidate callback on each entry.
|
// Clear empties the cache, calling the invalidate callback on each entry.
|
||||||
Clear()
|
Clear()
|
||||||
|
|
||||||
|
|
85
vendor/codeberg.org/gruf/go-cache/v3/result/cache.go
generated
vendored
85
vendor/codeberg.org/gruf/go-cache/v3/result/cache.go
generated
vendored
|
@ -97,20 +97,22 @@ func (c *Cache[Value]) SetEvictionCallback(hook func(Value)) {
|
||||||
// Ensure non-nil hook.
|
// Ensure non-nil hook.
|
||||||
hook = func(Value) {}
|
hook = func(Value) {}
|
||||||
}
|
}
|
||||||
c.cache.SetEvictionCallback(func(item *ttl.Entry[int64, result[Value]]) {
|
c.cache.SetEvictionCallback(func(pkey int64, res result[Value]) {
|
||||||
for _, key := range item.Value.Keys {
|
c.cache.Lock()
|
||||||
|
for _, key := range res.Keys {
|
||||||
// Delete key->pkey lookup
|
// Delete key->pkey lookup
|
||||||
pkeys := key.info.pkeys
|
pkeys := key.info.pkeys
|
||||||
delete(pkeys, key.key)
|
delete(pkeys, key.key)
|
||||||
}
|
}
|
||||||
|
c.cache.Unlock()
|
||||||
|
|
||||||
if item.Value.Error != nil {
|
if res.Error != nil {
|
||||||
// Skip error hooks
|
// Skip error hooks
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call user hook.
|
// Call user hook.
|
||||||
hook(item.Value.Value)
|
hook(res.Value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,20 +123,22 @@ func (c *Cache[Value]) SetInvalidateCallback(hook func(Value)) {
|
||||||
hook = func(Value) {}
|
hook = func(Value) {}
|
||||||
} // store hook.
|
} // store hook.
|
||||||
c.invalid = hook
|
c.invalid = hook
|
||||||
c.cache.SetInvalidateCallback(func(item *ttl.Entry[int64, result[Value]]) {
|
c.cache.SetInvalidateCallback(func(pkey int64, res result[Value]) {
|
||||||
for _, key := range item.Value.Keys {
|
c.cache.Lock()
|
||||||
|
for _, key := range res.Keys {
|
||||||
// Delete key->pkey lookup
|
// Delete key->pkey lookup
|
||||||
pkeys := key.info.pkeys
|
pkeys := key.info.pkeys
|
||||||
delete(pkeys, key.key)
|
delete(pkeys, key.key)
|
||||||
}
|
}
|
||||||
|
c.cache.Unlock()
|
||||||
|
|
||||||
if item.Value.Error != nil {
|
if res.Error != nil {
|
||||||
// Skip error hooks
|
// Skip error hooks
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call user hook.
|
// Call user hook.
|
||||||
hook(item.Value.Value)
|
hook(res.Value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,9 +182,17 @@ func (c *Cache[Value]) Load(lookup string, load func() (Value, error), keyParts
|
||||||
pkeys := keyInfo.pkeys[ckey]
|
pkeys := keyInfo.pkeys[ckey]
|
||||||
|
|
||||||
if ok = (len(pkeys) > 0); ok {
|
if ok = (len(pkeys) > 0); ok {
|
||||||
|
var entry *ttl.Entry[int64, result[Value]]
|
||||||
|
|
||||||
// Fetch the result for primary key
|
// Fetch the result for primary key
|
||||||
entry, _ := c.cache.Cache.Get(pkeys[0])
|
entry, ok = c.cache.Cache.Get(pkeys[0])
|
||||||
res = entry.Value
|
if ok {
|
||||||
|
// Since the invalidation / eviction hooks acquire a mutex
|
||||||
|
// lock separately, and only at this point are the pkeys
|
||||||
|
// updated, there is a chance that a primary key may return
|
||||||
|
// no matching entry. Hence we have to check for it here.
|
||||||
|
res = entry.Value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done with lock
|
// Done with lock
|
||||||
|
@ -213,12 +225,19 @@ func (c *Cache[Value]) Load(lookup string, load func() (Value, error), keyParts
|
||||||
res.Keys = c.lookups.generate(res.Value)
|
res.Keys = c.lookups.generate(res.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var evict func()
|
||||||
|
|
||||||
// Acquire cache lock.
|
// Acquire cache lock.
|
||||||
c.cache.Lock()
|
c.cache.Lock()
|
||||||
defer c.cache.Unlock()
|
defer func() {
|
||||||
|
c.cache.Unlock()
|
||||||
|
if evict != nil {
|
||||||
|
evict()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Cache result
|
// Store result in cache.
|
||||||
c.store(res)
|
evict = c.store(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Catch and return error
|
// Catch and return error
|
||||||
|
@ -244,12 +263,19 @@ func (c *Cache[Value]) Store(value Value, store func() error) error {
|
||||||
Error: nil,
|
Error: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var evict func()
|
||||||
|
|
||||||
// Acquire cache lock.
|
// Acquire cache lock.
|
||||||
c.cache.Lock()
|
c.cache.Lock()
|
||||||
defer c.cache.Unlock()
|
defer func() {
|
||||||
|
c.cache.Unlock()
|
||||||
|
if evict != nil {
|
||||||
|
evict()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Cache result
|
// Store result in cache.
|
||||||
c.store(result)
|
evict = c.store(result)
|
||||||
|
|
||||||
// Call invalidate.
|
// Call invalidate.
|
||||||
c.invalid(value)
|
c.invalid(value)
|
||||||
|
@ -278,9 +304,17 @@ func (c *Cache[Value]) Has(lookup string, keyParts ...any) bool {
|
||||||
pkeys := keyInfo.pkeys[ckey]
|
pkeys := keyInfo.pkeys[ckey]
|
||||||
|
|
||||||
if ok = (len(pkeys) > 0); ok {
|
if ok = (len(pkeys) > 0); ok {
|
||||||
|
var entry *ttl.Entry[int64, result[Value]]
|
||||||
|
|
||||||
// Fetch the result for primary key
|
// Fetch the result for primary key
|
||||||
entry, _ := c.cache.Cache.Get(pkeys[0])
|
entry, ok = c.cache.Cache.Get(pkeys[0])
|
||||||
res = entry.Value
|
if ok {
|
||||||
|
// Since the invalidation / eviction hooks acquire a mutex
|
||||||
|
// lock separately, and only at this point are the pkeys
|
||||||
|
// updated, there is a chance that a primary key may return
|
||||||
|
// no matching entry. Hence we have to check for it here.
|
||||||
|
res = entry.Value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done with lock
|
// Done with lock
|
||||||
|
@ -301,19 +335,18 @@ func (c *Cache[Value]) Invalidate(lookup string, keyParts ...any) {
|
||||||
// Look for primary key for cache key
|
// Look for primary key for cache key
|
||||||
c.cache.Lock()
|
c.cache.Lock()
|
||||||
pkeys := keyInfo.pkeys[ckey]
|
pkeys := keyInfo.pkeys[ckey]
|
||||||
|
delete(keyInfo.pkeys, ckey)
|
||||||
c.cache.Unlock()
|
c.cache.Unlock()
|
||||||
|
|
||||||
for _, pkey := range pkeys {
|
// Invalidate all primary keys.
|
||||||
// Invalidate each primary key
|
c.cache.InvalidateAll(pkeys...)
|
||||||
c.cache.Invalidate(pkey)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear empties the cache, calling the invalidate callback.
|
// Clear empties the cache, calling the invalidate callback.
|
||||||
func (c *Cache[Value]) Clear() { c.cache.Clear() }
|
func (c *Cache[Value]) Clear() { c.cache.Clear() }
|
||||||
|
|
||||||
// store will cache this result under all of its required cache keys.
|
// store will cache this result under all of its required cache keys.
|
||||||
func (c *Cache[Value]) store(res result[Value]) {
|
func (c *Cache[Value]) store(res result[Value]) (evict func()) {
|
||||||
// Get primary key
|
// Get primary key
|
||||||
pnext := c.next
|
pnext := c.next
|
||||||
c.next++
|
c.next++
|
||||||
|
@ -341,7 +374,7 @@ func (c *Cache[Value]) store(res result[Value]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drop these keys.
|
// Drop existing.
|
||||||
pkeys = pkeys[:0]
|
pkeys = pkeys[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,8 +389,10 @@ func (c *Cache[Value]) store(res result[Value]) {
|
||||||
Key: pnext,
|
Key: pnext,
|
||||||
Value: res,
|
Value: res,
|
||||||
}, func(_ int64, item *ttl.Entry[int64, result[Value]]) {
|
}, func(_ int64, item *ttl.Entry[int64, result[Value]]) {
|
||||||
c.cache.Evict(item)
|
evict = func() { c.cache.Evict(item.Key, item.Value) }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return evict
|
||||||
}
|
}
|
||||||
|
|
||||||
type result[Value any] struct {
|
type result[Value any] struct {
|
||||||
|
|
606
vendor/codeberg.org/gruf/go-cache/v3/ttl/ttl.go
generated
vendored
606
vendor/codeberg.org/gruf/go-cache/v3/ttl/ttl.go
generated
vendored
|
@ -20,10 +20,10 @@ type Cache[Key comparable, Value any] struct {
|
||||||
TTL time.Duration
|
TTL time.Duration
|
||||||
|
|
||||||
// Evict is the hook that is called when an item is evicted from the cache.
|
// Evict is the hook that is called when an item is evicted from the cache.
|
||||||
Evict func(*Entry[Key, Value])
|
Evict func(Key, Value)
|
||||||
|
|
||||||
// Invalid is the hook that is called when an item's data in the cache is invalidated, includes Add/Set.
|
// Invalid is the hook that is called when an item's data in the cache is invalidated, includes Add/Set.
|
||||||
Invalid func(*Entry[Key, Value])
|
Invalid func(Key, Value)
|
||||||
|
|
||||||
// Cache is the underlying hashmap used for this cache.
|
// Cache is the underlying hashmap used for this cache.
|
||||||
Cache maps.LRUMap[Key, *Entry[Key, Value]]
|
Cache maps.LRUMap[Key, *Entry[Key, Value]]
|
||||||
|
@ -97,66 +97,66 @@ func (c *Cache[K, V]) Stop() (ok bool) {
|
||||||
|
|
||||||
// Sweep attempts to evict expired items (with callback!) from cache.
|
// Sweep attempts to evict expired items (with callback!) from cache.
|
||||||
func (c *Cache[K, V]) Sweep(now time.Time) {
|
func (c *Cache[K, V]) Sweep(now time.Time) {
|
||||||
var after int
|
var (
|
||||||
|
// evicted key-values.
|
||||||
|
kvs []kv[K, V]
|
||||||
|
|
||||||
// Sweep within lock
|
// hook func ptrs.
|
||||||
c.Lock()
|
evict func(K, V)
|
||||||
defer c.Unlock()
|
)
|
||||||
|
|
||||||
// Sentinel value
|
c.locked(func() {
|
||||||
after = -1
|
// Sentinel value
|
||||||
|
after := -1
|
||||||
|
|
||||||
// The cache will be ordered by expiry date, we iterate until we reach the index of
|
// The cache will be ordered by expiry date, we iterate until we reach the index of
|
||||||
// the youngest item that hsa expired, as all succeeding items will also be expired.
|
// the youngest item that hsa expired, as all succeeding items will also be expired.
|
||||||
c.Cache.RangeIf(0, c.Cache.Len(), func(i int, _ K, item *Entry[K, V]) bool {
|
c.Cache.RangeIf(0, c.Cache.Len(), func(i int, _ K, item *Entry[K, V]) bool {
|
||||||
if now.After(item.Expiry) {
|
if now.After(item.Expiry) {
|
||||||
after = i
|
after = i
|
||||||
|
|
||||||
// All older than this (including) can be dropped
|
// evict all older items
|
||||||
return false
|
// than this (inclusive)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// cont. loop.
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if after == -1 {
|
||||||
|
// No Truncation needed
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue looping
|
// Set hook func ptr.
|
||||||
return true
|
evict = c.Evict
|
||||||
|
|
||||||
|
// Truncate determined size.
|
||||||
|
sz := c.Cache.Len() - after
|
||||||
|
kvs = c.truncate(sz, evict)
|
||||||
})
|
})
|
||||||
|
|
||||||
if after == -1 {
|
if evict != nil {
|
||||||
// No Truncation needed
|
for x := range kvs {
|
||||||
return
|
// Pass to eviction hook.
|
||||||
|
evict(kvs[x].K, kvs[x].V)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate items, calling eviction hook
|
|
||||||
c.truncate(c.Cache.Len()-after, c.Evict)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEvictionCallback: implements cache.Cache's SetEvictionCallback().
|
// SetEvictionCallback: implements cache.Cache's SetEvictionCallback().
|
||||||
func (c *Cache[K, V]) SetEvictionCallback(hook func(*Entry[K, V])) {
|
func (c *Cache[K, V]) SetEvictionCallback(hook func(K, V)) {
|
||||||
// Ensure non-nil hook
|
c.locked(func() {
|
||||||
if hook == nil {
|
c.Evict = hook
|
||||||
hook = func(*Entry[K, V]) {}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// Update within lock
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
// Update hook
|
|
||||||
c.Evict = hook
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetInvalidateCallback: implements cache.Cache's SetInvalidateCallback().
|
// SetInvalidateCallback: implements cache.Cache's SetInvalidateCallback().
|
||||||
func (c *Cache[K, V]) SetInvalidateCallback(hook func(*Entry[K, V])) {
|
func (c *Cache[K, V]) SetInvalidateCallback(hook func(K, V)) {
|
||||||
// Ensure non-nil hook
|
c.locked(func() {
|
||||||
if hook == nil {
|
c.Invalid = hook
|
||||||
hook = func(*Entry[K, V]) {}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// Update within lock
|
|
||||||
c.Lock()
|
|
||||||
defer c.Unlock()
|
|
||||||
|
|
||||||
// Update hook
|
|
||||||
c.Invalid = hook
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTTL: implements cache.Cache's SetTTL().
|
// SetTTL: implements cache.Cache's SetTTL().
|
||||||
|
@ -165,244 +165,417 @@ func (c *Cache[K, V]) SetTTL(ttl time.Duration, update bool) {
|
||||||
panic("ttl must be greater than zero")
|
panic("ttl must be greater than zero")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update within lock
|
c.locked(func() {
|
||||||
c.Lock()
|
// Set updated TTL
|
||||||
defer c.Unlock()
|
diff := ttl - c.TTL
|
||||||
|
c.TTL = ttl
|
||||||
|
|
||||||
// Set updated TTL
|
if update {
|
||||||
diff := ttl - c.TTL
|
// Update existing cache entries with new expiry time
|
||||||
c.TTL = ttl
|
c.Cache.Range(0, c.Cache.Len(), func(i int, _ K, item *Entry[K, V]) {
|
||||||
|
item.Expiry = item.Expiry.Add(diff)
|
||||||
if update {
|
})
|
||||||
// Update existing cache entries with new expiry time
|
}
|
||||||
c.Cache.Range(0, c.Cache.Len(), func(i int, _ K, item *Entry[K, V]) {
|
})
|
||||||
item.Expiry = item.Expiry.Add(diff)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get: implements cache.Cache's Get().
|
// Get: implements cache.Cache's Get().
|
||||||
func (c *Cache[K, V]) Get(key K) (V, bool) {
|
func (c *Cache[K, V]) Get(key K) (V, bool) {
|
||||||
// Read within lock
|
var (
|
||||||
c.Lock()
|
// did exist in cache?
|
||||||
defer c.Unlock()
|
ok bool
|
||||||
|
|
||||||
// Check for item in cache
|
// cached value.
|
||||||
item, ok := c.Cache.Get(key)
|
v V
|
||||||
if !ok {
|
)
|
||||||
var value V
|
|
||||||
return value, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update item expiry and return
|
c.locked(func() {
|
||||||
item.Expiry = time.Now().Add(c.TTL)
|
var item *Entry[K, V]
|
||||||
return item.Value, true
|
|
||||||
|
// Check for item in cache
|
||||||
|
item, ok = c.Cache.Get(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update fetched item's expiry
|
||||||
|
item.Expiry = time.Now().Add(c.TTL)
|
||||||
|
|
||||||
|
// Set value.
|
||||||
|
v = item.Value
|
||||||
|
})
|
||||||
|
|
||||||
|
return v, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add: implements cache.Cache's Add().
|
// Add: implements cache.Cache's Add().
|
||||||
func (c *Cache[K, V]) Add(key K, value V) bool {
|
func (c *Cache[K, V]) Add(key K, value V) bool {
|
||||||
// Write within lock
|
var (
|
||||||
c.Lock()
|
// did exist in cache?
|
||||||
defer c.Unlock()
|
ok bool
|
||||||
|
|
||||||
// Check if already exists
|
// was entry evicted?
|
||||||
item, ok := c.Cache.Get(key)
|
ev bool
|
||||||
if ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alloc new item
|
// evicted key values.
|
||||||
item = c.alloc()
|
evcK K
|
||||||
item.Key = key
|
evcV V
|
||||||
item.Value = value
|
|
||||||
item.Expiry = time.Now().Add(c.TTL)
|
|
||||||
|
|
||||||
var hook func(K, *Entry[K, V])
|
// hook func ptrs.
|
||||||
|
evict func(K, V)
|
||||||
|
)
|
||||||
|
|
||||||
if c.Evict != nil {
|
c.locked(func() {
|
||||||
// Pass evicted entry to user hook
|
// Check if in cache.
|
||||||
hook = func(_ K, item *Entry[K, V]) {
|
ok = c.Cache.Has(key)
|
||||||
c.Evict(item)
|
if ok {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alloc new entry.
|
||||||
|
new := c.alloc()
|
||||||
|
new.Expiry = time.Now().Add(c.TTL)
|
||||||
|
new.Key = key
|
||||||
|
new.Value = value
|
||||||
|
|
||||||
|
// Add new entry to cache and catched any evicted item.
|
||||||
|
c.Cache.SetWithHook(key, new, func(_ K, item *Entry[K, V]) {
|
||||||
|
evcK = item.Key
|
||||||
|
evcV = item.Value
|
||||||
|
ev = true
|
||||||
|
c.free(item)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Set hook func ptr.
|
||||||
|
evict = c.Evict
|
||||||
|
})
|
||||||
|
|
||||||
|
if ev && evict != nil {
|
||||||
|
// Pass to eviction hook.
|
||||||
|
evict(evcK, evcV)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place new item in the map with hook
|
return !ok
|
||||||
c.Cache.SetWithHook(key, item, hook)
|
|
||||||
|
|
||||||
if c.Invalid != nil {
|
|
||||||
// invalidate old
|
|
||||||
c.Invalid(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set: implements cache.Cache's Set().
|
// Set: implements cache.Cache's Set().
|
||||||
func (c *Cache[K, V]) Set(key K, value V) {
|
func (c *Cache[K, V]) Set(key K, value V) {
|
||||||
// Write within lock
|
var (
|
||||||
c.Lock()
|
// did exist in cache?
|
||||||
defer c.Unlock()
|
ok bool
|
||||||
|
|
||||||
// Check if already exists
|
// was entry evicted?
|
||||||
item, ok := c.Cache.Get(key)
|
ev bool
|
||||||
|
|
||||||
if !ok {
|
// old value.
|
||||||
var hook func(K, *Entry[K, V])
|
oldV V
|
||||||
|
|
||||||
// Allocate new item
|
// evicted key values.
|
||||||
item = c.alloc()
|
evcK K
|
||||||
item.Key = key
|
evcV V
|
||||||
|
|
||||||
if c.Evict != nil {
|
// hook func ptrs.
|
||||||
// Pass evicted entry to user hook
|
invalid func(K, V)
|
||||||
hook = func(_ K, item *Entry[K, V]) {
|
evict func(K, V)
|
||||||
c.Evict(item)
|
)
|
||||||
}
|
|
||||||
|
c.locked(func() {
|
||||||
|
var item *Entry[K, V]
|
||||||
|
|
||||||
|
// Check for item in cache
|
||||||
|
item, ok = c.Cache.Get(key)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
// Set old value.
|
||||||
|
oldV = item.Value
|
||||||
|
|
||||||
|
// Update the existing item.
|
||||||
|
item.Expiry = time.Now().Add(c.TTL)
|
||||||
|
item.Value = value
|
||||||
|
} else {
|
||||||
|
// Alloc new entry.
|
||||||
|
new := c.alloc()
|
||||||
|
new.Expiry = time.Now().Add(c.TTL)
|
||||||
|
new.Key = key
|
||||||
|
new.Value = value
|
||||||
|
|
||||||
|
// Add new entry to cache and catched any evicted item.
|
||||||
|
c.Cache.SetWithHook(key, new, func(_ K, item *Entry[K, V]) {
|
||||||
|
evcK = item.Key
|
||||||
|
evcV = item.Value
|
||||||
|
ev = true
|
||||||
|
c.free(item)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place new item in the map with hook
|
// Set hook func ptrs.
|
||||||
c.Cache.SetWithHook(key, item, hook)
|
invalid = c.Invalid
|
||||||
|
evict = c.Evict
|
||||||
|
})
|
||||||
|
|
||||||
|
if ok && invalid != nil {
|
||||||
|
// Pass to invalidate hook.
|
||||||
|
invalid(key, oldV)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Invalid != nil {
|
if ev && evict != nil {
|
||||||
// invalidate old
|
// Pass to eviction hook.
|
||||||
c.Invalid(item)
|
evict(evcK, evcV)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the item value + expiry
|
|
||||||
item.Expiry = time.Now().Add(c.TTL)
|
|
||||||
item.Value = value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CAS: implements cache.Cache's CAS().
|
// CAS: implements cache.Cache's CAS().
|
||||||
func (c *Cache[K, V]) CAS(key K, old V, new V, cmp func(V, V) bool) bool {
|
func (c *Cache[K, V]) CAS(key K, old V, new V, cmp func(V, V) bool) bool {
|
||||||
// CAS within lock
|
var (
|
||||||
c.Lock()
|
// did exist in cache?
|
||||||
defer c.Unlock()
|
ok bool
|
||||||
|
|
||||||
// Check for item in cache
|
// swapped value.
|
||||||
item, ok := c.Cache.Get(key)
|
oldV V
|
||||||
if !ok || !cmp(item.Value, old) {
|
|
||||||
return false
|
// hook func ptrs.
|
||||||
|
invalid func(K, V)
|
||||||
|
)
|
||||||
|
|
||||||
|
c.locked(func() {
|
||||||
|
var item *Entry[K, V]
|
||||||
|
|
||||||
|
// Check for item in cache
|
||||||
|
item, ok = c.Cache.Get(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the comparison
|
||||||
|
if !cmp(old, item.Value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set old value.
|
||||||
|
oldV = item.Value
|
||||||
|
|
||||||
|
// Update value + expiry.
|
||||||
|
item.Expiry = time.Now().Add(c.TTL)
|
||||||
|
item.Value = new
|
||||||
|
|
||||||
|
// Set hook func ptr.
|
||||||
|
invalid = c.Invalid
|
||||||
|
})
|
||||||
|
|
||||||
|
if ok && invalid != nil {
|
||||||
|
// Pass to invalidate hook.
|
||||||
|
invalid(key, oldV)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Invalid != nil {
|
|
||||||
// invalidate old
|
|
||||||
c.Invalid(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update item + Expiry
|
|
||||||
item.Value = new
|
|
||||||
item.Expiry = time.Now().Add(c.TTL)
|
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap: implements cache.Cache's Swap().
|
// Swap: implements cache.Cache's Swap().
|
||||||
func (c *Cache[K, V]) Swap(key K, swp V) V {
|
func (c *Cache[K, V]) Swap(key K, swp V) V {
|
||||||
// Swap within lock
|
var (
|
||||||
c.Lock()
|
// did exist in cache?
|
||||||
defer c.Unlock()
|
ok bool
|
||||||
|
|
||||||
// Check for item in cache
|
// swapped value.
|
||||||
item, ok := c.Cache.Get(key)
|
oldV V
|
||||||
if !ok {
|
|
||||||
var value V
|
// hook func ptrs.
|
||||||
return value
|
invalid func(K, V)
|
||||||
|
)
|
||||||
|
|
||||||
|
c.locked(func() {
|
||||||
|
var item *Entry[K, V]
|
||||||
|
|
||||||
|
// Check for item in cache
|
||||||
|
item, ok = c.Cache.Get(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set old value.
|
||||||
|
oldV = item.Value
|
||||||
|
|
||||||
|
// Update value + expiry.
|
||||||
|
item.Expiry = time.Now().Add(c.TTL)
|
||||||
|
item.Value = swp
|
||||||
|
|
||||||
|
// Set hook func ptr.
|
||||||
|
invalid = c.Invalid
|
||||||
|
})
|
||||||
|
|
||||||
|
if ok && invalid != nil {
|
||||||
|
// Pass to invalidate hook.
|
||||||
|
invalid(key, oldV)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Invalid != nil {
|
return oldV
|
||||||
// invalidate old
|
|
||||||
c.Invalid(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
old := item.Value
|
|
||||||
|
|
||||||
// update item + Expiry
|
|
||||||
item.Value = swp
|
|
||||||
item.Expiry = time.Now().Add(c.TTL)
|
|
||||||
|
|
||||||
return old
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has: implements cache.Cache's Has().
|
// Has: implements cache.Cache's Has().
|
||||||
func (c *Cache[K, V]) Has(key K) bool {
|
func (c *Cache[K, V]) Has(key K) (ok bool) {
|
||||||
c.Lock()
|
c.locked(func() {
|
||||||
ok := c.Cache.Has(key)
|
ok = c.Cache.Has(key)
|
||||||
c.Unlock()
|
})
|
||||||
return ok
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate: implements cache.Cache's Invalidate().
|
// Invalidate: implements cache.Cache's Invalidate().
|
||||||
func (c *Cache[K, V]) Invalidate(key K) bool {
|
func (c *Cache[K, V]) Invalidate(key K) (ok bool) {
|
||||||
// Delete within lock
|
var (
|
||||||
c.Lock()
|
// old value.
|
||||||
defer c.Unlock()
|
oldV V
|
||||||
|
|
||||||
// Check if we have item with key
|
// hook func ptrs.
|
||||||
item, ok := c.Cache.Get(key)
|
invalid func(K, V)
|
||||||
if !ok {
|
)
|
||||||
return false
|
|
||||||
|
c.locked(func() {
|
||||||
|
var item *Entry[K, V]
|
||||||
|
|
||||||
|
// Check for item in cache
|
||||||
|
item, ok = c.Cache.Get(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set old value.
|
||||||
|
oldV = item.Value
|
||||||
|
|
||||||
|
// Remove from cache map
|
||||||
|
_ = c.Cache.Delete(key)
|
||||||
|
|
||||||
|
// Free entry
|
||||||
|
c.free(item)
|
||||||
|
|
||||||
|
// Set hook func ptrs.
|
||||||
|
invalid = c.Invalid
|
||||||
|
})
|
||||||
|
|
||||||
|
if ok && invalid != nil {
|
||||||
|
// Pass to invalidate hook.
|
||||||
|
invalid(key, oldV)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove from cache map
|
return
|
||||||
_ = c.Cache.Delete(key)
|
}
|
||||||
|
|
||||||
if c.Invalid != nil {
|
// InvalidateAll: implements cache.Cache's InvalidateAll().
|
||||||
// Invalidate item
|
func (c *Cache[K, V]) InvalidateAll(keys ...K) (ok bool) {
|
||||||
c.Invalid(item)
|
var (
|
||||||
|
// invalidated kvs.
|
||||||
|
kvs []kv[K, V]
|
||||||
|
|
||||||
|
// hook func ptrs.
|
||||||
|
invalid func(K, V)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Allocate a slice for invalidated.
|
||||||
|
kvs = make([]kv[K, V], 0, len(keys))
|
||||||
|
|
||||||
|
c.locked(func() {
|
||||||
|
for _, key := range keys {
|
||||||
|
var item *Entry[K, V]
|
||||||
|
|
||||||
|
// Check for item in cache
|
||||||
|
item, ok = c.Cache.Get(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append this old value to slice
|
||||||
|
kvs = append(kvs, kv[K, V]{
|
||||||
|
K: key,
|
||||||
|
V: item.Value,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Remove from cache map
|
||||||
|
_ = c.Cache.Delete(key)
|
||||||
|
|
||||||
|
// Free entry
|
||||||
|
c.free(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set hook func ptrs.
|
||||||
|
invalid = c.Invalid
|
||||||
|
})
|
||||||
|
|
||||||
|
if invalid != nil {
|
||||||
|
for x := range kvs {
|
||||||
|
// Pass to invalidate hook.
|
||||||
|
invalid(kvs[x].K, kvs[x].V)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return item to pool
|
return
|
||||||
c.free(item)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear: implements cache.Cache's Clear().
|
// Clear: implements cache.Cache's Clear().
|
||||||
func (c *Cache[K, V]) Clear() {
|
func (c *Cache[K, V]) Clear() {
|
||||||
c.Lock()
|
var (
|
||||||
defer c.Unlock()
|
// deleted key-values.
|
||||||
c.truncate(c.Cache.Len(), c.Invalid)
|
kvs []kv[K, V]
|
||||||
|
|
||||||
|
// hook func ptrs.
|
||||||
|
invalid func(K, V)
|
||||||
|
)
|
||||||
|
|
||||||
|
c.locked(func() {
|
||||||
|
// Set hook func ptr.
|
||||||
|
invalid = c.Invalid
|
||||||
|
|
||||||
|
// Truncate the entire cache length.
|
||||||
|
kvs = c.truncate(c.Cache.Len(), invalid)
|
||||||
|
})
|
||||||
|
|
||||||
|
if invalid != nil {
|
||||||
|
for x := range kvs {
|
||||||
|
// Pass to invalidate hook.
|
||||||
|
invalid(kvs[x].K, kvs[x].V)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len: implements cache.Cache's Len().
|
// Len: implements cache.Cache's Len().
|
||||||
func (c *Cache[K, V]) Len() int {
|
func (c *Cache[K, V]) Len() (l int) {
|
||||||
c.Lock()
|
c.locked(func() { l = c.Cache.Len() })
|
||||||
l := c.Cache.Len()
|
return
|
||||||
c.Unlock()
|
|
||||||
return l
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cap: implements cache.Cache's Cap().
|
// Cap: implements cache.Cache's Cap().
|
||||||
func (c *Cache[K, V]) Cap() int {
|
func (c *Cache[K, V]) Cap() (l int) {
|
||||||
c.Lock()
|
c.locked(func() { l = c.Cache.Cap() })
|
||||||
l := c.Cache.Cap()
|
return
|
||||||
c.Unlock()
|
|
||||||
return l
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// truncate will call Cache.Truncate(sz), and if provided a hook will temporarily store deleted items before passing them to the hook. This is required in order to prevent cache writes during .Truncate().
|
func (c *Cache[K, V]) locked(fn func()) {
|
||||||
func (c *Cache[K, V]) truncate(sz int, hook func(*Entry[K, V])) {
|
c.Lock()
|
||||||
|
fn()
|
||||||
|
c.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// truncate will truncate the cache by given size, returning deleted items.
|
||||||
|
func (c *Cache[K, V]) truncate(sz int, hook func(K, V)) []kv[K, V] {
|
||||||
if hook == nil {
|
if hook == nil {
|
||||||
// No hook was provided, we can simply truncate and free items immediately.
|
// No hook to execute, simply free all truncated entries.
|
||||||
c.Cache.Truncate(sz, func(_ K, item *Entry[K, V]) { c.free(item) })
|
c.Cache.Truncate(sz, func(_ K, e *Entry[K, V]) { c.free(e) })
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store list of deleted items for later callbacks
|
// Allocate a slice for deleted k-v pairs.
|
||||||
deleted := make([]*Entry[K, V], 0, sz)
|
deleted := make([]kv[K, V], 0, sz)
|
||||||
|
|
||||||
// Truncate and store list of deleted items
|
|
||||||
c.Cache.Truncate(sz, func(_ K, item *Entry[K, V]) {
|
c.Cache.Truncate(sz, func(_ K, item *Entry[K, V]) {
|
||||||
deleted = append(deleted, item)
|
// Store key-value pair for later access.
|
||||||
|
deleted = append(deleted, kv[K, V]{
|
||||||
|
K: item.Key,
|
||||||
|
V: item.Value,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Free entry.
|
||||||
|
c.free(item)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Pass each deleted to hook, then free
|
return deleted
|
||||||
for _, item := range deleted {
|
|
||||||
hook(item)
|
|
||||||
c.free(item)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// alloc will acquire cache entry from pool, or allocate new.
|
// alloc will acquire cache entry from pool, or allocate new.
|
||||||
|
@ -416,14 +589,29 @@ func (c *Cache[K, V]) alloc() *Entry[K, V] {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clone allocates a new Entry and copies all info from passed Entry.
|
||||||
|
func (c *Cache[K, V]) clone(e *Entry[K, V]) *Entry[K, V] {
|
||||||
|
e2 := c.alloc()
|
||||||
|
e2.Key = e.Key
|
||||||
|
e2.Value = e.Value
|
||||||
|
e2.Expiry = e.Expiry
|
||||||
|
return e2
|
||||||
|
}
|
||||||
|
|
||||||
// free will reset entry fields and place back in pool.
|
// free will reset entry fields and place back in pool.
|
||||||
func (c *Cache[K, V]) free(e *Entry[K, V]) {
|
func (c *Cache[K, V]) free(e *Entry[K, V]) {
|
||||||
var (
|
var (
|
||||||
zk K
|
zk K
|
||||||
zv V
|
zv V
|
||||||
|
zt time.Time
|
||||||
)
|
)
|
||||||
|
e.Expiry = zt
|
||||||
e.Key = zk
|
e.Key = zk
|
||||||
e.Value = zv
|
e.Value = zv
|
||||||
e.Expiry = time.Time{}
|
|
||||||
c.pool = append(c.pool, e)
|
c.pool = append(c.pool, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type kv[K comparable, V any] struct {
|
||||||
|
K K
|
||||||
|
V V
|
||||||
|
}
|
||||||
|
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
|
@ -13,7 +13,7 @@ codeberg.org/gruf/go-bytesize
|
||||||
# codeberg.org/gruf/go-byteutil v1.1.2
|
# codeberg.org/gruf/go-byteutil v1.1.2
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
codeberg.org/gruf/go-byteutil
|
codeberg.org/gruf/go-byteutil
|
||||||
# codeberg.org/gruf/go-cache/v3 v3.3.0
|
# codeberg.org/gruf/go-cache/v3 v3.3.3
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
codeberg.org/gruf/go-cache/v3
|
codeberg.org/gruf/go-cache/v3
|
||||||
codeberg.org/gruf/go-cache/v3/result
|
codeberg.org/gruf/go-cache/v3/result
|
||||||
|
|
Loading…
Reference in a new issue