package ristretto
import (
"sync"
"time"
)
var (
bucketDurationSecs = int64 (5 )
)
func storageBucket(t time .Time ) int64 {
return (t .Unix () / bucketDurationSecs ) + 1
}
func cleanupBucket(t time .Time ) int64 {
return storageBucket (t ) - 1
}
type bucket map [uint64 ]uint64
type expirationMap[V any ] struct {
sync .RWMutex
buckets map [int64 ]bucket
lastCleanedBucketNum int64
}
func newExpirationMap[V any ]() *expirationMap [V ] {
return &expirationMap [V ]{
buckets : make (map [int64 ]bucket ),
lastCleanedBucketNum : cleanupBucket (time .Now ()),
}
}
func (m *expirationMap [_ ]) add (key , conflict uint64 , expiration time .Time ) {
if m == nil {
return
}
if expiration .IsZero () {
return
}
bucketNum := storageBucket (expiration )
m .Lock ()
defer m .Unlock ()
b , ok := m .buckets [bucketNum ]
if !ok {
b = make (bucket )
m .buckets [bucketNum ] = b
}
b [key ] = conflict
}
func (m *expirationMap [_ ]) update (key , conflict uint64 , oldExpTime , newExpTime time .Time ) {
if m == nil {
return
}
m .Lock ()
defer m .Unlock ()
oldBucketNum := storageBucket (oldExpTime )
oldBucket , ok := m .buckets [oldBucketNum ]
if ok {
delete (oldBucket , key )
}
if newExpTime .IsZero () {
return
}
newBucketNum := storageBucket (newExpTime )
newBucket , ok := m .buckets [newBucketNum ]
if !ok {
newBucket = make (bucket )
m .buckets [newBucketNum ] = newBucket
}
newBucket [key ] = conflict
}
func (m *expirationMap [_ ]) del (key uint64 , expiration time .Time ) {
if m == nil {
return
}
bucketNum := storageBucket (expiration )
m .Lock ()
defer m .Unlock ()
_ , ok := m .buckets [bucketNum ]
if !ok {
return
}
delete (m .buckets [bucketNum ], key )
}
func (m *expirationMap [V ]) cleanup (store store [V ], policy *defaultPolicy [V ], onEvict func (item *Item [V ])) int {
if m == nil {
return 0
}
m .Lock ()
now := time .Now ()
currentBucketNum := cleanupBucket (now )
var buckets []bucket
for bucketNum := m .lastCleanedBucketNum + 1 ; bucketNum <= currentBucketNum ; bucketNum ++ {
if b := m .buckets [bucketNum ]; b != nil {
buckets = append (buckets , b )
}
delete (m .buckets , bucketNum )
}
m .lastCleanedBucketNum = currentBucketNum
m .Unlock ()
for _ , keys := range buckets {
for key , conflict := range keys {
expr := store .Expiration (key )
if expr .After (now ) {
continue
}
cost := policy .Cost (key )
policy .Del (key )
_ , value := store .Del (key , conflict )
if onEvict != nil {
onEvict (&Item [V ]{Key : key ,
Conflict : conflict ,
Value : value ,
Cost : cost ,
Expiration : expr ,
})
}
}
}
cleanedBucketsCount := len (buckets )
return cleanedBucketsCount
}
func (m *expirationMap [V ]) clear () {
if m == nil {
return
}
m .Lock ()
m .buckets = make (map [int64 ]bucket )
m .lastCleanedBucketNum = cleanupBucket (time .Now ())
m .Unlock ()
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .