YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/util/metric_entity.cc
Line
Count
Source (jump to first uncovered line)
1
//
2
// Copyright (c) YugaByte, Inc.
3
//
4
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5
// in compliance with the License.  You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software distributed under the License
10
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11
// or implied.  See the License for the specific language governing permissions and limitations
12
// under the License.
13
//
14
//
15
#include "yb/util/metric_entity.h"
16
17
#include <regex>
18
19
#include "yb/gutil/map-util.h"
20
#include "yb/util/flag_tags.h"
21
#include "yb/util/jsonwriter.h"
22
#include "yb/util/metrics.h"
23
#include "yb/util/status_log.h"
24
25
DEFINE_int32(metrics_retirement_age_ms, 120 * 1000,
26
             "The minimum number of milliseconds a metric will be kept for after it is "
27
             "no longer active. (Advanced option)");
28
TAG_FLAG(metrics_retirement_age_ms, runtime);
29
TAG_FLAG(metrics_retirement_age_ms, advanced);
30
31
// TODO: changed to empty string and add logic to get this from cluster_uuid in case empty.
32
DEFINE_string(metric_node_name, "DEFAULT_NODE_NAME",
33
              "Value to use as node name for metrics reporting");
34
35
namespace yb {
36
37
namespace {
38
39
const std::regex prometheus_name_regex("[a-zA-Z_:][a-zA-Z0-9_:]*");
40
41
// Registry of all of the metric and entity prototypes that have been
42
// defined.
43
//
44
// Prototypes are typically defined as static variables in different compilation
45
// units, and their constructors register themselves here. The registry is then
46
// used in order to dump metrics metadata to generate a Cloudera Manager MDL
47
// file.
48
//
49
// This class is thread-safe.
50
class MetricPrototypeRegistry {
51
 public:
52
  // Get the singleton instance.
53
  static MetricPrototypeRegistry* get();
54
55
  // Dump a JSON document including all of the registered entity and metric
56
  // prototypes.
57
  void WriteAsJson(JsonWriter* writer) const;
58
59
  // Register a metric prototype in the registry.
60
  void AddMetric(const MetricPrototype* prototype);
61
62
  // Register a metric entity prototype in the registry.
63
  void AddEntity(const MetricEntityPrototype* prototype);
64
65
 private:
66
32.7k
  MetricPrototypeRegistry() {}
67
30.9k
  ~MetricPrototypeRegistry() {}
68
69
  mutable simple_spinlock lock_;
70
  std::vector<const MetricPrototype*> metrics_;
71
  std::vector<const MetricEntityPrototype*> entities_;
72
73
  DISALLOW_COPY_AND_ASSIGN(MetricPrototypeRegistry);
74
};
75
76
//
77
// MetricPrototypeRegistry
78
//
79
37.0M
MetricPrototypeRegistry* MetricPrototypeRegistry::get() {
80
37.0M
  static MetricPrototypeRegistry instance;
81
37.0M
  return &instance;
82
37.0M
}
83
84
36.9M
void MetricPrototypeRegistry::AddMetric(const MetricPrototype* prototype) {
85
36.9M
  std::lock_guard<simple_spinlock> l(lock_);
86
36.9M
  metrics_.push_back(prototype);
87
36.9M
}
88
89
100k
void MetricPrototypeRegistry::AddEntity(const MetricEntityPrototype* prototype) {
90
100k
  std::lock_guard<simple_spinlock> l(lock_);
91
100k
  entities_.push_back(prototype);
92
100k
}
93
94
1
void MetricPrototypeRegistry::WriteAsJson(JsonWriter* writer) const {
95
1
  std::lock_guard<simple_spinlock> l(lock_);
96
1
  MetricJsonOptions opts;
97
1
  opts.include_schema_info = true;
98
1
  writer->StartObject();
99
100
  // Dump metric prototypes.
101
1
  writer->String("metrics");
102
1
  writer->StartArray();
103
35
  for (const MetricPrototype* p : metrics_) {
104
35
    writer->StartObject();
105
35
    p->WriteFields(writer, opts);
106
35
    writer->String("entity_type");
107
35
    writer->String(p->entity_type());
108
35
    writer->EndObject();
109
35
  }
110
1
  writer->EndArray();
111
112
  // Dump entity prototypes.
113
1
  writer->String("entities");
114
1
  writer->StartArray();
115
2
  for (const MetricEntityPrototype* p : entities_) {
116
2
    writer->StartObject();
117
2
    writer->String("name");
118
2
    writer->String(p->name());
119
2
    writer->EndObject();
120
2
  }
121
1
  writer->EndArray();
122
123
1
  writer->EndObject();
124
1
}
125
126
} // namespace
127
128
//
129
// MetricEntityPrototype
130
//
131
132
MetricEntityPrototype::MetricEntityPrototype(const char* name)
133
100k
  : name_(name) {
134
100k
  MetricPrototypeRegistry::get()->AddEntity(this);
135
100k
}
136
137
34.7k
MetricEntityPrototype::~MetricEntityPrototype() {
138
34.7k
}
139
140
scoped_refptr<MetricEntity> MetricEntityPrototype::Instantiate(
141
    MetricRegistry* registry,
142
    const std::string& id,
143
350k
    const MetricEntity::AttributeMap& initial_attrs) const {
144
350k
  return registry->FindOrCreateEntity(this, id, initial_attrs);
145
350k
}
146
147
scoped_refptr<MetricEntity> MetricEntityPrototype::Instantiate(
148
49.4k
    MetricRegistry* registry, const std::string& id) const {
149
49.4k
  return Instantiate(registry, id, std::unordered_map<std::string, std::string>());
150
49.4k
}
151
152
//
153
// MetricEntity
154
//
155
156
MetricEntity::MetricEntity(const MetricEntityPrototype* prototype,
157
                           std::string id, AttributeMap attributes)
158
    : prototype_(prototype),
159
      id_(std::move(id)),
160
223k
      attributes_(std::move(attributes)) {
161
223k
}
162
163
14.0k
MetricEntity::~MetricEntity() {
164
14.0k
}
165
166
58.1M
const std::regex& PrometheusNameRegex() {
167
58.1M
  return prometheus_name_regex;
168
58.1M
}
169
170
58.1M
void MetricEntity::CheckInstantiation(const MetricPrototype* proto) const {
171
58.1M
  CHECK_STREQ(prototype_->name(), proto->entity_type())
172
0
    << "Metric " << proto->name() << " may not be instantiated entity of type "
173
0
    << prototype_->name() << " (expected: " << proto->entity_type() << ")";
174
18.4E
  DCHECK(regex_match(proto->name(), PrometheusNameRegex()))
175
18.4E
      << "Metric name is not compatible with Prometheus: " << proto->name();
176
58.1M
}
177
178
0
scoped_refptr<Metric> MetricEntity::FindOrNull(const MetricPrototype& prototype) const {
179
0
  std::lock_guard<simple_spinlock> l(lock_);
180
0
  return FindPtrOrNull(metric_map_, &prototype);
181
0
}
182
183
namespace {
184
185
const string kWildCardString = "*";
186
187
bool MatchMetricInList(const string& metric_name,
188
4.63M
                       const vector<string>& match_params) {
189
4.63M
  for (const string& param : match_params) {
190
    // Handle wildcard.
191
4.63M
    if (param == kWildCardString) 
return true645k
;
192
    // The parameter is a substring match of the metric name.
193
3.98M
    if (metric_name.find(param) != std::string::npos) {
194
22.9k
      return true;
195
22.9k
    }
196
3.98M
  }
197
3.96M
  return false;
198
4.63M
}
199
200
} // anonymous namespace
201
202
203
Status MetricEntity::WriteAsJson(JsonWriter* writer,
204
                                 const vector<string>& requested_metrics,
205
678k
                                 const MetricJsonOptions& opts) const {
206
678k
  bool select_all = MatchMetricInList(id(), requested_metrics);
207
208
  // We want the keys to be in alphabetical order when printing, so we use an ordered map here.
209
678k
  typedef std::map<const char*, scoped_refptr<Metric> > OrderedMetricMap;
210
678k
  OrderedMetricMap metrics;
211
678k
  AttributeMap attrs;
212
678k
  std::vector<ExternalJsonMetricsCb> external_metrics_cbs;
213
678k
  {
214
    // Snapshot the metrics, attributes & external metrics callbacks in this metrics entity. (Note:
215
    // this is not guaranteed to be a consistent snapshot).
216
678k
    std::lock_guard<simple_spinlock> l(lock_);
217
678k
    attrs = attributes_;
218
678k
    external_metrics_cbs = external_json_metrics_cbs_;
219
84.7M
    for (const MetricMap::value_type& val : metric_map_) {
220
84.7M
      const MetricPrototype* prototype = val.first;
221
84.7M
      const scoped_refptr<Metric>& metric = val.second;
222
223
84.7M
      if (select_all || 
MatchMetricInList(prototype->name(), requested_metrics)3.95M
) {
224
80.7M
        InsertOrDie(&metrics, prototype->name(), metric);
225
80.7M
      }
226
84.7M
    }
227
678k
  }
228
229
  // If we had a filter, and we didn't either match this entity or any metrics inside
230
  // it, don't print the entity at all.
231
678k
  if (!requested_metrics.empty() && !select_all && 
metrics.empty()33.1k
) {
232
10.3k
    return Status::OK();
233
10.3k
  }
234
235
668k
  writer->StartObject();
236
237
668k
  writer->String("type");
238
668k
  writer->String(prototype_->name());
239
240
668k
  writer->String("id");
241
668k
  writer->String(id_);
242
243
668k
  writer->String("attributes");
244
668k
  writer->StartObject();
245
1.95M
  for (const AttributeMap::value_type& val : attrs) {
246
1.95M
    writer->String(val.first);
247
1.95M
    writer->String(val.second);
248
1.95M
  }
249
668k
  writer->EndObject();
250
251
668k
  writer->String("metrics");
252
668k
  writer->StartArray();
253
80.7M
  for (OrderedMetricMap::value_type& val : metrics) {
254
80.7M
    WARN_NOT_OK(val.second->WriteAsJson(writer, opts),
255
80.7M
                Format("Failed to write $0 as JSON", val.first));
256
257
80.7M
  }
258
  // Run the external metrics collection callback if there is one set.
259
668k
  for (const ExternalJsonMetricsCb& cb : external_metrics_cbs) {
260
0
    cb(writer, opts);
261
0
  }
262
668k
  writer->EndArray();
263
264
668k
  writer->EndObject();
265
266
668k
  return Status::OK();
267
678k
}
268
269
CHECKED_STATUS MetricEntity::WriteForPrometheus(PrometheusWriter* writer,
270
                                                const vector<string>& requested_metrics,
271
274
                                                const MetricPrometheusOptions& opts) const {
272
274
  bool select_all = MatchMetricInList(id(), requested_metrics);
273
274
  // We want the keys to be in alphabetical order when printing, so we use an ordered map here.
275
274
  typedef std::map<const char*, scoped_refptr<Metric> > OrderedMetricMap;
276
274
  OrderedMetricMap metrics;
277
274
  AttributeMap attrs;
278
274
  std::vector<ExternalPrometheusMetricsCb> external_metrics_cbs;
279
274
  {
280
    // Snapshot the metrics, attributes & external metrics callbacks in this metrics entity. (Note:
281
    // this is not guaranteed to be a consistent snapshot).
282
274
    std::lock_guard<simple_spinlock> l(lock_);
283
274
    attrs = attributes_;
284
274
    external_metrics_cbs = external_prometheus_metrics_cbs_;
285
52.3k
    for (const MetricMap::value_type& val : metric_map_) {
286
52.3k
      const MetricPrototype* prototype = val.first;
287
52.3k
      const scoped_refptr<Metric>& metric = val.second;
288
289
52.3k
      if (select_all || 
MatchMetricInList(prototype->name(), requested_metrics)0
) {
290
52.3k
        InsertOrDie(&metrics, prototype->name(), metric);
291
52.3k
      }
292
52.3k
    }
293
274
  }
294
295
  // If we had a filter, and we didn't either match this entity or any metrics inside
296
  // it, don't print the entity at all.
297
  // If metrics is empty, we'd still call the callbacks if the entity matches,
298
  // i.e. requested_metrics and select_all is true.
299
274
  if (!requested_metrics.empty() && !select_all && 
metrics.empty()0
) {
300
0
    return Status::OK();
301
0
  }
302
303
274
  AttributeMap prometheus_attr;
304
  // Per tablet metrics come with tablet_id, as well as table_id and table_name attributes.
305
  // We ignore the tablet part to squash at the table level.
306
274
  if (strcmp(prototype_->name(), "tablet") == 0 || 
strcmp(prototype_->name(), "table") == 093
) {
307
218
    prometheus_attr["table_id"] = attrs["table_id"];
308
218
    prometheus_attr["table_name"] = attrs["table_name"];
309
218
    prometheus_attr["namespace_name"] = attrs["namespace_name"];
310
218
  } else if (
311
56
      strcmp(prototype_->name(), "server") == 0 || 
strcmp(prototype_->name(), "cluster") == 025
) {
312
56
    prometheus_attr = attrs;
313
    // This is tablet_id in the case of tablet, but otherwise names the server type, eg: yb.master
314
56
    prometheus_attr["metric_id"] = id_;
315
56
  } else 
if (0
strcmp(prototype_->name(), "cdc") == 00
) {
316
0
    prometheus_attr["table_id"] = attrs["table_id"];
317
0
    prometheus_attr["table_name"] = attrs["table_name"];
318
0
    prometheus_attr["namespace_name"] = attrs["namespace_name"];
319
0
    prometheus_attr["stream_id"] = attrs["stream_id"];
320
0
  } else {
321
0
    return Status::OK();
322
0
  }
323
  // This is currently tablet / server / cluster / cdc.
324
274
  prometheus_attr["metric_type"] = prototype_->name();
325
274
  prometheus_attr["exported_instance"] = FLAGS_metric_node_name;
326
327
52.3k
  for (OrderedMetricMap::value_type& val : metrics) {
328
52.3k
    WARN_NOT_OK(val.second->WriteForPrometheus(writer, prometheus_attr, opts),
329
52.3k
                Format("Failed to write $0 as Prometheus", val.first));
330
331
52.3k
  }
332
  // Run the external metrics collection callback if there is one set.
333
274
  for (const ExternalPrometheusMetricsCb& cb : external_metrics_cbs) {
334
0
    cb(writer, opts);
335
0
  }
336
337
274
  return Status::OK();
338
274
}
339
340
1.60M
void MetricEntity::Remove(const MetricPrototype* proto) {
341
1.60M
  std::lock_guard<simple_spinlock> l(lock_);
342
1.60M
  metric_map_.erase(proto);
343
1.60M
}
344
345
1.05M
void MetricEntity::RetireOldMetrics() {
346
1.05M
  MonoTime now = MonoTime::Now();
347
348
1.05M
  std::lock_guard<simple_spinlock> l(lock_);
349
171M
  for (auto it = metric_map_.begin(); it != metric_map_.end();) {
350
170M
    const scoped_refptr<Metric>& metric = it->second;
351
352
170M
    if (PREDICT_TRUE(!metric->HasOneRef())) {
353
      // The metric is still in use. Note that, in the case of "NeverRetire()", the metric
354
      // will have a ref-count of 2 because it is reffed by the 'never_retire_metrics_'
355
      // collection.
356
357
      // Ensure that it is not marked for later retirement (this could happen in the case
358
      // that a metric is un-reffed and then re-reffed later by looking it up from the
359
      // registry).
360
162M
      metric->retire_time_ = MonoTime();
361
162M
      ++it;
362
162M
      continue;
363
162M
    }
364
365
7.45M
    if (!metric->retire_time_.Initialized()) {
366
2.66M
      VLOG(3) << "Metric " << it->first << " has become un-referenced. Will retire after "
367
0
              << "the retention interval";
368
      // This is the first time we've seen this metric as retirable.
369
2.66M
      metric->retire_time_ = now;
370
2.66M
      metric->retire_time_.AddDelta(MonoDelta::FromMilliseconds(
371
2.66M
                                      FLAGS_metrics_retirement_age_ms));
372
2.66M
      ++it;
373
2.66M
      continue;
374
2.66M
    }
375
376
    // If we've already seen this metric in a previous scan, check if it's
377
    // time to retire it yet.
378
4.79M
    if (now.ComesBefore(metric->retire_time_)) {
379
4.08M
      VLOG(3) << "Metric " << it->first << " is un-referenced, but still within "
380
0
              << "the retention interval";
381
4.08M
      ++it;
382
4.08M
      continue;
383
4.08M
    }
384
385
386
704k
    VLOG
(2) << "Retiring metric " << it->first0
;
387
704k
    metric_map_.erase(it++);
388
704k
  }
389
1.05M
}
390
391
343k
void MetricEntity::NeverRetire(const scoped_refptr<Metric>& metric) {
392
343k
  std::lock_guard<simple_spinlock> l(lock_);
393
343k
  never_retire_metrics_.push_back(metric);
394
343k
}
395
396
127k
void MetricEntity::SetAttributes(const AttributeMap& attrs) {
397
127k
  std::lock_guard<simple_spinlock> l(lock_);
398
127k
  attributes_ = attrs;
399
127k
}
400
401
267k
void MetricEntity::SetAttribute(const string& key, const string& val) {
402
267k
  std::lock_guard<simple_spinlock> l(lock_);
403
267k
  attributes_[key] = val;
404
267k
}
405
406
scoped_refptr<Counter> MetricEntity::FindOrCreateCounter(
407
23.5M
    const CounterPrototype* proto) {
408
23.5M
  CheckInstantiation(proto);
409
23.5M
  std::lock_guard<simple_spinlock> l(lock_);
410
23.5M
  scoped_refptr<Counter> m = down_cast<Counter*>(FindPtrOrNull(metric_map_, proto).get());
411
23.5M
  if (!m) {
412
11.9M
    m = new Counter(proto);
413
11.9M
    InsertOrDie(&metric_map_, proto, m);
414
11.9M
  }
415
23.5M
  return m;
416
23.5M
}
417
418
scoped_refptr<Counter> MetricEntity::FindOrCreateCounter(
419
2
    std::unique_ptr<CounterPrototype> proto) {
420
2
  CheckInstantiation(proto.get());
421
2
  std::lock_guard<simple_spinlock> l(lock_);
422
2
  auto m = down_cast<Counter*>(FindPtrOrNull(metric_map_, proto.get()).get());
423
2
  if (!m) {
424
2
    m = new Counter(std::move(proto));
425
2
    InsertOrDie(&metric_map_, m->prototype(), m);
426
2
  }
427
2
  return m;
428
2
}
429
430
scoped_refptr<MillisLag> MetricEntity::FindOrCreateMillisLag(
431
0
    const MillisLagPrototype* proto) {
432
0
  CheckInstantiation(proto);
433
0
  std::lock_guard<simple_spinlock> l(lock_);
434
0
  scoped_refptr<MillisLag> m = down_cast<MillisLag*>(FindPtrOrNull(metric_map_, proto).get());
435
0
  if (!m) {
436
0
    m = new MillisLag(proto);
437
0
    InsertOrDie(&metric_map_, proto, m);
438
0
  }
439
0
  return m;
440
0
}
441
442
scoped_refptr<AtomicMillisLag> MetricEntity::FindOrCreateAtomicMillisLag(
443
176k
    const MillisLagPrototype* proto) {
444
176k
  CheckInstantiation(proto);
445
176k
  std::lock_guard<simple_spinlock> l(lock_);
446
176k
  scoped_refptr<AtomicMillisLag> m = down_cast<AtomicMillisLag*>(
447
176k
      FindPtrOrNull(metric_map_, proto).get());
448
176k
  if (!m) {
449
175k
    m = new AtomicMillisLag(proto);
450
175k
    InsertOrDie(&metric_map_, proto, m);
451
175k
  }
452
176k
  return m;
453
176k
}
454
455
scoped_refptr<Histogram> MetricEntity::FindOrCreateHistogram(
456
14.2M
    const HistogramPrototype* proto) {
457
14.2M
  CheckInstantiation(proto);
458
14.2M
  std::lock_guard<simple_spinlock> l(lock_);
459
14.2M
  scoped_refptr<Histogram> m = down_cast<Histogram*>(FindPtrOrNull(metric_map_, proto).get());
460
14.2M
  if (!m) {
461
3.82M
    m = new Histogram(proto);
462
3.82M
    InsertOrDie(&metric_map_, proto, m);
463
3.82M
  }
464
14.2M
  return m;
465
14.2M
}
466
467
scoped_refptr<Histogram> MetricEntity::FindOrCreateHistogram(
468
65.7k
    std::unique_ptr<HistogramPrototype> proto) {
469
65.7k
  CheckInstantiation(proto.get());
470
65.7k
  std::lock_guard<simple_spinlock> l(lock_);
471
65.7k
  auto m = down_cast<Histogram*>(FindPtrOrNull(metric_map_, proto.get()).get());
472
65.7k
  if (!m) {
473
65.7k
    uint64_t highest_trackable_value = proto->max_trackable_value();
474
65.7k
    int num_significant_digits = proto->num_sig_digits();
475
65.7k
    const ExportPercentiles export_percentile = proto->export_percentiles();
476
65.7k
    m = new Histogram(std::move(proto), highest_trackable_value, num_significant_digits,
477
65.7k
                      export_percentile);
478
65.7k
    InsertOrDie(&metric_map_, m->prototype(), m);
479
65.7k
  }
480
65.7k
  return m;
481
65.7k
}
482
483
1
void WriteRegistryAsJson(JsonWriter* writer) {
484
1
  MetricPrototypeRegistry::get()->WriteAsJson(writer);
485
1
}
486
487
36.9M
void RegisterMetricPrototype(const MetricPrototype* prototype) {
488
36.9M
  MetricPrototypeRegistry::get()->AddMetric(prototype);
489
36.9M
}
490
491
} // namespace yb