Skip to content

Commit bdf0d57

Browse files
authored
recover from panics during batch sub-requests (#86)
1 parent ef48922 commit bdf0d57

File tree

3 files changed

+25
-10
lines changed

3 files changed

+25
-10
lines changed

service/batchmdw/batch_processor.go

+20-7
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import (
66
"net/http"
77
"sync"
88

9+
"github.com/kava-labs/kava-proxy-service/logging"
910
"github.com/kava-labs/kava-proxy-service/service/cachemdw"
1011
)
1112

1213
// BatchProcessor makes multiple requests to the underlying handler and then combines all the
1314
// responses into a single response.
1415
// It assumes all individual responses are valid json. Each response is then marshaled into an array.
1516
type BatchProcessor struct {
17+
*logging.ServiceLogger
18+
1619
handler http.HandlerFunc
1720
requests []*http.Request
1821
responses []*bytes.Buffer
@@ -23,14 +26,15 @@ type BatchProcessor struct {
2326
}
2427

2528
// NewBatchProcessor creates a BatchProcessor for combining the responses of reqs to the handler
26-
func NewBatchProcessor(handler http.HandlerFunc, reqs []*http.Request) *BatchProcessor {
29+
func NewBatchProcessor(serviceLogger *logging.ServiceLogger, handler http.HandlerFunc, reqs []*http.Request) *BatchProcessor {
2730
return &BatchProcessor{
28-
handler: handler,
29-
requests: reqs,
30-
responses: make([]*bytes.Buffer, len(reqs)),
31-
header: nil,
32-
status: http.StatusOK,
33-
mu: sync.Mutex{},
31+
ServiceLogger: serviceLogger,
32+
handler: handler,
33+
requests: reqs,
34+
responses: make([]*bytes.Buffer, len(reqs)),
35+
header: nil,
36+
status: http.StatusOK,
37+
mu: sync.Mutex{},
3438
}
3539
}
3640

@@ -42,6 +46,15 @@ func (bp *BatchProcessor) RequestAndServe(w http.ResponseWriter) error {
4246
wg.Add(1)
4347

4448
go func(idx int, req *http.Request) {
49+
// if a client closes the connection prematurely, it can cause panics from batch sub-requests.
50+
// when that happens, log & discard the panic.
51+
// https://github.com/golang/go/issues/28239
52+
defer func() {
53+
if recover() != nil {
54+
wg.Done()
55+
bp.Error().Int("sub-request index", idx).Msg("sub-request for batch panicked")
56+
}
57+
}()
4558

4659
buf := new(bytes.Buffer)
4760
frw := newFakeResponseWriter(buf, bp.setErrStatus)

service/batchmdw/batchmdw.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,18 @@ func CreateBatchProcessingMiddleware(
6868
// build request as if it's the only one being requested.
6969
req, err := http.NewRequestWithContext(singleRequestContext, r.Method, r.URL.String(), bytes.NewBuffer(body))
7070
if err != nil {
71-
panic(fmt.Sprintf("failed build sub-request: %s", err))
71+
config.ServiceLogger.Error().Err(err).Any("req", single).Msg("failed to sub-request of batch")
72+
continue
7273
}
7374
req.Host = r.Host
7475
req.Header = r.Header
76+
req.Close = true
7577

7678
reqs = append(reqs, req)
7779
}
7880

7981
// process all requests and respond with results in an array
80-
batchProcessor := NewBatchProcessor(singleRequestHandler, reqs)
82+
batchProcessor := NewBatchProcessor(config.ServiceLogger, singleRequestHandler, reqs)
8183
batchProcessor.RequestAndServe(w)
8284
}
8385
}

service/shard.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func (hsp HeightShardingProxies) ProxyForRequest(r *http.Request) (*httputil.Rev
3030
_, _, found := hsp.pruningProxies.ProxyForRequest(r)
3131
// if the host isn't in the pruning proxies, short circuit fallback to default
3232
if !found {
33-
hsp.Debug().Msg(fmt.Sprintf("no pruning host backend configured for %s", r.Host))
33+
hsp.Trace().Msg(fmt.Sprintf("no pruning host backend configured for %s", r.Host))
3434
return hsp.defaultProxies.ProxyForRequest(r)
3535
}
3636

0 commit comments

Comments
 (0)