Skip to content
This repository was archived by the owner on Aug 23, 2023. It is now read-only.

Commit d572c16

Browse files
committed
move tag query expressions into their own package
this makes it easier to cleanly separate the expressions evaluation logic from all the other index operations it also moves the logic to get the meta records which match an expression from the tagQueryExpression package to the tagQuery, which is cleaner because it avoids writing expression evaluation logic specifically for one index type.
1 parent 7f9efdf commit d572c16

19 files changed

+719
-835
lines changed

idx/memory/meta_tags.go

+65-51
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"sort"
88
"strings"
99

10-
"github.com/grafana/metrictank/idx"
10+
"github.com/grafana/metrictank/idx/memory/tagQueryExpression"
1111
)
1212

1313
// the collision avoidance window defines how many times we try to find a higher
@@ -25,9 +25,9 @@ func init() {
2525
type metaRecordFilter func(map[string]string) bool
2626

2727
type metaTagRecord struct {
28-
metaTags []kv
29-
queries []expression
30-
filterByTags metaRecordFilter
28+
metaTags []kv
29+
queries []tagQueryExpression.Expression
30+
matchesTags metaRecordFilter
3131
}
3232

3333
// list of meta records keyed by random unique identifier
@@ -65,14 +65,53 @@ func (m metaTagIndex) insertRecord(keyValue kv, hash uint32) {
6565
values[keyValue.value] = append(values[keyValue.value], hash)
6666
}
6767

68+
// getMetaRecordIdsByExpression takes an expression and returns all meta record
69+
// ids of the records which match it.
70+
// It is important to note that negative expressions get evaluated as their
71+
// positive equivalent, f.e. != gets evaluated as =, to reduce the size of the
72+
// result set. The caller will then need to handle the negation according to the
73+
// expression type.
74+
func (m metaTagIndex) getMetaRecordIdsByExpression(expression tagQueryExpression.Expression) []uint32 {
75+
resultMap := make(map[uint32]struct{})
76+
if expression.IsTagOperator() {
77+
matcher := expression.GetMatcher()
78+
for tag, recordIdsByValue := range m {
79+
if matcher(tag) {
80+
for _, recordIds := range recordIdsByValue {
81+
for _, recordId := range recordIds {
82+
resultMap[recordId] = struct{}{}
83+
}
84+
}
85+
}
86+
}
87+
} else {
88+
if values, ok := m[expression.GetKey()]; ok {
89+
matcher := expression.GetMatcher()
90+
for value, recordIdsByValue := range values {
91+
if matcher(value) {
92+
for _, recordId := range recordIdsByValue {
93+
resultMap[recordId] = struct{}{}
94+
}
95+
}
96+
}
97+
}
98+
}
99+
100+
res := make([]uint32, 0, len(resultMap))
101+
for id := range resultMap {
102+
res = append(res, id)
103+
}
104+
return res
105+
}
106+
68107
// newMetaTagRecord takes two slices of strings, parses them and returns a metaTagRecord
69108
// The first slice of strings are the meta tags & values
70109
// The second slice is the tag query expressions which the meta key & values refer to
71110
// On parsing error the second returned value is an error, otherwise it is nil
72111
func newMetaTagRecord(metaTags []string, tagQueryExpressions []string) (metaTagRecord, error) {
73112
record := metaTagRecord{
74113
metaTags: make([]kv, 0, len(metaTags)),
75-
queries: make([]expression, 0, len(tagQueryExpressions)),
114+
queries: make([]tagQueryExpression.Expression, 0, len(tagQueryExpressions)),
76115
}
77116
if len(tagQueryExpressions) == 0 {
78117
return record, fmt.Errorf("Requiring at least one tag query expression, 0 given")
@@ -89,11 +128,11 @@ func newMetaTagRecord(metaTags []string, tagQueryExpressions []string) (metaTagR
89128

90129
haveTagOperator := false
91130
for _, query := range tagQueryExpressions {
92-
parsed, err := parseExpression(query)
131+
parsed, err := tagQueryExpression.ParseExpression(query)
93132
if err != nil {
94133
return record, err
95134
}
96-
if parsed.isTagQueryOperator() {
135+
if parsed.IsTagOperator() {
97136
if haveTagOperator {
98137
return record, fmt.Errorf("Only one tag operator is allowed per query")
99138
}
@@ -123,7 +162,7 @@ func (m *metaTagRecord) queryStrings(builder *strings.Builder) []string {
123162
res := make([]string, len(m.queries))
124163

125164
for i, query := range m.queries {
126-
query.stringIntoBuilder(builder)
165+
query.StringIntoBuilder(builder)
127166
res[i] = builder.String()
128167
builder.Reset()
129168
}
@@ -138,7 +177,7 @@ func (m *metaTagRecord) hashQueries() uint32 {
138177
if i > 0 {
139178
builder.WriteString(";")
140179
}
141-
query.stringIntoBuilder(&builder)
180+
query.StringIntoBuilder(&builder)
142181
}
143182
h := queryHash()
144183
h.Write([]byte(builder.String()))
@@ -149,13 +188,13 @@ func (m *metaTagRecord) hashQueries() uint32 {
149188
// operator. The order doesn't matter, it only needs to be consistent
150189
func (m *metaTagRecord) sortQueries() {
151190
sort.Slice(m.queries, func(i, j int) bool {
152-
if m.queries[i].key == m.queries[j].key {
153-
if m.queries[i].value == m.queries[j].value {
154-
return m.queries[i].operator < m.queries[j].operator
191+
if m.queries[i].GetKey() == m.queries[j].GetKey() {
192+
if m.queries[i].GetValue() == m.queries[j].GetValue() {
193+
return m.queries[i].GetOperator() < m.queries[j].GetOperator()
155194
}
156-
return m.queries[i].value < m.queries[j].value
195+
return m.queries[i].GetValue() < m.queries[j].GetValue()
157196
}
158-
return m.queries[i].key < m.queries[j].key
197+
return m.queries[i].GetKey() < m.queries[j].GetKey()
159198
})
160199
}
161200

@@ -171,15 +210,15 @@ func (m *metaTagRecord) matchesQueries(other metaTagRecord) bool {
171210
other.sortQueries()
172211

173212
for id, query := range m.queries {
174-
if query.key != other.queries[id].key {
213+
if query.GetKey() != other.queries[id].GetKey() {
175214
return false
176215
}
177216

178-
if query.operator != other.queries[id].operator {
217+
if query.GetOperator() != other.queries[id].GetOperator() {
179218
return false
180219
}
181220

182-
if query.value != other.queries[id].value {
221+
if query.GetValue() != other.queries[id].GetValue() {
183222
return false
184223
}
185224
}
@@ -197,13 +236,13 @@ func (m *metaTagRecord) hasMetaTags() bool {
197236
// it then evaluate whether the queries of this meta tag record all match with the given tags
198237
// this is used for the series enrichment
199238
func (m *metaTagRecord) buildMetaRecordFilter() {
200-
queries := make([]expression, len(m.queries))
239+
queries := make([]tagQueryExpression.Expression, len(m.queries))
201240
copy(queries, m.queries)
202241

203242
// we want to sort the queries so the regexes come last, because they are more expensive
204243
sort.Slice(queries, func(i, j int) bool {
205-
iHasRe := queries[i].hasRe()
206-
jHasRe := queries[j].hasRe()
244+
iHasRe := queries[i].HasRe()
245+
jHasRe := queries[j].HasRe()
207246

208247
// when both have a regex or both have no regex, they are considered equal
209248
if iHasRe == jHasRe {
@@ -222,14 +261,14 @@ func (m *metaTagRecord) buildMetaRecordFilter() {
222261
// generate all the filter functions for each of the queries
223262
filters := make([]func(string, string) bool, 0, len(queries))
224263
for _, query := range queries {
225-
matcher := query.getMatcher()
226-
if query.matchesTag() {
264+
matcher := query.GetMatcher()
265+
if query.MatchesTag() {
227266
filters = append(filters, func(tag, value string) bool {
228267
return matcher(tag)
229268
})
230269
continue
231270
}
232-
queryKey := query.getKey()
271+
queryKey := query.GetKey()
233272
filters = append(filters, func(tag, value string) bool {
234273
if tag != queryKey {
235274
return false
@@ -239,7 +278,8 @@ func (m *metaTagRecord) buildMetaRecordFilter() {
239278
}
240279

241280
// generate one function which applies all filters to the given set of tags & values
242-
m.filterByTags = func(tags map[string]string) bool {
281+
// it returns true if the given tags satisfy the meta records query conditions
282+
m.matchesTags = func(tags map[string]string) bool {
243283
FILTERS:
244284
for _, filter := range filters {
245285
for tag, value := range tags {
@@ -256,32 +296,6 @@ func (m *metaTagRecord) buildMetaRecordFilter() {
256296
}
257297
}
258298

259-
// a metaRecordEvaluator is a function that takes a metric definition, looks
260-
// at its metric tags, and then decides whether the given metric should be
261-
// tagged with this meta tag or not. It returns a bool
262-
type metaRecordEvaluator func(*idx.Archive) bool
263-
264-
// getEvaluator returns a metaRecordEvaluator for this meta record
265-
func (m *metaTagRecord) getEvaluator() metaRecordEvaluator {
266-
filters := make([]tagFilter, len(m.queries))
267-
defaultDecisions := make([]filterDecision, len(m.queries))
268-
for i, query := range m.queries {
269-
filters[i] = query.getFilter()
270-
defaultDecisions[i] = query.getDefaultDecision()
271-
}
272-
273-
return func(def *idx.Archive) bool {
274-
for i, filter := range filters {
275-
decision := filter(def)
276-
if decision == fail || (decision == none && defaultDecisions[i] == fail) {
277-
return false
278-
}
279-
}
280-
281-
return true
282-
}
283-
}
284-
285299
// upsert inserts or updates a meta tag record according to the given specifications
286300
// it uses the set of tag query expressions as the identity of the record, if a record with the
287301
// same identity is already present then its meta tags get updated to the specified ones.
@@ -363,7 +377,7 @@ func (m metaTagRecords) getRecords(ids []uint32) []metaTagRecord {
363377
func (m metaTagRecords) enrichTags(tags map[string]string) map[string]string {
364378
res := make(map[string]string)
365379
for _, mtr := range m {
366-
if mtr.filterByTags(tags) {
380+
if mtr.matchesTags(tags) {
367381
for _, kv := range mtr.metaTags {
368382
res[kv.key] = kv.value
369383
}

0 commit comments

Comments
 (0)