CachedGet: wait for lock on the URL, then check cache

This commit is contained in:
Evan Goode 2025-04-04 08:40:02 -04:00
parent 4ea506eae1
commit bf62ef54eb
2 changed files with 11 additions and 40 deletions

View File

@ -150,6 +150,10 @@ type RequestCacheValue struct {
func (app *App) CachedGet(url string, ttl int) (RequestCacheValue, error) { func (app *App) CachedGet(url string, ttl int) (RequestCacheValue, error) {
cacheKey := MakeRequestCacheKey(url, "GET", nil) cacheKey := MakeRequestCacheKey(url, "GET", nil)
if ttl > 0 { 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) cachedResponse, found := app.RequestCache.Get(cacheKey)
if found { if found {
return cachedResponse.(RequestCacheValue), nil return cachedResponse.(RequestCacheValue), nil
@ -173,43 +177,10 @@ func (app *App) CachedGet(url string, ttl int) (RequestCacheValue, error) {
BodyBytes: buf.Bytes(), BodyBytes: buf.Bytes(),
} }
if ttl > 0 { // Don't cache HTTP 429 responses
app.RequestCache.SetWithTTL(cacheKey, response, 0, time.Duration(ttl)*time.Second) if ttl > 0 && res.StatusCode != http.StatusTooManyRequests {
}
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 {
app.RequestCache.SetWithTTL(cacheKey, response, 0, time.Duration(ttl)*time.Second) app.RequestCache.SetWithTTL(cacheKey, response, 0, time.Duration(ttl)*time.Second)
app.RequestCache.Wait()
} }
return response, nil return response, nil

View File

@ -26,7 +26,6 @@ import (
"os" "os"
"path" "path"
"regexp" "regexp"
"sync"
"time" "time"
) )
@ -53,7 +52,8 @@ type App struct {
SessionURL string SessionURL string
AuthlibInjectorURL string AuthlibInjectorURL string
DB *gorm.DB DB *gorm.DB
FSMutex KeyedMutex GetURLMutex *KeyedMutex
FSMutex *KeyedMutex
RequestCache *ristretto.Cache RequestCache *ristretto.Cache
Config *Config Config *Config
TransientUsernameRegex *regexp.Regexp TransientUsernameRegex *regexp.Regexp
@ -65,7 +65,6 @@ type App struct {
PrivateKeyB3Sum256 [256 / 8]byte PrivateKeyB3Sum256 [256 / 8]byte
PrivateKeyB3Sum512 [512 / 8]byte PrivateKeyB3Sum512 [512 / 8]byte
AEAD cipher.AEAD AEAD cipher.AEAD
SkinMutex *sync.Mutex
VerificationSkinTemplate *image.NRGBA VerificationSkinTemplate *image.NRGBA
OIDCProviderNames []string OIDCProviderNames []string
OIDCProvidersByName map[string]*OIDCProvider OIDCProvidersByName map[string]*OIDCProvider
@ -550,7 +549,8 @@ func setup(config *Config) *App {
ValidPlayerNameRegex: validPlayerNameRegex, ValidPlayerNameRegex: validPlayerNameRegex,
Constants: Constants, Constants: Constants,
DB: db, DB: db,
FSMutex: KeyedMutex{}, FSMutex: &KeyedMutex{},
GetURLMutex: &KeyedMutex{},
PrivateKey: key, PrivateKey: key,
PrivateKeyB3Sum256: keyB3Sum256, PrivateKeyB3Sum256: keyB3Sum256,
PrivateKeyB3Sum512: keyB3Sum512, PrivateKeyB3Sum512: keyB3Sum512,