From e1c362ac2fab2a29dc7360b2087651bad666b11d Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Tue, 22 Sep 2020 16:42:36 +0200 Subject: [PATCH] Use mutexes to prevent cache stampede --- cache.go | 17 +++++++++++++++++ main.go | 1 + 2 files changed, 18 insertions(+) diff --git a/cache.go b/cache.go index 6f4db27..5127517 100644 --- a/cache.go +++ b/cache.go @@ -6,9 +6,16 @@ import ( "net/http" "net/http/httptest" "net/url" + "sync" "time" ) +var cacheMutexes map[string]*sync.Mutex + +func initCache() { + cacheMutexes = map[string]*sync.Mutex{} +} + func cacheMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requestUrl, _ := url.ParseRequestURI(r.RequestURI) @@ -16,8 +23,16 @@ func cacheMiddleware(next http.Handler) http.Handler { if appConfig.Cache.Enable && // check bypass query !(requestUrl != nil && requestUrl.Query().Get("cache") == "0") { + // Check cache mutex + if cacheMutexes[path] == nil { + cacheMutexes[path] = &sync.Mutex{} + } + // Lock mutex - prevents multiple new renderings + cacheMutexes[path].Lock() + // Get cache cacheTime, header, body := getCache(r.Context(), path) if cacheTime == 0 { + // No cache available recorder := httptest.NewRecorder() next.ServeHTTP(recorder, r) // copy values from recorder @@ -35,8 +50,10 @@ func cacheMiddleware(next http.Handler) http.Handler { if code == http.StatusOK { saveCache(path, now, recorder.Header(), recorder.Body.Bytes()) } + cacheMutexes[path].Unlock() return } + cacheMutexes[path].Unlock() cacheTimeString := time.Unix(cacheTime, 0).Format(time.RFC1123) expiresTimeString := time.Unix(cacheTime+appConfig.Cache.Expiration, 0).Format(time.RFC1123) // check conditional request diff --git a/main.go b/main.go index 51e3599..7015d3f 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,7 @@ func main() { log.Println("Initialize server components...") initMinify() initMarkdown() + initCache() err = initTemplateAssets() // Needs minify if err != nil { log.Fatal(err)