Mackenzie is a lightweight, highly flexible caching library for Go applications, leveraging the power of generics and reflection to offer enhanced caching capabilities.
- Uses generics for flexibility across different data types.
- Cache item expiration based on configurable lifetime.
- Automatic cleanup of expired items at regular intervals.
- Strong type checking ensures that caching methods match the expected signatures.
- Force-get to bypass cache and fetch fresh data.
- Provides time until a cached item expires.
- Efficient locking for thread safety.
go get github.com/nokusukun/mackenzie
This example caches http calls for one second. The first call takes 570ms, the second call takes >1ms.
package main
import (
"encoding/json"
"fmt"
"github.com/nokusukun/mackenzie"
"net/http"
"time"
)
func HTTPGetJson(url string) (map[string]any, error) {
client := http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
resp, err := client.Do(req)
r := map[string]any{}
err = json.NewDecoder(resp.Body).Decode(&r)
if err != nil {
return nil, err
}
return r, nil
}
func main() {
url := "https://dummyjson.com/products/1"
CGetJson, err := mackenzie.Create[map[string]any](HTTPGetJson, mackenzie.Config{Lifetime: 1 * time.Second})
if err != nil {
panic(err)
}
start := time.Now()
data, _ := CGetJson.Get(url)
elapsed := time.Since(start)
fmt.Println("Data", data)
fmt.Println("First call took", elapsed.String())
start = time.Now()
data, _ = CGetJson.Get(url)
elapsed = time.Since(start)
fmt.Println("Data", data)
fmt.Println("Second call took", elapsed.String())
}
----
$ go run examples/http
Data map[brand:Apple category:smartphones...
First call took 570.5082ms
Data map[brand:Apple category:smartphones...
Second call took 0s
To create a cache instance, use the Create
method:
cache, err := mackenzie.Create[YourType](yourFunction, mackenzie.Config{Lifetime: time.Minute, CleanInterval: time.Minute * 10})
if err != nil {
log.Fatal(err)
}
The passed function should return the data type you want to cache and may optionally return an error as its second return value.
To get data from the cache:
item, err := cache.Get(arguments...)
if err != nil {
log.Fatal(err)
}
If the data is in the cache and hasn't expired, it will be returned. Otherwise, the cache will call the provided function to fetch and cache the data.
To bypass the cache and fetch fresh data:
item, err := cache.ForceGet(arguments...)
Clear the entire cache:
cache.Clear()
Clear a specific key:
cache.ClearKey(arguments...)
To check how long until a cached item expires:
duration := cache.ExpiresIn(arguments...)
Before your application exits or if you're done with the cache instance:
cache.Unload()
You can check for specific Mackenzie-related errors using the helper functions:
IsErrCallInvalid(err)
: Returns true if the error is due to an invalid call.IsErrMackenzie(err)
: Returns true for generic Mackenzie errors.
This example illustrates the basic operation of a function that takes in a Point
structure and returns the product of its X
and Y
fields.
p := &Point{X: 5, Y: 5}
result := Square(p)
fmt.Println(result) // Outputs: 25
This example shows how to create a cache for the Square
function:
myCache, err := mackenzie.Create[int](Square, mackenzie.Config{Lifetime: 1 * time.Second})
if err != nil {
log.Fatalf("Failed to create cache: %v", err)
}
This demonstrates how to get data from the cache. If the data is not present in the cache, the Square
function will be called, and the result will be cached.
result, err := myCache.Get(&Point{1, 2})
if err != nil {
log.Fatal(err)
}
fmt.Println(result) // Outputs the product of 1 and 2, i.e., 2
You can check how long it takes for a cached item to expire:
expiry := myCache.ExpiresIn(&Point{1, 2})
fmt.Printf("Expiry time: %v\n", expiry)
This shows how to force a fetch, which bypasses the cache and retrieves fresh data:
result, err := myCache.ForceGet(&Point{1, 2})
if err != nil {
log.Fatal(err)
}
fmt.Println(result) // Outputs the product of 1 and 2, i.e., 2
In case you pass incorrect arguments to the Get
function, mackenzie
provides specific errors that you can check:
_, err = myCache.Get(1)
if mackenzie.IsErrCallInvalid(err) {
fmt.Println("Invalid function call!")
}
if mackenzie.IsErrMackenzie(err) {
fmt.Println("A Mackenzie-specific error occurred!")
}