7
7
"sort"
8
8
"strings"
9
9
10
- "github.com/grafana/metrictank/idx"
10
+ "github.com/grafana/metrictank/idx/memory/tagQueryExpression "
11
11
)
12
12
13
13
// the collision avoidance window defines how many times we try to find a higher
@@ -25,9 +25,9 @@ func init() {
25
25
type metaRecordFilter func (map [string ]string ) bool
26
26
27
27
type metaTagRecord struct {
28
- metaTags []kv
29
- queries [] expression
30
- filterByTags metaRecordFilter
28
+ metaTags []kv
29
+ queries []tagQueryExpression. Expression
30
+ matchesTags metaRecordFilter
31
31
}
32
32
33
33
// list of meta records keyed by random unique identifier
@@ -65,14 +65,53 @@ func (m metaTagIndex) insertRecord(keyValue kv, hash uint32) {
65
65
values [keyValue .value ] = append (values [keyValue .value ], hash )
66
66
}
67
67
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
+
68
107
// newMetaTagRecord takes two slices of strings, parses them and returns a metaTagRecord
69
108
// The first slice of strings are the meta tags & values
70
109
// The second slice is the tag query expressions which the meta key & values refer to
71
110
// On parsing error the second returned value is an error, otherwise it is nil
72
111
func newMetaTagRecord (metaTags []string , tagQueryExpressions []string ) (metaTagRecord , error ) {
73
112
record := metaTagRecord {
74
113
metaTags : make ([]kv , 0 , len (metaTags )),
75
- queries : make ([]expression , 0 , len (tagQueryExpressions )),
114
+ queries : make ([]tagQueryExpression. Expression , 0 , len (tagQueryExpressions )),
76
115
}
77
116
if len (tagQueryExpressions ) == 0 {
78
117
return record , fmt .Errorf ("Requiring at least one tag query expression, 0 given" )
@@ -89,11 +128,11 @@ func newMetaTagRecord(metaTags []string, tagQueryExpressions []string) (metaTagR
89
128
90
129
haveTagOperator := false
91
130
for _ , query := range tagQueryExpressions {
92
- parsed , err := parseExpression (query )
131
+ parsed , err := tagQueryExpression . ParseExpression (query )
93
132
if err != nil {
94
133
return record , err
95
134
}
96
- if parsed .isTagQueryOperator () {
135
+ if parsed .IsTagOperator () {
97
136
if haveTagOperator {
98
137
return record , fmt .Errorf ("Only one tag operator is allowed per query" )
99
138
}
@@ -123,7 +162,7 @@ func (m *metaTagRecord) queryStrings(builder *strings.Builder) []string {
123
162
res := make ([]string , len (m .queries ))
124
163
125
164
for i , query := range m .queries {
126
- query .stringIntoBuilder (builder )
165
+ query .StringIntoBuilder (builder )
127
166
res [i ] = builder .String ()
128
167
builder .Reset ()
129
168
}
@@ -138,7 +177,7 @@ func (m *metaTagRecord) hashQueries() uint32 {
138
177
if i > 0 {
139
178
builder .WriteString (";" )
140
179
}
141
- query .stringIntoBuilder (& builder )
180
+ query .StringIntoBuilder (& builder )
142
181
}
143
182
h := queryHash ()
144
183
h .Write ([]byte (builder .String ()))
@@ -149,13 +188,13 @@ func (m *metaTagRecord) hashQueries() uint32 {
149
188
// operator. The order doesn't matter, it only needs to be consistent
150
189
func (m * metaTagRecord ) sortQueries () {
151
190
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 ()
155
194
}
156
- return m .queries [i ].value < m .queries [j ].value
195
+ return m .queries [i ].GetValue () < m .queries [j ].GetValue ()
157
196
}
158
- return m .queries [i ].key < m .queries [j ].key
197
+ return m .queries [i ].GetKey () < m .queries [j ].GetKey ()
159
198
})
160
199
}
161
200
@@ -171,15 +210,15 @@ func (m *metaTagRecord) matchesQueries(other metaTagRecord) bool {
171
210
other .sortQueries ()
172
211
173
212
for id , query := range m .queries {
174
- if query .key != other .queries [id ].key {
213
+ if query .GetKey () != other .queries [id ].GetKey () {
175
214
return false
176
215
}
177
216
178
- if query .operator != other .queries [id ].operator {
217
+ if query .GetOperator () != other .queries [id ].GetOperator () {
179
218
return false
180
219
}
181
220
182
- if query .value != other .queries [id ].value {
221
+ if query .GetValue () != other .queries [id ].GetValue () {
183
222
return false
184
223
}
185
224
}
@@ -197,13 +236,13 @@ func (m *metaTagRecord) hasMetaTags() bool {
197
236
// it then evaluate whether the queries of this meta tag record all match with the given tags
198
237
// this is used for the series enrichment
199
238
func (m * metaTagRecord ) buildMetaRecordFilter () {
200
- queries := make ([]expression , len (m .queries ))
239
+ queries := make ([]tagQueryExpression. Expression , len (m .queries ))
201
240
copy (queries , m .queries )
202
241
203
242
// we want to sort the queries so the regexes come last, because they are more expensive
204
243
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 ()
207
246
208
247
// when both have a regex or both have no regex, they are considered equal
209
248
if iHasRe == jHasRe {
@@ -222,14 +261,14 @@ func (m *metaTagRecord) buildMetaRecordFilter() {
222
261
// generate all the filter functions for each of the queries
223
262
filters := make ([]func (string , string ) bool , 0 , len (queries ))
224
263
for _ , query := range queries {
225
- matcher := query .getMatcher ()
226
- if query .matchesTag () {
264
+ matcher := query .GetMatcher ()
265
+ if query .MatchesTag () {
227
266
filters = append (filters , func (tag , value string ) bool {
228
267
return matcher (tag )
229
268
})
230
269
continue
231
270
}
232
- queryKey := query .getKey ()
271
+ queryKey := query .GetKey ()
233
272
filters = append (filters , func (tag , value string ) bool {
234
273
if tag != queryKey {
235
274
return false
@@ -239,7 +278,8 @@ func (m *metaTagRecord) buildMetaRecordFilter() {
239
278
}
240
279
241
280
// 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 {
243
283
FILTERS:
244
284
for _ , filter := range filters {
245
285
for tag , value := range tags {
@@ -256,32 +296,6 @@ func (m *metaTagRecord) buildMetaRecordFilter() {
256
296
}
257
297
}
258
298
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
-
285
299
// upsert inserts or updates a meta tag record according to the given specifications
286
300
// it uses the set of tag query expressions as the identity of the record, if a record with the
287
301
// 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 {
363
377
func (m metaTagRecords ) enrichTags (tags map [string ]string ) map [string ]string {
364
378
res := make (map [string ]string )
365
379
for _ , mtr := range m {
366
- if mtr .filterByTags (tags ) {
380
+ if mtr .matchesTags (tags ) {
367
381
for _ , kv := range mtr .metaTags {
368
382
res [kv .key ] = kv .value
369
383
}
0 commit comments