@@ -43,25 +43,61 @@ var separatorByteSlice = []byte{model.SeparatorByte} // For convenient use with
43
43
// Use CachedTGatherer with classic Registry using NewMultiTRegistry and ToTransactionalGatherer helpers.
44
44
// NOTE(bwplotka): Experimental, API and behaviour can change.
45
45
type CachedTGatherer struct {
46
- metrics map [uint64 ]* dto.Metric
47
- metricFamilyByName map [string ]* dto.MetricFamily
46
+ metricFamilyByName map [string ]* family
48
47
mMu sync.RWMutex
49
48
}
50
49
51
50
func NewCachedTGatherer () * CachedTGatherer {
52
51
return & CachedTGatherer {
53
- metrics : make (map [uint64 ]* dto.Metric ),
54
- metricFamilyByName : map [string ]* dto.MetricFamily {},
52
+ metricFamilyByName : map [string ]* family {},
55
53
}
56
54
}
57
55
56
+ type family struct {
57
+ * dto.MetricFamily
58
+
59
+ metricsByHash map [uint64 ]* dto.Metric
60
+ }
61
+
62
+ // normalizeMetricFamilies returns a MetricFamily slice with empty
63
+ // MetricFamilies pruned and the remaining MetricFamilies sorted by name within
64
+ // the slice, with the contained Metrics sorted within each MetricFamily.
65
+ func normalizeMetricFamilies (metricFamiliesByName map [string ]* family ) []* dto.MetricFamily {
66
+ for _ , mf := range metricFamiliesByName {
67
+ if cap (mf .Metric ) < len (mf .metricsByHash ) {
68
+ mf .Metric = make ([]* dto.Metric , 0 , len (mf .metricsByHash ))
69
+ }
70
+ mf .Metric = mf .Metric [:0 ]
71
+ for _ , m := range mf .metricsByHash {
72
+ mf .Metric = append (mf .Metric , m )
73
+ }
74
+ sort .Sort (internal .MetricSorter (mf .Metric ))
75
+ }
76
+
77
+ for _ , mf := range metricFamiliesByName {
78
+ sort .Sort (internal .MetricSorter (mf .Metric ))
79
+ }
80
+ names := make ([]string , 0 , len (metricFamiliesByName ))
81
+ for name , mf := range metricFamiliesByName {
82
+ if len (mf .Metric ) > 0 {
83
+ names = append (names , name )
84
+ }
85
+ }
86
+ sort .Strings (names )
87
+ result := make ([]* dto.MetricFamily , 0 , len (names ))
88
+ for _ , name := range names {
89
+ result = append (result , metricFamiliesByName [name ].MetricFamily )
90
+ }
91
+ return result
92
+ }
93
+
58
94
// Gather implements TransactionalGatherer interface.
59
95
func (c * CachedTGatherer ) Gather () (_ []* dto.MetricFamily , done func (), err error ) {
60
96
c .mMu .RLock ()
61
97
62
- // BenchmarkCachedTGatherer_Update shows, even for 1 million metrics with 1000 families
98
+ // BenchmarkCachedTGatherer_Update shows, even for 1 million metrics among 1000 families
63
99
// this is efficient enough (~300µs and ~50 kB per op), no need to cache it for now.
64
- return internal . NormalizeMetricFamilies (c .metricFamilyByName ), c .mMu .RUnlock , nil
100
+ return normalizeMetricFamilies (c .metricFamilyByName ), c .mMu .RUnlock , nil
65
101
}
66
102
67
103
type Key struct {
@@ -123,11 +159,9 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
123
159
c .mMu .Lock ()
124
160
defer c .mMu .Unlock ()
125
161
126
- currMetrics := c .metrics
127
- currMetricFamilies := c .metricFamilyByName
162
+ currMetricFamilyByName := c .metricFamilyByName
128
163
if reset {
129
- currMetrics = make (map [uint64 ]* dto.Metric , len (c .metrics ))
130
- currMetricFamilies = make (map [string ]* dto.MetricFamily , len (c .metricFamilyByName ))
164
+ currMetricFamilyByName = make (map [string ]* family , len (c .metricFamilyByName ))
131
165
}
132
166
133
167
errs := prometheus.MultiError {}
@@ -139,22 +173,35 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
139
173
}
140
174
141
175
// Update metric family.
142
- mf , ok := c .metricFamilyByName [inserts [i ].FQName ]
176
+ mf , ok := currMetricFamilyByName [inserts [i ].FQName ]
177
+ oldMf , oldOk := c .metricFamilyByName [inserts [i ].FQName ]
143
178
if ! ok {
144
- mf = & dto.MetricFamily {}
145
- mf .Name = & inserts [i ].FQName
146
- } else if reset {
147
- // Reset metric slice, since we want to start from scratch.
148
- mf .Metric = mf .Metric [:0 ]
179
+ if ! oldOk {
180
+ mf = & family {
181
+ MetricFamily : & dto.MetricFamily {},
182
+ metricsByHash : map [uint64 ]* dto.Metric {},
183
+ }
184
+ mf .Name = & inserts [i ].FQName
185
+ } else if reset {
186
+ mf = & family {
187
+ MetricFamily : oldMf .MetricFamily ,
188
+ metricsByHash : make (map [uint64 ]* dto.Metric , len (oldMf .metricsByHash )),
189
+ }
190
+ }
149
191
}
192
+
150
193
mf .Type = inserts [i ].ValueType .ToDTO ()
151
194
mf .Help = & inserts [i ].Help
152
195
153
- currMetricFamilies [inserts [i ].FQName ] = mf
196
+ currMetricFamilyByName [inserts [i ].FQName ] = mf
154
197
155
198
// Update metric pointer.
156
199
hSum := inserts [i ].hash ()
157
- m , ok := c .metrics [hSum ]
200
+ m , ok := mf .metricsByHash [hSum ]
201
+ if ! ok && reset && oldOk {
202
+ m , ok = oldMf .metricsByHash [hSum ]
203
+ }
204
+
158
205
if ! ok {
159
206
m = & dto.Metric {Label : make ([]* dto.LabelPair , 0 , len (inserts [i ].LabelNames ))}
160
207
for j := range inserts [i ].LabelNames {
@@ -202,16 +249,7 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
202
249
if inserts [i ].Timestamp != nil {
203
250
m .TimestampMs = proto .Int64 (inserts [i ].Timestamp .Unix ()* 1000 + int64 (inserts [i ].Timestamp .Nanosecond ()/ 1000000 ))
204
251
}
205
- currMetrics [hSum ] = m
206
-
207
- if ! reset && ok {
208
- // If we did update without reset and we found metric in previous
209
- // map, we know metric pointer exists in metric family map, so just continue.
210
- continue
211
- }
212
-
213
- // Will be sorted later anyway, so just append.
214
- mf .Metric = append (mf .Metric , m )
252
+ mf .metricsByHash [hSum ] = m
215
253
}
216
254
217
255
for _ , del := range deletions {
@@ -220,42 +258,18 @@ func (c *CachedTGatherer) Update(reset bool, inserts []Insert, deletions []Key)
220
258
continue
221
259
}
222
260
223
- hSum := del .hash ()
224
- m , ok := currMetrics [hSum ]
225
- if ! ok {
226
- continue
227
- }
228
- delete (currMetrics , hSum )
229
-
230
- mf , ok := currMetricFamilies [del .FQName ]
261
+ mf , ok := currMetricFamilyByName [del .FQName ]
231
262
if ! ok {
232
- // Impossible, but well...
233
- errs .Append (fmt .Errorf ("could not remove metric %s(%s) from metric family, metric family does not exists" , del .FQName , del .LabelValues ))
234
263
continue
235
264
}
236
265
237
- toDel := - 1
238
- for i := range mf .Metric {
239
- if mf .Metric [i ] == m {
240
- toDel = i
241
- break
242
- }
243
- }
244
-
245
- if toDel == - 1 {
246
- errs .Append (fmt .Errorf ("could not remove metric %s(%s) from metric family, metric family does not have such metric" , del .FQName , del .LabelValues ))
247
- continue
248
- }
249
-
250
- if len (mf .Metric ) == 1 {
251
- delete (currMetricFamilies , del .FQName )
266
+ hSum := del .hash ()
267
+ if _ , ok := mf .metricsByHash [hSum ]; ! ok {
252
268
continue
253
269
}
254
-
255
- mf .Metric = append (mf .Metric [:toDel ], mf .Metric [toDel + 1 :]... )
270
+ delete (mf .metricsByHash , hSum )
256
271
}
257
272
258
- c .metrics = currMetrics
259
- c .metricFamilyByName = currMetricFamilies
273
+ c .metricFamilyByName = currMetricFamilyByName
260
274
return errs .MaybeUnwrap ()
261
275
}
0 commit comments