Skip to content

Commit baae166

Browse files
authored
Frontend: do not return 500 if the user request cancellation. (#2156)
* Frontent does not return 500 if the user request cancellation. Signed-off-by: Cyril Tovena <[email protected]>
1 parent bb3f403 commit baae166

File tree

3 files changed

+42
-5
lines changed

3 files changed

+42
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## master / unreleased
44

5+
* [CHANGE] The frontend http server will now send 502 in case of deadline exceeded and 499 if the user requested cancellation. #2156
56
* [CHANGE] Config file changed to remove top level `config_store` field in favor of a nested `configdb` field. #2125
67
* [CHANGE] Removed unnecessary `frontend.cache-split-interval` in favor of `querier.split-queries-by-interval` both to reduce configuration complexity and guarantee alignment of these two configs. Starting from now, `-querier.cache-results` may only be enabled in conjunction with `-querier.split-queries-by-interval` (previously the cache interval default was `24h` so if you want to preserve the same behaviour you should set `-querier.split-queries-by-interval=24h`). #2040
78
* [CHANGE] Removed remaining support for using denormalised tokens in the ring. If you're still running ingesters with denormalised tokens (Cortex 0.4 or earlier, with `-ingester.normalise-tokens=false`), such ingesters will now be completely invisible to distributors and need to be either switched to Cortex 0.6.0 or later, or be configured to use normalised tokens. #2034

pkg/querier/frontend/frontend.go

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,12 @@ var (
3838
Help: "Number of queries in the queue.",
3939
})
4040

41-
errTooManyRequest = httpgrpc.Errorf(http.StatusTooManyRequests, "too many outstanding requests")
42-
errCanceled = httpgrpc.Errorf(http.StatusInternalServerError, "context cancelled")
41+
// StatusClientClosedRequest is the status code for when a client request cancellation of an http request
42+
StatusClientClosedRequest = 499
43+
44+
errTooManyRequest = httpgrpc.Errorf(http.StatusTooManyRequests, "too many outstanding requests")
45+
errCanceled = httpgrpc.Errorf(StatusClientClosedRequest, context.Canceled.Error())
46+
errDeadlineExceeded = httpgrpc.Errorf(http.StatusGatewayTimeout, context.DeadlineExceeded.Error())
4347
)
4448

4549
// Config for a Frontend.
@@ -151,14 +155,14 @@ func (f *Frontend) handle(w http.ResponseWriter, r *http.Request) {
151155

152156
startTime := time.Now()
153157
resp, err := f.roundTripper.RoundTrip(r)
154-
queryResponseTime := time.Now().Sub(startTime)
158+
queryResponseTime := time.Since(startTime)
155159

156160
if f.cfg.LogQueriesLongerThan > 0 && queryResponseTime > f.cfg.LogQueriesLongerThan {
157161
level.Info(f.log).Log("msg", "slow query", "org_id", userID, "url", fmt.Sprintf("http://%s", r.Host+r.RequestURI), "time_taken", queryResponseTime.String())
158162
}
159163

160164
if err != nil {
161-
server.WriteError(w, err)
165+
writeError(w, err)
162166
return
163167
}
164168

@@ -170,6 +174,17 @@ func (f *Frontend) handle(w http.ResponseWriter, r *http.Request) {
170174
io.Copy(w, resp.Body)
171175
}
172176

177+
func writeError(w http.ResponseWriter, err error) {
178+
switch err {
179+
case context.Canceled:
180+
err = errCanceled
181+
case context.DeadlineExceeded:
182+
err = errDeadlineExceeded
183+
default:
184+
}
185+
server.WriteError(w, err)
186+
}
187+
173188
// RoundTrip implement http.Transport.
174189
func (f *Frontend) RoundTrip(r *http.Request) (*http.Response, error) {
175190
req, err := server.HTTPRequest(r)
@@ -230,7 +245,7 @@ func (f *Frontend) RoundTripGRPC(ctx context.Context, req *ProcessRequest) (*Pro
230245

231246
select {
232247
case <-ctx.Done():
233-
return nil, errCanceled
248+
return nil, ctx.Err()
234249

235250
case resp := <-request.response:
236251
return resp, nil

pkg/querier/frontend/frontend_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package frontend
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"io/ioutil"
78
"net"
89
"net/http"
10+
"net/http/httptest"
911
"sync/atomic"
1012
"testing"
1113
"time"
@@ -18,6 +20,7 @@ import (
1820
"github.com/stretchr/testify/require"
1921
jaeger "github.com/uber/jaeger-client-go"
2022
"github.com/uber/jaeger-client-go/config"
23+
"github.com/weaveworks/common/httpgrpc"
2124
httpgrpc_server "github.com/weaveworks/common/httpgrpc/server"
2225
"github.com/weaveworks/common/middleware"
2326
"github.com/weaveworks/common/user"
@@ -133,6 +136,24 @@ func TestFrontendCancel(t *testing.T) {
133136
testFrontend(t, handler, test)
134137
}
135138

139+
func TestFrontendCancelStatusCode(t *testing.T) {
140+
for _, test := range []struct {
141+
status int
142+
err error
143+
}{
144+
{http.StatusInternalServerError, errors.New("unknown")},
145+
{http.StatusGatewayTimeout, context.DeadlineExceeded},
146+
{StatusClientClosedRequest, context.Canceled},
147+
{http.StatusBadRequest, httpgrpc.Errorf(http.StatusBadRequest, "")},
148+
} {
149+
t.Run(test.err.Error(), func(t *testing.T) {
150+
w := httptest.NewRecorder()
151+
writeError(w, test.err)
152+
require.Equal(t, test.status, w.Result().StatusCode)
153+
})
154+
}
155+
}
156+
136157
func defaultOverrides(t *testing.T) *validation.Overrides {
137158
var limits validation.Limits
138159
flagext.DefaultValues(&limits)

0 commit comments

Comments
 (0)