ttlcache

repository·v3·Indexed 23 days ago

https://github.com/jellydator/ttlcache

A high-performance, thread-safe, in-memory cache for Go supporting generics, item expiration, and automatic deletion. It features event handlers for insertions, updates, and evictions, custom capacity management via max cost, and lazy loading through the Loader interface and SuppressedLoader to prevent cache stampedes.

Tokens
4.4K
Snippets
15
Records
33
Agent score
74%

What's inside ttlcache

  1. Use a Loader to lazily initialize cache items

    v3

    The Loader interface allows you to define a function that is called when a requested key is missing from the cache. This is useful for loading data from external sources like files or HTTP requests. You can use ttlcache.LoaderFunc to create a loader from a simple function.

    loader := ttlcache.LoaderFunc[K, V](func(c *ttlcache.Cache[K, V], key K) *ttlcache.Item[K, V] {
        // logic to load data
        return c.Set(key, value)
    })
    
    cache := ttlcache.New[K, V](ttlcache.WithLoader[K, V](loader))
    func main() {
    	loader := ttlcache.LoaderFunc[string, string](
    		func(c *ttlcache.Cache[string, string], key string) *ttlcache.Item[string, string] {
    			// load from file/make an HTTP request
    			item := c.Set("key from file", "value from file")
    			return item
    		})
    	cache := ttlcache.New[string, string](
    		ttlcache.WithLoader[string, string](loader),
    	)
    
    	item := cache.Get("key from file")
    }
  2. Enable automatic item expiration and deletion

    v3

    To enable automatic expiration, pass a TTL option to ttlcache.New() using ttlcache.WithTTL[K, V](duration) and then call cache.Start() in a separate goroutine to handle the background deletion of expired items.

    func main() {
    	cache := ttlcache.New[string, string](
    		ttlcache.WithTTL[string, string](30 * time.Minute),
    	)
    
    	go cache.Start() // starts automatic expired item deletion
    }
  3. Manually delete expired items

    v3

    If you want to control exactly when expired items are removed (for example, during low-traffic periods), do not call cache.Start(). Instead, call cache.DeleteExpired() periodically in your own control loop.

    func main() {
    	cache := ttlcache.New[string, string](
    		ttlcache.WithTTL[string, string](30 * time.Minute),
    	)
    
    	for {
    		time.Sleep(4 * time.Hour)
    		cache.DeleteExpired()
    	}
    }
  4. Run the httpcache example

    v3

    The httpcache example demonstrates how to cache HTTP server responses based on the request path and query parameters. To run the example, use the following command:

    go run cmd/main.go

    Once running, an HTTP server will be active on port :8080 with a /reports/{name} route. The first request to a specific name will take approximately 5 seconds, while subsequent requests for the same name within one minute will return a cached response in milliseconds.

  5. Run the dbcache example

    v3

    The dbcache example demonstrates a cache implementation for a database layer that caches recent reads and writes to accelerate subsequent reads.

    To run the example, use the standard go run command. The application runs a mock service that consumes orders from a streamer package. Once processing is complete, the application exits and reports the total processing time.

    To observe how different expiration settings affect performance, run the example multiple times with varying -exp flags.

  6. Implement custom capacity limits with WithMaxCost

    v3

    Beyond simple item counts, you can restrict cache capacity based on a custom cost (e.g., memory usage) using ttlcache.WithMaxCost. This requires providing a cost function that calculates a uint64 cost for a given ttlcache.CostItem[K, V].

    func main() {
        cache := ttlcache.New[string, string](
            ttlcache.WithMaxCost[string, string](5120, func(item ttlcache.CostItem[string, string]) uint64 {
                // Note: The below line doesn't include memory used by internal
                // structures or string metadata for the key and the value.
                return uint64(len(item.Key) + len(item.Value))
            }), 
        )
    
        cache.Set("first", "value1", ttlcache.DefaultTTL)
    }
  7. Subscribe to cache events (Insertion, Update, Eviction)

    v3

    You can react to cache changes by registering handlers for insertion, updates, and evictions using OnInsertion, OnUpdate, and OnEviction.

    OnEviction provides an EvictionReason, which allows you to distinguish between items that expired and items removed because the cache reached its capacity (ttlcache.EvictionReasonCapacityReached).

    func main() {
    	cache := ttlcache.New[string, string](
    		ttlcache.WithTTL[string, string](30 * time.Minute),
    		ttlcache.WithCapacity[string, string](300),
    	)
    
    	cache.OnInsertion(func(ctx context.Context, item *ttlcache.Item[string, string]) {
    		fmt.Println(item.Value(), item.ExpiresAt())
    	})
    	cache.OnUpdate(func(ctx context.Context, item *ttlcache.Item[string, string]) {
    		fmt.Println(item.Value(), item.ExpiresAt())
    	})
    	cache.OnEviction(func(ctx context.Context, reason ttlcache.EvictionReason, item *ttlcache.Item[string, string]) {
    		if reason == ttlcache.EvictionReasonCapacityReached {
    			fmt.Println(item.Key(), item.Value())
    		}
    	})
    
    	cache.Set("first", "value1", ttlcache.DefaultTTL)
    	cache.DeleteAll()
    }
  8. Initialize a new TTLCache instance

    v3

    The primary type in this library is ttlcache.Cache, which represents an in-memory data store. You create a new instance using ttlcache.New[K, V](), where K and V are the types for keys and values respectively.

    By default, a new cache instance does not expire or automatically delete items. To enable expiration, you must provide a TTL option and call cache.Start() to run the background deletion process.

    func main() {
    	cache := ttlcache.New[string, string]()
    }
  9. Perform basic cache operations (Set, Get, Delete, Has)

    v3

    The ttlcache.Cache provides standard methods for managing data:

    • Set(key, value, ttl): Inserts or updates an item. Use ttlcache.DefaultTTL or ttlcache.NoTTL for the TTL parameter.
    • Get(key): Retrieves an item. Returns an *ttlcache.Item containing the value and expiration metadata.
    • Has(key): Checks if a key exists.
    • Delete(key): Removes a specific key.
    • DeleteAll(): Clears the entire cache.
    • GetOrSet(key, value, ttl): Retrieves an item if it exists; otherwise, inserts the provided value.
    • GetAndDelete(key): Retrieves and removes an item in one operation.
    func main() {
    	cache := ttlcache.New[string, string](
    		ttlcache.WithTTL[string, string](30 * time.Minute),
    	)
    
    	// insert data
    	cache.Set("first", "value1", ttlcache.DefaultTTL)
    	cache.Set("second", "value2", ttlcache.NoTTL)
    	cache.Set("third", "value3", ttlcache.DefaultTTL)
    
    	// retrieve data
    	item := cache.Get("first")
    	fmt.Println(item.Value(), item.ExpiresAt())
    
    	// check key 
    	ok := cache.Has("third")
    	
    	// delete data
    	cache.Delete("second")
    	cache.DeleteExpired()
    	cache.DeleteAll()
    
    	// retrieve data if in cache otherwise insert data
    	item, retrieved := cache.GetOrSet("fourth", "value4", WithTTL[string, string](ttlcache.DefaultTTL))
    
    	// retrieve and delete data
    	item, present := cache.GetAndDelete("fourth")
    }
  10. Initialize a new Cache with New()

    v3
    Use New[K, V](opts ...Option[K, V]) to create a new instance of a synchronized, generic in-memory cache. The cache is stopped by default and requires calling Start() to enable automatic expiration cleanup.