diff --git a/app/vmselect/netstorage/netstorage.go b/app/vmselect/netstorage/netstorage.go index 3e50ba4aa0..6cdb67ed2c 100644 --- a/app/vmselect/netstorage/netstorage.go +++ b/app/vmselect/netstorage/netstorage.go @@ -101,7 +101,7 @@ func (rss *Results) RunParallel(f func(rs *Result, workerID uint)) error { rowsProcessed := 0 for pts := range workCh { if time.Until(rss.deadline.Deadline) < 0 { - err = fmt.Errorf("timeout exceeded during query execution: %s", rss.deadline.Timeout) + err = fmt.Errorf("timeout exceeded during query execution: %s", rss.deadline.String()) break } if err = pts.Unpack(rss.tbf, rs, rss.tr, rss.fetchData, rss.at, maxWorkersCount); err != nil { @@ -1010,7 +1010,7 @@ func (sn *storageNode) execOnConn(rpcName string, f func(bc *handshake.BufferedC // since it may be broken. _ = bc.Close() } - return fmt.Errorf("cannot execute rpcName=%q on vmstorage %q with timeout %s: %s", rpcName, remoteAddr, deadline.Timeout, err) + return fmt.Errorf("cannot execute rpcName=%q on vmstorage %q with timeout %s: %s", rpcName, remoteAddr, deadline.String(), err) } // Return the connection back to the pool, assuming it is healthy. sn.connPool.Put(bc) @@ -1406,13 +1406,24 @@ var rsPool sync.Pool // Deadline contains deadline with the corresponding timeout for pretty error messages. type Deadline struct { Deadline time.Time - Timeout time.Duration + + timeout time.Duration + flagHint string } // NewDeadline returns deadline for the given timeout. -func NewDeadline(timeout time.Duration) Deadline { +// +// flagHint must contain a hit for command-line flag, which could be used +// in order to increase timeout. +func NewDeadline(timeout time.Duration, flagHint string) Deadline { return Deadline{ Deadline: time.Now().Add(timeout), - Timeout: timeout, + timeout: timeout, + flagHint: flagHint, } } + +// String returns human-readable string representation for d. +func (d *Deadline) String() string { + return fmt.Sprintf("%.3f seconds; the timeout can be adjusted with `%s` command-line flag", d.timeout.Seconds(), d.flagHint) +} diff --git a/app/vmselect/prometheus/prometheus.go b/app/vmselect/prometheus/prometheus.go index 1dd8aa85e2..7bb34b990e 100644 --- a/app/vmselect/prometheus/prometheus.go +++ b/app/vmselect/prometheus/prometheus.go @@ -953,15 +953,15 @@ func getMaxLookback(r *http.Request) (int64, error) { func getDeadlineForQuery(r *http.Request) netstorage.Deadline { dMax := int64(maxQueryDuration.Seconds() * 1e3) - return getDeadlineWithMaxDuration(r, dMax) + return getDeadlineWithMaxDuration(r, dMax, "-search.maxQueryDuration") } func getDeadlineForExport(r *http.Request) netstorage.Deadline { dMax := int64(maxExportDuration.Seconds() * 1e3) - return getDeadlineWithMaxDuration(r, dMax) + return getDeadlineWithMaxDuration(r, dMax, "-search.maxExportDuration") } -func getDeadlineWithMaxDuration(r *http.Request, dMax int64) netstorage.Deadline { +func getDeadlineWithMaxDuration(r *http.Request, dMax int64, flagHint string) netstorage.Deadline { d, err := getDuration(r, "timeout", 0) if err != nil { d = 0 @@ -970,7 +970,7 @@ func getDeadlineWithMaxDuration(r *http.Request, dMax int64) netstorage.Deadline d = dMax } timeout := time.Duration(d) * time.Millisecond - return netstorage.NewDeadline(timeout) + return netstorage.NewDeadline(timeout, flagHint) } func getBool(r *http.Request, argKey string) bool { diff --git a/app/vmselect/promql/exec_test.go b/app/vmselect/promql/exec_test.go index 3dc49614e1..0ddb46d6b5 100644 --- a/app/vmselect/promql/exec_test.go +++ b/app/vmselect/promql/exec_test.go @@ -31,7 +31,7 @@ func TestExecSuccess(t *testing.T) { Start: start, End: end, Step: step, - Deadline: netstorage.NewDeadline(time.Minute), + Deadline: netstorage.NewDeadline(time.Minute, ""), } for i := 0; i < 5; i++ { result, err := Exec(ec, q, false) @@ -5243,7 +5243,7 @@ func TestExecError(t *testing.T) { Start: 1000, End: 2000, Step: 100, - Deadline: netstorage.NewDeadline(time.Minute), + Deadline: netstorage.NewDeadline(time.Minute, ""), } for i := 0; i < 4; i++ { rv, err := Exec(ec, q, false)