rework caching functions

This commit is contained in:
perennial
2024-10-17 20:13:18 +11:00
parent 3c53f70928
commit 54dcde38af
+59 -34
View File
@@ -42,6 +42,19 @@ type CachedItem struct {
ExpiresAt time.Time
}
// CachePolicy defines the caching behavior for a request.
type CachePolicy struct {
ShouldUseCached bool // whether to attempt fetching from the cache
ShouldStore bool // whether to store the API response in the cache after retrieval
CachedResponse *SimpleHTTPResponse // the cached response if available and valid
}
// CachingResult encapsulates the response and whether it was retrieved from the cache.
type CachingResult struct {
Response *SimpleHTTPResponse
FromCache bool
}
var (
retryClient *retryablehttp.Client
cacheSeed uint64
@@ -116,20 +129,17 @@ func generateCacheKey(url, userToken string) string {
return fmt.Sprintf("%x", hash)
}
// shouldUseCache determines the caching policy for a given request.
//
// It returns three values:
// - shouldFetch: whether to attempt fetching from the cache.
// - shouldStore: whether to store the response in the cache after retrieval.
// - cachedResponse: the cached response if available and valid.
func shouldUseCache(rawURL, userToken string, incomingHeaders http.Header) (shouldFetch bool, shouldStore bool, cachedResponse *SimpleHTTPResponse) {
// determineCachePolicy determines the caching policy for a given request.
// It returns a CachePolicy struct indicating whether to fetch from cache,
// whether to store the response in cache, and the cached response if available.
func determineCachePolicy(rawURL, userToken string, headers http.Header) CachePolicy {
if !config.GlobalConfig.CacheEnabled {
return false, false, nil
return CachePolicy{}
}
parsedURL, err := url.Parse(rawURL)
if err != nil {
return false, false, nil
return CachePolicy{}
}
urlPath := path.Clean(parsedURL.Path)
@@ -137,49 +147,61 @@ func shouldUseCache(rawURL, userToken string, incomingHeaders http.Header) (shou
// Check if the path is excluded from caching
for _, exclPath := range excludedCachePaths {
if strings.HasPrefix(urlPath, exclPath) {
return false, false, nil
return CachePolicy{}
}
}
cacheKey := generateCacheKey(rawURL, userToken)
// Check if cache invalidation is requested
redirectedHeader := incomingHeaders.Get("X-Handled-Redirected")
redirectedHeader := headers.Get("X-Handled-Redirected")
if strings.Contains(strings.ToLower(redirectedHeader), "true") {
cache.Remove(cacheKey)
return false, false, nil
return CachePolicy{}
}
// Attempt to fetch from cache
if cachedItem, found := cache.Get(cacheKey); found {
item := cachedItem.(CachedItem)
if time.Now().Before(item.ExpiresAt) {
return false, false, item.Response
return CachePolicy{
ShouldUseCached: false,
CachedResponse: item.Response,
}
}
// Cache expired
cache.Remove(cacheKey)
}
// Determine if response should be cached based on headers
cacheControl := incomingHeaders.Get("Cache-Control")
shouldStore = cacheControl == "" || !strings.Contains(strings.ToLower(cacheControl), "no-cache")
cacheControl := headers.Get("Cache-Control")
shouldStore := cacheControl == "" || !strings.Contains(strings.ToLower(cacheControl), "no-store")
return true, shouldStore, nil
return CachePolicy{
ShouldUseCached: shouldStore, // Use cached version if storage is allowed
ShouldStore: shouldStore,
}
}
// handleCaching manages the caching logic during and after a request.
// manageCaching manages the caching logic during and after a request.
// It either returns a cached response or allows the request to proceed and optionally caches the new response.
func handleCaching(rawURL, userToken string, incomingHeaders http.Header, resp *SimpleHTTPResponse) (*SimpleHTTPResponse, bool) {
shouldFetch, shouldStore, cachedResponse := shouldUseCache(rawURL, userToken, incomingHeaders)
if !shouldFetch && cachedResponse != nil {
return cachedResponse, true
func manageCaching(rawURL, userToken string, headers http.Header, response *SimpleHTTPResponse) CachingResult {
policy := determineCachePolicy(rawURL, userToken, headers)
if policy.ShouldUseCached && policy.CachedResponse != nil {
return CachingResult{
Response: policy.CachedResponse,
FromCache: true,
}
}
if shouldStore && resp != nil {
if policy.ShouldStore && response != nil {
ttl := config.GlobalConfig.CacheTTL
parsedURL, err := url.Parse(rawURL)
if err != nil {
return resp, false
return CachingResult{
Response: response,
FromCache: false,
}
}
urlPath := path.Clean(parsedURL.Path)
for _, shortPath := range shortTTLPaths {
@@ -190,12 +212,15 @@ func handleCaching(rawURL, userToken string, incomingHeaders http.Header, resp *
}
cache.Add(generateCacheKey(rawURL, userToken), CachedItem{
Response: resp,
Response: response,
ExpiresAt: time.Now().Add(ttl),
})
}
return resp, false
return CachingResult{
Response: response,
FromCache: false,
}
}
// makeRequest is a helper function that encapsulates the common logic for making HTTP requests.
@@ -300,13 +325,13 @@ func executeGetWithRetries(
if resp.StatusCode == http.StatusOK {
tokenManager.MarkTokenStatus(token, token_manager.Good)
// Handle caching logic after we receieve an HTTP 200 response
cachedResp, isCached := handleCaching(url, userToken, incomingHeaders, resp)
if isCached {
return cachedResp, nil
// Handle caching logic after we receive an HTTP 200 response
cachingResult := manageCaching(url, userToken, incomingHeaders, resp)
if cachingResult.FromCache {
return cachingResult.Response, nil
}
return resp, nil
return cachingResult.Response, nil
}
lastErr = i18n.Errorf("HTTP status code: %d", resp.StatusCode)
@@ -336,10 +361,10 @@ func retryRequest(
return executeSinglePostRequest(ctx, reqFunc, userToken, url)
}
// Handle caching logic before we send a request
shouldFetch, _, cachedResponse := shouldUseCache(url, userToken, incomingHeaders)
if !shouldFetch && cachedResponse != nil {
return cachedResponse, nil
// Determine caching policy before we send a request
policy := determineCachePolicy(url, userToken, incomingHeaders)
if !policy.ShouldUseCached && policy.CachedResponse != nil {
return policy.CachedResponse, nil
}
return executeGetWithRetries(ctx, reqFunc, userToken, url, incomingHeaders)