app/vmalert: retry on empty data

This commit is contained in:
AndrewChubatiuk 2024-07-25 17:13:56 +03:00 committed by Andrii Chubatiuk
parent d53dd6ecf3
commit 3bf05ba979

View file

@ -20,6 +20,8 @@ type datasourceType string
const ( const (
datasourcePrometheus datasourceType = "prometheus" datasourcePrometheus datasourceType = "prometheus"
datasourceGraphite datasourceType = "graphite" datasourceGraphite datasourceType = "graphite"
retries = 2
) )
func toDatasourceType(s string) datasourceType { func toDatasourceType(s string) datasourceType {
@ -135,36 +137,41 @@ func NewVMStorage(baseURL string, authCfg *promauth.Config, queryStep time.Durat
// Query executes the given query and returns parsed response // Query executes the given query and returns parsed response
func (s *VMStorage) Query(ctx context.Context, query string, ts time.Time) (Result, *http.Request, error) { func (s *VMStorage) Query(ctx context.Context, query string, ts time.Time) (Result, *http.Request, error) {
req, err := s.newQueryRequest(ctx, query, ts) var e error
if err != nil { for retry := 1; retry < retries; retry++ {
return Result{}, nil, err req, err := s.newQueryRequest(ctx, query, ts)
} if err != nil {
resp, err := s.do(req)
if err != nil {
if !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) && !netutil.IsTrivialNetworkError(err) {
// Return unexpected error to the caller.
return Result{}, nil, err return Result{}, nil, err
} }
// Something in the middle between client and datasource might be closing resp, err := s.do(req)
// the connection. So we do a one more attempt in hope request will succeed.
req, err = s.newQueryRequest(ctx, query, ts)
if err != nil { if err != nil {
return Result{}, nil, fmt.Errorf("second attempt: %w", err) // Something in the middle between client and datasource might be closing
// the connection. So we do a one more attempt in hope request will succeed.
if !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) && !netutil.IsTrivialNetworkError(err) {
// Return unexpected error to the caller.
return Result{}, nil, err
}
e = err
continue
} }
resp, err = s.do(req) // Process the received response.
parseFn := parsePrometheusResponse
if s.dataSourceType != datasourcePrometheus {
parseFn = parseGraphiteResponse
}
result, err := parseFn(req, resp)
_ = resp.Body.Close()
if err != nil { if err != nil {
return Result{}, nil, fmt.Errorf("second attempt: %w", err) // Covering possible empty response
if !errors.Is(err, io.EOF) {
return Result{}, nil, err
}
e = err
continue
} }
return result, req, err
} }
return Result{}, nil, fmt.Errorf("failed after %d retries: %w", retries, e)
// Process the received response.
parseFn := parsePrometheusResponse
if s.dataSourceType != datasourcePrometheus {
parseFn = parseGraphiteResponse
}
result, err := parseFn(req, resp)
_ = resp.Body.Close()
return result, req, err
} }
// QueryRange executes the given query on the given time range. // QueryRange executes the given query on the given time range.
@ -180,32 +187,38 @@ func (s *VMStorage) QueryRange(ctx context.Context, query string, start, end tim
if end.IsZero() { if end.IsZero() {
return res, fmt.Errorf("end param is missing") return res, fmt.Errorf("end param is missing")
} }
req, err := s.newQueryRangeRequest(ctx, query, start, end) var e error
if err != nil { for retry := 1; retry < retries; retry++ {
return res, err req, err := s.newQueryRangeRequest(ctx, query, start, end)
} if err != nil {
resp, err := s.do(req)
if err != nil {
if !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) && !netutil.IsTrivialNetworkError(err) {
// Return unexpected error to the caller.
return res, err return res, err
} }
// Something in the middle between client and datasource might be closing resp, err := s.do(req)
// the connection. So we do a one more attempt in hope request will succeed.
req, err = s.newQueryRangeRequest(ctx, query, start, end)
if err != nil { if err != nil {
return res, fmt.Errorf("second attempt: %w", err) // Something in the middle between client and datasource might be closing
// the connection. So we do a one more attempt in hope request will succeed.
if !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) && !netutil.IsTrivialNetworkError(err) {
// Return unexpected error to the caller.
return res, err
}
e = err
continue
} }
resp, err = s.do(req)
if err != nil {
return res, fmt.Errorf("second attempt: %w", err)
}
}
// Process the received response. // Process the received response.
res, err = parsePrometheusResponse(req, resp) res, err = parsePrometheusResponse(req, resp)
_ = resp.Body.Close() _ = resp.Body.Close()
return res, err if err != nil {
// Covering possible empty response
if !errors.Is(err, io.EOF) {
return Result{}, err
}
e = err
continue
}
return res, err
}
return Result{}, fmt.Errorf("failed after %d retries: %w", retries, e)
} }
func (s *VMStorage) do(req *http.Request) (*http.Response, error) { func (s *VMStorage) do(req *http.Request) (*http.Response, error) {