promql optimization: rate & sum

This commit is contained in:
akvlad
2024-06-20 12:25:19 +03:00
parent b592684902
commit 8fa69d2a1d
9 changed files with 90 additions and 96 deletions

View File

@ -121,6 +121,7 @@ module.exports.getData = async (matchers, fromMs, toMs, subqueries) => {
const db = DATABASE_NAME() const db = DATABASE_NAME()
const subq = (subqueries || {})[getMetricName(matchers)] const subq = (subqueries || {})[getMetricName(matchers)]
if (subq) { if (subq) {
console.log(subq)
const data = await rawRequest(subq + ' FORMAT RowBinary', const data = await rawRequest(subq + ' FORMAT RowBinary',
null, db, { responseType: 'arraybuffer' }) null, db, { responseType: 'arraybuffer' })
return new Uint8Array(data.data) return new Uint8Array(data.data)

View File

@ -274,6 +274,9 @@ func pql(id uint32, c *ctx, optimizable bool,
} }
} }
maxDurationMs := getMaxDurationMs(rq.Statement())
fromMs -= maxDurationMs
subsels := strings.Builder{} subsels := strings.Builder{}
subsels.WriteString("{") subsels.WriteString("{")
if optimizable { if optimizable {
@ -291,7 +294,7 @@ func pql(id uint32, c *ctx, optimizable bool,
if i != 0 { if i != 0 {
subsels.WriteString(",") subsels.WriteString(",")
} }
subsels.WriteString(fmt.Sprintf(`"%s":"%s"`, strconv.Quote(k), strconv.Quote(v))) subsels.WriteString(fmt.Sprintf(`%s:%s`, strconv.Quote(k), strconv.Quote(v)))
i++ i++
} }
} }
@ -299,7 +302,10 @@ func pql(id uint32, c *ctx, optimizable bool,
matchersJSON := getmatchersJSON(rq) matchersJSON := getmatchersJSON(rq)
c.response = []byte(fmt.Sprintf(`{"subqueries": %s, "matchers": %s}`, subsels.String(), matchersJSON)) c.response = []byte(fmt.Sprintf(`{"subqueries": %s, "matchers": %s, "fromMs": %d}`,
subsels.String(),
matchersJSON,
fromMs))
c.onDataLoad = func(c *ctx) { c.onDataLoad = func(c *ctx) {
ctxId := gcContext.GetContextID() ctxId := gcContext.GetContextID()
gcContext.SetContext(id) gcContext.SetContext(id)
@ -312,6 +318,21 @@ func pql(id uint32, c *ctx, optimizable bool,
return 0 return 0
} }
func getMaxDurationMs(q parser.Node) int64 {
maxDurationMs := int64(0)
for _, c := range parser.Children(q) {
_m := getMaxDurationMs(c)
if _m > maxDurationMs {
maxDurationMs = _m
}
}
ms, _ := q.(*parser.MatrixSelector)
if ms != nil && maxDurationMs < ms.Range.Milliseconds() {
return ms.Range.Milliseconds()
}
return maxDurationMs
}
func optimizeQuery(q promql.Query, fromMs int64, toMs int64, stepMs int64) (map[string]string, promql.Query, error) { func optimizeQuery(q promql.Query, fromMs int64, toMs int64, stepMs int64) (map[string]string, promql.Query, error) {
appliableNodes := findAppliableNodes(q.Statement(), nil) appliableNodes := findAppliableNodes(q.Statement(), nil)
var err error var err error
@ -352,7 +373,7 @@ func optimizeQuery(q promql.Query, fromMs int64, toMs int64, stepMs int64) (map[
IsCluster: false, IsCluster: false,
From: time.Unix(0, fromMs*1000000), From: time.Unix(0, fromMs*1000000),
To: time.Unix(0, toMs*1000000), To: time.Unix(0, toMs*1000000),
Step: time.Millisecond * time.Duration(stepMs), Step: time.Millisecond * 15000, /*time.Duration(stepMs)*/
TimeSeriesTable: "time_series", TimeSeriesTable: "time_series",
TimeSeriesDistTable: "time_series_dist", TimeSeriesDistTable: "time_series_dist",
TimeSeriesGinTable: "time_series_gin", TimeSeriesGinTable: "time_series_gin",
@ -448,76 +469,6 @@ func writeVector(v promql.Vector) string {
} }
func main() { func main() {
queriable := &TestQueryable{id: 0, stepMs: 15000}
rq, err := getEng().NewRangeQuery(
queriable,
nil,
"histogram_quantile(0.5, sum by (container) (rate(network_usage{container=~\"awesome\"}[5m])))",
time.Now().Add(time.Hour*-24),
time.Now(),
time.Millisecond*time.Duration(15000))
if err != nil {
panic(err)
}
matchers := findAppliableNodes(rq.Statement(), nil)
for _, m := range matchers {
fmt.Println(m)
opt := m.optimizer
opt = &promql2.FinalizerOptimizer{
SubOptimizer: opt,
}
opt, err = promql2.PlanOptimize(m.node, opt)
if err != nil {
panic(err)
}
planner, err := opt.Optimize(m.node)
if err != nil {
panic(err)
}
fakeMetric := fmt.Sprintf("fake_metric_%d", time.Now().UnixNano())
fmt.Println(rq.Statement())
swapChild(m.parent, m.node, &parser.VectorSelector{
Name: fakeMetric,
OriginalOffset: 0,
Offset: 0,
Timestamp: nil,
StartOrEnd: 0,
LabelMatchers: []*labels.Matcher{
{
Type: labels.MatchEqual,
Name: "__name__",
Value: fakeMetric,
},
},
UnexpandedSeriesSet: nil,
Series: nil,
PosRange: parser.PositionRange{},
})
fmt.Println(rq.Statement())
sel, err := planner.Process(&shared2.PlannerContext{
IsCluster: false,
From: time.Now().Add(time.Hour * -24),
To: time.Now(),
Step: time.Millisecond * time.Duration(15000),
TimeSeriesTable: "time_series",
TimeSeriesDistTable: "time_series_dist",
TimeSeriesGinTable: "time_series_gin",
MetricsTable: "metrics_15s",
MetricsDistTable: "metrics_15s_dist",
})
if err != nil {
panic(err)
}
strSel, err := sel.String(&sql.Ctx{
Params: map[string]sql.SQLObject{},
Result: map[string]sql.SQLObject{},
})
if err != nil {
panic(err)
}
println(strSel)
}
} }
func getOptimizer(n parser.Node) promql2.IOptimizer { func getOptimizer(n parser.Node) promql2.IOptimizer {
@ -696,10 +647,11 @@ type TestSeries struct {
data []byte data []byte
stepMs int64 stepMs int64
labels labels.Labels labels labels.Labels
tsMs int64 tsMs int64
val float64 val float64
i int lastValTs int64
i int
state int state int
} }
@ -721,11 +673,14 @@ func (t *TestSeries) Next() bool {
t.tsMs += t.stepMs t.tsMs += t.stepMs
if t.tsMs >= ts { if t.tsMs >= ts {
t.state = 0 t.state = 0
} else if t.lastValTs+300000 < t.tsMs {
t.state = 0
} }
} }
if t.state == 0 { if t.state == 0 {
t.tsMs = ts t.tsMs = ts
t.val = *(*float64)(unsafe.Pointer(&t.data[t.i*16+8])) t.val = *(*float64)(unsafe.Pointer(&t.data[t.i*16+8]))
t.lastValTs = t.tsMs
t.i++ t.i++
t.state = 1 t.state = 1
} }

View File

@ -62,7 +62,7 @@ module.exports.pqlRangeQuery = async (query, startMs, endMs, stepMs, getData) =>
const step = stepMs || 15000 const step = stepMs || 15000
return await pql(query, return await pql(query,
(ctx) => _wasm.exportsWrap.pqlRangeQuery(ctx.id, start, end, step, process.env.EXPERIMENTAL_PROMQL_OPTIMIZE ? 1 : 0), (ctx) => _wasm.exportsWrap.pqlRangeQuery(ctx.id, start, end, step, process.env.EXPERIMENTAL_PROMQL_OPTIMIZE ? 1 : 0),
(matchers, subq) => getData(matchers, start, end, subq)) (matchers, subq, startMs) => getData(matchers, startMs, end, subq))
} }
/** /**
@ -75,9 +75,10 @@ module.exports.pqlRangeQuery = async (query, startMs, endMs, stepMs, getData) =>
module.exports.pqlInstantQuery = async (query, timeMs, getData) => { module.exports.pqlInstantQuery = async (query, timeMs, getData) => {
const time = timeMs || Date.now() const time = timeMs || Date.now()
const _wasm = getWasm() const _wasm = getWasm()
const start = time - 300000
return await pql(query, return await pql(query,
(ctx) => _wasm.exportsWrap.pqlInstantQuery(ctx.id, time, process.env.EXPERIMENTAL_PROMQL_OPTIMIZE ? 1 : 0), (ctx) => _wasm.exportsWrap.pqlInstantQuery(ctx.id, time, process.env.EXPERIMENTAL_PROMQL_OPTIMIZE ? 1 : 0),
(matchers, subq) => getData(matchers, time - 300000, time, subq)) (matchers, subq, start) => getData(matchers, start, time, subq))
} }
module.exports.pqlMatchers = (query) => { module.exports.pqlMatchers = (query) => {
@ -162,8 +163,8 @@ const pql = async (query, wasmCall, getData) => {
const matchersObj = JSON.parse(ctx.read()) const matchersObj = JSON.parse(ctx.read())
const matchersResults = await Promise.all( const matchersResults = await Promise.all(
matchersObj.map(async (matchers, i) => { matchersObj.matchers.map(async (matchers, i) => {
const data = await getData(matchers.matchers, matchers.subqueries) const data = await getData(matchers, matchersObj.subqueries, matchersObj.fromMs)
return { matchers, data } return { matchers, data }
})) }))

Binary file not shown.

View File

@ -32,16 +32,16 @@ func (f *FinalizePlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, erro
withMain := sql.NewWith(main, "pre_final") withMain := sql.NewWith(main, "pre_final")
res := sql.NewSelect().With(withMain).Select(withMain). res := sql.NewSelect().With(withMain).Select(withMain).
Select( Select(
sql.NewSimpleCol("pre_final.fingerprint", "fingerprint"),
sql.NewSimpleCol(withLabels.GetAlias()+".labels", "labels"), sql.NewSimpleCol(withLabels.GetAlias()+".labels", "labels"),
sql.NewSimpleCol("arraySort(groupArray((pre_final.timestamp_ms, pre_final.value)))", "values"), sql.NewSimpleCol("arraySort(groupArray((pre_final.timestamp_ms, pre_final.value)))", "values"),
).From(sql.NewWithRef(withMain)). ).From(sql.NewWithRef(withMain)).
//AndWhere(sql.Neq(sql.NewRawObject("pre_final.value"), sql.NewIntVal(0))).
Join(sql.NewJoin( Join(sql.NewJoin(
"ANY LEFT", "ANY LEFT",
sql.NewWithRef(withLabels), sql.NewWithRef(withLabels),
sql.Eq( sql.Eq(
sql.NewRawObject("pre_final.fingerprint"), sql.NewRawObject("pre_final.fingerprint"),
sql.NewRawObject(withLabels.GetAlias()+".new_fingerprint")))). sql.NewRawObject(withLabels.GetAlias()+".new_fingerprint")))).
GroupBy(sql.NewRawObject("pre_final.fingerprint"), sql.NewRawObject(withLabels.GetAlias()+".labels")) GroupBy(sql.NewRawObject(withLabels.GetAlias() + ".labels"))
return res, nil return res, nil
} }

View File

@ -22,7 +22,13 @@ func (m *MetricsExtendPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect,
withMain := sql.NewWith(main, "pre_extend") withMain := sql.NewWith(main, "pre_extend")
extendedCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { extendedCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) {
return fmt.Sprintf( return fmt.Sprintf(
"argMaxIf(value, timestamp_ms, isNaN(value) = 0) OVER ("+ "argMaxIf(value, timestamp_ms, pre_extend.original = 1) OVER ("+
"PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN %d PRECEDING AND CURRENT ROW"+
")", extendCnt), nil
})
origCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) {
return fmt.Sprintf(
"max(original) OVER ("+
"PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN %d PRECEDING AND CURRENT ROW"+ "PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN %d PRECEDING AND CURRENT ROW"+
")", extendCnt), nil ")", extendCnt), nil
}) })
@ -30,7 +36,11 @@ func (m *MetricsExtendPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect,
Select( Select(
sql.NewSimpleCol("fingerprint", "fingerprint"), sql.NewSimpleCol("fingerprint", "fingerprint"),
sql.NewSimpleCol("timestamp_ms", "timestamp_ms"), sql.NewSimpleCol("timestamp_ms", "timestamp_ms"),
sql.NewCol(extendedCol, "value")). sql.NewCol(extendedCol, "value"),
sql.NewCol(origCol, "original")).
From(sql.NewWithRef(withMain)) From(sql.NewWithRef(withMain))
return extend, nil withExtend := sql.NewWith(extend, "extend")
return sql.NewSelect().With(withExtend).Select(sql.NewRawObject("*")).
From(sql.NewWithRef(withExtend)).
AndWhere(sql.Eq(sql.NewRawObject("original"), sql.NewIntVal(1))), nil
} }

View File

@ -34,18 +34,39 @@ func (m *RatePlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, error) {
"PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN %d PRECEDING AND CURRENT ROW"+ "PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN %d PRECEDING AND CURRENT ROW"+
")", rateCnt), nil ")", rateCnt), nil
}) })
valueCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) { resetCol := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) {
return fmt.Sprintf( return fmt.Sprintf(
"if(last > first, last - first, last) / %f", m.Duration.Seconds()), nil "if(value < (any(value) OVER (" +
"PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING" +
") as lastValue), lastValue, 0)"), nil
}) })
extend := sql.NewSelect().With(withMain). reset := sql.NewSelect().With(withMain).
Select(
sql.NewSimpleCol("fingerprint", "fingerprint"),
sql.NewSimpleCol("timestamp_ms", "timestamp_ms"),
sql.NewCol(resetCol, "reset"),
sql.NewSimpleCol("value", "value")).
From(sql.NewWithRef(withMain))
withReset := sql.NewWith(reset, "pre_reset")
resetColSum := sql.NewCustomCol(func(ctx *sql.Ctx, options ...int) (string, error) {
_rateCnt := rateCnt - 1
if rateCnt <= 1 {
_rateCnt = 1
}
return fmt.Sprintf(
"sum(reset) OVER ("+
"PARTITION BY fingerprint ORDER BY timestamp_ms ROWS BETWEEN %d PRECEDING AND CURRENT ROW"+
")", _rateCnt), nil
})
extend := sql.NewSelect().With(withReset).
Select( Select(
sql.NewSimpleCol("fingerprint", "fingerprint"), sql.NewSimpleCol("fingerprint", "fingerprint"),
sql.NewSimpleCol("timestamp_ms", "timestamp_ms"), sql.NewSimpleCol("timestamp_ms", "timestamp_ms"),
sql.NewCol(lastCol, "last"), sql.NewCol(lastCol, "last"),
sql.NewCol(firstCol, "first"), sql.NewCol(firstCol, "first"),
sql.NewCol(valueCol, "_value")). sql.NewCol(resetColSum, "reset"),
From(sql.NewWithRef(withMain)) sql.NewSimpleCol(fmt.Sprintf("(last - first + reset) / %f", m.Duration.Seconds()), "_value")).
From(sql.NewWithRef(withReset))
withExtend := sql.NewWith(extend, "rate") withExtend := sql.NewWith(extend, "rate")
return sql.NewSelect(). return sql.NewSelect().
With(withExtend). With(withExtend).

View File

@ -26,7 +26,8 @@ func (m *MetricsInitPlanner) Process(ctx *shared.PlannerContext) (sql.ISelect, e
return sql.NewSelect().With(withFpReq).Select( return sql.NewSelect().With(withFpReq).Select(
sql.NewSimpleCol("fingerprint", "fingerprint"), sql.NewSimpleCol("fingerprint", "fingerprint"),
sql.NewCol(tsNsCol, "timestamp_ms"), sql.NewCol(tsNsCol, "timestamp_ms"),
sql.NewCol(m.ValueCol, "value")). sql.NewCol(m.ValueCol, "value"),
sql.NewSimpleCol("1::UInt8", "original")).
From(sql.NewSimpleCol(ctx.MetricsTable, "metrics")). From(sql.NewSimpleCol(ctx.MetricsTable, "metrics")).
AndWhere( AndWhere(
sql.Ge(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())), sql.Ge(sql.NewRawObject("timestamp_ns"), sql.NewIntVal(ctx.From.UnixNano())),

View File

@ -15,7 +15,12 @@ func (m *MetricsZeroFillPlanner) Process(ctx *shared.PlannerContext) (sql.ISelec
if err != nil { if err != nil {
return nil, err return nil, err
} }
withMain := sql.NewWith(main, "prezerofill") main.OrderBy(sql.NewRawObject("fingerprint"), sql.NewCustomCol(func(_ *sql.Ctx, options ...int) (string, error) {
return fmt.Sprintf("timestamp_ms WITH FILL FROM %d TO %d STEP %d",
ctx.From.UnixMilli(), ctx.To.UnixMilli(), ctx.Step.Milliseconds()), nil
}))
return main, nil
/*withMain := sql.NewWith(main, "prezerofill")
arrLen := (ctx.To.UnixNano()-ctx.From.UnixNano())/ctx.Step.Nanoseconds() + 1 arrLen := (ctx.To.UnixNano()-ctx.From.UnixNano())/ctx.Step.Nanoseconds() + 1
zeroFillCol := sql.NewCustomCol(func(_ *sql.Ctx, options ...int) (string, error) { zeroFillCol := sql.NewCustomCol(func(_ *sql.Ctx, options ...int) (string, error) {
return fmt.Sprintf("groupArrayInsertAt(nan, %d)(value, toUInt32(intDiv(timestamp_ms - %d, %d)))", return fmt.Sprintf("groupArrayInsertAt(nan, %d)(value, toUInt32(intDiv(timestamp_ms - %d, %d)))",
@ -37,9 +42,9 @@ func (m *MetricsZeroFillPlanner) Process(ctx *shared.PlannerContext) (sql.ISelec
postZeroFill := sql.NewSelect().With(withZeroFill). postZeroFill := sql.NewSelect().With(withZeroFill).
Select( Select(
sql.NewSimpleCol("fingerprint", "fingerprint"), sql.NewSimpleCol("fingerprint", "fingerprint"),
sql.NewSimpleCol("val.1", "timestamp_ms"), sql.NewSimpleCol("timestamp_ms", "timestamp_ms"),
sql.NewSimpleCol("val.2", "value")). sql.NewSimpleCol("val.2", "value")).
From(sql.NewWithRef(withZeroFill)). From(sql.NewWithRef(withMain)).
Join(sql.NewJoin("array", sql.NewCol(joinZeroFillStmt, "val"), nil)) Join(sql.NewJoin("array", sql.NewCol(joinZeroFillStmt, "val"), nil))
return postZeroFill, nil return postZeroFill, nil*/
} }