diff --git a/cache/cache.go b/cache/cache.go index 70e7a8705..acb1450e4 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -240,7 +240,7 @@ func (c *cache[T]) get(key string) (T, bool, error) { recordRequest(c.metrics, StatusSuccess) return res, false, nil } - if item.expiresAt.UnixNano() > 0 { + if !item.expiresAt.IsZero() { if item.expiresAt.Compare(time.Now()) < 0 { c.mu.RUnlock() recordRequest(c.metrics, StatusSuccess) @@ -430,7 +430,7 @@ func (c *Cache[T]) GetExpiration(object T) (time.Time, error) { recordRequest(c.metrics, StatusSuccess) return time.Time{}, KeyError{object, ErrNotFound} } - if item.expiresAt.UnixNano() > 0 { + if !item.expiresAt.IsZero() { if item.expiresAt.Compare(time.Now()) < 0 { c.mu.RUnlock() recordRequest(c.metrics, StatusSuccess) @@ -459,10 +459,10 @@ func (c *cache[T]) deleteExpired() { c.sorted = true } - t := time.Now().UnixNano() + t := time.Now() index := sort.Search(len(c.items), func(i int) bool { // smallest index with an expiration greater than t - return c.items[i].expiresAt.UnixNano() > t + return c.items[i].expiresAt.Compare(t) > 0 }) // delete the expired indexes diff --git a/cache/lru.go b/cache/lru.go index ea5f35910..ebe641854 100644 --- a/cache/lru.go +++ b/cache/lru.go @@ -42,6 +42,26 @@ func (n *node[T]) addPrev(node *node[T]) { // All methods are safe for concurrent use. // All operations are O(1). The hash map lookup is O(1) and so is the doubly // linked list insertion/deletion. +// +// The LRU is implemented as a doubly linked list, where the most recently accessed +// item is at the front of the list and the least recently accessed item is at +// the back. When an item is accessed, it is moved to the front of the list. +// When the cache is full, the least recently accessed item is removed from the +// back of the list. +// +// Cache +// ┌───────────────────────────────────────────────────┐ +// │ │ +// empty │ obj obj obj obj │ empty +// ┌───────┐ │ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │ ┌───────┐ +// │ │ │ │ │ │ │ ... │ │ │ │ │ │ │ +// │ HEAD │◄─┼─►│ │◄─►│ │◄───►│ │◄─►│ │◄─┼─►│ TAIL │ +// │ │ │ │ │ │ │ │ │ │ │ │ │ │ +// └───────┘ │ └───────┘ └───────┘ └───────┘ └───────┘ │ └───────┘ +// │ │ +// │ │ +// └───────────────────────────────────────────────────┘ +// // A function to extract the key from the object must be provided. // Use the NewLRU function to create a new cache that is ready to use. type LRU[T any] struct { @@ -165,6 +185,7 @@ func (c *LRU[T]) Delete(object T) error { func (c *LRU[T]) delete(node *node[T]) { node.prev.next, node.next.prev = node.next, node.prev + node.next, node.prev = nil, nil // avoid memory leaks delete(c.cache, node.key) }