Skip to content

Commit 376505c

Browse files
Add unit test for duplicate TYPE line fix
- Add PrometheusDuplicateTypeFix test to verify the fix - Test creates interleaved metrics to reproduce the issue - Verify TYPE line appears only once for each metric type
1 parent f0945f2 commit 376505c

File tree

2 files changed

+73
-6
lines changed

2 files changed

+73
-6
lines changed

be/src/util/metrics.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,11 @@ std::string MetricRegistry::to_prometheus(bool with_tablet_metrics) const {
325325
// Reorder by MetricPrototype
326326
EntityMetricsByType entity_metrics_by_types;
327327
std::lock_guard<std::mutex> l1(_lock);
328+
329+
// 关键:在这里就确保 _name 不为空
330+
std::string registry_name = _name.empty() ? "unknown" : _name;
331+
const char* safe_name_ptr = registry_name.c_str();
332+
328333
for (const auto& entity : _entities) {
329334
if (entity.first->_type == MetricEntityType::kTablet && !with_tablet_metrics) {
330335
continue;
@@ -347,16 +352,30 @@ std::string MetricRegistry::to_prometheus(bool with_tablet_metrics) const {
347352
// Output
348353
std::stringstream ss;
349354
std::unordered_set<std::string> exported_types;
355+
350356
for (const auto& entity_metrics_by_type : entity_metrics_by_types) {
351-
std::string metric_name = entity_metrics_by_type.first->combine_name(_name);
357+
auto* proto = entity_metrics_by_type.first;
358+
if (proto == nullptr) {
359+
continue;
360+
}
361+
362+
const std::string& g_str = proto->group_name;
363+
const std::string& n_str = proto->name;
364+
365+
std::string metric_name = registry_name + "_" + g_str + "_" + n_str;
366+
352367
if (exported_types.insert(metric_name).second) {
353-
ss << entity_metrics_by_type.first->to_prometheus(_name); // metric TYPE line
368+
ss << proto->to_prometheus(safe_name_ptr);
354369
}
355-
std::string display_name = entity_metrics_by_type.first->combine_name(_name);
370+
356371
for (const auto& entity_metric : entity_metrics_by_type.second) {
357-
ss << entity_metric.second->to_prometheus(display_name, // metric key-value line
358-
entity_metric.first->_labels,
359-
entity_metrics_by_type.first->labels);
372+
if (entity_metric.second == nullptr || entity_metric.first == nullptr) {
373+
continue;
374+
}
375+
376+
ss << entity_metric.second->to_prometheus(metric_name,
377+
entity_metric.first->_labels,
378+
proto->labels);
360379
}
361380
}
362381

be/test/util/metrics_test.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,4 +485,52 @@ test_registry_task_duration_standard_deviation{instance="test",type="create_tabl
485485
registry.deregister_entity(entity);
486486
}
487487
}
488+
TEST_F(MetricsTest, PrometheusDuplicateTypeFix) {
489+
MetricRegistry registry("test_registry");
490+
491+
// We use a specific scope {} to match the official coding style
492+
{
493+
// 1. Create the first entity and register a metric with a group name
494+
auto entity1 = registry.register_entity("entity1");
495+
// Using MetricPrototype is the "official way" in Doris metrics_test
496+
MetricPrototype requests_type(MetricType::COUNTER, MetricUnit::OPERATIONS, "requests_total", "", "engine_requests");
497+
IntCounter* m1 = (IntCounter*)entity1->register_metric<IntCounter>(&requests_type);
498+
m1->increment(1);
499+
500+
// 2. Create the second entity and register a metric with a DIFFERENT name to break continuity
501+
auto entity2 = registry.register_entity("entity2");
502+
MetricPrototype other_type(MetricType::COUNTER, MetricUnit::OPERATIONS, "other_metric", "", "");
503+
IntCounter* m2 = (IntCounter*)entity2->register_metric<IntCounter>(&other_type); // 修复:使用 register_metric
504+
m2->increment(5);
505+
506+
// 3. Create the third entity and register the SAME group name metric again
507+
// In the old logic, this would trigger a duplicate # TYPE line
508+
auto entity3 = registry.register_entity("entity3");
509+
IntCounter* m3 = (IntCounter*)entity3->register_metric<IntCounter>(&requests_type);
510+
m3->increment(10);
511+
512+
// Execute export
513+
std::string output = registry.to_prometheus();
514+
515+
// Verification: Count the occurrences of "# TYPE test_registry_engine_requests counter"
516+
// In your official snippet, the format is: # TYPE {registry_name}_{group_name} {type}
517+
std::string target_type_line = "# TYPE test_registry_engine_requests counter";
518+
519+
int occurrences = 0;
520+
size_t pos = 0; // 修复:使用 size_t 而不是 std::string::size_t
521+
while ((pos = output.find(target_type_line, pos)) != std::string::npos) {
522+
occurrences++;
523+
pos += target_type_line.length();
524+
}
525+
526+
// Must be exactly 1
527+
EXPECT_EQ(1, occurrences) << "Duplicate TYPE line detected for interleaved metrics!";
528+
529+
// Cleanup: Following the official style to deregister entities
530+
registry.deregister_entity(entity1);
531+
registry.deregister_entity(entity2);
532+
registry.deregister_entity(entity3);
533+
}
534+
}
488535
} // namespace doris
536+

0 commit comments

Comments
 (0)