Categorygithub.com/jkratz55/redis-cache
modulepackage
1.6.0
Repository: https://github.com/jkratz55/redis-cache.git
Documentation: pkg.go.dev

# README

Redis Cache

Redis Cache is a library for caching any data structure in Redis. Redis Cache is meant to be used with the official Redis Go client and works by unmarshalling and marshaling data structures from/to bytes automatically. By default, Redis Cache will use msgpack to marshal/unmarshal data, but you can customize the behavior by providing your own Marshaller and Unmarshaller using the Serialization option with the NewCache function.

Features

  • Save/Load any data structure that can be represented as bytes/string
  • Marshalling/Unmarshalling
  • Compression
  • Instrumentation/Metrics for Prometheus or OpenTelemetry

Requirements

  • Go 1.22+
  • Redis 6+

Getting Redis Cache

go get github.com/jkratz55/redis-cache

Usage

Under the hood Redis Cache was designed to be used with go-redis. However, it can work with any type that implements the RedisClient interface.

type RedisClient interface {
    Get(ctx context.Context, key string) *redis.StringCmd
    GetEx(ctx context.Context, key string, expiration time.Duration) *redis.StringCmd
    MGet(ctx context.Context, keys ...string) *redis.SliceCmd
    Set(ctx context.Context, key string, val any, ttl time.Duration) *redis.StatusCmd
    SetNX(ctx context.Context, key string, value any, expiration time.Duration) *redis.BoolCmd
    SetXX(ctx context.Context, key string, value any, expiration time.Duration) *redis.BoolCmd
    Del(ctx context.Context, keys ...string) *redis.IntCmd
    Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) error
    Scan(ctx context.Context, cursor uint64, match string, count int64) *redis.ScanCmd
    FlushDB(ctx context.Context) *redis.StatusCmd
    FlushDBAsync(ctx context.Context) *redis.StatusCmd
    Ping(ctx context.Context) *redis.StatusCmd
    TTL(ctx context.Context, key string) *redis.DurationCmd
    Expire(ctx context.Context, key string, expiration time.Duration) *redis.BoolCmd
}

This means that the Cache type can work with the following types in the go-redis client library.

  • redis.Client
  • redis.ClusterClient
  • redis.Ring

The following example shows the basic usage of the Redis Cache library.

package main

import (
	"context"
	"fmt"

	"github.com/redis/go-redis/v9"

	rcache "github.com/jkratz55/redis-cache"
)

type Person struct {
	FirstName string
	LastName  string
	Age       int
}

func main() {
	client := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})

	cache := rcache.NewCache(client)

	if err := cache.Set(context.Background(), "person", Person{
		FirstName: "Biily",
		LastName:  "Bob",
		Age:       45,
	}); err != nil {
		panic("ohhhhh snap!")
	}

	var p Person
	if err := cache.Get(context.Background(), "person", &p); err != nil {
		panic("ohhhhh snap")
	}
	fmt.Printf("%v\n", p)

	if err := cache.Delete(context.Background(), "person"); err != nil {
		panic("ohhh snap!")
	}

	if err := cache.Get(context.Background(), "person", &p); err != rcache.ErrKeyNotFound {
		panic("ohhhhh snap, this key should be gone!")
	}
}

If you wanted to use json instead of msgpack you could have customized the Cache like the example below.

marshaller := func(v any) ([]byte, error) {
    return json.Marshal(v)
}
unmarshaller := func(data []byte, v any) error {
    return json.Unmarshal(data, v)
}
rdb := rcache.NewCache(client, rcache.Serialization(marshaller, unmarshaller))

Because of limitations in GO's implementation of generics MGet is a function instead of a method on the Cache type. The MGet function accepts the Cache type as an argument to leverage the same marshaller and unmarshaller.

This library also supports atomic updates of existing keys by using the Upsert and UpsertTTL functions. If the key was modified while the upsert is in progress it will return RetryableError signaling the operation can be retried and the UpsertCallback can decide how to handle merging the changes.

Compression

In some cases compressing values stored in Redis can have tremendous benefits, particularly when storing large volumes of data, large values per key, or both. Compression reduces the size of the cache, significantly decreases bandwidth and latency but at the cost of additional CPU consumption.

This library can be configured to automatically compress and decompress values using the Compression option when calling NewCache. It accepts a Codec and out of the box gzip, flate, and lz4 are supported. However, you are free to compress data any way you please by implementing the Codec interface.

package main

import (
	"context"
	"fmt"

	"github.com/redis/go-redis/v9"

	rcache "github.com/jkratz55/redis-cache"
)

type Person struct {
	FirstName string
	LastName  string
	Age       int
}

func main() {
	client := redis.NewClient(&redis.Options{
		Addr: "localhost:6379",
	})

	c := rcache.NewCache(client, rcache.GZip())

	if err := c.Set(context.Background(), "person", Person{
		FirstName: "Biily",
		LastName:  "Bob",
		Age:       45,
	}); err != nil {
		panic("ohhhhh snap!")
	}

	var p Person
	if err := c.Get(context.Background(), "person", &p); err != nil {
		panic("ohhhhh snap")
	}
	fmt.Printf("%v\n", p)

	if err := c.Delete(context.Background(), "person"); err != nil {
		panic("ohhh snap!")
	}

	if err := c.Get(context.Background(), "person", &p); err != rcache.ErrKeyNotFound {
		panic("ohhhhh snap, this key should be gone!")
	}
}

Instrumentation

This library provides out of the box instrumentation for either Prometheus or OpenTelemetry. Instrumentation is provided for both the Redis Client and the Cache with minimal code.

Example for Prometheus:

func main() {

	redisClient := redis.NewClient(&redis.Options{
		Addr:         "localhost:6379",
		MinIdleConns: 10,
		MaxIdleConns: 100,
		PoolSize:     1000,
	})

	if err := redisClient.Ping(context.Background()).Err(); err != nil {
		fmt.Println("Opps ping to Redis failed!", err)
	}

	// Enable Redis client metrics
	if err := prometheus.InstrumentClientMetrics(redisClient); err != nil {
		panic(err)
	}

	rdb := cache.NewCache(redisClient)

	// Enable Cache metrics
	if err := prometheus.InstrumentMetrics(rdb); err != nil {
		panic(err)
	}

	// write some useful code here ...
}

The InstrumentClientMetrics and InstrumentMetrics functions accept Options to customize the metrics configuration if needed.

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# Functions

BatchMultiGets configures the Cache to use pipelining and split keys up into multiple MGET commands for increased throughput and lower latency when dealing with MGet operations with very large sets of keys.
Brotli configures the Cache to use Brotli for compressing and decompressing values stored in Redis.
Compression allows for the values to be flated and deflated to conserve bandwidth and memory at the cost of higher CPU time.
DefaultMarshaller returns a Marshaller using msgpack to marshall values.
DefaultUnmarshaller returns an Unmarshaller using msgpack to unmarshall values.
Flate configures the Cache to use Flate Codec for compressing and decompressing values stored in Redis.
GZip configures the Cache to use gzip for compressing and decompressing values stored in Redis.
IsRetryable accepts an error and returns a boolean indicating if the operation that generated the error is retryable.
JSON is a convenient Option for configuring Cache to use JSON for serializing data stored in the cache.
LZ4 configures the Cache to use lz4 for compressing and decompressing values stored in Redis.
MGet uses the provided Cache to retrieve multiple keys from Redis and returns a MultiResult.
MGetValues fetches multiple keys from Redis and returns only the values.
New creates and initializes a new Cache instance.
NewCache creates and initializes a new Cache instance.
Scan retrieves all the keys and values from Redis matching the given pattern.
Serialization allows for the marshalling and unmarshalling behavior to be customized for the Cache.
Upsert retrieves the existing value for a given key and invokes the UpsertCallback.
UpsertTTL retrieves the existing value for a given key and invokes the UpsertCallback.
Version returns the current version of redis-cache.

# Constants

InfiniteTTL indicates a key will never expire.
KeepTTL indicates to keep the existing TTL on the key on SET commands.

# Variables

ErrKeyNotFound is an error value that signals the key requested does not exist in the cache.

# Structs

Cache is a simple type that provides basic caching functionality: store, retrieve, and delete.
No description provided by the author
No description provided by the author

# Interfaces

Codec is an interface type that defines the behavior for compressing and decompressing data.
Hook is an interface type defining the operations that can be intercepted and potentially allow for their behavior to be modified.
RedisClient is an interface type that defines the Redis functionality this package requires to use Redis as a cache.

# Type aliases

No description provided by the author
CompressionHook is a function type that is invoked prior to compressing or decompressing data.
Marshaller is a function type that marshals the value of a cache entry for storage.
MultiResult is a type representing returning multiple entries from the Cache.
Option allows for the Cache behavior/configuration to be customized.
Unmarshaller is a function type that unmarshalls the value retrieved from the cache into the target type.
UpsertCallback is a callback function that is invoked by Upsert.