From 77f6a53a7e80d79d0d191e8c558759775f0e09b4 Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Mon, 19 Oct 2020 23:33:08 +0200 Subject: [PATCH] Add garbage collection to cache and cache redirects --- cache.go | 51 ++++++++++++++++++++++++++++++++++++++------------- http.go | 2 +- main.go | 1 + 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/cache.go b/cache.go index 456e557..bbc71b8 100644 --- a/cache.go +++ b/cache.go @@ -9,10 +9,27 @@ import ( "golang.org/x/sync/singleflight" ) -var cacheMap = map[string]*cacheItem{} -var cacheMutex = &sync.RWMutex{} +var ( + cacheGroup singleflight.Group + cacheMap = map[string]*cacheItem{} + cacheMutex = &sync.RWMutex{} +) -var requestGroup singleflight.Group +func initCache() { + go func() { + for { + // GC the entries every 60 seconds + time.Sleep(60 * time.Second) + cacheMutex.Lock() + for key, item := range cacheMap { + if item.expired() { + delete(cacheMap, key) + } + } + cacheMutex.Unlock() + } + }() +} func cacheMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -21,11 +38,10 @@ func cacheMiddleware(next http.Handler) http.Handler { !(r.URL.Query().Get("cache") == "0") && // check method (r.Method == http.MethodGet || r.Method == http.MethodHead) { - // Fix path - path := slashTrimmedPath(r) + key := cacheKey(r) // Get cache or render it - cacheInterface, _, _ := requestGroup.Do(path, func() (interface{}, error) { - return getCache(path, next, r), nil + cacheInterface, _, _ := cacheGroup.Do(key, func() (interface{}, error) { + return getCache(key, next, r), nil }) cache := cacheInterface.(*cacheItem) // log.Println(string(cache.body)) @@ -55,6 +71,10 @@ func cacheMiddleware(next http.Handler) http.Handler { }) } +func cacheKey(r *http.Request) string { + return slashTrimmedPath(r) +} + func setCacheHeaders(w http.ResponseWriter, cacheTimeString string, expiresTimeString string) { w.Header().Set("Cache-Control", "public") w.Header().Set("Last-Modified", cacheTimeString) @@ -68,16 +88,21 @@ type cacheItem struct { body []byte } -func getCache(path string, next http.Handler, r *http.Request) *cacheItem { +func (c *cacheItem) expired() bool { + return c.creationTime < time.Now().Unix()-appConfig.Cache.Expiration +} + +func getCache(key string, next http.Handler, r *http.Request) *cacheItem { cacheMutex.RLock() - item, ok := cacheMap[path] + item, ok := cacheMap[key] cacheMutex.RUnlock() - if !ok || item.creationTime < time.Now().Unix()-appConfig.Cache.Expiration { - item = &cacheItem{} + if !ok || item.expired() { // No cache available + item = &cacheItem{} + // Record request recorder := httptest.NewRecorder() next.ServeHTTP(recorder, r) - // copy values from recorder + // Cache values from recorder now := time.Now() item.creationTime = now.Unix() item.code = recorder.Code @@ -85,7 +110,7 @@ func getCache(path string, next http.Handler, r *http.Request) *cacheItem { item.body = recorder.Body.Bytes() // Save cache cacheMutex.Lock() - cacheMap[path] = item + cacheMap[key] = item cacheMutex.Unlock() } return item diff --git a/http.go b/http.go index 2632a7f..e474631 100644 --- a/http.go +++ b/http.go @@ -137,7 +137,7 @@ func buildHandler() (http.Handler, error) { } for _, path := range allRedirectPaths { if path != "" { - r.With(minifier.Middleware).Get(path, serveRedirect) + r.With(cacheMiddleware, minifier.Middleware).Get(path, serveRedirect) } } diff --git a/main.go b/main.go index bbaf17d..439eade 100644 --- a/main.go +++ b/main.go @@ -49,6 +49,7 @@ func main() { log.Fatal(err) return } + initCache() // Start cron hooks startHourlyHooks()