Deterministic render ordering
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -218,6 +218,11 @@ bool is_valid_metric_name(std::string_view name);
|
||||
bool is_valid_label_key(std::string_view key);
|
||||
bool is_valid_label_value(std::string_view value);
|
||||
|
||||
// Reset all metrics state - WARNING: Only safe for testing!
|
||||
// This clears all registered families and metrics. Should only be called
|
||||
// when no metric objects are in use and no concurrent render() calls.
|
||||
void reset_metrics_for_testing();
|
||||
|
||||
// Note: Histograms do not support callbacks due to their multi-value nature
|
||||
// (buckets + sum + count). Use static histogram metrics only.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user