Skip to content

Commit d51c0bf

Browse files
committed
fix: Ensure response body is always closed in async revalidation. fix #46
1 parent 392219c commit d51c0bf

1 file changed

Lines changed: 22 additions & 21 deletions

File tree

server/middleware/caching/caching_revalidate.go

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ func calculateSoftTTL(respUnix, expiresAt int64, fuzzyRate float64) int64 {
3030
if hardTTL <= 0 {
3131
return expiresAt
3232
}
33-
33+
3434
// Ensure fuzzy rate is in valid range (0, 1.0]
3535
if fuzzyRate <= 0 || fuzzyRate > 1 {
3636
fuzzyRate = 0.8 // default to 0.8 if invalid
3737
}
38-
38+
3939
softTTL := int64(float64(hardTTL) * fuzzyRate)
4040
return respUnix + softTTL
4141
}
@@ -48,22 +48,22 @@ func shouldTriggerFuzzyRefresh(now, softTTL, hardTTL int64) bool {
4848
// Before soft TTL, no refresh needed
4949
return false
5050
}
51-
51+
5252
if now >= hardTTL {
5353
// After hard TTL, force refresh (handled by hasExpired)
5454
return false
5555
}
56-
56+
5757
// In the fuzzy refresh zone [soft_ttl, hard_ttl)
5858
// Calculate linear probability: P = (now - soft_ttl) / (hard_ttl - soft_ttl)
5959
totalWindow := float64(hardTTL - softTTL)
6060
if totalWindow <= 0 {
6161
return false
6262
}
63-
63+
6464
elapsed := float64(now - softTTL)
6565
probability := elapsed / totalWindow
66-
66+
6767
// Random trigger based on probability using math/rand/v2 which is thread-safe
6868
return rand.Float64() < probability
6969
}
@@ -72,14 +72,14 @@ func (r *RevalidateProcessor) Lookup(c *Caching, req *http.Request) (bool, error
7272
if c.md == nil {
7373
return false, nil
7474
}
75-
75+
7676
now := time.Now().Unix()
7777
hardTTL := c.md.ExpiresAt
78-
78+
7979
// Fuzzy Refresh Logic
8080
if c.opt.FuzzyRefresh && c.opt.FuzzyRefreshRate > 0 {
8181
softTTL := calculateSoftTTL(c.md.RespUnix, c.md.ExpiresAt, c.opt.FuzzyRefreshRate)
82-
82+
8383
// Check if we're in the fuzzy refresh zone [soft_ttl, hard_ttl)
8484
if now >= softTTL && now < hardTTL {
8585
// We're in the fuzzy refresh zone
@@ -90,17 +90,17 @@ func (r *RevalidateProcessor) Lookup(c *Caching, req *http.Request) (bool, error
9090
c.id.Key(),
9191
time.Unix(softTTL, 0).Format(time.DateTime),
9292
time.Unix(hardTTL, 0).Format(time.DateTime))
93-
93+
9494
// Trigger async revalidation in background
9595
go r.asyncRevalidate(c, req)
9696
}
9797
}
98-
98+
9999
// Still return cache hit - serve stale content while refreshing
100100
return true, nil
101101
}
102102
}
103-
103+
104104
// check if metadata is expired (hard expiration).
105105
if !hasExpired(c.md) {
106106
return true, nil
@@ -236,38 +236,39 @@ func (r *RevalidateProcessor) asyncRevalidate(c *Caching, req *http.Request) {
236236
// Create a background context with timeout
237237
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
238238
defer cancel()
239-
239+
240240
// Clone the request for background processing
241241
bgReq := req.Clone(ctx)
242-
242+
243243
// Set conditional headers for revalidation
244244
if c.md.Headers.Get("ETag") != "" {
245245
bgReq.Header.Set("If-None-Match", c.md.Headers.Get("ETag"))
246246
}
247247
if c.md.Headers.Get("Last-Modified") != "" {
248248
bgReq.Header.Set("If-Modified-Since", c.md.Headers.Get("Last-Modified"))
249249
}
250-
250+
251251
// Remove Range header for full object revalidation
252252
bgReq.Header.Del("Range")
253-
253+
254254
c.log.Debugf("async fuzzy refresh started for object: %s", c.id.Key())
255-
255+
256256
// Perform the upstream request
257257
resp, err := c.doProxy(bgReq, false)
258+
defer closeBody(resp) // always check resp nil
259+
258260
if err != nil {
259261
c.log.Warnf("async fuzzy refresh failed for object %s: %v", c.id.Key(), err)
260262
return
261263
}
262-
defer closeBody(resp)
263-
264+
264265
// Handle 304 Not Modified - just update freshness metadata
265266
if resp.StatusCode == http.StatusNotModified {
266267
r.freshness(c, resp)
267268
c.log.Debugf("async fuzzy refresh completed (304) for object: %s", c.id.Key())
268269
return
269270
}
270-
271+
271272
// For non-304 responses, the content has changed
272273
// The doProxy method has already wrapped the response body with cache writing logic
273274
// We need to consume the body to trigger the cache update
@@ -282,7 +283,7 @@ func (r *RevalidateProcessor) asyncRevalidate(c *Caching, req *http.Request) {
282283
c.log.Debugf("async fuzzy refresh completed (%d) for object: %s - content updated", resp.StatusCode, c.id.Key())
283284
return
284285
}
285-
286+
286287
c.log.Debugf("async fuzzy refresh completed (%d) for object: %s", resp.StatusCode, c.id.Key())
287288
}
288289

0 commit comments

Comments
 (0)