Deterministic render ordering

This commit is contained in:
2025-08-31 22:55:01 -04:00
parent 8b828be0a9
commit 8326c67b9c
3 changed files with 162 additions and 24 deletions

View File

@@ -11,6 +11,7 @@
#include <cstdlib>
#include <cstring>
#include <functional>
#include <map>
#include <mutex>
#include <string>
#include <thread>
@@ -168,6 +169,21 @@ struct LabelsKey {
}
return true;
}
bool operator<(const LabelsKey &other) const {
if (labels.size() != other.labels.size()) {
return labels.size() < other.labels.size();
}
for (size_t i = 0; i < labels.size(); ++i) {
if (labels[i].first != other.labels[i].first) {
return labels[i].first < other.labels[i].first;
}
if (labels[i].second != other.labels[i].second) {
return labels[i].second < other.labels[i].second;
}
}
return false; // They are equal
}
};
} // namespace metric
@@ -214,9 +230,8 @@ template <> struct Family<Counter>::State {
global_accumulated_values;
// Callback-based metrics (global, not per-thread)
std::unordered_map<
LabelsKey, MetricCallback<Counter>, std::hash<LabelsKey>,
std::equal_to<LabelsKey>,
std::map<
LabelsKey, MetricCallback<Counter>, std::less<LabelsKey>,
ArenaStlAllocator<std::pair<const LabelsKey, MetricCallback<Counter>>>>
callbacks;
@@ -238,10 +253,8 @@ template <> struct Family<Gauge>::State {
instances;
// Callback-based metrics
std::unordered_map<
LabelsKey, MetricCallback<Gauge>, std::hash<LabelsKey>,
std::equal_to<LabelsKey>,
ArenaStlAllocator<std::pair<const LabelsKey, MetricCallback<Gauge>>>>
std::map<LabelsKey, MetricCallback<Gauge>, std::less<LabelsKey>,
ArenaStlAllocator<std::pair<const LabelsKey, MetricCallback<Gauge>>>>
callbacks;
State(ArenaAllocator &arena)
@@ -321,9 +334,8 @@ struct Metric {
// Function-local statics to avoid static initialization order fiasco
static auto &get_counter_families() {
using FamilyMap = std::unordered_map<
std::string_view, Family<Counter>::State *, std::hash<std::string_view>,
std::equal_to<std::string_view>,
using FamilyMap = std::map<
std::string_view, Family<Counter>::State *, std::less<std::string_view>,
ArenaStlAllocator<
std::pair<const std::string_view, Family<Counter>::State *>>>;
static FamilyMap *counterFamilies = new FamilyMap(
@@ -334,9 +346,8 @@ struct Metric {
}
static auto &get_gauge_families() {
using FamilyMap = std::unordered_map<
std::string_view, Family<Gauge>::State *, std::hash<std::string_view>,
std::equal_to<std::string_view>,
using FamilyMap = std::map<
std::string_view, Family<Gauge>::State *, std::less<std::string_view>,
ArenaStlAllocator<
std::pair<const std::string_view, Family<Gauge>::State *>>>;
static FamilyMap *gaugeFamilies = new FamilyMap(
@@ -347,11 +358,11 @@ struct Metric {
}
static auto &get_histogram_families() {
using FamilyMap = std::unordered_map<
std::string_view, Family<Histogram>::State *,
std::hash<std::string_view>, std::equal_to<std::string_view>,
ArenaStlAllocator<
std::pair<const std::string_view, Family<Histogram>::State *>>>;
using FamilyMap =
std::map<std::string_view, Family<Histogram>::State *,
std::less<std::string_view>,
ArenaStlAllocator<std::pair<const std::string_view,
Family<Histogram>::State *>>>;
static FamilyMap *histogramFamilies = new FamilyMap(
ArenaStlAllocator<
std::pair<const std::string_view, Family<Histogram>::State *>>(
@@ -997,9 +1008,8 @@ std::span<std::string_view> render(ArenaAllocator &arena) {
}
// Aggregate all counter values (thread-local + global accumulated)
std::unordered_map<LabelsKey, double, std::hash<LabelsKey>,
std::equal_to<LabelsKey>,
ArenaStlAllocator<std::pair<const LabelsKey, double>>>
std::map<LabelsKey, double, std::less<LabelsKey>,
ArenaStlAllocator<std::pair<const LabelsKey, double>>>
aggregated_values{
ArenaStlAllocator<std::pair<const LabelsKey, double>>(&arena)};
@@ -1094,9 +1104,8 @@ std::span<std::string_view> render(ArenaAllocator &arena) {
AggregatedHistogram(ArenaAllocator &arena)
: thresholds(&arena), counts(&arena), sum(0.0), observations(0) {}
};
std::unordered_map<
LabelsKey, AggregatedHistogram *, std::hash<LabelsKey>,
std::equal_to<LabelsKey>,
std::map<
LabelsKey, AggregatedHistogram *, std::less<LabelsKey>,
ArenaStlAllocator<std::pair<const LabelsKey, AggregatedHistogram *>>>
aggregated_histograms{ArenaStlAllocator<
std::pair<const LabelsKey, AggregatedHistogram *>>(&arena)};
@@ -1295,4 +1304,31 @@ void Family<Gauge>::register_callback(
std::mutex Metric::mutex;
thread_local Metric::ThreadInit Metric::thread_init;
void reset_metrics_for_testing() {
std::lock_guard _{Metric::mutex};
// WARNING: This function assumes no metric objects are in use!
// Clear all family maps - this will leak the Family::State objects but
// that's acceptable for testing since they were allocated in the global arena
// Get references to the maps
auto &counter_families = Metric::get_counter_families();
auto &gauge_families = Metric::get_gauge_families();
auto &histogram_families = Metric::get_histogram_families();
auto &interned_labels = Metric::get_interned_labels();
// Clear all family registrations
counter_families.clear();
gauge_families.clear();
histogram_families.clear();
interned_labels.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();
// Note: Thread-local arenas will be cleaned up by ThreadInit destructors
// when threads exit naturally
}
} // namespace metric