@ -47,15 +47,15 @@ func New[V any](opts ...Option) Cache[V] {
if opt . localSlotNum > 0 && opt . localSlotSize > 0 {
if opt . localSlotNum > 0 && opt . localSlotSize > 0 {
createSimpleLRU := func ( ) lru . LRU [ string , V ] {
createSimpleLRU := func ( ) lru . LRU [ string , V ] {
if opt . expirationEvict {
if opt . expirationEvict {
return lru . NewExpirationLRU ( opt . localSlotSize , opt . localSuccessTTL , opt . localFailedTTL , opt . target , c . onEvict )
return lru . NewExpirationLRU [ string , V ] ( opt . localSlotSize , opt . localSuccessTTL , opt . localFailedTTL , opt . target , c . onEvict )
} else {
} else {
return lru . NewLazyLRU ( opt . localSlotSize , opt . localSuccessTTL , opt . localFailedTTL , opt . target , c . onEvict )
return lru . NewLazyLRU [ string , V ] ( opt . localSlotSize , opt . localSuccessTTL , opt . localFailedTTL , opt . target , c . onEvict )
}
}
}
}
if opt . localSlotNum == 1 {
if opt . localSlotNum == 1 {
c . local = createSimpleLRU ( )
c . local = createSimpleLRU ( )
} else {
} else {
c . local = lru . NewSlotLRU ( opt . localSlotNum , LRUStringHash , createSimpleLRU )
c . local = lru . NewSlotLRU [ string , V ] ( opt . localSlotNum , LRUStringHash , createSimpleLRU )
}
}
if opt . linkSlotNum > 0 {
if opt . linkSlotNum > 0 {
c . link = link . New ( opt . linkSlotNum )
c . link = link . New ( opt . linkSlotNum )
@ -71,15 +71,20 @@ type cache[V any] struct {
}
}
func ( c * cache [ V ] ) onEvict ( key string , value V ) {
func ( c * cache [ V ] ) onEvict ( key string , value V ) {
_ = value
if c . link != nil {
if c . link != nil {
lks := c . link . Del ( key )
// Do not delete other keys while the underlying LRU still holds its lock;
for k := range lks {
// defer linked deletions to avoid re-entering the same slot and deadlocking.
if key != k { // prevent deadlock
if lks := c . link . Del ( key ) ; len ( lks ) > 0 {
c . local . Del ( k )
go c . delLinked ( key , lks )
}
}
}
}
}
func ( c * cache [ V ] ) delLinked ( src string , keys map [ string ] struct { } ) {
for k := range keys {
if src != k {
c . local . Del ( k )
}
}
}
}
}
@ -105,7 +110,7 @@ func (c *cache[V]) Get(ctx context.Context, key string, fetch func(ctx context.C
func ( c * cache [ V ] ) GetLink ( ctx context . Context , key string , fetch func ( ctx context . Context ) ( V , error ) , link ... string ) ( V , error ) {
func ( c * cache [ V ] ) GetLink ( ctx context . Context , key string , fetch func ( ctx context . Context ) ( V , error ) , link ... string ) ( V , error ) {
if c . local != nil {
if c . local != nil {
return c . local . Get ( key , func ( ) ( V , error ) {
return c . local . Get ( key , func ( ) ( V , error ) {
if len ( link ) > 0 {
if len ( link ) > 0 && c . link != nil {
c . link . Link ( key , link ... )
c . link . Link ( key , link ... )
}
}
return fetch ( ctx )
return fetch ( ctx )