Skip to content

Commit 7f621fd

Browse files
committed
Add freelru.SetUpdateLifetimeOnGet/GetWithLifetime
1 parent ae139d9 commit 7f621fd

File tree

5 files changed

+60
-21
lines changed

5 files changed

+60
-21
lines changed

common/udpnat2/service.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func New(handler N.UDPConnectionHandlerEx, prepare PrepareFunc, timeout time.Dur
3737
cache = common.Must1(freelru.NewSharded[netip.AddrPort, *Conn](1024, maphash.NewHasher[netip.AddrPort]().Hash32))
3838
}
3939
cache.SetLifetime(timeout)
40+
cache.SetUpdateLifetimeOnGet(true)
4041
cache.SetHealthCheck(func(port netip.AddrPort, conn *Conn) bool {
4142
select {
4243
case <-conn.doneChan:

contrab/freelru/cache.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ type Cache[K comparable, V any] interface {
2424
// Lifetime 0 means "forever".
2525
SetLifetime(lifetime time.Duration)
2626

27+
SetUpdateLifetimeOnGet(update bool)
28+
2729
SetHealthCheck(healthCheck HealthCheckCallback[K, V])
2830

2931
// SetOnEvict sets the OnEvict callback function.
@@ -47,6 +49,8 @@ type Cache[K comparable, V any] interface {
4749
// and the return value indicates that the key was not found.
4850
Get(key K) (V, bool)
4951

52+
GetWithLifetime(key K) (value V, lifetime time.Time, ok bool)
53+
5054
// Peek looks up a key's value from the cache, without changing its recent-ness.
5155
// If the found entry is already expired, the evict function is called.
5256
Peek(key K) (V, bool)

contrab/freelru/lru.go

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,14 @@ const emptyBucket = math.MaxUint32
6363

6464
// LRU implements a non-thread safe fixed size LRU cache.
6565
type LRU[K comparable, V any] struct {
66-
buckets []uint32 // contains positions of bucket lists or 'emptyBucket'
67-
elements []element[K, V]
68-
onEvict OnEvictCallback[K, V]
69-
hash HashKeyCallback[K]
70-
healthCheck HealthCheckCallback[K, V]
71-
lifetime time.Duration
72-
metrics Metrics
66+
buckets []uint32 // contains positions of bucket lists or 'emptyBucket'
67+
elements []element[K, V]
68+
onEvict OnEvictCallback[K, V]
69+
hash HashKeyCallback[K]
70+
healthCheck HealthCheckCallback[K, V]
71+
lifetime time.Duration
72+
updateLifetimeOnGet bool
73+
metrics Metrics
7374

7475
// used for element clearing after removal or expiration
7576
emptyKey K
@@ -100,6 +101,10 @@ func (lru *LRU[K, V]) SetLifetime(lifetime time.Duration) {
100101
lru.lifetime = lifetime
101102
}
102103

104+
func (lru *LRU[K, V]) SetUpdateLifetimeOnGet(update bool) {
105+
lru.updateLifetimeOnGet = update
106+
}
107+
103108
// SetOnEvict sets the OnEvict callback function.
104109
// The onEvict function is called for each evicted lru entry.
105110
// Eviction happens
@@ -303,10 +308,10 @@ func (lru *LRU[K, V]) clearKeyAndValue(pos uint32) {
303308
lru.elements[pos].value = lru.emptyValue
304309
}
305310

306-
func (lru *LRU[K, V]) findKey(hash uint32, key K, updateLifetimeOnGet bool) (uint32, bool) {
311+
func (lru *LRU[K, V]) findKey(hash uint32, key K, updateLifetimeOnGet bool) (uint32, int64, bool) {
307312
_, startPos := lru.hashToPos(hash)
308313
if startPos == emptyBucket {
309-
return emptyBucket, false
314+
return emptyBucket, 0, false
310315
}
311316

312317
pos := startPos
@@ -315,18 +320,18 @@ func (lru *LRU[K, V]) findKey(hash uint32, key K, updateLifetimeOnGet bool) (uin
315320
elem := lru.elements[pos]
316321
if (elem.expire != 0 && elem.expire <= now()) || (lru.healthCheck != nil && !lru.healthCheck(key, elem.value)) {
317322
lru.removeAt(pos)
318-
return emptyBucket, false
323+
return emptyBucket, elem.expire, false
319324
}
320325
if updateLifetimeOnGet {
321326
lru.elements[pos].expire = expire(lru.lifetime)
322327
}
323-
return pos, true
328+
return pos, elem.expire, true
324329
}
325330

326331
pos = lru.elements[pos].nextBucket
327332
if pos == startPos {
328333
// Key not found
329-
return emptyBucket, false
334+
return emptyBucket, 0, false
330335
}
331336
}
332337
}
@@ -439,17 +444,24 @@ func (lru *LRU[K, V]) add(hash uint32, key K, value V) (evicted bool) {
439444
// If the found cache item is already expired, the evict function is called
440445
// and the return value indicates that the key was not found.
441446
func (lru *LRU[K, V]) Get(key K) (value V, ok bool) {
442-
return lru.get(lru.hash(key), key)
447+
value, _, ok = lru.get(lru.hash(key), key)
448+
return
443449
}
444450

445-
func (lru *LRU[K, V]) get(hash uint32, key K) (value V, ok bool) {
446-
if pos, ok := lru.findKey(hash, key, true); ok {
451+
func (lru *LRU[K, V]) GetWithLifetime(key K) (value V, lifetime time.Time, ok bool) {
452+
value, expireMills, ok := lru.get(lru.hash(key), key)
453+
lifetime = time.UnixMilli(expireMills)
454+
return
455+
}
456+
457+
func (lru *LRU[K, V]) get(hash uint32, key K) (value V, expire int64, ok bool) {
458+
if pos, expire, ok := lru.findKey(hash, key, lru.updateLifetimeOnGet); ok {
447459
if pos != lru.head {
448460
lru.unlinkElement(pos)
449461
lru.setHead(pos)
450462
}
451463
lru.metrics.Hits++
452-
return lru.elements[pos].value, ok
464+
return lru.elements[pos].value, expire, ok
453465
}
454466

455467
lru.metrics.Misses++
@@ -463,7 +475,7 @@ func (lru *LRU[K, V]) Peek(key K) (value V, ok bool) {
463475
}
464476

465477
func (lru *LRU[K, V]) peek(hash uint32, key K) (value V, ok bool) {
466-
if pos, ok := lru.findKey(hash, key, false); ok {
478+
if pos, _, ok := lru.findKey(hash, key, false); ok {
467479
return lru.elements[pos].value, ok
468480
}
469481

@@ -490,7 +502,7 @@ func (lru *LRU[K, V]) Remove(key K) (removed bool) {
490502
}
491503

492504
func (lru *LRU[K, V]) remove(hash uint32, key K) (removed bool) {
493-
if pos, ok := lru.findKey(hash, key, false); ok {
505+
if pos, _, ok := lru.findKey(hash, key, false); ok {
494506
lru.removeAt(pos)
495507
return ok
496508
}

contrab/freelru/lru_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,21 @@ func TestMyChange0(t *testing.T) {
1414
t.Parallel()
1515
lru, err := freelru.New[string, string](1024, maphash.NewHasher[string]().Hash32)
1616
require.NoError(t, err)
17+
lru.SetUpdateLifetimeOnGet(true)
1718
lru.AddWithLifetime("hello", "world", 2*time.Second)
1819
time.Sleep(time.Second)
19-
lru.Get("hello")
20-
time.Sleep(time.Second + time.Millisecond*100)
2120
_, ok := lru.Get("hello")
2221
require.True(t, ok)
22+
time.Sleep(time.Second + time.Millisecond*100)
23+
_, ok = lru.Get("hello")
24+
require.True(t, ok)
2325
}
2426

2527
func TestMyChange1(t *testing.T) {
2628
t.Parallel()
2729
lru, err := freelru.New[string, string](1024, maphash.NewHasher[string]().Hash32)
2830
require.NoError(t, err)
31+
lru.SetUpdateLifetimeOnGet(true)
2932
lru.AddWithLifetime("hello", "world", 2*time.Second)
3033
time.Sleep(time.Second)
3134
lru.Peek("hello")

contrab/freelru/sharedlru.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ func (lru *ShardedLRU[K, V]) SetLifetime(lifetime time.Duration) {
3232
}
3333
}
3434

35+
func (lru *ShardedLRU[K, V]) SetUpdateLifetimeOnGet(update bool) {
36+
for shard := range lru.lrus {
37+
lru.mus[shard].Lock()
38+
lru.lrus[shard].SetUpdateLifetimeOnGet(update)
39+
lru.mus[shard].Unlock()
40+
}
41+
}
42+
3543
// SetOnEvict sets the OnEvict callback function.
3644
// The onEvict function is called for each evicted lru entry.
3745
func (lru *ShardedLRU[K, V]) SetOnEvict(onEvict OnEvictCallback[K, V]) {
@@ -170,12 +178,23 @@ func (lru *ShardedLRU[K, V]) Get(key K) (value V, ok bool) {
170178
shard := (hash >> 16) & lru.mask
171179

172180
lru.mus[shard].Lock()
173-
value, ok = lru.lrus[shard].get(hash, key)
181+
value, _, ok = lru.lrus[shard].get(hash, key)
174182
lru.mus[shard].Unlock()
175183

176184
return
177185
}
178186

187+
func (lru *ShardedLRU[K, V]) GetWithLifetime(key K) (value V, lifetime time.Time, ok bool) {
188+
hash := lru.hash(key)
189+
shard := (hash >> 16) & lru.mask
190+
191+
lru.mus[shard].Lock()
192+
value, expireMills, ok := lru.lrus[shard].get(hash, key)
193+
lru.mus[shard].Unlock()
194+
lifetime = time.UnixMilli(expireMills)
195+
return
196+
}
197+
179198
// Peek looks up a key's value from the cache, without changing its recent-ness.
180199
// If the found entry is already expired, the evict function is called.
181200
func (lru *ShardedLRU[K, V]) Peek(key K) (value V, ok bool) {

0 commit comments

Comments
 (0)