proxyRequest proxies the given request to clickhouse and sends response to rw. srw is required only for setting non-200 status codes on timeouts or on client connection disconnects.
(s *scope, rw ResponseWriterWithCode, srw *statResponseWriter, req *http.Request)
| 283 | // srw is required only for setting non-200 status codes on timeouts |
| 284 | // or on client connection disconnects. |
| 285 | func (rp *reverseProxy) proxyRequest(s *scope, rw ResponseWriterWithCode, srw *statResponseWriter, req *http.Request) { |
| 286 | // wrap body into cachedReadCloser, so we could obtain the original |
| 287 | // request on error. |
| 288 | if _, ok := req.Body.(*cachedReadCloser); !ok { |
| 289 | req.Body = &cachedReadCloser{ |
| 290 | ReadCloser: req.Body, |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | timeout, timeoutErrMsg := s.getTimeoutWithErrMsg() |
| 295 | ctx := context.Background() |
| 296 | if timeout > 0 { |
| 297 | var cancel context.CancelFunc |
| 298 | ctx, cancel = context.WithTimeout(ctx, timeout) |
| 299 | defer cancel() |
| 300 | } |
| 301 | |
| 302 | // Cancel the ctx if client closes the remote connection, |
| 303 | // so the proxied query may be killed instantly. |
| 304 | ctx, ctxCancel := listenToCloseNotify(ctx, rw) |
| 305 | defer ctxCancel() |
| 306 | |
| 307 | req = req.WithContext(ctx) |
| 308 | |
| 309 | startTime := time.Now() |
| 310 | |
| 311 | executeDuration, err := executeWithRetry(ctx, s, s.cluster.retryNumber, rp.rp.ServeHTTP, rw, srw, req, func(duration float64) { |
| 312 | proxiedResponseDuration.With(s.labels).Observe(duration) |
| 313 | }, func(labels prometheus.Labels) { retryRequest.With(labels).Inc() }) |
| 314 | |
| 315 | statusCodesClickhouse.With( |
| 316 | prometheus.Labels{ |
| 317 | "cluster": s.cluster.name, |
| 318 | "replica": s.host.ReplicaName(), |
| 319 | "cluster_node": s.host.Host(), |
| 320 | "code": strconv.Itoa(rw.StatusCode()), |
| 321 | }, |
| 322 | ).Inc() |
| 323 | switch { |
| 324 | case err == nil: |
| 325 | return |
| 326 | case errors.Is(err, context.Canceled): |
| 327 | canceledRequest.With(s.labels).Inc() |
| 328 | |
| 329 | q := getQuerySnippet(req) |
| 330 | log.Debugf("%s: remote client closed the connection in %s; query: %q", s, time.Since(startTime), q) |
| 331 | if err := s.killQuery(); err != nil { |
| 332 | log.Errorf("%s: cannot kill query: %s; query: %q", s, err, q) |
| 333 | } |
| 334 | srw.statusCode = 499 // See https://httpstatuses.com/499 . |
| 335 | case errors.Is(err, context.DeadlineExceeded): |
| 336 | timeoutRequest.With(s.labels).Inc() |
| 337 | |
| 338 | // Penalize host with the timed out query, because it may be overloaded. |
| 339 | s.host.Penalize() |
| 340 | |
| 341 | q := getQuerySnippet(req) |
| 342 | log.Debugf("%s: query timeout in %f; query: %q", s, executeDuration, q) |
no test coverage detected