Skip to content

Instantly share code, notes, and snippets.

@hxzhouh

hxzhouh/cache.go Secret

Created December 16, 2024 10:31
Show Gist options
  • Save hxzhouh/1945d4a1e5a6567f084628d60b63f125 to your computer and use it in GitHub Desktop.
Save hxzhouh/1945d4a1e5a6567f084628d60b63f125 to your computer and use it in GitHub Desktop.
weak cache
package main
import (
"container/list"
"fmt"
"runtime"
"sync"
"time"
"weak"
)
type CacheItem struct {
key string
value any
}
type WeakCache struct {
cache map[string]weak.Pointer[list.Element] // Use weak references to store values
mu sync.Mutex
storage Storage
}
// Storage is a fixed-length cache based on doubly linked tables and weak
type Storage struct {
capacity int // Maximum size of the cache
list *list.List
}
// NewWeakCache creates a fixed-length weak reference cache.
func NewWeakCache(capacity int) *WeakCache {
return &WeakCache{
cache: make(map[string]weak.Pointer[list.Element]),
storage: Storage{capacity: capacity, list: list.New()},
}
}
// Set adds or updates cache entries
func (c *WeakCache) Set(key string, value any) {
// If the element already exists, update the value and move it to the head of the chain table
if elem, exists := c.cache[key]; exists {
if elemValue := elem.Value(); elemValue != nil {
elemValue.Value = &CacheItem{key: key, value: value}
c.storage.list.MoveToFront(elemValue)
elemWeak := weak.Make(elemValue)
c.cache[key] = elemWeak
return
} else {
c.removeElement(key)
}
}
// remove the oldest unused element if capacity is full
if c.storage.list.Len() >= c.storage.capacity {
c.evict()
}
// Add new element
elem := c.storage.list.PushFront(&CacheItem{key: key, value: value})
elemWeak := weak.Make(elem)
c.cache[key] = elemWeak
}
// Get gets the value of the cached item
func (c *WeakCache) Get(key string) (any, bool) {
if elem, exists := c.cache[key]; exists {
// Check if the weak reference is still valid
if elemValue := elem.Value(); elemValue != nil {
// Moving to the head of the chain indicates the most recent visit
c.storage.list.MoveToFront(elemValue)
return elemValue.Value.(*CacheItem).value, true
} else {
c.removeElement(key)
}
}
return nil, false
}
// evict removes the cache item that has not been used for the longest time
func (c *WeakCache) evict() {
if elem := c.storage.list.Back(); elem != nil {
item := elem.Value.(*CacheItem)
c.removeElement(item.key)
}
}
// removeElement removes elements from chains and dictionaries.
func (c *WeakCache) removeElement(key string) {
c.mu.Lock()
defer c.mu.Unlock()
if elem, exists := c.cache[key]; exists {
// Check if the weak reference is still valid
if elemValue := elem.Value(); elemValue != nil {
c.storage.list.Remove(elemValue)
}
delete(c.cache, key)
}
}
// Debug prints the contents of the cache
func (c *WeakCache) Debug() {
fmt.Println("Cache content:")
for k, v := range c.cache {
if v.Value() != nil {
fmt.Printf("Key: %s, Value: %v\n", k, v.Value().Value.(*CacheItem).value)
}
}
}
func (c *WeakCache) CleanCache() {
c.storage.list.Init()
c.storage.capacity = 0
}
func main() {
cache := NewWeakCache(3)
cache.Set("a", "value1")
cache.Set("b", "value2")
cache.Set("c", "value3")
runtime.GC()
time.Sleep(1 * time.Millisecond)
cache.Debug()
// Access "a" to make it the most recently used
_, _ = cache.Get("a")
runtime.GC()
time.Sleep(1 * time.Millisecond)
cache.Debug()
// Add new element "d", triggering the elimination of the oldest unused "b".
cache.Set("d", "value4")
runtime.GC()
time.Sleep(1 * time.Millisecond)
cache.Debug()
cache.CleanCache()
runtime.GC()
time.Sleep(1 * time.Millisecond)
cache.Debug()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment