Aias00 opened a new issue, #3546:
URL: https://github.com/apache/dubbo-go/issues/3546

   ### Problem
   
   `protocol/jsonrpc/server.go` `handlePkg` runs for the lifetime of one 
keep-alive TCP connection and serves many HTTP requests in a `for` loop. Each 
request carrying a `Timeout` header creates a `context.WithTimeout` whose 
`cancel` is `defer`red **inside the loop**:
   
   ```go
   // protocol/jsonrpc/server.go handlePkg, per request in the for-loop
   if len(reqHeader["Timeout"]) > 0 {
       timeout, err := time.ParseDuration(reqHeader["Timeout"])
       if err == nil {
           httpTimeout = timeout
           var cancel context.CancelFunc
           ctx, cancel = context.WithTimeout(ctx, httpTimeout)
           defer cancel()   // <-- defer inside a loop, in a long-lived fn
       }
       delete(reqHeader, "Timeout")
   }
   ```
   
   `defer` only runs when `handlePkg` returns (i.e. when the connection 
closes), so each keep-alive request with a `Timeout` header accumulates one 
deferred `cancel` + its retained context (and the canceler/timer) in the 
connection goroutine until the connection tears down.
   
   ### Current behavior
   
   A client opens a persistent/keep-alive connection and sends many requests 
each with a `Timeout` header. Each request accumulates one deferred `cancel` + 
retained context in the connection goroutine → unbounded growth of the defer 
chain and retained context objects → slow memory growth / eventual OOM under 
sustained keep-alive traffic. The `ctx` is also not cancelled promptly after 
`serveRequest` returns (the timer holds resources until it fires or the 
connection closes).
   
   ### Expected behavior
   
   The timeout context should be cancelled promptly after `serveRequest` 
returns for that request, with no accumulation across the keep-alive connection.
   
   ### Suggested approach
   
   Replace `defer cancel()` (inside the loop) with an explicit `cancel()` call 
after `serveRequest` returns:
   
   ```go
   var cancel context.CancelFunc
   if len(reqHeader["Timeout"]) > 0 {
       timeout, err := time.ParseDuration(reqHeader["Timeout"])
       if err == nil {
           httpTimeout = timeout
           ctx, cancel = context.WithTimeout(ctx, httpTimeout)
       }
       delete(reqHeader, "Timeout")
   }
   setTimeout(conn, httpTimeout)
   
   serveErr := serveRequest(ctx, reqHeader, reqBody, conn)
   if cancel != nil { cancel() }
   if serveErr != nil { ... return }
   ```
   
   ### Acceptance criteria
   
   - [ ] No `defer` accumulates per request in the `handlePkg` loop.
   - [ ] The timeout context is cancelled after `serveRequest` returns.
   - [ ] Existing jsonrpc tests remain green.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to