mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-20 06:28:13 +00:00
fix(coalesce): surface panic error to coalesced waiters
When the primary request panicked, waiters received a nil response and nil error, which the proxy turned into an empty HTTP 200. Recover in the deferred release, store an error response for the waiters, then re-panic so the primary panic semantics are unchanged. The test gates the panic on the live waiter count, so every waiter is coalesced before the release.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -142,7 +143,26 @@ func (rc *RequestCoalescer) Do(key string, fn func() (*CoalescedResponse, error)
|
||||
// Without this, a panicking fn() leaves the key in the inflight map and
|
||||
// the WaitGroup counter positive, so every coalesced waiter for that key
|
||||
// blocks on wg.Wait() forever (permanent deadlock + leak).
|
||||
//
|
||||
// A panic also never reaches the "store the result" block below, which
|
||||
// would otherwise leave inflight.response nil: waiters would then read
|
||||
// (nil, nil) instead of an error. Recover here, record an error response
|
||||
// for the waiters, release them, then re-panic so the primary caller's
|
||||
// own panic semantics are unchanged.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
inflight.mu.Lock()
|
||||
inflight.response = &CoalescedResponse{
|
||||
Err: fmt.Errorf("coalesced request panicked: %v", r),
|
||||
}
|
||||
inflight.mu.Unlock()
|
||||
|
||||
rc.inflight.Delete(key)
|
||||
inflight.wg.Done()
|
||||
|
||||
panic(r)
|
||||
}
|
||||
|
||||
rc.inflight.Delete(key)
|
||||
inflight.wg.Done()
|
||||
}()
|
||||
|
||||
Reference in New Issue
Block a user