valkeyaside

package
v1.0.55 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 16, 2025 License: Apache-2.0 Imports: 0 Imported by: 0

README

valkeyaside

A Cache-Aside pattern implementation enhanced by Client Side Caching.

Features backed by the Valkey Client Side Caching

Cache-Aside is a widely used pattern to cache other data sources into Valkey. However, there are many issues to be considered when implementing it.

For example, an implementation without locking or versioning may cause a fresh cache be overridden by a stale one. And if using a locking mechanism, how to get notified when a lock is released? If using versioning mechanism, how to version an empty value?

Thankfully, the above issues can be addressed better with the client-side caching along with the following additional benefits:

  • Avoiding unnecessary network round trips. Valkey will proactively invalidate the client-side cache.
  • Avoiding Cache Stampede by locking keys with the client-side caching, the same technique used in valkeylock. Only the first cache missed call can update the cache and others will wait for notifications.

Example

package main

import (
	"context"
	"database/sql"
	"time"

	"github.com/valkey-io/valkey-go"
	"github.com/valkey-io/valkey-go/valkeyaside"
)

func main() {
	var db sql.DB
	client, err := valkeyaside.NewClient(valkeyaside.ClientOption{
		ClientOption: valkey.ClientOption{InitAddress: []string{"127.0.0.1:6379"}},
	})
	if err != nil {
		panic(err)
	}
	val, err := client.Get(context.Background(), time.Minute, "mykey", func(ctx context.Context, key string) (val string, err error) {
		if err = db.QueryRowContext(ctx, "SELECT val FROM mytab WHERE id = ?", key).Scan(&val); err == sql.ErrNoRows {
			val = "_nil_" // cache nil to avoid penetration.
			err = nil     // clear err in case of sql.ErrNoRows.
		}
		return
	})
	if err != nil {
		panic(err)
	} else if val == "_nil_" {
		val = ""
		err = sql.ErrNoRows
	} else {
		// ...
	}
}

If you want to use cache typed value, not string, you can use valkeyaside.TypedCacheAsideClient.

package main

import (
	"context"
	"database/sql"
	"encoding/json"
	"time"

	"github.com/valkey-io/valkey-go"
	"github.com/valkey-io/valkey-go/valkeyaside"
)

type MyValue struct {
	Val string `json:"val"`
}

func main() {
	var db sql.DB
	client, err := valkeyaside.NewClient(valkeyaside.ClientOption{
		ClientOption: valkey.ClientOption{InitAddress: []string{"127.0.0.1:6379"}},
	})
	if err != nil {
		panic(err)
	}

	serializer := func(val *MyValue) (string, error) {
		b, err := json.Marshal(val)
		return string(b), err
	}
	deserializer := func(s string) (*MyValue, error) {
		var val *MyValue
		if err := json.Unmarshal([]byte(s), &val); err != nil {
			return nil, err
		}
		return val, nil
	}

	typedClient := valkeyaside.NewTypedCacheAsideClient(client, serializer, deserializer)
	val, err := typedClient.Get(context.Background(), time.Minute, "myKey", func(ctx context.Context, key string) (*MyValue, error) {
		var val MyValue
		if err := db.QueryRowContext(ctx, "SELECT val FROM mytab WHERE id = ?", key).Scan(&val.Val); err == sql.ErrNoRows {
			return nil, nil
		} else if err != nil {
			return nil, err
		}
		return &val, nil
	})
	// ...
}

Limitation

Currently, requires Valkey >= 7.0. However, the UseLuaLock option is available and allows you to use the valkeyaside with older Redis versions < 7.0 as well.

To configure the Lua fallback option:

client, err := valkeyaside.NewClient(valkeyaside.ClientOption{
    ClientOption: valkey.ClientOption{
        InitAddress: []string{"127.0.0.1:6379"},
    },
    UseLuaLock: true, // Enable Lua script for older Redis versions
})
if err != nil {
    panic(err)
}

Documentation

Index

Constants

View Source
const PlaceholderPrefix = "valkeyid:"

Variables

This section is empty.

Functions

This section is empty.

Types

type CacheAsideClient

type CacheAsideClient interface {
	Get(ctx context.Context, ttl time.Duration, key string, fn func(ctx context.Context, key string) (val string, err error)) (val string, err error)
	Del(ctx context.Context, key string) error
	Client() valkey.Client
	Close()
}

func NewClient

func NewClient(option ClientOption) (cc CacheAsideClient, err error)

type Client

type Client struct {
	// contains filtered or unexported fields
}

func (*Client) Client

func (c *Client) Client() valkey.Client

Client exports the underlying valkey.Client

func (*Client) Close

func (c *Client) Close()

func (*Client) Del

func (c *Client) Del(ctx context.Context, key string) error

func (*Client) Get

func (c *Client) Get(ctx context.Context, ttl time.Duration, key string, fn func(ctx context.Context, key string) (val string, err error)) (string, error)

type ClientOption

type ClientOption struct {
	// ClientBuilder can be used to modify valkey.Client used by Locker
	ClientBuilder func(option valkey.ClientOption) (valkey.Client, error)
	ClientOption  valkey.ClientOption
	ClientTTL     time.Duration // TTL for the client marker, refreshed every 1/2 TTL. Defaults to 10s. The marker allows other client to know if this client is still alive.
	UseLuaLock    bool
}

type TypedCacheAsideClient

type TypedCacheAsideClient[T any] interface {
	Get(ctx context.Context, ttl time.Duration, key string, fn func(ctx context.Context, key string) (val *T, err error)) (val *T, err error)
	Del(ctx context.Context, key string) error
	Client() CacheAsideClient
}

TypedCacheAsideClient is an interface that provides a typed cache-aside client. It allows you to cache and retrieve values of a specific type T.

func NewTypedCacheAsideClient

func NewTypedCacheAsideClient[T any](
	client CacheAsideClient,
	serializer func(*T) (string, error),
	deserializer func(string) (*T, error),
) TypedCacheAsideClient[T]

NewTypedCacheAsideClient creates a new TypedCacheAsideClient instance that provides a typed cache-aside client. The client, serializer, and deserializer functions are used to interact with the underlying cache. The serializer function is used to convert the provided value of type T to a string, and the deserializer function is used to convert the cached string value back to the original type T.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL