From bf62ef54eb23092f34daa28f3897b1cd3b6d1b08 Mon Sep 17 00:00:00 2001 From: Evan Goode Date: Fri, 4 Apr 2025 08:40:02 -0400 Subject: [PATCH] CachedGet: wait for lock on the URL, then check cache --- common.go | 43 +++++++------------------------------------ main.go | 8 ++++---- 2 files changed, 11 insertions(+), 40 deletions(-) diff --git a/common.go b/common.go index 2cb72d6..4353c08 100644 --- a/common.go +++ b/common.go @@ -150,6 +150,10 @@ type RequestCacheValue struct { func (app *App) CachedGet(url string, ttl int) (RequestCacheValue, error) { cacheKey := MakeRequestCacheKey(url, "GET", nil) if ttl > 0 { + // If another GET to this URL is already in progress, wait for it to + // finish and then check the cache. + unlock := app.GetURLMutex.Lock(url) + defer unlock() cachedResponse, found := app.RequestCache.Get(cacheKey) if found { return cachedResponse.(RequestCacheValue), nil @@ -173,43 +177,10 @@ func (app *App) CachedGet(url string, ttl int) (RequestCacheValue, error) { BodyBytes: buf.Bytes(), } - if ttl > 0 { - app.RequestCache.SetWithTTL(cacheKey, response, 0, time.Duration(ttl)*time.Second) - } - - return response, nil -} - -// The only use of CachedPostJSON at the time of writing is to -// /profiles/minecraft, which is idempotent. -func (app *App) CachedPostJSON(url string, body []byte, ttl int) (RequestCacheValue, error) { - cacheKey := MakeRequestCacheKey(url, "GET", body) - if ttl > 0 { - cachedResponse, found := app.RequestCache.Get(cacheKey) - if found { - return cachedResponse.(RequestCacheValue), nil - } - } - - res, err := MakeHTTPClient().Post(url, "application/json", bytes.NewBuffer(body)) - if err != nil { - return RequestCacheValue{}, err - } - defer res.Body.Close() - - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(res.Body) - if err != nil { - return RequestCacheValue{}, err - } - - response := RequestCacheValue{ - StatusCode: res.StatusCode, - BodyBytes: buf.Bytes(), - } - - if ttl > 0 { + // Don't cache HTTP 429 responses + if ttl > 0 && res.StatusCode != http.StatusTooManyRequests { app.RequestCache.SetWithTTL(cacheKey, response, 0, time.Duration(ttl)*time.Second) + app.RequestCache.Wait() } return response, nil diff --git a/main.go b/main.go index c88da73..2918c48 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,6 @@ import ( "os" "path" "regexp" - "sync" "time" ) @@ -53,7 +52,8 @@ type App struct { SessionURL string AuthlibInjectorURL string DB *gorm.DB - FSMutex KeyedMutex + GetURLMutex *KeyedMutex + FSMutex *KeyedMutex RequestCache *ristretto.Cache Config *Config TransientUsernameRegex *regexp.Regexp @@ -65,7 +65,6 @@ type App struct { PrivateKeyB3Sum256 [256 / 8]byte PrivateKeyB3Sum512 [512 / 8]byte AEAD cipher.AEAD - SkinMutex *sync.Mutex VerificationSkinTemplate *image.NRGBA OIDCProviderNames []string OIDCProvidersByName map[string]*OIDCProvider @@ -550,7 +549,8 @@ func setup(config *Config) *App { ValidPlayerNameRegex: validPlayerNameRegex, Constants: Constants, DB: db, - FSMutex: KeyedMutex{}, + FSMutex: &KeyedMutex{}, + GetURLMutex: &KeyedMutex{}, PrivateKey: key, PrivateKeyB3Sum256: keyB3Sum256, PrivateKeyB3Sum512: keyB3Sum512,