Skip to content

btschwartz12/generic-cache

Repository files navigation

image

generic-cache

A generic cache for "any" key types and value types, with an in-memory (Ristretto) and distrubuted (Redis) implementation.

Usage

The following example is not super useful, but it shows how to properly use each implementation. For more concrete examples, check out the examples/ directory.

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/alicebob/miniredis/v2"
	"github.com/redis/go-redis/v9"

	cache "github.com/btschwartz12/generic-cache"
)

type Data struct{ Name string }

type UserID struct{ ID int }

func (u *UserID) CacheKey() string { return fmt.Sprintf("user:%d", u.ID) }

func main() {
	// in-memory cache
	inMemory, _ := cache.NewInMemoryCache[*UserID, *Data](
		cache.WithTTL[*cache.InMemoryCacheConfig](time.Hour),
	)

	// distrubuted cache
	mr, _ := miniredis.Run()
	redisCache, _ := cache.NewRedisCache[*UserID, *Data](
		redis.NewClient(&redis.Options{
			Addr: mr.Addr(),
		}),
		cache.WithTTL[*cache.RedisCacheConfig](time.Hour),
	)

	key := &UserID{ID: 1}
	val := &Data{Name: "John"}

	for _, c := range []cache.Cache[*UserID, *Data]{inMemory, redisCache} {
		ctx := context.Background()
		c.Set(ctx, key, val)
		c.Wait()
		if v, ok := c.Get(ctx, key); ok {
			fmt.Println(v.Name)
		}
		c.Delete(ctx, key)
	}
}
  • Note 1: the Wait() is only neccessary for this example. The in-memory cache performs asynchronous writes, so we need to wait for the write to complete for the immediate call to Get().

  • Note 2: don't blame me for the cache.WithTTL[*cache.InMemoryCacheConfig] syntax; blame go generics. Here's an article and issue on this topic.

What do you mean by "any" key and value types?

The goal for this library is to be able to choose your own types for the cache key and stored data. However, this is not technically the case.

Since this library needs to be able to verify that the types provided can actually work, a good amount of reflect is used, but only during instantiation; an error will be returned if the types can't be used.

Valid Key Types

  • string
  • anything that implements Keyable (i.e. CacheKey() string)
  • anything that implements fmt.Stringer (i.e. String() string)

Valid Value Types

For the in-memory cache, I haven't been able to find a type that is not supported, but the testing isn't super thourough (yet).

For the distributed cache, things get a bit more complicated. Since another system is managing the data, it must be serialized to a []byte. I've chosen to use encoding/gob for this, since it is language-specific and should be able to handle most cases.

To be honested, I can't give an exhaustive list of types that are gob-serializable, but most common types should work. Check out isGobbable() for more details.

AI Disclaimer

Most tests and a portion of the reflect-specific code were written with AI-assistive tools.

About

in-memory & distributed generic cache implementations

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages