Don't copy static_text in render
This commit is contained in:
@@ -414,6 +414,17 @@ struct Metric {
|
||||
return *internedLabels;
|
||||
}
|
||||
|
||||
// Global static text interning set to avoid duplicate text allocations
|
||||
static auto &get_interned_static_text() {
|
||||
using StaticTextSet =
|
||||
std::unordered_set<std::string_view, std::hash<std::string_view>,
|
||||
std::equal_to<std::string_view>,
|
||||
ArenaStlAllocator<std::string_view>>;
|
||||
static StaticTextSet *internedStaticText = new StaticTextSet(
|
||||
ArenaStlAllocator<std::string_view>(&get_global_arena()));
|
||||
return *internedStaticText;
|
||||
}
|
||||
|
||||
// Thread cleanup for per-family thread-local storage
|
||||
struct ThreadInit {
|
||||
ArenaAllocator arena;
|
||||
@@ -528,6 +539,22 @@ struct Metric {
|
||||
return *result.first;
|
||||
}
|
||||
|
||||
// Intern static text to avoid duplicate allocations
|
||||
static std::string_view intern_static_text(std::string_view text) {
|
||||
auto &interned_set = get_interned_static_text();
|
||||
|
||||
// Check if text is already interned
|
||||
auto it = interned_set.find(text);
|
||||
if (it != interned_set.end()) {
|
||||
return *it;
|
||||
}
|
||||
|
||||
// Not found - copy to global arena and intern
|
||||
auto interned_text = arena_copy_string(text, get_global_arena());
|
||||
auto result = interned_set.emplace(interned_text);
|
||||
return *result.first;
|
||||
}
|
||||
|
||||
static Counter create_counter_instance(
|
||||
Family<Counter> *family,
|
||||
std::span<const std::pair<std::string_view, std::string_view>> labels) {
|
||||
@@ -883,11 +910,12 @@ struct Metric {
|
||||
// Callback instructions and static text
|
||||
for (const auto &[labels_key, callback] : family->callbacks) {
|
||||
plan.instructions.push_back(CallCounterCallback{&callback});
|
||||
plan.static_text.push_back(format(
|
||||
auto static_text = format(
|
||||
plan.arena, "%.*s\n%.*s%.*s ", static_cast<int>(help_line.size()),
|
||||
help_line.data(), static_cast<int>(name.length()), name.data(),
|
||||
static_cast<int>(labels_key.prometheus_format.length()),
|
||||
labels_key.prometheus_format.data()));
|
||||
labels_key.prometheus_format.data());
|
||||
plan.static_text.push_back(intern_static_text(static_text));
|
||||
help_line = "";
|
||||
}
|
||||
|
||||
@@ -896,11 +924,12 @@ struct Metric {
|
||||
for (const auto &data : family_data) {
|
||||
plan.instructions.push_back(
|
||||
AggregateCounter{data.thread_states, data.global_state});
|
||||
plan.static_text.push_back(format(
|
||||
auto static_text = format(
|
||||
plan.arena, "%.*s\n%.*s%.*s ", static_cast<int>(help_line.size()),
|
||||
help_line.data(), static_cast<int>(name.length()), name.data(),
|
||||
static_cast<int>(data.labels_key.prometheus_format.length()),
|
||||
data.labels_key.prometheus_format.data()));
|
||||
data.labels_key.prometheus_format.data());
|
||||
plan.static_text.push_back(intern_static_text(static_text));
|
||||
help_line = "";
|
||||
}
|
||||
}
|
||||
@@ -919,11 +948,12 @@ struct Metric {
|
||||
// Callback instructions and static text
|
||||
for (const auto &[labels_key, callback] : family->callbacks) {
|
||||
plan.instructions.push_back(CallGaugeCallback{&callback});
|
||||
plan.static_text.push_back(format(
|
||||
auto static_text = format(
|
||||
plan.arena, "%.*s\n%.*s%.*s ", static_cast<int>(help_line.size()),
|
||||
help_line.data(), static_cast<int>(name.length()), name.data(),
|
||||
static_cast<int>(labels_key.prometheus_format.length()),
|
||||
labels_key.prometheus_format.data()));
|
||||
labels_key.prometheus_format.data());
|
||||
plan.static_text.push_back(intern_static_text(static_text));
|
||||
help_line = "";
|
||||
}
|
||||
|
||||
@@ -931,11 +961,12 @@ struct Metric {
|
||||
const auto &family_data = label_sets.gauge_data[gauge_family_idx++];
|
||||
for (const auto &data : family_data) {
|
||||
plan.instructions.push_back(AggregateGauge{data.instance_state});
|
||||
plan.static_text.push_back(format(
|
||||
auto static_text = format(
|
||||
plan.arena, "%.*s\n%.*s%.*s ", static_cast<int>(help_line.size()),
|
||||
help_line.data(), static_cast<int>(name.length()), name.data(),
|
||||
static_cast<int>(data.labels_key.prometheus_format.length()),
|
||||
data.labels_key.prometheus_format.data()));
|
||||
data.labels_key.prometheus_format.data());
|
||||
plan.static_text.push_back(intern_static_text(static_text));
|
||||
help_line = "";
|
||||
}
|
||||
}
|
||||
@@ -961,35 +992,39 @@ struct Metric {
|
||||
auto bucket_value = static_format(plan.arena, family->buckets[i]);
|
||||
auto labels = append_label_to_format(
|
||||
data.labels_key.prometheus_format, "le", bucket_value);
|
||||
plan.static_text.push_back(
|
||||
auto static_text =
|
||||
format(plan.arena, "%.*s\n%.*s_bucket%.*s ",
|
||||
static_cast<int>(help_line.size()), help_line.data(),
|
||||
static_cast<int>(name.length()), name.data(),
|
||||
static_cast<int>(labels.length()), labels.data()));
|
||||
static_cast<int>(labels.length()), labels.data());
|
||||
plan.static_text.push_back(intern_static_text(static_text));
|
||||
help_line = "";
|
||||
}
|
||||
|
||||
// Static text for +Inf bucket
|
||||
auto inf_labels = append_label_to_format(
|
||||
data.labels_key.prometheus_format, "le", "+Inf");
|
||||
plan.static_text.push_back(
|
||||
auto inf_static_text =
|
||||
format(plan.arena, "\n%.*s_bucket%.*s ",
|
||||
static_cast<int>(name.length()), name.data(),
|
||||
static_cast<int>(inf_labels.length()), inf_labels.data()));
|
||||
static_cast<int>(inf_labels.length()), inf_labels.data());
|
||||
plan.static_text.push_back(intern_static_text(inf_static_text));
|
||||
|
||||
// Static text for sum
|
||||
plan.static_text.push_back(
|
||||
auto sum_static_text =
|
||||
format(plan.arena, "\n%.*s_sum%.*s ",
|
||||
static_cast<int>(name.length()), name.data(),
|
||||
static_cast<int>(data.labels_key.prometheus_format.length()),
|
||||
data.labels_key.prometheus_format.data()));
|
||||
data.labels_key.prometheus_format.data());
|
||||
plan.static_text.push_back(intern_static_text(sum_static_text));
|
||||
|
||||
// Static text for count
|
||||
plan.static_text.push_back(
|
||||
auto count_static_text =
|
||||
format(plan.arena, "\n%.*s_count%.*s ",
|
||||
static_cast<int>(name.length()), name.data(),
|
||||
static_cast<int>(data.labels_key.prometheus_format.length()),
|
||||
data.labels_key.prometheus_format.data()));
|
||||
data.labels_key.prometheus_format.data());
|
||||
plan.static_text.push_back(intern_static_text(count_static_text));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1104,16 +1139,13 @@ struct Metric {
|
||||
ArenaVector<std::string_view> output(&arena);
|
||||
|
||||
for (size_t i = 0; i < static_text.size(); ++i) {
|
||||
// Copy static text into caller's arena
|
||||
// NOTE: This copying is REQUIRED for memory safety:
|
||||
// - static_text lives in cached_plan's arena (persistent across renders)
|
||||
// - dynamic_text lives in caller's arena (single render lifetime)
|
||||
// - output must live entirely in caller's arena for consistent lifetime
|
||||
// Without copying, output would have mixed arena ownership causing
|
||||
// use-after-free
|
||||
output.push_back(arena_copy_string(static_text[i], arena));
|
||||
// Static text is interned in global arena (application lifetime)
|
||||
// Safe to reference directly without copying since:
|
||||
// - global arena lifetime > caller arena lifetime ≥ output usage lifetime
|
||||
// - static text is truly static for a given registry state
|
||||
output.push_back(static_text[i]);
|
||||
|
||||
// Add corresponding dynamic text
|
||||
// Add corresponding dynamic text (already in caller's arena)
|
||||
output.push_back(dynamic_text[i]);
|
||||
}
|
||||
// Trailing newline
|
||||
@@ -1679,6 +1711,10 @@ void reset_metrics_for_testing() {
|
||||
histogram_families.clear();
|
||||
interned_labels.clear();
|
||||
|
||||
// Clear interned static text
|
||||
auto &interned_static_text = Metric::get_interned_static_text();
|
||||
interned_static_text.clear();
|
||||
|
||||
// Reset the global arena - this will invalidate all arena-allocated strings
|
||||
// but since we're clearing everything, that's OK
|
||||
Metric::get_global_arena().reset();
|
||||
|
||||
Reference in New Issue
Block a user