๐Ÿ“ฆ go-chi / stampede

Function and HTTP request coalescer

โ˜… 71 stars โ‘‚ 8 forks ๐Ÿ‘ 71 watching โš–๏ธ MIT License
๐Ÿ“ฅ Clone https://github.com/go-chi/stampede.git
HTTPS git clone https://github.com/go-chi/stampede.git
SSH git clone git@github.com:go-chi/stampede.git
CLI gh repo clone go-chi/stampede
Peter Kieltyka Peter Kieltyka ttl=0 means no cache e70fde3 9 months ago ๐Ÿ“ History
๐Ÿ“‚ master View all commits โ†’
๐Ÿ“ _example
๐Ÿ“ .github
๐Ÿ“„ go.mod
๐Ÿ“„ go.sum
๐Ÿ“„ http_test.go
๐Ÿ“„ http.go
๐Ÿ“„ LICENSE
๐Ÿ“„ Makefile
๐Ÿ“„ options.go
๐Ÿ“„ README.md
๐Ÿ“„ singleflight.go
๐Ÿ“„ stampede_test.go
๐Ÿ“„ stampede.go
๐Ÿ“„ README.md

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