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 b0eb50238b
commit 95893eb211
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) {
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

View File

@ -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,