stampede

package module
v0.9.1 Latest Latest
Warning

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

Go to latest
Published: Apr 17, 2025 License: MIT Imports: 12 Imported by: 9

README

Stampede

Prevents cache stampede https://en.wikipedia.org/wiki/Cache_stampede by only running a single data fetch operation per expired / missing key regardless of number of requests to that key.

Example: HTTP Middleware

import (
	"log/slog"
	"net/http"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/go-chi/stampede"
	memcache "github.com/goware/cachestore-mem"
)

func main() {
	r := chi.NewRouter()
	r.Use(middleware.Logger)
	r.Use(middleware.Recoverer)

	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("index"))
	})

	cache, err := memcache.NewBackend(1000)
	if err != nil {
		panic(err)
	}

	cacheMiddleware := stampede.Handler(
		slog.Default(), cache, 5*time.Second,
		stampede.WithHTTPCacheKeyRequestHeaders([]string{"AuthorizatioN"}),
	)

	r.With(cacheMiddleware).Get("/cached", func(w http.ResponseWriter, r *http.Request) {
		// processing..
		time.Sleep(1 * time.Second)

		w.WriteHeader(200)
		w.Write([]byte("...hi"))
	})

	http.ListenAndServe(":3333", r)
}

Notes

  • Requests passed through the stampede handler will be batched into a single request when there are parallel requests for the same endpoint/resource. This is also known as request coalescing.
  • Parallel requests for the same endpoint / resource, will be just a single handler call and the remaining requests will receive the response of the first request handler.
  • The response payload for the endpoint / resource will then be cached for up to ttl time duration for subequence requests, which offers further caching. You may also use a ttl value of 0 if you want the response to be as fresh as possible, and still prevent a stampede scenario on your handler.
  • Security note: response headers will be the same for all requests, so make sure to not include anything sensitive or user specific. In the case you require user-specific stampede handlers, make sure you pass a custom keyFunc to the stampede.Handler and split the cache by an account's id. NOTE: we do avoid caching response headers for CORS, set-cookie and x-ratelimit.

See example for a variety of examples.

LICENSE

MIT

Documentation

Index

Constants

View Source
const (
	// DefaultCacheTTL is the default TTL for cache entries. However,
	// you can pass WithTTL(d) to set your own ttl, or pass
	// WithSkipCache() to disable caching
	DefaultCacheTTL = 1 * time.Minute
)

Variables

This section is empty.

Functions

func BytesToHash

func BytesToHash(b ...[]byte) uint64

func Handler

func Handler(logger *slog.Logger, cacheBackend cachestore.Backend, ttl time.Duration, options ...Option) func(next http.Handler) http.Handler

func HandlerWithKey

func HandlerWithKey(logger *slog.Logger, cacheBackend cachestore.Backend, ttl time.Duration, cacheKeyFunc CacheKeyFunc, options ...Option) func(next http.Handler) http.Handler

func NewStampede added in v0.9.0

func NewStampede[V any](logger *slog.Logger, cache cachestore.Store[V], options ...Option) *stampede[V]

func Singleflight added in v0.9.0

func Singleflight(logger *slog.Logger, varyRequestHeaders []string) func(next http.Handler) http.Handler

func StringToHash

func StringToHash(s ...string) uint64

Types

type CacheKeyFunc added in v0.8.0

type CacheKeyFunc func(r *http.Request) (uint64, error)

type Option added in v0.9.0

type Option func(*Options)

func WithHTTPCacheKeyRequestBody added in v0.9.0

func WithHTTPCacheKeyRequestBody(b bool) Option

WithHTTPCacheKeyRequestBody sets the HTTPCacheKeyRequestBody flag. This ensures we use the request body contents so we can properly cache different requests that have the same URL but different query params or body content.

Default: true

func WithHTTPCacheKeyRequestHeaders added in v0.9.0

func WithHTTPCacheKeyRequestHeaders(headers []string) Option

WithHTTPCacheKeyRequestHeaders sets the HTTPCacheKeyRequestHeaders list. This is useful for varying the cachekey based on request headers.

Default: []

func WithHTTPStatusTTL added in v0.9.0

func WithHTTPStatusTTL(fn func(status int) time.Duration) Option

WithHTTPStatusTTL sets the HTTPStatusTTL function. This allows you to customize the TTL for different HTTP status codes.

Default: nil

func WithSkipCache added in v0.9.0

func WithSkipCache(skip bool) Option

WithSkipCache sets the SkipCache flag. If true, the cache will not be used, but the request will still use singleflight request coalescing.

Default: false

func WithTTL added in v0.9.0

func WithTTL(ttl time.Duration) Option

WithTTL sets the TTL for the cache.

Default: 1 minute

type Options added in v0.9.0

type Options struct {
	// TTL is the time-to-live for the cache. NOTE: if this is not set,
	// then we use the package-level default of 1 minute. You can override
	// by passing `WithTTL(time.Second * 10)` to stampede.Do(), or passing
	// `WithSkipCache(true)` to skip the cache entirely.
	//
	// Default: 1 minute
	TTL time.Duration

	// SkipCache is a flag that determines whether the cache should be skipped.
	// If true, the cache will not be used, but the request will still use
	// singleflight request coalescing.
	//
	// Default: false
	SkipCache bool

	// HTTPCacheKeyRequestBody is a flag that determines whether the request body
	// should be used to generate the cache key. This is useful for varying the cache
	// key based on request headers.
	//
	// Default: true
	HTTPCacheKeyRequestBody bool

	// HTTPCacheKeyRequestHeaders is a list of headers that will be used to generate
	// the cache key. This ensures we use the request body contents so we can properly
	// cache different requests that have the same URL but different query params or
	// body content.
	//
	// Default: []
	HTTPCacheKeyRequestHeaders []string

	// HTTPStatusTTL is a function that returns the time-to-live for a given HTTP
	// status code. This allows you to customize the TTL for different HTTP status codes.
	//
	// Default: nil
	HTTPStatusTTL func(status int) time.Duration
}

Jump to

Keyboard shortcuts

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