Skip to content

Commit 95a097a

Browse files
authored
Override exporter: expose all fields that can be converted to float64 (#6979)
* Override exporter: expose all fields that can be converted to float64 Signed-off-by: SungJin1212 <tjdwls1201@gmail.com> * fix test Signed-off-by: SungJin1212 <tjdwls1201@gmail.com> * fix changelog Signed-off-by: SungJin1212 <tjdwls1201@gmail.com> --------- Signed-off-by: SungJin1212 <tjdwls1201@gmail.com>
1 parent 2ef5de0 commit 95a097a

File tree

3 files changed

+213
-6
lines changed

3 files changed

+213
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* [FEATURE] Querier: Support for configuring query optimizers and enabling XFunctions in the Thanos engine. #6873
2525
* [FEATURE] Query Frontend: Add support /api/v1/format_query API for formatting queries. #6893
2626
* [FEATURE] Query Frontend: Add support for /api/v1/parse_query API (experimental) to parse a PromQL expression and return it as a JSON-formatted AST (abstract syntax tree). #6978
27+
* [ENHANCEMENT] Overrides Exporter: Expose all fields that can be converted to float64. Also, the label value `max_local_series_per_metric` got renamed to `max_series_per_metric`, and `max_local_series_per_user` got renamed to `max_series_per_user`. #6979
2728
* [ENHANCEMENT] Ingester: Add `cortex_ingester_tsdb_wal_replay_unknown_refs_total` and `cortex_ingester_tsdb_wbl_replay_unknown_refs_total` metrics to track unknown series references during wal/wbl replaying. #6945
2829
* [ENHANCEMENT] Ruler: Emit an error message when the rule synchronization fails. #6902
2930
* [ENHANCEMENT] Querier: Support snappy and zstd response compression for `-querier.response-compression` flag. #6848

pkg/util/validation/exporter.go

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package validation
22

33
import (
4+
"reflect"
5+
"strings"
6+
"time"
7+
48
"github.com/prometheus/client_golang/prometheus"
59
)
610

@@ -31,12 +35,56 @@ func (oe *OverridesExporter) Describe(ch chan<- *prometheus.Desc) {
3135
func (oe *OverridesExporter) Collect(ch chan<- prometheus.Metric) {
3236
allLimits := oe.tenantLimits.AllByUserID()
3337
for tenant, limits := range allLimits {
34-
ch <- prometheus.MustNewConstMetric(oe.description, prometheus.GaugeValue, limits.IngestionRate, "ingestion_rate", tenant)
35-
ch <- prometheus.MustNewConstMetric(oe.description, prometheus.GaugeValue, float64(limits.IngestionBurstSize), "ingestion_burst_size", tenant)
38+
for metricName, value := range ExtractNumericalValues(limits) {
39+
ch <- prometheus.MustNewConstMetric(oe.description, prometheus.GaugeValue, value, metricName, tenant)
40+
}
41+
}
42+
}
43+
44+
func ExtractNumericalValues(l *Limits) map[string]float64 {
45+
metrics := make(map[string]float64)
46+
47+
v := reflect.ValueOf(l).Elem()
48+
t := v.Type()
49+
50+
for i := 0; i < v.NumField(); i++ {
51+
field := v.Field(i)
52+
fieldType := t.Field(i)
53+
54+
tag := fieldType.Tag.Get("yaml")
55+
if tag == "" || tag == "-" {
56+
// not exist tag or tag is "-"
57+
continue
58+
}
59+
60+
// remove options like omitempty
61+
if idx := strings.Index(tag, ","); idx != -1 {
62+
tag = tag[:idx]
63+
}
3664

37-
ch <- prometheus.MustNewConstMetric(oe.description, prometheus.GaugeValue, float64(limits.MaxLocalSeriesPerUser), "max_local_series_per_user", tenant)
38-
ch <- prometheus.MustNewConstMetric(oe.description, prometheus.GaugeValue, float64(limits.MaxLocalSeriesPerMetric), "max_local_series_per_metric", tenant)
39-
ch <- prometheus.MustNewConstMetric(oe.description, prometheus.GaugeValue, float64(limits.MaxGlobalSeriesPerUser), "max_global_series_per_user", tenant)
40-
ch <- prometheus.MustNewConstMetric(oe.description, prometheus.GaugeValue, float64(limits.MaxGlobalSeriesPerMetric), "max_global_series_per_metric", tenant)
65+
switch field.Kind() {
66+
case reflect.Int, reflect.Int64:
67+
if field.Type().String() == "model.Duration" {
68+
// we export the model.Duration in seconds
69+
metrics[tag] = time.Duration(field.Int()).Seconds()
70+
} else {
71+
metrics[tag] = float64(field.Int())
72+
}
73+
case reflect.Uint, reflect.Uint64:
74+
metrics[tag] = float64(field.Uint())
75+
case reflect.Float64:
76+
metrics[tag] = field.Float()
77+
case reflect.Bool:
78+
if field.Bool() {
79+
// true as 1.0
80+
metrics[tag] = 1.0
81+
} else {
82+
// false as 0.0
83+
metrics[tag] = 0.0
84+
}
85+
case reflect.String, reflect.Slice, reflect.Map, reflect.Struct:
86+
continue
87+
}
4188
}
89+
return metrics
4290
}

pkg/util/validation/exporter_test.go

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package validation
22

33
import (
4+
"flag"
5+
"strings"
46
"testing"
7+
"time"
58

69
"github.com/prometheus/client_golang/prometheus/testutil"
710
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
812
)
913

1014
func TestOverridesExporter_noConfig(t *testing.T) {
@@ -21,10 +25,164 @@ func TestOverridesExporter_withConfig(t *testing.T) {
2125
MaxQueriersPerTenant: 5,
2226
},
2327
}
28+
fs := flag.NewFlagSet("test", flag.ContinueOnError)
29+
tenantLimits["tenant-a"].RegisterFlags(fs)
2430

2531
exporter := NewOverridesExporter(newMockTenantLimits(tenantLimits))
2632

2733
// There should be at least a few metrics generated by receiving an override configuration map
2834
count := testutil.CollectAndCount(exporter, "cortex_overrides")
2935
assert.Greater(t, count, 0)
36+
require.NoError(t, testutil.CollectAndCompare(exporter, strings.NewReader(`
37+
# HELP cortex_overrides Resource limit overrides applied to tenants
38+
# TYPE cortex_overrides gauge
39+
cortex_overrides{limit_name="accept_ha_samples",user="tenant-a"} 0
40+
cortex_overrides{limit_name="accept_mixed_ha_samples",user="tenant-a"} 0
41+
cortex_overrides{limit_name="alertmanager_max_alerts_count",user="tenant-a"} 0
42+
cortex_overrides{limit_name="alertmanager_max_alerts_size_bytes",user="tenant-a"} 0
43+
cortex_overrides{limit_name="alertmanager_max_config_size_bytes",user="tenant-a"} 0
44+
cortex_overrides{limit_name="alertmanager_max_dispatcher_aggregation_groups",user="tenant-a"} 0
45+
cortex_overrides{limit_name="alertmanager_max_silences_count",user="tenant-a"} 0
46+
cortex_overrides{limit_name="alertmanager_max_silences_size_bytes",user="tenant-a"} 0
47+
cortex_overrides{limit_name="alertmanager_max_template_size_bytes",user="tenant-a"} 0
48+
cortex_overrides{limit_name="alertmanager_max_templates_count",user="tenant-a"} 0
49+
cortex_overrides{limit_name="alertmanager_notification_rate_limit",user="tenant-a"} 0
50+
cortex_overrides{limit_name="alertmanager_receivers_firewall_block_private_addresses",user="tenant-a"} 0
51+
cortex_overrides{limit_name="compactor_blocks_retention_period",user="tenant-a"} 0
52+
cortex_overrides{limit_name="compactor_partition_index_size_bytes",user="tenant-a"} 6.8719476736e+10
53+
cortex_overrides{limit_name="compactor_partition_series_count",user="tenant-a"} 0
54+
cortex_overrides{limit_name="compactor_tenant_shard_size",user="tenant-a"} 0
55+
cortex_overrides{limit_name="creation_grace_period",user="tenant-a"} 600
56+
cortex_overrides{limit_name="enable_native_histograms",user="tenant-a"} 0
57+
cortex_overrides{limit_name="enforce_metadata_metric_name",user="tenant-a"} 1
58+
cortex_overrides{limit_name="enforce_metric_name",user="tenant-a"} 1
59+
cortex_overrides{limit_name="ha_max_clusters",user="tenant-a"} 0
60+
cortex_overrides{limit_name="ingestion_burst_size",user="tenant-a"} 50000
61+
cortex_overrides{limit_name="ingestion_rate",user="tenant-a"} 25000
62+
cortex_overrides{limit_name="ingestion_tenant_shard_size",user="tenant-a"} 0
63+
cortex_overrides{limit_name="max_cache_freshness",user="tenant-a"} 60
64+
cortex_overrides{limit_name="max_downloaded_bytes_per_request",user="tenant-a"} 0
65+
cortex_overrides{limit_name="max_exemplars",user="tenant-a"} 0
66+
cortex_overrides{limit_name="max_fetched_chunk_bytes_per_query",user="tenant-a"} 0
67+
cortex_overrides{limit_name="max_fetched_chunks_per_query",user="tenant-a"} 2e+06
68+
cortex_overrides{limit_name="max_fetched_data_bytes_per_query",user="tenant-a"} 0
69+
cortex_overrides{limit_name="max_fetched_series_per_query",user="tenant-a"} 0
70+
cortex_overrides{limit_name="max_global_metadata_per_metric",user="tenant-a"} 0
71+
cortex_overrides{limit_name="max_global_metadata_per_user",user="tenant-a"} 0
72+
cortex_overrides{limit_name="max_global_native_histogram_series_per_user",user="tenant-a"} 0
73+
cortex_overrides{limit_name="max_global_series_per_metric",user="tenant-a"} 0
74+
cortex_overrides{limit_name="max_global_series_per_user",user="tenant-a"} 0
75+
cortex_overrides{limit_name="max_label_name_length",user="tenant-a"} 1024
76+
cortex_overrides{limit_name="max_label_names_per_series",user="tenant-a"} 30
77+
cortex_overrides{limit_name="max_label_value_length",user="tenant-a"} 2048
78+
cortex_overrides{limit_name="max_labels_size_bytes",user="tenant-a"} 0
79+
cortex_overrides{limit_name="max_metadata_length",user="tenant-a"} 1024
80+
cortex_overrides{limit_name="max_metadata_per_metric",user="tenant-a"} 10
81+
cortex_overrides{limit_name="max_metadata_per_user",user="tenant-a"} 8000
82+
cortex_overrides{limit_name="max_native_histogram_buckets",user="tenant-a"} 0
83+
cortex_overrides{limit_name="max_native_histogram_sample_size_bytes",user="tenant-a"} 0
84+
cortex_overrides{limit_name="max_native_histogram_series_per_user",user="tenant-a"} 0
85+
cortex_overrides{limit_name="max_outstanding_requests_per_tenant",user="tenant-a"} 100
86+
cortex_overrides{limit_name="max_queriers_per_tenant",user="tenant-a"} 0
87+
cortex_overrides{limit_name="max_query_length",user="tenant-a"} 0
88+
cortex_overrides{limit_name="max_query_lookback",user="tenant-a"} 0
89+
cortex_overrides{limit_name="max_query_parallelism",user="tenant-a"} 14
90+
cortex_overrides{limit_name="max_query_response_size",user="tenant-a"} 0
91+
cortex_overrides{limit_name="max_series_per_metric",user="tenant-a"} 50000
92+
cortex_overrides{limit_name="max_series_per_user",user="tenant-a"} 5e+06
93+
cortex_overrides{limit_name="native_histogram_ingestion_burst_size",user="tenant-a"} 0
94+
cortex_overrides{limit_name="native_histogram_ingestion_rate",user="tenant-a"} 1.7976931348623157e+308
95+
cortex_overrides{limit_name="out_of_order_time_window",user="tenant-a"} 0
96+
cortex_overrides{limit_name="parquet_converter_enabled",user="tenant-a"} 0
97+
cortex_overrides{limit_name="parquet_converter_tenant_shard_size",user="tenant-a"} 0
98+
cortex_overrides{limit_name="parquet_max_fetched_chunk_bytes",user="tenant-a"} 0
99+
cortex_overrides{limit_name="parquet_max_fetched_data_bytes",user="tenant-a"} 0
100+
cortex_overrides{limit_name="parquet_max_fetched_row_count",user="tenant-a"} 0
101+
cortex_overrides{limit_name="query_partial_data",user="tenant-a"} 0
102+
cortex_overrides{limit_name="query_vertical_shard_size",user="tenant-a"} 0
103+
cortex_overrides{limit_name="reject_old_samples",user="tenant-a"} 0
104+
cortex_overrides{limit_name="reject_old_samples_max_age",user="tenant-a"} 1.2096e+06
105+
cortex_overrides{limit_name="ruler_evaluation_delay_duration",user="tenant-a"} 0
106+
cortex_overrides{limit_name="ruler_max_rule_groups_per_tenant",user="tenant-a"} 0
107+
cortex_overrides{limit_name="ruler_max_rules_per_rule_group",user="tenant-a"} 0
108+
cortex_overrides{limit_name="ruler_query_offset",user="tenant-a"} 0
109+
cortex_overrides{limit_name="ruler_tenant_shard_size",user="tenant-a"} 0
110+
cortex_overrides{limit_name="rules_partial_data",user="tenant-a"} 0
111+
cortex_overrides{limit_name="store_gateway_tenant_shard_size",user="tenant-a"} 0
112+
`), "cortex_overrides"))
113+
}
114+
115+
func TestExtractNumericalValues(t *testing.T) {
116+
limits := &Limits{}
117+
fs := flag.NewFlagSet("test", flag.ContinueOnError)
118+
limits.RegisterFlags(fs)
119+
extracted := ExtractNumericalValues(limits)
120+
t.Run("float64 should be converted", func(t *testing.T) {
121+
require.Equal(t, limits.IngestionRate, extracted["ingestion_rate"])
122+
})
123+
t.Run("int should be converted", func(t *testing.T) {
124+
require.Equal(t, float64(limits.IngestionBurstSize), extracted["ingestion_burst_size"])
125+
})
126+
t.Run("int64 should be converted", func(t *testing.T) {
127+
require.Equal(t, float64(limits.MaxQueryResponseSize), extracted["max_query_response_size"])
128+
})
129+
t.Run("string shouldn't be converted", func(t *testing.T) {
130+
_, ok := extracted["ingestion_rate_strategy"]
131+
require.False(t, ok, "string should be not converted")
132+
})
133+
t.Run("bool should be converted, default value false converted to 0", func(t *testing.T) {
134+
val, ok := extracted["accept_ha_samples"]
135+
require.True(t, ok)
136+
require.Equal(t, 0.0, val)
137+
})
138+
t.Run("bool should be converted, default value true converted to 1", func(t *testing.T) {
139+
val, ok := extracted["enforce_metric_name"]
140+
require.True(t, ok)
141+
require.Equal(t, 1.0, val)
142+
})
143+
t.Run("flagext.StringSlice shouldn't be converted", func(t *testing.T) {
144+
_, ok := extracted["drop_labels"]
145+
require.False(t, ok)
146+
})
147+
t.Run("model.Duration should be converted", func(t *testing.T) {
148+
val, ok := extracted["reject_old_samples_max_age"]
149+
require.True(t, ok)
150+
require.Equal(t, time.Duration(limits.RejectOldSamplesMaxAge).Seconds(), val)
151+
})
152+
t.Run("[]*relabel.Config shouldn't be converted", func(t *testing.T) {
153+
_, ok := extracted["metric_relabel_configs"]
154+
require.False(t, ok)
155+
})
156+
t.Run("[]string shouldn't be converted", func(t *testing.T) {
157+
_, ok := extracted["promote_resource_attributes"]
158+
require.False(t, ok)
159+
})
160+
t.Run("[]LimitsPerLabelSet shouldn't be converted", func(t *testing.T) {
161+
_, ok := extracted["limits_per_label_set"]
162+
require.False(t, ok)
163+
})
164+
t.Run("QueryPriority shouldn't be converted", func(t *testing.T) {
165+
_, ok := extracted["query_priority"]
166+
require.False(t, ok)
167+
})
168+
t.Run("QueryRejection shouldn't be converted", func(t *testing.T) {
169+
_, ok := extracted["query_rejection"]
170+
require.False(t, ok)
171+
})
172+
t.Run("labels.Labels shouldn't be converted", func(t *testing.T) {
173+
_, ok := extracted["ruler_external_labels"]
174+
require.False(t, ok)
175+
})
176+
t.Run("flagext.CIDRSliceCSV shouldn't be converted", func(t *testing.T) {
177+
_, ok := extracted["alertmanager_receivers_firewall_block_cidr_networks"]
178+
require.False(t, ok)
179+
})
180+
t.Run("NotificationRateLimitMap shouldn't be converted", func(t *testing.T) {
181+
_, ok := extracted["alertmanager_notification_rate_limit_per_integration"]
182+
require.False(t, ok)
183+
})
184+
t.Run("DisabledRuleGroups shouldn't be converted", func(t *testing.T) {
185+
_, ok := extracted["disabled_rule_groups"]
186+
require.False(t, ok)
187+
})
30188
}

0 commit comments

Comments
 (0)