Skip to content

Commit

Permalink
🔇 silent changes: update codebase #2
Browse files Browse the repository at this point in the history
  • Loading branch information
pnguyen215 committed Jan 22, 2025
1 parent d06a0aa commit b8fb841
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 23 deletions.
221 changes: 201 additions & 20 deletions lru.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,18 @@ import (
"time"
)

// NewLRU creates a new LRUCache with the specified capacity.
// NewLRU creates a new LRU cache with the specified capacity.
//
// Parameters:
// - capacity: The maximum number of items the cache can hold.
//
// Returns:
// - A pointer to an initialized LRU cache.
//
// Details:
// - The cache uses a combination of a map and a doubly linked list for efficient
// O(1) insertion, deletion, and lookup operations.
// - Items are evicted based on the "least recently used" policy when the capacity is exceeded.
func NewLRU(capacity int) *LRU {
return &LRU{
capacity: capacity,
Expand All @@ -14,15 +25,34 @@ func NewLRU(capacity int) *LRU {
}
}

// NewLRUCache creates a new LRUCache with the specified capacity and an optional eviction callback.
// NewLRUCallback creates a new LRU cache with the specified capacity and eviction callback.
//
// Parameters:
// - capacity: The maximum number of items the cache can hold.
// - callback: A function of type `OnCallback` that gets invoked when an item is evicted.
//
// Returns:
// - A pointer to an initialized LRU cache.
//
// Details:
// - The callback function is executed before an item is removed from the cache.
func NewLRUCallback(capacity int, callback OnCallback) *LRU {
c := NewLRU(capacity)
c.onEvict = callback
return c
}

// NewLRUCache creates a new LRUCache with the specified capacity, an optional eviction callback,
// and an optional time-to-live for cache entries.
// NewLRUExpires creates a new LRU cache with a time-to-live for entries.
//
// Parameters:
// - capacity: The maximum number of items the cache can hold.
// - expiry: The expiration duration for each cache entry.
//
// Returns:
// - A pointer to an initialized LRU cache.
//
// Details:
// - Starts a background goroutine to periodically remove expired items.
func NewLRUExpires(capacity int, expiry time.Duration) *LRU {
c := NewLRU(capacity)
c.SetExpiry(expiry)
Expand All @@ -32,7 +62,18 @@ func NewLRUExpires(capacity int, expiry time.Duration) *LRU {
return c
}

// Get retrieves a value from the cache based on the key.
// Get retrieves the value associated with a given key from the cache.
//
// Parameters:
// - key: The key whose value is to be retrieved.
//
// Returns:
// - The value associated with the key, or nil if the key is not found.
// - A boolean indicating whether the key exists.
//
// Details:
// - Moves the accessed item to the front of the list, marking it as most recently used.
// - Evicts the item if it is expired (when expiration is enabled).
func (c *LRU) Get(key string) (value interface{}, ok bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
Expand All @@ -51,7 +92,13 @@ func (c *LRU) Get(key string) (value interface{}, ok bool) {
return nil, false
}

// GetAll returns all key-value pairs in the cache.
// GetAll retrieves all key-value pairs currently in the cache.
//
// Returns:
// - A map containing all key-value pairs in the cache.
//
// Details:
// - Does not modify the order of items in the cache.
func (c *LRU) GetAll() map[string]interface{} {
c.mutex.RLock()
defer c.mutex.RUnlock()
Expand All @@ -64,7 +111,11 @@ func (c *LRU) GetAll() map[string]interface{} {
return allEntries
}

// Pairs returns the least recently used key-value pair without removing it from the cache.
// Pairs retrieves the least recently used key-value pair without removing it.
//
// Returns:
// - The key and value of the least recently used item.
// - A boolean indicating whether such an item exists.
func (c *LRU) Pairs() (key string, value interface{}, ok bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
Expand All @@ -77,7 +128,16 @@ func (c *LRU) Pairs() (key string, value interface{}, ok bool) {
return "", nil, false
}

// Set adds a key-value pair to the cache. If the cache is full, it removes the least recently used item.
// Set inserts or updates a key-value pair in the cache.
//
// Parameters:
// - key: The key to be added or updated.
// - value: The value to be associated with the key.
//
// Details:
// - If the key exists, updates its value and moves it to the front of the list.
// - If the key does not exist and the cache is full, evicts the least recently used item.
// - The expiration time is reset or initialized based on the cache's expiration setting.
func (c *LRU) Set(key string, value interface{}) {
c.mutex.Lock()
defer c.mutex.Unlock()
Expand Down Expand Up @@ -108,7 +168,10 @@ func (c *LRU) Set(key string, value interface{}) {
}
}

// Update updates the value associated with a specific key in the cache.
// Update updates the value associated with a key in the cache.
// Parameters:
// - key: The key to update.
// - value: The new value to associate with the key.
func (c *LRU) Update(key string, value interface{}) {
c.mutex.Lock()
defer c.mutex.Unlock()
Expand All @@ -121,7 +184,13 @@ func (c *LRU) Update(key string, value interface{}) {
}
}

// Remove removes a specific key from the cache.
// Remove deletes a specific key-value pair from the cache.
//
// Parameters:
// - key: The key to be removed.
//
// Details:
// - If the key does not exist, the method does nothing.
func (c *LRU) Remove(key string) {
c.mutex.Lock()
defer c.mutex.Unlock()
Expand All @@ -130,22 +199,31 @@ func (c *LRU) Remove(key string) {
}
}

// Clear removes all items from the cache.
// Clear removes all key-value pairs from the cache.
//
// Details:
// - Resets the internal data structures to their initial state.
func (c *LRU) Clear() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.cache = make(map[string]*list.Element)
c.list.Init()
}

// Len returns the number of items in the cache.
// Len returns the current number of items in the cache.
//
// Returns:
// - The number of items in the cache.
func (c *LRU) Len() int {
c.mutex.RLock()
defer c.mutex.RUnlock()
return len(c.cache)
}

// IsEmpty checks if the cache is empty.
//
// Returns:
// - A boolean indicating whether the cache contains no items.
func (c *LRU) IsEmpty() bool {
return c.Len() == 0
}
Expand Down Expand Up @@ -187,20 +265,51 @@ func (c *LRU) SetCapacity(capacity int) {
}

// SetCallback sets the eviction callback function.
//
// Parameters:
// - callback: A function of type `OnCallback` to be invoked when an item is evicted from the cache.
//
// Details:
// - Uses write locking to ensure thread-safe updates to the `onEvict` field.
// - Replaces the existing callback (if any) with the provided one.
// - This callback function will be triggered during evictions, allowing custom behavior
// (e.g., logging, cleanup, or persisting evicted data).
//
// Example Usage:
//
// cache.SetCallback(func(key string, value interface{}) {
// fmt.Printf("Evicted: key=%s, value=%v\n", key, value)
// })
func (c *LRU) SetCallback(callback OnCallback) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.onEvict = callback
}

// SetExpiry updates the expiration time for cache entries.
// SetExpiry sets the default expiration duration for cache entries.
//
// Parameters:
// - expiry: The duration after which a cache entry should expire.
//
// Details:
// - This affects only new entries or updated entries after the call to SetExpiry.
// - Existing entries retain their current expiration times until updated.
func (c *LRU) SetExpiry(expiry time.Duration) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.expiration = expiry
}

// GetStates returns a snapshot of the current cache state.
//
// Returns:
// - A slice of `state` objects representing all the items in the cache.
// - Each `state` includes the key, value, access time, and expiration time.
//
// Details:
// - Uses read locking to ensure safe concurrent access.
// - Iterates through all cache entries, capturing their metadata.
// - Creates a new `state` object for each entry using a builder-like pattern.
func (c *LRU) GetStates() []state {
c.mutex.RLock()
defer c.mutex.RUnlock()
Expand All @@ -219,7 +328,16 @@ func (c *LRU) GetStates() []state {
return snapshot
}

// GetState returns the metadata of the least recently used item without removing it from the cache.
// GetState returns the metadata of the least recently used (LRU) item without removing it.
//
// Returns:
// - A pointer to a `state` object representing the LRU item, or nil if the cache is empty.
// - A boolean indicating whether a valid state was retrieved.
//
// Details:
// - Uses read locking to safely access the cache state.
// - Retrieves the least recently used item from the tail of the doubly-linked list.
// - Constructs a `state` object to represent the item's metadata.
func (c *LRU) GetState() (m *state, ok bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
Expand All @@ -238,6 +356,17 @@ func (c *LRU) GetState() (m *state, ok bool) {
}

// IsMostRecentlyUsed checks if a specific key is the most recently used item in the cache.
//
// Parameters:
// - key: The key to check.
//
// Returns:
// - true if the specified key is the most recently used item.
// - false otherwise or if the cache is empty.
//
// Details:
// - Uses read locking to safely access the cache state.
// - Compares the provided key with the key of the item at the front of the list (MRU).
func (c *LRU) IsMostRecentlyUsed(key string) bool {
c.mutex.RLock()
defer c.mutex.RUnlock()
Expand All @@ -249,7 +378,16 @@ func (c *LRU) IsMostRecentlyUsed(key string) bool {
return false
}

// GetMostRecentlyUsed returns the most recently used key-value pair without removing it from the cache.
// GetMostRecentlyUsed returns the most recently used (MRU) key-value pair without removing it.
//
// Returns:
// - A pointer to a `state` object representing the MRU item, or nil if the cache is empty.
// - A boolean indicating whether a valid state was retrieved.
//
// Details:
// - Uses read locking to safely access the cache state.
// - Retrieves the most recently used item from the head of the doubly-linked list.
// - Constructs a `state` object to represent the item's metadata.
func (c *LRU) GetMostRecentlyUsed() (m *state, ok bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
Expand All @@ -268,6 +406,15 @@ func (c *LRU) GetMostRecentlyUsed() (m *state, ok bool) {
}

// ExpandExpiry extends the expiration time of a specific key in the cache.
//
// Parameters:
// - key: The key of the cache entry to extend the expiration for.
// - expiry: The duration by which to extend the expiration time.
//
// Details:
// - Uses write locking to ensure safe updates.
// - If the key exists, updates its expiration time and moves it to the front of the list.
// - Does nothing if the key does not exist in the cache.
func (c *LRU) ExpandExpiry(key string, expiry time.Duration) {
c.mutex.Lock()
defer c.mutex.Unlock()
Expand All @@ -280,6 +427,18 @@ func (c *LRU) ExpandExpiry(key string, expiry time.Duration) {
}

// PersistExpiry returns the remaining time until expiration for a specific key.
//
// Parameters:
// - key: The key of the cache entry to check.
//
// Returns:
// - The remaining time until the entry expires.
// - A boolean indicating whether the key exists in the cache.
//
// Details:
// - Uses read locking to safely access the cache state.
// - If the key exists, calculates the time remaining until expiration.
// - Returns 0 and false if the key does not exist.
func (c *LRU) PersistExpiry(key string) (remain time.Duration, ok bool) {
c.mutex.RLock()
defer c.mutex.RUnlock()
Expand All @@ -295,12 +454,21 @@ func (c *LRU) PersistExpiry(key string) (remain time.Duration, ok bool) {
return 0, false
}

// DestroyCleanup stops the background goroutine for periodic cache cleanup.
// DestroyCleanup stops the background cleanup process.
//
// Details:
// - Should be called when the cache is no longer needed to prevent goroutine leaks.
func (c *LRU) DestroyCleanup() {
close(c.stopCleanup)
}

// evict evicts an element from the cache.
// evict removes a given element from the cache.
//
// Parameters:
// - element: The list element to be removed.
//
// Details:
// - Executes the eviction callback (if any) before removal.
func (c *LRU) evict(element *list.Element) {
// Invoke the eviction callback before removing the item
if c.onEvict != nil {
Expand All @@ -311,7 +479,10 @@ func (c *LRU) evict(element *list.Element) {
c.list.Remove(element)
}

// cleanupExpired removes expired entries from the cache.
// cleanupExpired removes all expired entries from the cache.
//
// Details:
// - Iterates through all items and evicts those that have exceeded their expiration time.
func (c *LRU) cleanupExpired() {
c.mutex.Lock()
defer c.mutex.Unlock()
Expand All @@ -326,7 +497,11 @@ func (c *LRU) cleanupExpired() {
}
}

// startCleanup starts a background goroutine for periodic cache cleanup.
// startCleanup starts a background goroutine to periodically remove expired entries.
//
// Details:
// - Runs a cleanup operation at regular intervals to evict expired items.
// - Stops when the `stopCleanup` channel is closed.
func (c *LRU) startCleanup() {
ticker := time.NewTicker(c.expiration / 2) // Run cleanup at half the expiration interval
defer ticker.Stop()
Expand All @@ -340,7 +515,13 @@ func (c *LRU) startCleanup() {
}
}

// calculateExpiry calculates the expiration time for a cache entry.
// calculateExpiry calculates the expiration time for a new cache entry.
//
// Returns:
// - A time.Time value representing the expiration time.
//
// Details:
// - If no expiration is set, returns the zero value for time.Time.
func (c *LRU) calculateExpiry() time.Time {
if c.expiration > 0 {
return time.Now().Add(c.expiration)
Expand Down
Loading

0 comments on commit b8fb841

Please sign in to comment.