[performance] update go-cache library (#1917)

* update go-cache library

Signed-off-by: kim <grufwub@gmail.com>

* fix broken test after cache library upgrade

Signed-off-by: kim <grufwub@gmail.com>

* fix the webfinger test

Signed-off-by: kim <grufwub@gmail.com>

---------

Signed-off-by: kim <grufwub@gmail.com>
This commit is contained in:
kim 2023-06-21 20:08:48 +01:00 committed by GitHub
parent 831ae09f8b
commit 8e0043104d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 58 additions and 28 deletions

2
go.mod
View file

@ -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.3 codeberg.org/gruf/go-cache/v3 v3.4.1
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
View file

@ -48,8 +48,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.3 h1:CzOFg6JV+typ8Jst1rrYDbZjxEV7bUxKggkbfN5Y79o= codeberg.org/gruf/go-cache/v3 v3.4.1 h1:dejl5nJC7wEsmbcU8D4EgZlo/tZgVO6iwPdrLBAa7eQ=
codeberg.org/gruf/go-cache/v3 v3.3.3/go.mod h1:pTeVPEb9DshXUkd8Dg76UcsLpU6EC/tXQ2qb+JrmxEc= codeberg.org/gruf/go-cache/v3 v3.4.1/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=

View file

@ -87,11 +87,9 @@ func (suite *FingerTestSuite) TestFingerWithHostMetaCacheStrategy() {
// the TTL of the entry should have extended because we did a second // the TTL of the entry should have extended because we did a second
// successful finger // successful finger
if repeatTime.Equal(initialTime) { if repeatTime == initialTime {
suite.FailNowf("expected webfinger cache entry to have different expiry times", "initial: '%s', repeat: '%s'", initialTime, repeatTime) suite.FailNowf("expected webfinger cache entry to have different expiry times", "initial: '%s', repeat: '%s'", initialTime, repeatTime)
} } else if repeatTime < initialTime {
if repeatTime.Before(initialTime) {
suite.FailNowf("expected webfinger cache entry to not be a time traveller", "initial: '%s', repeat: '%s'", initialTime, repeatTime) suite.FailNowf("expected webfinger cache entry to not be a time traveller", "initial: '%s', repeat: '%s'", initialTime, repeatTime)
} }

View file

@ -4,6 +4,7 @@
"context" "context"
"reflect" "reflect"
"time" "time"
_ "unsafe"
"codeberg.org/gruf/go-cache/v3/ttl" "codeberg.org/gruf/go-cache/v3/ttl"
"codeberg.org/gruf/go-errors/v2" "codeberg.org/gruf/go-errors/v2"
@ -385,7 +386,7 @@ func (c *Cache[Value]) store(res result[Value]) (evict func()) {
// Store main entry under primary key, using evict hook if needed // Store main entry under primary key, using evict hook if needed
c.cache.Cache.SetWithHook(pnext, &ttl.Entry[int64, result[Value]]{ c.cache.Cache.SetWithHook(pnext, &ttl.Entry[int64, result[Value]]{
Expiry: time.Now().Add(c.cache.TTL), Expiry: c.expiry(),
Key: pnext, Key: pnext,
Value: res, Value: res,
}, func(_ int64, item *ttl.Entry[int64, result[Value]]) { }, func(_ int64, item *ttl.Entry[int64, result[Value]]) {
@ -395,6 +396,19 @@ func (c *Cache[Value]) store(res result[Value]) (evict func()) {
return evict return evict
} }
//go:linkname runtime_nanotime runtime.nanotime
func runtime_nanotime() uint64
// expiry returns an the next expiry time to use for an entry,
// which is equivalent to time.Now().Add(ttl), or zero if disabled.
func (c *Cache[Value]) expiry() uint64 {
if ttl := c.cache.TTL; ttl > 0 {
return runtime_nanotime() +
uint64(c.cache.TTL)
}
return 0
}
type result[Value any] struct { type result[Value any] struct {
// keys accessible under // keys accessible under
Keys cacheKeys Keys cacheKeys

View file

@ -3,6 +3,7 @@
import ( import (
"sync" "sync"
"time" "time"
_ "unsafe"
"codeberg.org/gruf/go-maps" "codeberg.org/gruf/go-maps"
) )
@ -11,7 +12,7 @@
type Entry[Key comparable, Value any] struct { type Entry[Key comparable, Value any] struct {
Key Key Key Key
Value Value Value Value
Expiry time.Time Expiry uint64
} }
// Cache is the underlying Cache implementation, providing both the base Cache interface and unsafe access to underlying map to allow flexibility in building your own. // Cache is the underlying Cache implementation, providing both the base Cache interface and unsafe access to underlying map to allow flexibility in building your own.
@ -67,7 +68,7 @@ func (c *Cache[K, V]) Start(freq time.Duration) (ok bool) {
// Safely start // Safely start
c.Lock() c.Lock()
if ok = c.stop == nil; ok { if ok = (c.stop == nil); ok {
// Not yet running, schedule us // Not yet running, schedule us
c.stop = schedule(c.Sweep, freq) c.stop = schedule(c.Sweep, freq)
} }
@ -83,7 +84,7 @@ func (c *Cache[K, V]) Stop() (ok bool) {
// Safely stop // Safely stop
c.Lock() c.Lock()
if ok = c.stop != nil; ok { if ok = (c.stop != nil); ok {
// We're running, cancel evicts // We're running, cancel evicts
c.stop() c.stop()
c.stop = nil c.stop = nil
@ -96,23 +97,32 @@ 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(_ time.Time) {
var ( var (
// evicted key-values. // evicted key-values.
kvs []kv[K, V] kvs []kv[K, V]
// hook func ptrs. // hook func ptrs.
evict func(K, V) evict func(K, V)
// get current nanoseconds.
now = runtime_nanotime()
) )
c.locked(func() { c.locked(func() {
if c.TTL <= 0 {
// sweep is
// disabled
return
}
// Sentinel value // Sentinel value
after := -1 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 > item.Expiry {
after = i after = i
// evict all older items // evict all older items
@ -161,10 +171,6 @@ func (c *Cache[K, V]) SetInvalidateCallback(hook func(K, V)) {
// SetTTL: implements cache.Cache's SetTTL(). // SetTTL: implements cache.Cache's SetTTL().
func (c *Cache[K, V]) SetTTL(ttl time.Duration, update bool) { func (c *Cache[K, V]) SetTTL(ttl time.Duration, update bool) {
if ttl < 0 {
panic("ttl must be greater than zero")
}
c.locked(func() { c.locked(func() {
// Set updated TTL // Set updated TTL
diff := ttl - c.TTL diff := ttl - c.TTL
@ -173,7 +179,7 @@ func (c *Cache[K, V]) SetTTL(ttl time.Duration, update bool) {
if update { if update {
// Update existing cache entries with new expiry time // Update existing cache entries with new expiry time
c.Cache.Range(0, c.Cache.Len(), func(i int, _ K, item *Entry[K, V]) { c.Cache.Range(0, c.Cache.Len(), func(i int, _ K, item *Entry[K, V]) {
item.Expiry = item.Expiry.Add(diff) item.Expiry += uint64(diff)
}) })
} }
}) })
@ -198,8 +204,8 @@ func (c *Cache[K, V]) Get(key K) (V, bool) {
return return
} }
// Update fetched item's expiry // Update fetched's expiry
item.Expiry = time.Now().Add(c.TTL) item.Expiry = c.expiry()
// Set value. // Set value.
v = item.Value v = item.Value
@ -234,7 +240,7 @@ func (c *Cache[K, V]) Add(key K, value V) bool {
// Alloc new entry. // Alloc new entry.
new := c.alloc() new := c.alloc()
new.Expiry = time.Now().Add(c.TTL) new.Expiry = c.expiry()
new.Key = key new.Key = key
new.Value = value new.Value = value
@ -290,12 +296,12 @@ func (c *Cache[K, V]) Set(key K, value V) {
oldV = item.Value oldV = item.Value
// Update the existing item. // Update the existing item.
item.Expiry = time.Now().Add(c.TTL) item.Expiry = c.expiry()
item.Value = value item.Value = value
} else { } else {
// Alloc new entry. // Alloc new entry.
new := c.alloc() new := c.alloc()
new.Expiry = time.Now().Add(c.TTL) new.Expiry = c.expiry()
new.Key = key new.Key = key
new.Value = value new.Value = value
@ -355,7 +361,7 @@ func (c *Cache[K, V]) CAS(key K, old V, new V, cmp func(V, V) bool) bool {
oldV = item.Value oldV = item.Value
// Update value + expiry. // Update value + expiry.
item.Expiry = time.Now().Add(c.TTL) item.Expiry = c.expiry()
item.Value = new item.Value = new
// Set hook func ptr. // Set hook func ptr.
@ -396,7 +402,7 @@ func (c *Cache[K, V]) Swap(key K, swp V) V {
oldV = item.Value oldV = item.Value
// Update value + expiry. // Update value + expiry.
item.Expiry = time.Now().Add(c.TTL) item.Expiry = c.expiry()
item.Value = swp item.Value = swp
// Set hook func ptr. // Set hook func ptr.
@ -603,14 +609,26 @@ 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.Expiry = 0
e.Key = zk e.Key = zk
e.Value = zv e.Value = zv
c.pool = append(c.pool, e) c.pool = append(c.pool, e)
} }
//go:linkname runtime_nanotime runtime.nanotime
func runtime_nanotime() uint64
// expiry returns an the next expiry time to use for an entry,
// which is equivalent to time.Now().Add(ttl), or zero if disabled.
func (c *Cache[K, V]) expiry() uint64 {
if ttl := c.TTL; ttl > 0 {
return runtime_nanotime() +
uint64(c.TTL)
}
return 0
}
type kv[K comparable, V any] struct { type kv[K comparable, V any] struct {
K K K K
V V V V

2
vendor/modules.txt vendored
View file

@ -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.3 # codeberg.org/gruf/go-cache/v3 v3.4.1
## 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