Skip to content

coefficient-engineering/cache

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cache

A hybrid L1+L2 cache for Go, ported from FusionCache. The core library has zero (one 😳) external dependencies.


Features

  • In-process L1 memory cache with optional distributed L2 backend
  • Cache stampede protection via singleflight
  • Fail-safe: return stale values when the factory or L2 fails
  • Soft and hard factory timeouts with background completion
  • Eager refresh: background factory call before expiry, zero-latency misses
  • TTL jitter to spread expiry across nodes
  • Tag-based bulk invalidation across L1 and L2
  • Inter-node invalidation via a pluggable backplane
  • Circuit breakers for L2 and the backplane
  • Observable lifecycle events (EventEmitter)
  • Type-safe generic helpers (GetOrSet[T], Get[T])
  • Every pluggable point is a pure interface; bring your own adapters

Requirements

Go 1.21 or later.


Modules

module version
github.com/coefficient-engineering/cache v0.1.1
github.com/coefficient-engineering/cache/adapters/l2/redis v0.1.1
github.com/coefficient-engineering/cache/adapters/backplane/redis v0.1.1

Installation

Core library (L1 only, no external dependencies):

go get github.com/coefficient-engineering/cache@v0.1.1

Redis L2 adapter:

go get github.com/coefficient-engineering/cache/adapters/l2/redis@v0.1.1

Redis backplane adapter:

go get github.com/coefficient-engineering/cache/adapters/backplane/redis@v0.1.1

Quick Start

This example uses the Redis L2 adapter and Redis backplane for a production-style setup. If you only need an in-process cache, skip the L2, serializer, and backplane setup and call cache.New() with only WithDefaultEntryOptions.

package main

import (
	"context"
	"log/slog"
	"time"

	"github.com/coefficient-engineering/cache"
	redisbp "github.com/coefficient-engineering/cache/adapters/backplane/redis"
	redisl2 "github.com/coefficient-engineering/cache/adapters/l2/redis"
	"github.com/coefficient-engineering/cache/adapters/serializer/json"
	"github.com/redis/go-redis/v9"
)

func main() {
	ctx := context.Background()

	rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})

	l2 := redisl2.New(rdb,
		redisl2.WithKeyPrefix("myapp:"),
		redisl2.WithLogger(slog.Default()),
	)

	bp := redisbp.New(rdb,
		redisbp.WithChannel("myapp:backplane"),
		redisbp.WithLogger(slog.Default()),
	)

	c, err := cache.New(
		cache.WithCacheName("myapp"),
		cache.WithL2(l2),
		cache.WithSerializer(&json.Serializer{}),
		cache.WithBackplane(bp),
		cache.WithLogger(slog.Default()),
		cache.WithL2CircuitBreaker(5, 10*time.Second),
		cache.WithDefaultEntryOptions(cache.EntryOptions{
			Duration:                 5 * time.Minute,
			IsFailSafeEnabled:        true,
			FailSafeMaxDuration:      2 * time.Hour,
			FailSafeThrottleDuration: 30 * time.Second,
			EagerRefreshThreshold:    0.9,
			FactorySoftTimeout:       100 * time.Millisecond,
			FactoryHardTimeout:       2 * time.Second,
			JitterMaxDuration:        10 * time.Second,
		}),
	)
	if err != nil {
		panic(err)
	}
	defer c.Close()

	type Product struct {
		ID   int
		Name string
	}

	product, err := cache.GetOrSet[*Product](ctx, c, "product:42",
		func(ctx context.Context) (*Product, error) {
			// Replace with a real database call.
			return &Product{ID: 42, Name: "Widget"}, nil
		},
	)
	if err != nil {
		panic(err)
	}

	_ = product
}

Adapters

adapter purpose
github.com/coefficient-engineering/cache/adapters/l2/memory In-process L2 using sync.Map (testing)
github.com/coefficient-engineering/cache/adapters/l2/redis Redis L2 (accepts redis.Cmdable)
github.com/coefficient-engineering/cache/adapters/backplane/memory In-process backplane via Go channel (testing)
github.com/coefficient-engineering/cache/adapters/backplane/noop No-op backplane for single-node deployments
github.com/coefficient-engineering/cache/adapters/backplane/redis Redis pub/sub backplane
github.com/coefficient-engineering/cache/adapters/serializer/json encoding/json serializer (stdlib, zero extra deps)

To use a different backend, implement the relevant interface:

interface package methods
l1.Adapter github.com/coefficient-engineering/cache/l1 Get, Set, Delete, CompareAndSwap, Range, Clear
l2.Adapter github.com/coefficient-engineering/cache/l2 Get, Set, Delete, DeleteByTag, Clear, Ping
backplane.Backplane github.com/coefficient-engineering/cache/backplane Publish, Subscribe, Close
serializer.Serializer github.com/coefficient-engineering/cache/serializer Marshal, Unmarshal

About

A robust hybrid cache for distributed Go systems inspired by ZiggyCreature's FusionCache

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages