mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
app/vmselect: export seriesFetched
stat for /query responses (#3925)
The change adds a new field `seriesFetched` to EvalConfig object. Since EvalConfig object can be copied inside `Exec`, `seriesFetched` is a pointer which can be updated by all copied objects. The reason for having stats is that other components, like vmalert, could benefit from this information. Signed-off-by: hagen1778 <roman@victoriametrics.com> Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
3214b1c315
commit
4021aa11b5
6 changed files with 72 additions and 24 deletions
|
@ -227,7 +227,7 @@ func StreamListGroups(qw422016 *qt422016.Writer, r *http.Request, groups []APIGr
|
||||||
//line app/vmalert/web.qtpl:55
|
//line app/vmalert/web.qtpl:55
|
||||||
qw422016.N().FPrec(g.Interval, 0)
|
qw422016.N().FPrec(g.Interval, 0)
|
||||||
//line app/vmalert/web.qtpl:55
|
//line app/vmalert/web.qtpl:55
|
||||||
qw422016.N().S(`s) #</a>
|
qw422016.N().S(`s) #</a>
|
||||||
`)
|
`)
|
||||||
//line app/vmalert/web.qtpl:56
|
//line app/vmalert/web.qtpl:56
|
||||||
if rNotOk[g.ID] > 0 {
|
if rNotOk[g.ID] > 0 {
|
||||||
|
|
|
@ -786,7 +786,8 @@ func QueryHandler(qt *querytracer.Tracer, startTime time.Time, w http.ResponseWr
|
||||||
qtDone := func() {
|
qtDone := func() {
|
||||||
qt.Donef("query=%s, time=%d: series=%d", query, start, len(result))
|
qt.Donef("query=%s, time=%d: series=%d", query, start, len(result))
|
||||||
}
|
}
|
||||||
WriteQueryResponse(bw, result, qt, qtDone)
|
|
||||||
|
WriteQueryResponse(bw, result, qt, qtDone, ec.SeriesFetched())
|
||||||
if err := bw.Flush(); err != nil {
|
if err := bw.Flush(); err != nil {
|
||||||
return fmt.Errorf("cannot flush query response to remote client: %w", err)
|
return fmt.Errorf("cannot flush query response to remote client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
{% stripspace %}
|
{% stripspace %}
|
||||||
QueryResponse generates response for /api/v1/query.
|
QueryResponse generates response for /api/v1/query.
|
||||||
See https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries
|
See https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries
|
||||||
{% func QueryResponse(rs []netstorage.Result, qt *querytracer.Tracer, qtDone func()) %}
|
{% func QueryResponse(rs []netstorage.Result, qt *querytracer.Tracer, qtDone func(), seriesFetched int) %}
|
||||||
{
|
{
|
||||||
{% code seriesCount := len(rs) %}
|
{% code seriesCount := len(rs) %}
|
||||||
"status":"success",
|
"status":"success",
|
||||||
|
@ -28,6 +28,9 @@ See https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"stats":{
|
||||||
|
"seriesFetched": "{%d seriesFetched %}"
|
||||||
}
|
}
|
||||||
{% code
|
{% code
|
||||||
qt.Printf("generate /api/v1/query response for series=%d", seriesCount)
|
qt.Printf("generate /api/v1/query response for series=%d", seriesCount)
|
||||||
|
|
|
@ -26,7 +26,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:9
|
//line app/vmselect/prometheus/query_response.qtpl:9
|
||||||
func StreamQueryResponse(qw422016 *qt422016.Writer, rs []netstorage.Result, qt *querytracer.Tracer, qtDone func()) {
|
func StreamQueryResponse(qw422016 *qt422016.Writer, rs []netstorage.Result, qt *querytracer.Tracer, qtDone func(), seriesFetched int) {
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:9
|
//line app/vmselect/prometheus/query_response.qtpl:9
|
||||||
qw422016.N().S(`{`)
|
qw422016.N().S(`{`)
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:11
|
//line app/vmselect/prometheus/query_response.qtpl:11
|
||||||
|
@ -69,40 +69,44 @@ func StreamQueryResponse(qw422016 *qt422016.Writer, rs []netstorage.Result, qt *
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:29
|
//line app/vmselect/prometheus/query_response.qtpl:29
|
||||||
}
|
}
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:29
|
//line app/vmselect/prometheus/query_response.qtpl:29
|
||||||
qw422016.N().S(`]}`)
|
qw422016.N().S(`]},"stats":{"seriesFetched": "`)
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:33
|
//line app/vmselect/prometheus/query_response.qtpl:33
|
||||||
|
qw422016.N().D(seriesFetched)
|
||||||
|
//line app/vmselect/prometheus/query_response.qtpl:33
|
||||||
|
qw422016.N().S(`"}`)
|
||||||
|
//line app/vmselect/prometheus/query_response.qtpl:36
|
||||||
qt.Printf("generate /api/v1/query response for series=%d", seriesCount)
|
qt.Printf("generate /api/v1/query response for series=%d", seriesCount)
|
||||||
qtDone()
|
qtDone()
|
||||||
|
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:36
|
//line app/vmselect/prometheus/query_response.qtpl:39
|
||||||
streamdumpQueryTrace(qw422016, qt)
|
streamdumpQueryTrace(qw422016, qt)
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:36
|
//line app/vmselect/prometheus/query_response.qtpl:39
|
||||||
qw422016.N().S(`}`)
|
qw422016.N().S(`}`)
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
}
|
}
|
||||||
|
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
func WriteQueryResponse(qq422016 qtio422016.Writer, rs []netstorage.Result, qt *querytracer.Tracer, qtDone func()) {
|
func WriteQueryResponse(qq422016 qtio422016.Writer, rs []netstorage.Result, qt *querytracer.Tracer, qtDone func(), seriesFetched int) {
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
qw422016 := qt422016.AcquireWriter(qq422016)
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
StreamQueryResponse(qw422016, rs, qt, qtDone)
|
StreamQueryResponse(qw422016, rs, qt, qtDone, seriesFetched)
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
qt422016.ReleaseWriter(qw422016)
|
qt422016.ReleaseWriter(qw422016)
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
}
|
}
|
||||||
|
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
func QueryResponse(rs []netstorage.Result, qt *querytracer.Tracer, qtDone func()) string {
|
func QueryResponse(rs []netstorage.Result, qt *querytracer.Tracer, qtDone func(), seriesFetched int) string {
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
qb422016 := qt422016.AcquireByteBuffer()
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
WriteQueryResponse(qb422016, rs, qt, qtDone)
|
WriteQueryResponse(qb422016, rs, qt, qtDone, seriesFetched)
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
qs422016 := string(qb422016.B)
|
qs422016 := string(qb422016.B)
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
qt422016.ReleaseByteBuffer(qb422016)
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
return qs422016
|
return qs422016
|
||||||
//line app/vmselect/prometheus/query_response.qtpl:38
|
//line app/vmselect/prometheus/query_response.qtpl:41
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,6 +134,12 @@ type EvalConfig struct {
|
||||||
|
|
||||||
timestamps []int64
|
timestamps []int64
|
||||||
timestampsOnce sync.Once
|
timestampsOnce sync.Once
|
||||||
|
|
||||||
|
// seriesFetched stores the number of time series fetched
|
||||||
|
// from the storage during the evaluation.
|
||||||
|
// It is defined as a pointer since EvalConfig can be forked
|
||||||
|
// during the evaluation but we want to keep its state.
|
||||||
|
seriesFetched *int
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyEvalConfig returns src copy.
|
// copyEvalConfig returns src copy.
|
||||||
|
@ -151,10 +157,28 @@ func copyEvalConfig(src *EvalConfig) *EvalConfig {
|
||||||
ec.EnforcedTagFilterss = src.EnforcedTagFilterss
|
ec.EnforcedTagFilterss = src.EnforcedTagFilterss
|
||||||
ec.GetRequestURI = src.GetRequestURI
|
ec.GetRequestURI = src.GetRequestURI
|
||||||
|
|
||||||
|
ec.seriesFetched = src.seriesFetched
|
||||||
|
|
||||||
// do not copy src.timestamps - they must be generated again.
|
// do not copy src.timestamps - they must be generated again.
|
||||||
return &ec
|
return &ec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *EvalConfig) addStats(series int) {
|
||||||
|
if ec.seriesFetched == nil {
|
||||||
|
ec.seriesFetched = new(int)
|
||||||
|
}
|
||||||
|
*ec.seriesFetched += series
|
||||||
|
}
|
||||||
|
|
||||||
|
// SeriesFetched returns the number of series fetched from storages
|
||||||
|
// during the evaluation.
|
||||||
|
func (ec *EvalConfig) SeriesFetched() int {
|
||||||
|
if ec.seriesFetched == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return *ec.seriesFetched
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *EvalConfig) validate() {
|
func (ec *EvalConfig) validate() {
|
||||||
if ec.Start > ec.End {
|
if ec.Start > ec.End {
|
||||||
logger.Panicf("BUG: start cannot exceed end; got %d vs %d", ec.Start, ec.End)
|
logger.Panicf("BUG: start cannot exceed end; got %d vs %d", ec.Start, ec.End)
|
||||||
|
@ -1077,6 +1101,7 @@ func evalRollupFuncWithMetricExpr(qt *querytracer.Tracer, ec *EvalConfig, funcNa
|
||||||
tss := mergeTimeseries(tssCached, nil, start, ec)
|
tss := mergeTimeseries(tssCached, nil, start, ec)
|
||||||
return tss, nil
|
return tss, nil
|
||||||
}
|
}
|
||||||
|
ec.addStats(rssLen)
|
||||||
|
|
||||||
// Verify timeseries fit available memory after the rollup.
|
// Verify timeseries fit available memory after the rollup.
|
||||||
// Take into account points from tssCached.
|
// Take into account points from tssCached.
|
||||||
|
|
|
@ -76,3 +76,18 @@ func TestValidateMaxPointsPerSeriesSuccess(t *testing.T) {
|
||||||
f(1659962171908, 1659966077742, 5000, 800)
|
f(1659962171908, 1659966077742, 5000, 800)
|
||||||
f(1659962150000, 1659966070000, 10000, 393)
|
f(1659962150000, 1659966070000, 10000, 393)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEvalConfig_SeriesFetched(t *testing.T) {
|
||||||
|
ec := &EvalConfig{}
|
||||||
|
ec.addStats(1)
|
||||||
|
|
||||||
|
if ec.SeriesFetched() != 1 {
|
||||||
|
t.Fatalf("expected to get 1; got %d instead", ec.SeriesFetched())
|
||||||
|
}
|
||||||
|
|
||||||
|
ecNew := copyEvalConfig(ec)
|
||||||
|
ecNew.addStats(3)
|
||||||
|
if ec.SeriesFetched() != 4 {
|
||||||
|
t.Fatalf("expected to get 4; got %d instead", ec.SeriesFetched())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue