Skip to content

Commit 1a71a2e

Browse files
committed
refactor tag count querying, use it in autocomplete
1 parent 3b4cdf8 commit 1a71a2e

File tree

6 files changed

+269
-167
lines changed

6 files changed

+269
-167
lines changed

autocomplete/autocomplete.go

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,28 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
7171
}
7272
}
7373

74-
func (h *Handler) requestExpr(r *http.Request) (*where.Where, *where.Where, map[string]bool, error) {
75-
f := r.Form["expr"]
76-
expr := make([]string, 0, len(f))
74+
func getTagCountQuerier(config *config.Config, opts clickhouse.Options) *finder.TagCountQuerier {
75+
var tcq *finder.TagCountQuerier = nil
76+
if config.ClickHouse.TagsCountTable != "" {
77+
tcq = finder.NewTagCountQuerier(
78+
config.ClickHouse.URL,
79+
config.ClickHouse.TagsCountTable,
80+
opts,
81+
config.FeatureFlags.UseCarbonBehavior,
82+
config.FeatureFlags.DontMatchMissingTags,
83+
config.ClickHouse.TaggedUseDaily,
84+
)
85+
}
86+
return tcq
87+
}
7788

78-
for i := 0; i < len(f); i++ {
79-
if f[i] != "" {
80-
expr = append(expr, f[i])
89+
func (h *Handler) requestExpr(r *http.Request, tcq *finder.TagCountQuerier, from, until int64) (*where.Where, *where.Where, map[string]bool, error) {
90+
formExpr := r.Form["expr"]
91+
expr := make([]string, 0, len(formExpr))
92+
93+
for i := 0; i < len(formExpr); i++ {
94+
if formExpr[i] != "" {
95+
expr = append(expr, formExpr[i])
8196
}
8297
}
8398

@@ -95,6 +110,15 @@ func (h *Handler) requestExpr(r *http.Request) (*where.Where, *where.Where, map[
95110
return wr, pw, usedTags, err
96111
}
97112

113+
if tcq != nil {
114+
tagValuesCosts, err := tcq.GetCostsFromCountTable(r.Context(), terms, from, until)
115+
if err != nil {
116+
return wr, pw, usedTags, err
117+
}
118+
finder.SetCosts(terms, tagValuesCosts)
119+
}
120+
finder.SortTaggedTermsByCost(terms)
121+
98122
wr, pw, err = finder.TaggedWhere(terms, h.config.FeatureFlags.UseCarbonBehavior, h.config.FeatureFlags.DontMatchMissingTags)
99123
if err != nil {
100124
return wr, pw, usedTags, err
@@ -214,6 +238,7 @@ func (h *Handler) ServeTags(w http.ResponseWriter, r *http.Request) {
214238
queueFail bool
215239
queueDuration time.Duration
216240
findCache bool
241+
opts clickhouse.Options
217242
)
218243

219244
username := r.Header.Get("X-Forwarded-User")
@@ -290,7 +315,21 @@ func (h *Handler) ServeTags(w http.ResponseWriter, r *http.Request) {
290315
}
291316
}
292317

293-
wr, pw, usedTags, err := h.requestExpr(r)
318+
opts = clickhouse.Options{
319+
TLSConfig: h.config.ClickHouse.TLSConfig,
320+
Timeout: h.config.ClickHouse.IndexTimeout,
321+
ConnectTimeout: h.config.ClickHouse.ConnectTimeout,
322+
CheckRequestProgress: h.config.FeatureFlags.LogQueryProgress,
323+
ProgressSendingInterval: h.config.ClickHouse.ProgressSendingInterval,
324+
}
325+
326+
wr, pw, usedTags, err := h.requestExpr(
327+
r,
328+
getTagCountQuerier(h.config, opts),
329+
start.AddDate(0, 0, -h.config.ClickHouse.TaggedAutocompleDays).Unix(),
330+
start.Unix(),
331+
)
332+
294333
if err != nil {
295334
status = http.StatusBadRequest
296335
http.Error(w, err.Error(), status)
@@ -366,13 +405,7 @@ func (h *Handler) ServeTags(w http.ResponseWriter, r *http.Request) {
366405
scope.WithTable(r.Context(), h.config.ClickHouse.TaggedTable),
367406
h.config.ClickHouse.URL,
368407
sql,
369-
clickhouse.Options{
370-
TLSConfig: h.config.ClickHouse.TLSConfig,
371-
Timeout: h.config.ClickHouse.IndexTimeout,
372-
ConnectTimeout: h.config.ClickHouse.ConnectTimeout,
373-
CheckRequestProgress: h.config.FeatureFlags.LogQueryProgress,
374-
ProgressSendingInterval: h.config.ClickHouse.ProgressSendingInterval,
375-
},
408+
opts,
376409
nil,
377410
)
378411

@@ -490,6 +523,7 @@ func (h *Handler) ServeValues(w http.ResponseWriter, r *http.Request) {
490523
queueFail bool
491524
queueDuration time.Duration
492525
findCache bool
526+
opts clickhouse.Options
493527
)
494528

495529
username := r.Header.Get("X-Forwarded-User")
@@ -567,8 +601,23 @@ func (h *Handler) ServeValues(w http.ResponseWriter, r *http.Request) {
567601
}
568602
}
569603

604+
opts = clickhouse.Options{
605+
TLSConfig: h.config.ClickHouse.TLSConfig,
606+
Timeout: h.config.ClickHouse.IndexTimeout,
607+
ConnectTimeout: h.config.ClickHouse.ConnectTimeout,
608+
CheckRequestProgress: h.config.FeatureFlags.LogQueryProgress,
609+
ProgressSendingInterval: h.config.ClickHouse.ProgressSendingInterval,
610+
}
611+
570612
if !findCache {
571-
wr, pw, usedTags, err := h.requestExpr(r)
613+
614+
wr, pw, usedTags, err := h.requestExpr(
615+
r,
616+
getTagCountQuerier(h.config, opts),
617+
start.AddDate(0, 0, -h.config.ClickHouse.TaggedAutocompleDays).Unix(),
618+
start.Unix(),
619+
)
620+
572621
if err == finder.ErrCostlySeriesByTag {
573622
status = http.StatusForbidden
574623
http.Error(w, err.Error(), status)
@@ -640,13 +689,7 @@ func (h *Handler) ServeValues(w http.ResponseWriter, r *http.Request) {
640689
scope.WithTable(r.Context(), h.config.ClickHouse.TaggedTable),
641690
h.config.ClickHouse.URL,
642691
sql,
643-
clickhouse.Options{
644-
TLSConfig: h.config.ClickHouse.TLSConfig,
645-
Timeout: h.config.ClickHouse.IndexTimeout,
646-
ConnectTimeout: h.config.ClickHouse.ConnectTimeout,
647-
CheckRequestProgress: h.config.FeatureFlags.LogQueryProgress,
648-
ProgressSendingInterval: h.config.ClickHouse.ProgressSendingInterval,
649-
},
692+
opts,
650693
nil,
651694
)
652695

deploy/doc/config.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,4 +225,4 @@ Here we additionally create a materialized view to automatically save the quanti
225225
graphite-clickhouse will query this table when it tries to decide which tag should be used when querying graphite_tagged table.
226226
Overall using this parameter will somewhat increase writing load but can improve reading tagged metrics greatly in some cases.
227227

228-
Note that this option only works for terms with '=' operator in them.
228+
Note that this option only works for terms with '=' operator in them. Using it will also override tag costs that were set manually with tagged-costs option.

doc/config.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ Here we additionally create a materialized view to automatically save the quanti
228228
graphite-clickhouse will query this table when it tries to decide which tag should be used when querying graphite_tagged table.
229229
Overall using this parameter will somewhat increase writing load but can improve reading tagged metrics greatly in some cases.
230230

231-
Note that this option only works for terms with '=' operator in them.
231+
Note that this option only works for terms with '=' operator in them. Using it will also override tag costs that were set manually with tagged-costs option.
232232

233233
```toml
234234
[common]

finder/tagged.go

Lines changed: 23 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"net/http"
88
"sort"
9-
"strconv"
109
"strings"
1110

1211
"github.com/lomik/graphite-clickhouse/config"
@@ -79,10 +78,10 @@ func (s TaggedTermList) Less(i, j int) bool {
7978
type TaggedFinder struct {
8079
url string // clickhouse dsn
8180
table string // graphite_tag table
82-
tag1CountTable string // table that helps to choose the most optimal Tag1
81+
tcq *TagCountQuerier // An object for querying tag weights from clickhouse. See doc/config.md for details.
8382
absKeepEncoded bool // Abs returns url encoded value. For queries from prometheus
8483
opts clickhouse.Options // clickhouse query timeout
85-
taggedCosts map[string]*config.Costs // costs for taggs (sor tune index search)
84+
configuredTagCosts map[string]*config.Costs // costs for taggs (sor tune index search)
8685
dailyEnabled bool
8786
useCarbonBehavior bool
8887
dontMatchMissingTags bool
@@ -92,19 +91,30 @@ type TaggedFinder struct {
9291
}
9392

9493
func NewTagged(url string, table, tag1CountTable string, dailyEnabled, useCarbonBehavior, dontMatchMissingTags, absKeepEncoded bool, opts clickhouse.Options, taggedCosts map[string]*config.Costs) *TaggedFinder {
95-
return &TaggedFinder{
94+
fnd := &TaggedFinder{
9695
url: url,
9796
table: table,
98-
tag1CountTable: tag1CountTable,
97+
tcq: nil,
9998
absKeepEncoded: absKeepEncoded,
10099
opts: opts,
101-
taggedCosts: taggedCosts,
100+
configuredTagCosts: taggedCosts,
102101
dailyEnabled: dailyEnabled,
103102
useCarbonBehavior: useCarbonBehavior,
104103
dontMatchMissingTags: dontMatchMissingTags,
105104
metricMightExists: true,
106105
stats: make([]metrics.FinderStat, 0),
107106
}
107+
if tag1CountTable != "" {
108+
fnd.tcq = NewTagCountQuerier(
109+
url,
110+
tag1CountTable,
111+
opts,
112+
useCarbonBehavior,
113+
dontMatchMissingTags,
114+
dailyEnabled,
115+
)
116+
}
117+
return fnd
108118
}
109119

110120
func (term *TaggedTerm) concat() string {
@@ -606,17 +616,18 @@ func (t *TaggedFinder) PrepareTaggedTerms(ctx context.Context, cfg *config.Confi
606616
return nil, err
607617
}
608618

609-
if t.tag1CountTable != "" {
610-
err = t.SetCostsFromCountTable(ctx, terms, from, until)
619+
var tagCounts map[string]*config.Costs = nil
620+
if t.tcq != nil {
621+
tagCounts, err = t.tcq.GetCostsFromCountTable(ctx, terms, from, until)
611622
if err != nil {
612623
return nil, err
613624
}
614625
}
615626

616-
// Set costs from count table only if it has all tag-value pairs contained in the seriesByTag query (t.metricMightExist == true)
617-
// or if tagged costs were set in the config file and t.metricMightExist == false
618-
if t.metricMightExists || len(t.taggedCosts) != 0 {
619-
SetCosts(terms, t.taggedCosts)
627+
if tagCounts != nil {
628+
SetCosts(terms, tagCounts)
629+
} else if len(t.configuredTagCosts) != 0 {
630+
SetCosts(terms, t.configuredTagCosts)
620631
}
621632

622633
SortTaggedTermsByCost(terms)
@@ -659,126 +670,10 @@ func SortTaggedTermsByCost(terms []TaggedTerm) {
659670
})
660671
}
661672

662-
func (t *TaggedFinder) SetCostsFromCountTable(ctx context.Context, terms []TaggedTerm, from int64, until int64) error {
663-
w := where.New()
664-
eqTermCount := 0
665-
666-
for i := 0; i < len(terms); i++ {
667-
if terms[i].Op == TaggedTermEq && !terms[i].HasWildcard && terms[i].Value != "" {
668-
sqlTerm, err := TaggedTermWhere1(&terms[i], t.useCarbonBehavior, t.dontMatchMissingTags)
669-
if err != nil {
670-
return err
671-
}
672-
673-
w.Or(sqlTerm)
674-
675-
eqTermCount++
676-
}
677-
}
678-
679-
if w.SQL() == "" {
680-
return nil
681-
}
682-
683-
if t.dailyEnabled {
684-
w.Andf(
685-
"Date >= '%s' AND Date <= '%s'",
686-
date.FromTimestampToDaysFormat(from),
687-
date.UntilTimestampToDaysFormat(until),
688-
)
689-
} else {
690-
w.Andf(
691-
"Date >= '%s'",
692-
date.FromTimestampToDaysFormat(from),
693-
)
694-
}
695-
696-
sql := fmt.Sprintf("SELECT Tag1, sum(Count) as cnt FROM %s %s GROUP BY Tag1 FORMAT TabSeparatedRaw", t.tag1CountTable, w.SQL())
697-
698-
var err error
699-
700-
t.stats = append(t.stats, metrics.FinderStat{})
701-
stat := &t.stats[len(t.stats)-1]
702-
stat.Table = t.tag1CountTable
703-
704-
t.body, stat.ChReadRows, stat.ChReadBytes, err = clickhouse.Query(scope.WithTable(ctx, t.tag1CountTable), t.url, sql, t.opts, nil)
705-
if err != nil {
706-
return err
707-
}
708-
709-
rows := t.List()
710-
711-
// create cost var to validate CH response without writing to t.taggedCosts
712-
var costs map[string]*config.Costs
713-
714-
costs, err = chResultToCosts(rows)
715-
if err != nil {
716-
return err
717-
}
718-
719-
// The metric does not exist if the response has less rows
720-
// than there were tags with '=' op in the initial request
721-
// This is due to each tag-value pair of a metric being written
722-
// exactly one time as Tag1
723-
if len(rows) < eqTermCount {
724-
t.body = []byte{}
725-
t.metricMightExists = false
726-
727-
return nil
728-
}
729-
730-
t.taggedCosts = costs
731-
732-
return nil
733-
}
734-
735673
func SetCosts(terms []TaggedTerm, costs map[string]*config.Costs) {
736674
for i := 0; i < len(terms); i++ {
737675
if cost, ok := costs[terms[i].Key]; ok {
738676
setCost(&terms[i], cost)
739677
}
740678
}
741679
}
742-
743-
func chResultToCosts(body [][]byte) (map[string]*config.Costs, error) {
744-
costs := make(map[string]*config.Costs, 0)
745-
746-
for i := 0; i < len(body); i++ {
747-
s := stringutils.UnsafeString(body[i])
748-
749-
tag, val, count, err := parseTag1CountRow(s)
750-
if err != nil {
751-
return nil, fmt.Errorf("failed to parse result from clickhouse while querying for tag costs: %s", err.Error())
752-
}
753-
754-
if costs[tag] == nil {
755-
costs[tag] = &config.Costs{Cost: nil, ValuesCost: make(map[string]int, 0)}
756-
}
757-
758-
costs[tag].ValuesCost[val] = count
759-
}
760-
761-
return costs, nil
762-
}
763-
764-
func parseTag1CountRow(s string) (string, string, int, error) {
765-
var (
766-
tag1, count, tag, val string
767-
cnt, n int
768-
err error
769-
)
770-
771-
if tag1, count, n = stringutils.Split2(s, "\t"); n != 2 {
772-
return "", "", 0, fmt.Errorf("no tag count")
773-
}
774-
775-
if tag, val, n = stringutils.Split2(tag1, "="); n != 2 {
776-
return "", "", 0, fmt.Errorf("no '=' in Tag1")
777-
}
778-
779-
if cnt, err = strconv.Atoi(count); err != nil {
780-
return "", "", 0, fmt.Errorf("can't convert count to int")
781-
}
782-
783-
return tag, val, cnt, nil
784-
}

0 commit comments

Comments
 (0)