fix(proxy): prevent use-after-recycle in background cache refresh
Copy the request before spawning the background goroutine. The fasthttp.RequestCtx is recycled after the handler returns, so passing it to a goroutine causes data corruption under high concurrency. The caller now AcquireRequest+CopyTo before go(), and the goroutine releases it. backgroundRefresh no longer accepts ctx directly.
This commit is contained in:
parent
c2dd4fa9a3
commit
2c3cc1ba38
@ -55,20 +55,14 @@ func (p *Proxy) writeCachedResponse(ctx *fasthttp.RequestCtx, entry *cache.Proxy
|
|||||||
// 该方法在独立 goroutine 中运行,不阻塞主请求流程。
|
// 该方法在独立 goroutine 中运行,不阻塞主请求流程。
|
||||||
//
|
//
|
||||||
// 参数:
|
// 参数:
|
||||||
// - ctx: 原始 FastHTTP 请求上下文(仅用于复制请求信息)
|
// - req: 预复制的请求副本(调用方负责 Acquire/Release)
|
||||||
// - target: 要刷新的后端目标
|
// - target: 要刷新的后端目标
|
||||||
// - hashKey: 缓存哈希键
|
// - hashKey: 缓存哈希键
|
||||||
// - origKey: 缓存原始键
|
// - origKey: 缓存原始键
|
||||||
func (p *Proxy) backgroundRefresh(ctx *fasthttp.RequestCtx, target *loadbalance.Target, hashKey uint64, origKey string) {
|
func (p *Proxy) backgroundRefresh(req *fasthttp.Request, target *loadbalance.Target, hashKey uint64, origKey string) {
|
||||||
// 创建新的请求上下文副本
|
|
||||||
req := fasthttp.AcquireRequest()
|
|
||||||
resp := fasthttp.AcquireResponse()
|
resp := fasthttp.AcquireResponse()
|
||||||
defer fasthttp.ReleaseRequest(req)
|
|
||||||
defer fasthttp.ReleaseResponse(resp)
|
defer fasthttp.ReleaseResponse(resp)
|
||||||
|
|
||||||
// 复制原始请求
|
|
||||||
ctx.Request.CopyTo(req)
|
|
||||||
|
|
||||||
// 如果启用 Revalidate,添加条件请求头
|
// 如果启用 Revalidate,添加条件请求头
|
||||||
if p.config.Cache.Revalidate {
|
if p.config.Cache.Revalidate {
|
||||||
if entry, ok, _ := p.cache.Get(hashKey, origKey); ok {
|
if entry, ok, _ := p.cache.Get(hashKey, origKey); ok {
|
||||||
|
|||||||
@ -651,14 +651,17 @@ func (p *Proxy) ServeHTTP(ctx *fasthttp.RequestCtx) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 过期缓存,尝试后台刷新,同时返回旧数据
|
// 过期缓存,尝试后台刷新,同时返回旧数据
|
||||||
if !p.config.Cache.BackgroundUpdateDisable {
|
if !p.config.Cache.BackgroundUpdateDisable {
|
||||||
entry.Updating.Store(true)
|
entry.Updating.Store(true)
|
||||||
go func() {
|
reqCopy := fasthttp.AcquireRequest()
|
||||||
defer entry.Updating.Store(false)
|
ctx.Request.CopyTo(reqCopy)
|
||||||
p.backgroundRefresh(ctx, target, hashKey, origKey)
|
go func() {
|
||||||
}()
|
defer entry.Updating.Store(false)
|
||||||
}
|
defer fasthttp.ReleaseRequest(reqCopy)
|
||||||
|
p.backgroundRefresh(reqCopy, target, hashKey, origKey)
|
||||||
|
}()
|
||||||
|
}
|
||||||
upstreamAddr = upstreamCache
|
upstreamAddr = upstreamCache
|
||||||
upstreamStatus = entry.Status
|
upstreamStatus = entry.Status
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user