-
Notifications
You must be signed in to change notification settings - Fork 757
Expand file tree
/
Copy pathlimits.go
More file actions
1041 lines (878 loc) · 61.8 KB
/
limits.go
File metadata and controls
1041 lines (878 loc) · 61.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// SPDX-License-Identifier: AGPL-3.0-only
// Provenance-includes-location: https://github.com/cortexproject/cortex/blob/master/pkg/util/validation/limits.go
// Provenance-includes-license: Apache-2.0
// Provenance-includes-copyright: The Cortex Authors.
package validation
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"math"
"reflect"
"strings"
"time"
"github.com/grafana/dskit/flagext"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/model/relabel"
"golang.org/x/time/rate"
"gopkg.in/yaml.v3"
"github.com/grafana/mimir/pkg/ingester/activeseries"
"github.com/grafana/mimir/pkg/storage/tsdb/block"
)
const (
MaxSeriesPerMetricFlag = "ingester.max-global-series-per-metric"
MaxMetadataPerMetricFlag = "ingester.max-global-metadata-per-metric"
MaxSeriesPerUserFlag = "ingester.max-global-series-per-user"
MaxMetadataPerUserFlag = "ingester.max-global-metadata-per-user"
MaxChunksPerQueryFlag = "querier.max-fetched-chunks-per-query"
MaxChunkBytesPerQueryFlag = "querier.max-fetched-chunk-bytes-per-query"
MaxSeriesPerQueryFlag = "querier.max-fetched-series-per-query"
maxLabelNamesPerSeriesFlag = "validation.max-label-names-per-series"
maxLabelNameLengthFlag = "validation.max-length-label-name"
maxLabelValueLengthFlag = "validation.max-length-label-value"
maxMetadataLengthFlag = "validation.max-metadata-length"
creationGracePeriodFlag = "validation.create-grace-period"
maxQueryLengthFlag = "store.max-query-length"
maxPartialQueryLengthFlag = "querier.max-partial-query-length"
maxTotalQueryLengthFlag = "query-frontend.max-total-query-length"
maxQueryExpressionSizeBytesFlag = "query-frontend.max-query-expression-size-bytes"
requestRateFlag = "distributor.request-rate-limit"
requestBurstSizeFlag = "distributor.request-burst-size"
ingestionRateFlag = "distributor.ingestion-rate-limit"
ingestionBurstSizeFlag = "distributor.ingestion-burst-size"
HATrackerMaxClustersFlag = "distributor.ha-tracker.max-clusters"
resultsCacheTTLFlag = "query-frontend.results-cache-ttl"
resultsCacheTTLForOutOfOrderWindowFlag = "query-frontend.results-cache-ttl-for-out-of-order-time-window"
// MinCompactorPartialBlockDeletionDelay is the minimum partial blocks deletion delay that can be configured in Mimir.
MinCompactorPartialBlockDeletionDelay = 4 * time.Hour
)
// LimitError are errors that do not comply with the limits specified.
type LimitError string
func (e LimitError) Error() string {
return string(e)
}
type ForwardingRule struct {
// Ingest defines whether a metric should still be pushed to the Ingesters despite it being forwarded.
Ingest bool `yaml:"ingest" json:"ingest"`
}
// ForwardingRules are keyed by metric names, excluding labels.
type ForwardingRules map[string]ForwardingRule
// Limits describe all the limits for users; can be used to describe global default
// limits via flags, or per-user limits via yaml config.
type Limits struct {
// Distributor enforced limits.
RequestRate float64 `yaml:"request_rate" json:"request_rate" category:"experimental"`
RequestBurstSize int `yaml:"request_burst_size" json:"request_burst_size" category:"experimental"`
IngestionRate float64 `yaml:"ingestion_rate" json:"ingestion_rate"`
IngestionBurstSize int `yaml:"ingestion_burst_size" json:"ingestion_burst_size"`
AcceptHASamples bool `yaml:"accept_ha_samples" json:"accept_ha_samples"`
HAClusterLabel string `yaml:"ha_cluster_label" json:"ha_cluster_label"`
HAReplicaLabel string `yaml:"ha_replica_label" json:"ha_replica_label"`
HAMaxClusters int `yaml:"ha_max_clusters" json:"ha_max_clusters"`
DropLabels flagext.StringSlice `yaml:"drop_labels" json:"drop_labels" category:"advanced"`
MaxLabelNameLength int `yaml:"max_label_name_length" json:"max_label_name_length"`
MaxLabelValueLength int `yaml:"max_label_value_length" json:"max_label_value_length"`
MaxLabelNamesPerSeries int `yaml:"max_label_names_per_series" json:"max_label_names_per_series"`
MaxMetadataLength int `yaml:"max_metadata_length" json:"max_metadata_length"`
CreationGracePeriod model.Duration `yaml:"creation_grace_period" json:"creation_grace_period" category:"advanced"`
EnforceMetadataMetricName bool `yaml:"enforce_metadata_metric_name" json:"enforce_metadata_metric_name" category:"advanced"`
IngestionTenantShardSize int `yaml:"ingestion_tenant_shard_size" json:"ingestion_tenant_shard_size"`
MetricRelabelConfigs []*relabel.Config `yaml:"metric_relabel_configs,omitempty" json:"metric_relabel_configs,omitempty" doc:"nocli|description=List of metric relabel configurations. Note that in most situations, it is more effective to use metrics relabeling directly in the Prometheus server, e.g. remote_write.write_relabel_configs." category:"experimental"`
// Ingester enforced limits.
// Series
MaxGlobalSeriesPerUser int `yaml:"max_global_series_per_user" json:"max_global_series_per_user"`
MaxGlobalSeriesPerMetric int `yaml:"max_global_series_per_metric" json:"max_global_series_per_metric"`
// Metadata
MaxGlobalMetricsWithMetadataPerUser int `yaml:"max_global_metadata_per_user" json:"max_global_metadata_per_user"`
MaxGlobalMetadataPerMetric int `yaml:"max_global_metadata_per_metric" json:"max_global_metadata_per_metric"`
// Exemplars
MaxGlobalExemplarsPerUser int `yaml:"max_global_exemplars_per_user" json:"max_global_exemplars_per_user" category:"experimental"`
// Native histograms
NativeHistogramsIngestionEnabled bool `yaml:"native_histograms_ingestion_enabled" json:"native_histograms_ingestion_enabled" category:"experimental"`
// Active series custom trackers
ActiveSeriesCustomTrackersConfig activeseries.CustomTrackersConfig `yaml:"active_series_custom_trackers" json:"active_series_custom_trackers" doc:"description=Additional custom trackers for active metrics. If there are active series matching a provided matcher (map value), the count will be exposed in the custom trackers metric labeled using the tracker name (map key). Zero valued counts are not exposed (and removed when they go back to zero)." category:"advanced"`
// Max allowed time window for out-of-order samples.
OutOfOrderTimeWindow model.Duration `yaml:"out_of_order_time_window" json:"out_of_order_time_window" category:"experimental"`
OutOfOrderBlocksExternalLabelEnabled bool `yaml:"out_of_order_blocks_external_label_enabled" json:"out_of_order_blocks_external_label_enabled" category:"experimental"`
// User defined label to give the option of subdividing specific metrics by another label
SeparateMetricsGroupLabel string `yaml:"separate_metrics_group_label" json:"separate_metrics_group_label" category:"experimental"`
// Querier enforced limits.
MaxChunksPerQuery int `yaml:"max_fetched_chunks_per_query" json:"max_fetched_chunks_per_query"`
MaxFetchedSeriesPerQuery int `yaml:"max_fetched_series_per_query" json:"max_fetched_series_per_query"`
MaxFetchedChunkBytesPerQuery int `yaml:"max_fetched_chunk_bytes_per_query" json:"max_fetched_chunk_bytes_per_query"`
MaxQueryLookback model.Duration `yaml:"max_query_lookback" json:"max_query_lookback"`
MaxQueryLength model.Duration `yaml:"max_query_length" json:"max_query_length" doc:"hidden"` // TODO: deprecated, remove in 2.8
MaxPartialQueryLength model.Duration `yaml:"max_partial_query_length" json:"max_partial_query_length"`
MaxQueryParallelism int `yaml:"max_query_parallelism" json:"max_query_parallelism"`
MaxLabelsQueryLength model.Duration `yaml:"max_labels_query_length" json:"max_labels_query_length"`
MaxCacheFreshness model.Duration `yaml:"max_cache_freshness" json:"max_cache_freshness" category:"advanced"`
MaxQueriersPerTenant int `yaml:"max_queriers_per_tenant" json:"max_queriers_per_tenant"`
QueryShardingTotalShards int `yaml:"query_sharding_total_shards" json:"query_sharding_total_shards"`
QueryShardingMaxShardedQueries int `yaml:"query_sharding_max_sharded_queries" json:"query_sharding_max_sharded_queries"`
SplitInstantQueriesByInterval model.Duration `yaml:"split_instant_queries_by_interval" json:"split_instant_queries_by_interval" category:"experimental"`
// Query-frontend limits.
MaxTotalQueryLength model.Duration `yaml:"max_total_query_length" json:"max_total_query_length"`
ResultsCacheTTL model.Duration `yaml:"results_cache_ttl" json:"results_cache_ttl" category:"experimental"`
ResultsCacheTTLForOutOfOrderTimeWindow model.Duration `yaml:"results_cache_ttl_for_out_of_order_time_window" json:"results_cache_ttl_for_out_of_order_time_window" category:"experimental"`
MaxQueryExpressionSizeBytes int `yaml:"max_query_expression_size_bytes" json:"max_query_expression_size_bytes" category:"experimental"`
// Cardinality
CardinalityAnalysisEnabled bool `yaml:"cardinality_analysis_enabled" json:"cardinality_analysis_enabled"`
LabelNamesAndValuesResultsMaxSizeBytes int `yaml:"label_names_and_values_results_max_size_bytes" json:"label_names_and_values_results_max_size_bytes"`
LabelValuesMaxCardinalityLabelNamesPerRequest int `yaml:"label_values_max_cardinality_label_names_per_request" json:"label_values_max_cardinality_label_names_per_request"`
// Ruler defaults and limits.
RulerEvaluationDelay model.Duration `yaml:"ruler_evaluation_delay_duration" json:"ruler_evaluation_delay_duration"`
RulerTenantShardSize int `yaml:"ruler_tenant_shard_size" json:"ruler_tenant_shard_size"`
RulerMaxRulesPerRuleGroup int `yaml:"ruler_max_rules_per_rule_group" json:"ruler_max_rules_per_rule_group"`
RulerMaxRuleGroupsPerTenant int `yaml:"ruler_max_rule_groups_per_tenant" json:"ruler_max_rule_groups_per_tenant"`
RulerRecordingRulesEvaluationEnabled bool `yaml:"ruler_recording_rules_evaluation_enabled" json:"ruler_recording_rules_evaluation_enabled" category:"experimental"`
RulerAlertingRulesEvaluationEnabled bool `yaml:"ruler_alerting_rules_evaluation_enabled" json:"ruler_alerting_rules_evaluation_enabled" category:"experimental"`
// Store-gateway.
StoreGatewayTenantShardSize int `yaml:"store_gateway_tenant_shard_size" json:"store_gateway_tenant_shard_size"`
// Compactor.
CompactorBlocksRetentionPeriod model.Duration `yaml:"compactor_blocks_retention_period" json:"compactor_blocks_retention_period"`
CompactorSplitAndMergeShards int `yaml:"compactor_split_and_merge_shards" json:"compactor_split_and_merge_shards"`
CompactorSplitGroups int `yaml:"compactor_split_groups" json:"compactor_split_groups"`
CompactorTenantShardSize int `yaml:"compactor_tenant_shard_size" json:"compactor_tenant_shard_size"`
CompactorPartialBlockDeletionDelay model.Duration `yaml:"compactor_partial_block_deletion_delay" json:"compactor_partial_block_deletion_delay"`
CompactorBlockUploadEnabled bool `yaml:"compactor_block_upload_enabled" json:"compactor_block_upload_enabled"`
CompactorBlockUploadValidationEnabled bool `yaml:"compactor_block_upload_validation_enabled" json:"compactor_block_upload_validation_enabled"`
CompactorBlockUploadVerifyChunks bool `yaml:"compactor_block_upload_verify_chunks" json:"compactor_block_upload_verify_chunks"`
// This config doesn't have a CLI flag registered here because they're registered in
// their own original config struct.
S3SSEType string `yaml:"s3_sse_type" json:"s3_sse_type" doc:"nocli|description=S3 server-side encryption type. Required to enable server-side encryption overrides for a specific tenant. If not set, the default S3 client settings are used."`
S3SSEKMSKeyID string `yaml:"s3_sse_kms_key_id" json:"s3_sse_kms_key_id" doc:"nocli|description=S3 server-side encryption KMS Key ID. Ignored if the SSE type override is not set."`
S3SSEKMSEncryptionContext string `yaml:"s3_sse_kms_encryption_context" json:"s3_sse_kms_encryption_context" doc:"nocli|description=S3 server-side encryption KMS encryption context. If unset and the key ID override is set, the encryption context will not be provided to S3. Ignored if the SSE type override is not set."`
// Alertmanager.
AlertmanagerReceiversBlockCIDRNetworks flagext.CIDRSliceCSV `yaml:"alertmanager_receivers_firewall_block_cidr_networks" json:"alertmanager_receivers_firewall_block_cidr_networks"`
AlertmanagerReceiversBlockPrivateAddresses bool `yaml:"alertmanager_receivers_firewall_block_private_addresses" json:"alertmanager_receivers_firewall_block_private_addresses"`
NotificationRateLimit float64 `yaml:"alertmanager_notification_rate_limit" json:"alertmanager_notification_rate_limit"`
NotificationRateLimitPerIntegration NotificationRateLimitMap `yaml:"alertmanager_notification_rate_limit_per_integration" json:"alertmanager_notification_rate_limit_per_integration"`
AlertmanagerMaxConfigSizeBytes int `yaml:"alertmanager_max_config_size_bytes" json:"alertmanager_max_config_size_bytes"`
AlertmanagerMaxTemplatesCount int `yaml:"alertmanager_max_templates_count" json:"alertmanager_max_templates_count"`
AlertmanagerMaxTemplateSizeBytes int `yaml:"alertmanager_max_template_size_bytes" json:"alertmanager_max_template_size_bytes"`
AlertmanagerMaxDispatcherAggregationGroups int `yaml:"alertmanager_max_dispatcher_aggregation_groups" json:"alertmanager_max_dispatcher_aggregation_groups"`
AlertmanagerMaxAlertsCount int `yaml:"alertmanager_max_alerts_count" json:"alertmanager_max_alerts_count"`
AlertmanagerMaxAlertsSizeBytes int `yaml:"alertmanager_max_alerts_size_bytes" json:"alertmanager_max_alerts_size_bytes"`
ForwardingEndpoint string `yaml:"forwarding_endpoint" json:"forwarding_endpoint" doc:"nocli|description=Remote-write endpoint where metrics specified in forwarding_rules are forwarded to. If set, takes precedence over endpoints specified in forwarding rules."`
ForwardingDropOlderThan model.Duration `yaml:"forwarding_drop_older_than" json:"forwarding_drop_older_than" doc:"nocli|description=If set, forwarding drops samples that are older than this duration. If unset or 0, no samples get dropped."`
ForwardingRules ForwardingRules `yaml:"forwarding_rules" json:"forwarding_rules" doc:"nocli|description=Rules based on which the Distributor decides whether a metric should be forwarded to an alternative remote_write API endpoint."`
extensions map[string]interface{}
}
// RegisterFlags adds the flags required to config this to the given FlagSet
func (l *Limits) RegisterFlags(f *flag.FlagSet) {
f.IntVar(&l.IngestionTenantShardSize, "distributor.ingestion-tenant-shard-size", 0, "The tenant's shard size used by shuffle-sharding. Must be set both on ingesters and distributors. 0 disables shuffle sharding.")
f.Float64Var(&l.RequestRate, requestRateFlag, 0, "Per-tenant request rate limit in requests per second. 0 to disable.")
f.IntVar(&l.RequestBurstSize, requestBurstSizeFlag, 0, "Per-tenant allowed request burst size. 0 to disable.")
f.Float64Var(&l.IngestionRate, ingestionRateFlag, 10000, "Per-tenant ingestion rate limit in samples per second.")
f.IntVar(&l.IngestionBurstSize, ingestionBurstSizeFlag, 200000, "Per-tenant allowed ingestion burst size (in number of samples).")
f.BoolVar(&l.AcceptHASamples, "distributor.ha-tracker.enable-for-all-users", false, "Flag to enable, for all tenants, handling of samples with external labels identifying replicas in an HA Prometheus setup.")
f.StringVar(&l.HAClusterLabel, "distributor.ha-tracker.cluster", "cluster", "Prometheus label to look for in samples to identify a Prometheus HA cluster.")
f.StringVar(&l.HAReplicaLabel, "distributor.ha-tracker.replica", "__replica__", "Prometheus label to look for in samples to identify a Prometheus HA replica.")
f.IntVar(&l.HAMaxClusters, HATrackerMaxClustersFlag, 100, "Maximum number of clusters that HA tracker will keep track of for a single tenant. 0 to disable the limit.")
f.Var(&l.DropLabels, "distributor.drop-label", "This flag can be used to specify label names that to drop during sample ingestion within the distributor and can be repeated in order to drop multiple labels.")
f.IntVar(&l.MaxLabelNameLength, maxLabelNameLengthFlag, 1024, "Maximum length accepted for label names")
f.IntVar(&l.MaxLabelValueLength, maxLabelValueLengthFlag, 2048, "Maximum length accepted for label value. This setting also applies to the metric name")
f.IntVar(&l.MaxLabelNamesPerSeries, maxLabelNamesPerSeriesFlag, 30, "Maximum number of label names per series.")
f.IntVar(&l.MaxMetadataLength, maxMetadataLengthFlag, 1024, "Maximum length accepted for metric metadata. Metadata refers to Metric Name, HELP and UNIT. Longer metadata is dropped except for HELP which is truncated.")
_ = l.CreationGracePeriod.Set("10m")
f.Var(&l.CreationGracePeriod, creationGracePeriodFlag, "Controls how far into the future incoming samples are accepted compared to the wall clock. Any sample with timestamp `t` will be rejected if `t > (now + validation.create-grace-period)`. Also used by query-frontend to avoid querying too far into the future. 0 to disable.")
f.BoolVar(&l.EnforceMetadataMetricName, "validation.enforce-metadata-metric-name", true, "Enforce every metadata has a metric name.")
f.IntVar(&l.MaxGlobalSeriesPerUser, MaxSeriesPerUserFlag, 150000, "The maximum number of in-memory series per tenant, across the cluster before replication. 0 to disable.")
f.IntVar(&l.MaxGlobalSeriesPerMetric, MaxSeriesPerMetricFlag, 0, "The maximum number of in-memory series per metric name, across the cluster before replication. 0 to disable.")
f.IntVar(&l.MaxGlobalMetricsWithMetadataPerUser, MaxMetadataPerUserFlag, 0, "The maximum number of in-memory metrics with metadata per tenant, across the cluster. 0 to disable.")
f.IntVar(&l.MaxGlobalMetadataPerMetric, MaxMetadataPerMetricFlag, 0, "The maximum number of metadata per metric, across the cluster. 0 to disable.")
f.IntVar(&l.MaxGlobalExemplarsPerUser, "ingester.max-global-exemplars-per-user", 0, "The maximum number of exemplars in memory, across the cluster. 0 to disable exemplars ingestion.")
f.Var(&l.ActiveSeriesCustomTrackersConfig, "ingester.active-series-custom-trackers", "Additional active series metrics, matching the provided matchers. Matchers should be in form <name>:<matcher>, like 'foobar:{foo=\"bar\"}'. Multiple matchers can be provided either providing the flag multiple times or providing multiple semicolon-separated values to a single flag.")
f.Var(&l.OutOfOrderTimeWindow, "ingester.out-of-order-time-window", fmt.Sprintf("Non-zero value enables out-of-order support for most recent samples that are within the time window in relation to the TSDB's maximum time, i.e., within [db.maxTime-timeWindow, db.maxTime]). The ingester will need more memory as a factor of rate of out-of-order samples being ingested and the number of series that are getting out-of-order samples. If query falls into this window, cached results will use value from -%s option to specify TTL for resulting cache entry.", resultsCacheTTLForOutOfOrderWindowFlag))
f.BoolVar(&l.NativeHistogramsIngestionEnabled, "ingester.native-histograms-ingestion-enabled", false, "Enable ingestion of native histogram samples. If false, native histogram samples are ignored without an error. To query native histograms with query-sharding enabled make sure to set -query-frontend.query-result-response-format to 'protobuf'.")
f.BoolVar(&l.OutOfOrderBlocksExternalLabelEnabled, "ingester.out-of-order-blocks-external-label-enabled", false, "Whether the shipper should label out-of-order blocks with an external label before uploading them. Setting this label will compact out-of-order blocks separately from non-out-of-order blocks")
f.StringVar(&l.SeparateMetricsGroupLabel, "validation.separate-metrics-group-label", "", "Label used to define the group label for metrics separation. For each write request, the group is obtained from the first non-empty group label from the first timeseries in the incoming list of timeseries. Specific distributor and ingester metrics will be further separated adding a 'group' label with group label's value. Currently applies to the following metrics: cortex_discarded_samples_total")
f.IntVar(&l.MaxChunksPerQuery, MaxChunksPerQueryFlag, 2e6, "Maximum number of chunks that can be fetched in a single query from ingesters and long-term storage. This limit is enforced in the querier, ruler and store-gateway. 0 to disable.")
f.IntVar(&l.MaxFetchedSeriesPerQuery, MaxSeriesPerQueryFlag, 0, "The maximum number of unique series for which a query can fetch samples from each ingesters and storage. This limit is enforced in the querier, ruler and store-gateway. 0 to disable")
f.IntVar(&l.MaxFetchedChunkBytesPerQuery, MaxChunkBytesPerQueryFlag, 0, "The maximum size of all chunks in bytes that a query can fetch from each ingester and storage. This limit is enforced in the querier and ruler. 0 to disable.")
// TODO: Deprecated in Mimir 2.6, remove in Mimir 2.8
f.Var(&l.MaxQueryLength, maxQueryLengthFlag, fmt.Sprintf("Deprecated: Limit the query time range (end - start time). This limit is enforced in the querier (on the query possibly split by the query-frontend) and ruler. 0 to disable. This option is deprecated, use -%s or -%s instead.", maxPartialQueryLengthFlag, maxTotalQueryLengthFlag))
f.Var(&l.MaxPartialQueryLength, maxPartialQueryLengthFlag, fmt.Sprintf("Limit the time range for partial queries at the querier level. Defaults to the value of -%s if set to 0.", maxQueryLengthFlag))
f.Var(&l.MaxQueryLookback, "querier.max-query-lookback", "Limit how long back data (series and metadata) can be queried, up until <lookback> duration ago. This limit is enforced in the query-frontend, querier and ruler. If the requested time range is outside the allowed range, the request will not fail but will be manipulated to only query data within the allowed time range. 0 to disable.")
f.IntVar(&l.MaxQueryParallelism, "querier.max-query-parallelism", 14, "Maximum number of split (by time) or partial (by shard) queries that will be scheduled in parallel by the query-frontend for a single input query. This limit is introduced to have a fairer query scheduling and avoid a single query over a large time range saturating all available queriers.")
f.Var(&l.MaxLabelsQueryLength, "store.max-labels-query-length", "Limit the time range (end - start time) of series, label names and values queries. This limit is enforced in the querier. If the requested time range is outside the allowed range, the request will not fail but will be manipulated to only query data within the allowed time range. 0 to disable.")
f.IntVar(&l.LabelNamesAndValuesResultsMaxSizeBytes, "querier.label-names-and-values-results-max-size-bytes", 400*1024*1024, "Maximum size in bytes of distinct label names and values. When querier receives response from ingester, it merges the response with responses from other ingesters. This maximum size limit is applied to the merged(distinct) results. If the limit is reached, an error is returned.")
f.BoolVar(&l.CardinalityAnalysisEnabled, "querier.cardinality-analysis-enabled", false, "Enables endpoints used for cardinality analysis.")
f.IntVar(&l.LabelValuesMaxCardinalityLabelNamesPerRequest, "querier.label-values-max-cardinality-label-names-per-request", 100, "Maximum number of label names allowed to be queried in a single /api/v1/cardinality/label_values API call.")
_ = l.MaxCacheFreshness.Set("1m")
f.Var(&l.MaxCacheFreshness, "query-frontend.max-cache-freshness", "Most recent allowed cacheable result per-tenant, to prevent caching very recent results that might still be in flux.")
f.IntVar(&l.MaxQueriersPerTenant, "query-frontend.max-queriers-per-tenant", 0, "Maximum number of queriers that can handle requests for a single tenant. If set to 0 or value higher than number of available queriers, *all* queriers will handle requests for the tenant. Each frontend (or query-scheduler, if used) will select the same set of queriers for the same tenant (given that all queriers are connected to all frontends / query-schedulers). This option only works with queriers connecting to the query-frontend / query-scheduler, not when using downstream URL.")
f.IntVar(&l.QueryShardingTotalShards, "query-frontend.query-sharding-total-shards", 16, "The amount of shards to use when doing parallelisation via query sharding by tenant. 0 to disable query sharding for tenant. Query sharding implementation will adjust the number of query shards based on compactor shards. This allows querier to not search the blocks which cannot possibly have the series for given query shard.")
f.IntVar(&l.QueryShardingMaxShardedQueries, "query-frontend.query-sharding-max-sharded-queries", 128, "The max number of sharded queries that can be run for a given received query. 0 to disable limit.")
f.Var(&l.SplitInstantQueriesByInterval, "query-frontend.split-instant-queries-by-interval", "Split instant queries by an interval and execute in parallel. 0 to disable it.")
_ = l.RulerEvaluationDelay.Set("1m")
f.Var(&l.RulerEvaluationDelay, "ruler.evaluation-delay-duration", "Duration to delay the evaluation of rules to ensure the underlying metrics have been pushed.")
f.IntVar(&l.RulerTenantShardSize, "ruler.tenant-shard-size", 0, "The tenant's shard size when sharding is used by ruler. Value of 0 disables shuffle sharding for the tenant, and tenant rules will be sharded across all ruler replicas.")
f.IntVar(&l.RulerMaxRulesPerRuleGroup, "ruler.max-rules-per-rule-group", 20, "Maximum number of rules per rule group per-tenant. 0 to disable.")
f.IntVar(&l.RulerMaxRuleGroupsPerTenant, "ruler.max-rule-groups-per-tenant", 70, "Maximum number of rule groups per-tenant. 0 to disable.")
f.BoolVar(&l.RulerRecordingRulesEvaluationEnabled, "ruler.recording-rules-evaluation-enabled", true, "Controls whether recording rules evaluation is enabled. This configuration option can be used to forcefully disable recording rules evaluation on a per-tenant basis.")
f.BoolVar(&l.RulerAlertingRulesEvaluationEnabled, "ruler.alerting-rules-evaluation-enabled", true, "Controls whether alerting rules evaluation is enabled. This configuration option can be used to forcefully disable alerting rules evaluation on a per-tenant basis.")
f.Var(&l.CompactorBlocksRetentionPeriod, "compactor.blocks-retention-period", "Delete blocks containing samples older than the specified retention period. Also used by query-frontend to avoid querying beyond the retention period. 0 to disable.")
f.IntVar(&l.CompactorSplitAndMergeShards, "compactor.split-and-merge-shards", 0, "The number of shards to use when splitting blocks. 0 to disable splitting.")
f.IntVar(&l.CompactorSplitGroups, "compactor.split-groups", 1, "Number of groups that blocks for splitting should be grouped into. Each group of blocks is then split separately. Number of output split shards is controlled by -compactor.split-and-merge-shards.")
f.IntVar(&l.CompactorTenantShardSize, "compactor.compactor-tenant-shard-size", 0, "Max number of compactors that can compact blocks for single tenant. 0 to disable the limit and use all compactors.")
f.Var(&l.CompactorPartialBlockDeletionDelay, "compactor.partial-block-deletion-delay", fmt.Sprintf("If a partial block (unfinished block without %s file) hasn't been modified for this time, it will be marked for deletion. The minimum accepted value is %s: a lower value will be ignored and the feature disabled. 0 to disable.", block.MetaFilename, MinCompactorPartialBlockDeletionDelay.String()))
f.BoolVar(&l.CompactorBlockUploadEnabled, "compactor.block-upload-enabled", false, "Enable block upload API for the tenant.")
f.BoolVar(&l.CompactorBlockUploadValidationEnabled, "compactor.block-upload-validation-enabled", true, "Enable block upload validation for the tenant.")
f.BoolVar(&l.CompactorBlockUploadVerifyChunks, "compactor.block-upload-verify-chunks", true, "Verify chunks when uploading blocks via the upload API for the tenant.")
// Query-frontend.
f.Var(&l.MaxTotalQueryLength, maxTotalQueryLengthFlag, fmt.Sprintf("Limit the total query time range (end - start time). This limit is enforced in the query-frontend on the received query. Defaults to the value of -%s if set to 0.", maxQueryLengthFlag))
_ = l.ResultsCacheTTL.Set("7d")
f.Var(&l.ResultsCacheTTL, resultsCacheTTLFlag, fmt.Sprintf("Time to live duration for cached query results. If query falls into out-of-order time window, -%s is used instead.", resultsCacheTTLForOutOfOrderWindowFlag))
_ = l.ResultsCacheTTLForOutOfOrderTimeWindow.Set("10m")
f.Var(&l.ResultsCacheTTLForOutOfOrderTimeWindow, resultsCacheTTLForOutOfOrderWindowFlag, fmt.Sprintf("Time to live duration for cached query results if query falls into out-of-order time window. This is lower than -%s so that incoming out-of-order samples are returned in the query results sooner.", resultsCacheTTLFlag))
f.IntVar(&l.MaxQueryExpressionSizeBytes, maxQueryExpressionSizeBytesFlag, 0, "Max size of the raw query, in bytes. 0 to not apply a limit to the size of the query.")
// Store-gateway.
f.IntVar(&l.StoreGatewayTenantShardSize, "store-gateway.tenant-shard-size", 0, "The tenant's shard size, used when store-gateway sharding is enabled. Value of 0 disables shuffle sharding for the tenant, that is all tenant blocks are sharded across all store-gateway replicas.")
// Alertmanager.
f.Var(&l.AlertmanagerReceiversBlockCIDRNetworks, "alertmanager.receivers-firewall-block-cidr-networks", "Comma-separated list of network CIDRs to block in Alertmanager receiver integrations.")
f.BoolVar(&l.AlertmanagerReceiversBlockPrivateAddresses, "alertmanager.receivers-firewall-block-private-addresses", false, "True to block private and local addresses in Alertmanager receiver integrations. It blocks private addresses defined by RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses), as well as loopback, local unicast and local multicast addresses.")
f.Float64Var(&l.NotificationRateLimit, "alertmanager.notification-rate-limit", 0, "Per-tenant rate limit for sending notifications from Alertmanager in notifications/sec. 0 = rate limit disabled. Negative value = no notifications are allowed.")
if l.NotificationRateLimitPerIntegration == nil {
l.NotificationRateLimitPerIntegration = NotificationRateLimitMap{}
}
f.Var(&l.NotificationRateLimitPerIntegration, "alertmanager.notification-rate-limit-per-integration", "Per-integration notification rate limits. Value is a map, where each key is integration name and value is a rate-limit (float). On command line, this map is given in JSON format. Rate limit has the same meaning as -alertmanager.notification-rate-limit, but only applies for specific integration. Allowed integration names: "+strings.Join(allowedIntegrationNames, ", ")+".")
f.IntVar(&l.AlertmanagerMaxConfigSizeBytes, "alertmanager.max-config-size-bytes", 0, "Maximum size of configuration file for Alertmanager that tenant can upload via Alertmanager API. 0 = no limit.")
f.IntVar(&l.AlertmanagerMaxTemplatesCount, "alertmanager.max-templates-count", 0, "Maximum number of templates in tenant's Alertmanager configuration uploaded via Alertmanager API. 0 = no limit.")
f.IntVar(&l.AlertmanagerMaxTemplateSizeBytes, "alertmanager.max-template-size-bytes", 0, "Maximum size of single template in tenant's Alertmanager configuration uploaded via Alertmanager API. 0 = no limit.")
f.IntVar(&l.AlertmanagerMaxDispatcherAggregationGroups, "alertmanager.max-dispatcher-aggregation-groups", 0, "Maximum number of aggregation groups in Alertmanager's dispatcher that a tenant can have. Each active aggregation group uses single goroutine. When the limit is reached, dispatcher will not dispatch alerts that belong to additional aggregation groups, but existing groups will keep working properly. 0 = no limit.")
f.IntVar(&l.AlertmanagerMaxAlertsCount, "alertmanager.max-alerts-count", 0, "Maximum number of alerts that a single tenant can have. Inserting more alerts will fail with a log message and metric increment. 0 = no limit.")
f.IntVar(&l.AlertmanagerMaxAlertsSizeBytes, "alertmanager.max-alerts-size-bytes", 0, "Maximum total size of alerts that a single tenant can have, alert size is the sum of the bytes of its labels, annotations and generatorURL. Inserting more alerts will fail with a log message and metric increment. 0 = no limit.")
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (l *Limits) UnmarshalYAML(value *yaml.Node) error {
return l.unmarshal(func(v any) error {
return value.DecodeWithOptions(v, yaml.DecodeOptions{KnownFields: true})
})
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (l *Limits) UnmarshalJSON(data []byte) error {
return l.unmarshal(func(v any) error {
dec := json.NewDecoder(bytes.NewReader(data))
dec.DisallowUnknownFields()
return dec.Decode(v)
})
}
// unmarshal does both YAML and JSON.
func (l *Limits) unmarshal(decode func(any) error) error {
// We want to set l to the defaults and then overwrite it with the input.
if defaultLimits != nil {
*l = *defaultLimits
// Make copy of default limits, otherwise unmarshalling would modify map in default limits.
l.copyNotificationIntegrationLimits(defaultLimits.NotificationRateLimitPerIntegration)
}
// Decode into a reflection-crafted struct that has fields for the extensions.
cfg, getExtensions := newLimitsWithExtensions((*plainLimits)(l))
err := decode(cfg)
if err != nil {
return err
}
l.extensions = getExtensions()
return l.validate()
}
func (l *Limits) validate() error {
for _, cfg := range l.MetricRelabelConfigs {
if cfg == nil {
return fmt.Errorf("invalid metric_relabel_configs")
}
}
return nil
}
func (l *Limits) copyNotificationIntegrationLimits(defaults NotificationRateLimitMap) {
l.NotificationRateLimitPerIntegration = make(map[string]float64, len(defaults))
for k, v := range defaults {
l.NotificationRateLimitPerIntegration[k] = v
}
}
// When we load YAML from disk, we want the various per-customer limits
// to default to any values specified on the command line, not default
// command line values. This global contains those values. I (Tom) cannot
// find a nicer way I'm afraid.
var defaultLimits *Limits
// SetDefaultLimitsForYAMLUnmarshalling sets global default limits, used when loading
// Limits from YAML files. This is used to ensure per-tenant limits are defaulted to
// those values.
func SetDefaultLimitsForYAMLUnmarshalling(defaults Limits) {
defaultLimits = &defaults
}
// TenantLimits exposes per-tenant limit overrides to various resource usage limits
type TenantLimits interface {
// ByUserID gets limits specific to a particular tenant or nil if there are none
ByUserID(userID string) *Limits
// AllByUserID gets a mapping of all tenant IDs and limits for that user
AllByUserID() map[string]*Limits
}
// Overrides periodically fetch a set of per-user overrides, and provides convenience
// functions for fetching the correct value.
type Overrides struct {
defaultLimits *Limits
tenantLimits TenantLimits
}
// NewOverrides makes a new Overrides.
func NewOverrides(defaults Limits, tenantLimits TenantLimits) (*Overrides, error) {
return &Overrides{
tenantLimits: tenantLimits,
defaultLimits: &defaults,
}, nil
}
// RequestRate returns the limit on request rate (requests per second).
func (o *Overrides) RequestRate(userID string) float64 {
return o.getOverridesForUser(userID).RequestRate
}
// RequestBurstSize returns the burst size for request rate.
func (o *Overrides) RequestBurstSize(userID string) int {
return o.getOverridesForUser(userID).RequestBurstSize
}
// IngestionRate returns the limit on ingester rate (samples per second).
func (o *Overrides) IngestionRate(userID string) float64 {
return o.getOverridesForUser(userID).IngestionRate
}
// LabelNamesAndValuesResultsMaxSizeBytes returns the maximum size in bytes of distinct label names and values
func (o *Overrides) LabelNamesAndValuesResultsMaxSizeBytes(userID string) int {
return o.getOverridesForUser(userID).LabelNamesAndValuesResultsMaxSizeBytes
}
func (o *Overrides) CardinalityAnalysisEnabled(userID string) bool {
return o.getOverridesForUser(userID).CardinalityAnalysisEnabled
}
// LabelValuesMaxCardinalityLabelNamesPerRequest returns the maximum number of label names per cardinality request.
func (o *Overrides) LabelValuesMaxCardinalityLabelNamesPerRequest(userID string) int {
return o.getOverridesForUser(userID).LabelValuesMaxCardinalityLabelNamesPerRequest
}
// IngestionBurstSize returns the burst size for ingestion rate.
func (o *Overrides) IngestionBurstSize(userID string) int {
return o.getOverridesForUser(userID).IngestionBurstSize
}
// AcceptHASamples returns whether the distributor should track and accept samples from HA replicas for this user.
func (o *Overrides) AcceptHASamples(userID string) bool {
return o.getOverridesForUser(userID).AcceptHASamples
}
// HAClusterLabel returns the cluster label to look for when deciding whether to accept a sample from a Prometheus HA replica.
func (o *Overrides) HAClusterLabel(userID string) string {
return o.getOverridesForUser(userID).HAClusterLabel
}
// HAReplicaLabel returns the replica label to look for when deciding whether to accept a sample from a Prometheus HA replica.
func (o *Overrides) HAReplicaLabel(userID string) string {
return o.getOverridesForUser(userID).HAReplicaLabel
}
// DropLabels returns the list of labels to be dropped when ingesting HA samples for the user.
func (o *Overrides) DropLabels(userID string) flagext.StringSlice {
return o.getOverridesForUser(userID).DropLabels
}
// MaxLabelNameLength returns maximum length a label name can be.
func (o *Overrides) MaxLabelNameLength(userID string) int {
return o.getOverridesForUser(userID).MaxLabelNameLength
}
// MaxLabelValueLength returns maximum length a label value can be. This also is
// the maximum length of a metric name.
func (o *Overrides) MaxLabelValueLength(userID string) int {
return o.getOverridesForUser(userID).MaxLabelValueLength
}
// MaxLabelNamesPerSeries returns maximum number of label/value pairs timeseries.
func (o *Overrides) MaxLabelNamesPerSeries(userID string) int {
return o.getOverridesForUser(userID).MaxLabelNamesPerSeries
}
// MaxMetadataLength returns maximum length metadata can be. Metadata refers
// to the Metric Name, HELP and UNIT.
func (o *Overrides) MaxMetadataLength(userID string) int {
return o.getOverridesForUser(userID).MaxMetadataLength
}
// CreationGracePeriod is misnamed, and actually returns how far into the future
// we should accept samples.
func (o *Overrides) CreationGracePeriod(userID string) time.Duration {
return time.Duration(o.getOverridesForUser(userID).CreationGracePeriod)
}
// MaxGlobalSeriesPerUser returns the maximum number of series a user is allowed to store across the cluster.
func (o *Overrides) MaxGlobalSeriesPerUser(userID string) int {
return o.getOverridesForUser(userID).MaxGlobalSeriesPerUser
}
// MaxGlobalSeriesPerMetric returns the maximum number of series allowed per metric across the cluster.
func (o *Overrides) MaxGlobalSeriesPerMetric(userID string) int {
return o.getOverridesForUser(userID).MaxGlobalSeriesPerMetric
}
func (o *Overrides) MaxChunksPerQuery(userID string) int {
return o.getOverridesForUser(userID).MaxChunksPerQuery
}
// MaxFetchedSeriesPerQuery returns the maximum number of series allowed per query when fetching
// chunks from ingesters and blocks storage.
func (o *Overrides) MaxFetchedSeriesPerQuery(userID string) int {
return o.getOverridesForUser(userID).MaxFetchedSeriesPerQuery
}
// MaxFetchedChunkBytesPerQuery returns the maximum number of bytes for chunks allowed per query when fetching
// chunks from ingesters and blocks storage.
func (o *Overrides) MaxFetchedChunkBytesPerQuery(userID string) int {
return o.getOverridesForUser(userID).MaxFetchedChunkBytesPerQuery
}
// MaxQueryLookback returns the max lookback period of queries.
func (o *Overrides) MaxQueryLookback(userID string) time.Duration {
return time.Duration(o.getOverridesForUser(userID).MaxQueryLookback)
}
// maxQueryLength returns the limit of the length (in time) of a query.
func (o *Overrides) maxQueryLength(userID string) time.Duration {
return time.Duration(o.getOverridesForUser(userID).MaxQueryLength)
}
// MaxPartialQueryLength returns the limit of the length (in time) of a (partial) query.
func (o *Overrides) MaxPartialQueryLength(userID string) time.Duration {
t := time.Duration(o.getOverridesForUser(userID).MaxPartialQueryLength)
if t == time.Duration(0) {
t = o.maxQueryLength(userID)
}
return t
}
// MaxTotalQueryLength returns the limit of the total length (in time) of a query.
func (o *Overrides) MaxTotalQueryLength(userID string) time.Duration {
t := time.Duration(o.getOverridesForUser(userID).MaxTotalQueryLength)
if t == time.Duration(0) {
return o.maxQueryLength(userID)
}
return t
}
// MaxQueryExpressionSizeBytes returns the limit of the raw query size, in bytes.
func (o *Overrides) MaxQueryExpressionSizeBytes(userID string) int {
return o.getOverridesForUser(userID).MaxQueryExpressionSizeBytes
}
// MaxLabelsQueryLength returns the limit of the length (in time) of a label names or values request.
func (o *Overrides) MaxLabelsQueryLength(userID string) time.Duration {
return time.Duration(o.getOverridesForUser(userID).MaxLabelsQueryLength)
}
// MaxCacheFreshness returns the period after which results are cacheable,
// to prevent caching of very recent results.
func (o *Overrides) MaxCacheFreshness(userID string) time.Duration {
return time.Duration(o.getOverridesForUser(userID).MaxCacheFreshness)
}
// MaxQueriersPerUser returns the maximum number of queriers that can handle requests for this user.
func (o *Overrides) MaxQueriersPerUser(userID string) int {
return o.getOverridesForUser(userID).MaxQueriersPerTenant
}
// MaxQueryParallelism returns the limit to the number of split queries the
// frontend will process in parallel.
func (o *Overrides) MaxQueryParallelism(userID string) int {
return o.getOverridesForUser(userID).MaxQueryParallelism
}
// QueryShardingTotalShards returns the total amount of shards to use when splitting queries via querysharding
// the frontend. When a query is shardable, each shards will be processed in parallel.
func (o *Overrides) QueryShardingTotalShards(userID string) int {
return o.getOverridesForUser(userID).QueryShardingTotalShards
}
// QueryShardingMaxShardedQueries returns the max number of sharded queries that can
// be run for a given received query. 0 to disable limit.
func (o *Overrides) QueryShardingMaxShardedQueries(userID string) int {
return o.getOverridesForUser(userID).QueryShardingMaxShardedQueries
}
// SplitInstantQueriesByInterval returns the split time interval to use when splitting an instant query
// via the query-frontend. 0 to disable limit.
func (o *Overrides) SplitInstantQueriesByInterval(userID string) time.Duration {
return time.Duration(o.getOverridesForUser(userID).SplitInstantQueriesByInterval)
}
// EnforceMetadataMetricName whether to enforce the presence of a metric name on metadata.
func (o *Overrides) EnforceMetadataMetricName(userID string) bool {
return o.getOverridesForUser(userID).EnforceMetadataMetricName
}
// MaxGlobalMetricsWithMetadataPerUser returns the maximum number of metrics with metadata a user is allowed to store across the cluster.
func (o *Overrides) MaxGlobalMetricsWithMetadataPerUser(userID string) int {
return o.getOverridesForUser(userID).MaxGlobalMetricsWithMetadataPerUser
}
// MaxGlobalMetadataPerMetric returns the maximum number of metadata allowed per metric across the cluster.
func (o *Overrides) MaxGlobalMetadataPerMetric(userID string) int {
return o.getOverridesForUser(userID).MaxGlobalMetadataPerMetric
}
// MaxGlobalExemplarsPerUser returns the maximum number of exemplars held in memory across the cluster.
func (o *Overrides) MaxGlobalExemplarsPerUser(userID string) int {
return o.getOverridesForUser(userID).MaxGlobalExemplarsPerUser
}
func (o *Overrides) ActiveSeriesCustomTrackersConfig(userID string) activeseries.CustomTrackersConfig {
return o.getOverridesForUser(userID).ActiveSeriesCustomTrackersConfig
}
// OutOfOrderTimeWindow returns the out-of-order time window for the user.
func (o *Overrides) OutOfOrderTimeWindow(userID string) time.Duration {
return time.Duration(o.getOverridesForUser(userID).OutOfOrderTimeWindow)
}
// OutOfOrderBlocksExternalLabelEnabled returns if the shipper is flagging out-of-order blocks with an external label.
func (o *Overrides) OutOfOrderBlocksExternalLabelEnabled(userID string) bool {
return o.getOverridesForUser(userID).OutOfOrderBlocksExternalLabelEnabled
}
// SeparateMetricsGroupLabel returns the custom label used to separate specific metrics
func (o *Overrides) SeparateMetricsGroupLabel(userID string) string {
return o.getOverridesForUser(userID).SeparateMetricsGroupLabel
}
// IngestionTenantShardSize returns the ingesters shard size for a given user.
func (o *Overrides) IngestionTenantShardSize(userID string) int {
return o.getOverridesForUser(userID).IngestionTenantShardSize
}
// CompactorTenantShardSize returns number of compactors that this user can use. 0 = all compactors.
func (o *Overrides) CompactorTenantShardSize(userID string) int {
return o.getOverridesForUser(userID).CompactorTenantShardSize
}
// EvaluationDelay returns the rules evaluation delay for a given user.
func (o *Overrides) EvaluationDelay(userID string) time.Duration {
return time.Duration(o.getOverridesForUser(userID).RulerEvaluationDelay)
}
// CompactorBlocksRetentionPeriod returns the retention period for a given user.
func (o *Overrides) CompactorBlocksRetentionPeriod(userID string) time.Duration {
return time.Duration(o.getOverridesForUser(userID).CompactorBlocksRetentionPeriod)
}
// CompactorSplitAndMergeShards returns the number of shards to use when splitting blocks.
func (o *Overrides) CompactorSplitAndMergeShards(userID string) int {
return o.getOverridesForUser(userID).CompactorSplitAndMergeShards
}
// CompactorSplitGroups returns the number of groups that blocks for splitting should be grouped into.
func (o *Overrides) CompactorSplitGroups(userID string) int {
return o.getOverridesForUser(userID).CompactorSplitGroups
}
// CompactorPartialBlockDeletionDelay returns the partial block deletion delay time period for a given user,
// and whether the configured value was valid. If the value wasn't valid, the returned delay is the default one
// and the caller is responsible to warn the Mimir operator about it.
func (o *Overrides) CompactorPartialBlockDeletionDelay(userID string) (delay time.Duration, valid bool) {
delay = time.Duration(o.getOverridesForUser(userID).CompactorPartialBlockDeletionDelay)
// Forcefully disable partial blocks deletion if the configured delay is too low.
if delay > 0 && delay < MinCompactorPartialBlockDeletionDelay {
return 0, false
}
return delay, true
}
// CompactorBlockUploadEnabled returns whether block upload is enabled for a certain tenant.
func (o *Overrides) CompactorBlockUploadEnabled(tenantID string) bool {
return o.getOverridesForUser(tenantID).CompactorBlockUploadEnabled
}
// CompactorBlockUploadValidationEnabled returns whether block upload validation is enabled for a certain tenant.
func (o *Overrides) CompactorBlockUploadValidationEnabled(tenantID string) bool {
return o.getOverridesForUser(tenantID).CompactorBlockUploadValidationEnabled
}
// CompactorBlockUploadVerifyChunks returns whether compaction chunk verification is enabled for a certain tenant.
func (o *Overrides) CompactorBlockUploadVerifyChunks(tenantID string) bool {
return o.getOverridesForUser(tenantID).CompactorBlockUploadVerifyChunks
}
// MetricRelabelConfigs returns the metric relabel configs for a given user.
func (o *Overrides) MetricRelabelConfigs(userID string) []*relabel.Config {
return o.getOverridesForUser(userID).MetricRelabelConfigs
}
// NativeHistogramsIngestionEnabled returns whether to ingest native histograms in the ingester
func (o *Overrides) NativeHistogramsIngestionEnabled(userID string) bool {
return o.getOverridesForUser(userID).NativeHistogramsIngestionEnabled
}
// RulerTenantShardSize returns shard size (number of rulers) used by this tenant when using shuffle-sharding strategy.
func (o *Overrides) RulerTenantShardSize(userID string) int {
return o.getOverridesForUser(userID).RulerTenantShardSize
}
// RulerMaxRulesPerRuleGroup returns the maximum number of rules per rule group for a given user.
func (o *Overrides) RulerMaxRulesPerRuleGroup(userID string) int {
return o.getOverridesForUser(userID).RulerMaxRulesPerRuleGroup
}
// RulerMaxRuleGroupsPerTenant returns the maximum number of rule groups for a given user.
func (o *Overrides) RulerMaxRuleGroupsPerTenant(userID string) int {
return o.getOverridesForUser(userID).RulerMaxRuleGroupsPerTenant
}
// RulerRecordingRulesEvaluationEnabled returns whether the recording rules evaluation is enabled for a given user.
func (o *Overrides) RulerRecordingRulesEvaluationEnabled(userID string) bool {
return o.getOverridesForUser(userID).RulerRecordingRulesEvaluationEnabled
}
// RulerAlertingRulesEvaluationEnabled returns whether the alerting rules evaluation is enabled for a given user.
func (o *Overrides) RulerAlertingRulesEvaluationEnabled(userID string) bool {
return o.getOverridesForUser(userID).RulerAlertingRulesEvaluationEnabled
}
// StoreGatewayTenantShardSize returns the store-gateway shard size for a given user.
func (o *Overrides) StoreGatewayTenantShardSize(userID string) int {
return o.getOverridesForUser(userID).StoreGatewayTenantShardSize
}
// MaxHAClusters returns maximum number of clusters that HA tracker will track for a user.
func (o *Overrides) MaxHAClusters(user string) int {
return o.getOverridesForUser(user).HAMaxClusters
}
// S3SSEType returns the per-tenant S3 SSE type.
func (o *Overrides) S3SSEType(user string) string {
return o.getOverridesForUser(user).S3SSEType
}
// S3SSEKMSKeyID returns the per-tenant S3 KMS-SSE key id.
func (o *Overrides) S3SSEKMSKeyID(user string) string {
return o.getOverridesForUser(user).S3SSEKMSKeyID
}
// S3SSEKMSEncryptionContext returns the per-tenant S3 KMS-SSE encryption context.
func (o *Overrides) S3SSEKMSEncryptionContext(user string) string {
return o.getOverridesForUser(user).S3SSEKMSEncryptionContext
}
// AlertmanagerReceiversBlockCIDRNetworks returns the list of network CIDRs that should be blocked
// in the Alertmanager receivers for the given user.
func (o *Overrides) AlertmanagerReceiversBlockCIDRNetworks(user string) []flagext.CIDR {
return o.getOverridesForUser(user).AlertmanagerReceiversBlockCIDRNetworks
}
// AlertmanagerReceiversBlockPrivateAddresses returns true if private addresses should be blocked
// in the Alertmanager receivers for the given user.
func (o *Overrides) AlertmanagerReceiversBlockPrivateAddresses(user string) bool {
return o.getOverridesForUser(user).AlertmanagerReceiversBlockPrivateAddresses
}
// Notification limits are special. Limits are returned in following order:
// 1. per-tenant limits for given integration
// 2. default limits for given integration
// 3. per-tenant limits
// 4. default limits
func (o *Overrides) getNotificationLimitForUser(user, integration string) float64 {
u := o.getOverridesForUser(user)
if n, ok := u.NotificationRateLimitPerIntegration[integration]; ok {
return n
}
return u.NotificationRateLimit
}
func (o *Overrides) NotificationRateLimit(user string, integration string) rate.Limit {
l := o.getNotificationLimitForUser(user, integration)
if l == 0 || math.IsInf(l, 1) {
return rate.Inf // No rate limit.
}
if l < 0 {
l = 0 // No notifications will be sent.
}
return rate.Limit(l)
}
const maxInt = int(^uint(0) >> 1)
func (o *Overrides) NotificationBurstSize(user string, integration string) int {
// Burst size is computed from rate limit. Rate limit is already normalized to [0, +inf), where 0 means disabled.
l := o.NotificationRateLimit(user, integration)
if l == 0 {
return 0
}
// floats can be larger than max int. This also handles case where l == rate.Inf.
if float64(l) >= float64(maxInt) {
return maxInt
}
// For values between (0, 1), allow single notification per second (every 1/limit seconds).
if l < 1 {
return 1
}
return int(l)
}
func (o *Overrides) AlertmanagerMaxConfigSize(userID string) int {
return o.getOverridesForUser(userID).AlertmanagerMaxConfigSizeBytes
}
func (o *Overrides) AlertmanagerMaxTemplatesCount(userID string) int {
return o.getOverridesForUser(userID).AlertmanagerMaxTemplatesCount
}
func (o *Overrides) AlertmanagerMaxTemplateSize(userID string) int {
return o.getOverridesForUser(userID).AlertmanagerMaxTemplateSizeBytes
}
func (o *Overrides) AlertmanagerMaxDispatcherAggregationGroups(userID string) int {
return o.getOverridesForUser(userID).AlertmanagerMaxDispatcherAggregationGroups
}
func (o *Overrides) AlertmanagerMaxAlertsCount(userID string) int {
return o.getOverridesForUser(userID).AlertmanagerMaxAlertsCount
}
func (o *Overrides) AlertmanagerMaxAlertsSizeBytes(userID string) int {
return o.getOverridesForUser(userID).AlertmanagerMaxAlertsSizeBytes
}
func (o *Overrides) ForwardingRules(user string) ForwardingRules {
return o.getOverridesForUser(user).ForwardingRules
}
func (o *Overrides) ForwardingEndpoint(user string) string {
return o.getOverridesForUser(user).ForwardingEndpoint
}
func (o *Overrides) ForwardingDropOlderThan(user string) time.Duration {
return time.Duration(o.getOverridesForUser(user).ForwardingDropOlderThan)
}
func (o *Overrides) ResultsCacheTTL(user string) time.Duration {
return time.Duration(o.getOverridesForUser(user).ResultsCacheTTL)
}
func (o *Overrides) ResultsCacheTTLForOutOfOrderTimeWindow(user string) time.Duration {
return time.Duration(o.getOverridesForUser(user).ResultsCacheTTLForOutOfOrderTimeWindow)
}
func (o *Overrides) getOverridesForUser(userID string) *Limits {
if o.tenantLimits != nil {
l := o.tenantLimits.ByUserID(userID)
if l != nil {
return l
}
}
return o.defaultLimits
}
// SmallestPositiveIntPerTenant is returning the minimal positive value of the
// supplied limit function for all given tenants.
func SmallestPositiveIntPerTenant(tenantIDs []string, f func(string) int) int {
var result *int
for _, tenantID := range tenantIDs {
v := f(tenantID)
if result == nil || v < *result {
result = &v
}
}
if result == nil {
return 0
}
return *result
}
// SmallestPositiveNonZeroIntPerTenant is returning the minimal positive and
// non-zero value of the supplied limit function for all given tenants. In many
// limits a value of 0 means unlimited so the method will return 0 only if all
// inputs have a limit of 0 or an empty tenant list is given.
func SmallestPositiveNonZeroIntPerTenant(tenantIDs []string, f func(string) int) int {
var result *int
for _, tenantID := range tenantIDs {
v := f(tenantID)
if v > 0 && (result == nil || v < *result) {
result = &v
}
}
if result == nil {
return 0
}
return *result
}
// SmallestPositiveNonZeroDurationPerTenant is returning the minimal positive
// and non-zero value of the supplied limit function for all given tenants. In
// many limits a value of 0 means unlimited so the method will return 0 only if
// all inputs have a limit of 0 or an empty tenant list is given.
func SmallestPositiveNonZeroDurationPerTenant(tenantIDs []string, f func(string) time.Duration) time.Duration {
var result *time.Duration
for _, tenantID := range tenantIDs {
v := f(tenantID)
if v > 0 && (result == nil || v < *result) {
result = &v
}
}
if result == nil {
return 0
}
return *result
}
// LargestPositiveNonZeroDurationPerTenant is returning the maximum positive
// and non-zero value of the supplied limit function for all given tenants. In
// many limits a value of 0 means unlimited so the method will return 0 only if
// all inputs have a limit of 0 or an empty tenant list is given.
func LargestPositiveNonZeroDurationPerTenant(tenantIDs []string, f func(string) time.Duration) time.Duration {
var result *time.Duration
for _, tenantID := range tenantIDs {
v := f(tenantID)
if v > 0 && (result == nil || v > *result) {
result = &v
}
}
if result == nil {
return 0
}
return *result
}
// MaxDurationPerTenant is returning the maximum duration per tenant. Without
// tenants given it will return a time.Duration(0).
func MaxDurationPerTenant(tenantIDs []string, f func(string) time.Duration) time.Duration {
result := time.Duration(0)
for _, tenantID := range tenantIDs {
v := f(tenantID)
if v > result {
result = v
}
}
return result
}
// MustRegisterExtension registers the extensions type with given name
// and returns a function to get the extensions value from a *Limits instance.
//
// The provided name will be used as YAML/JSON key to decode the extensions.
//
// The returned getter will return the result of E.Default() if *Limits is nil.
//
// This method is not thread safe and should be called only during package initialization.
// Registering same name twice, or registering a name that is already a *Limits JSON or YAML key will cause a panic.
func MustRegisterExtension[E interface{ Default() E }](name string) func(*Limits) E {
if name == "" {
panic("extension name cannot be empty")
}
if _, ok := standardLimitsYAMLJSONKeys[name]; ok {
panic(fmt.Errorf("extension %s cannot be registered because it's a standard limits field", name))
}
if _, ok := registeredExtensions[name]; ok {
panic(fmt.Errorf("extension %s already registered", name))
}
var zeroE E
registeredExtensions[name] = registeredExtension{
index: len(registeredExtensions),
reflectedDefault: func() reflect.Value { return reflect.ValueOf(zeroE.Default()) },
}
limitsExtensionsFields = append(limitsExtensionsFields, reflect.StructField{
Name: strings.ToUpper(name),
Type: reflect.TypeOf(zeroE),
Tag: reflect.StructTag(fmt.Sprintf(`yaml:"%s" json:"%s"`, name, name)),
})
return func(l *Limits) (e E) {
if l == nil {
// Call e.Default() here every time instead of storing it when the extension is being registered, as it might change over time.
// Especially when the default values are initialized after package initialization phase, where this is registered.
return e.Default()
}
return l.extensions[name].(E)
}
}
var standardLimitsYAMLJSONKeys = map[string]struct{}{}
func init() {
limitsType := reflect.TypeOf(Limits{})
for i := 0; i < limitsType.NumField(); i++ {
// yamlKey/jsonKey could be empty, but we also shouldn't allow registering a field with an empty name, so just add it to the map.
yamlKey, _, _ := strings.Cut(limitsType.Field(i).Tag.Get("yaml"), ",")
jsonKey, _, _ := strings.Cut(limitsType.Field(i).Tag.Get("json"), ",")
standardLimitsYAMLJSONKeys[yamlKey] = struct{}{}
standardLimitsYAMLJSONKeys[jsonKey] = struct{}{}
}
}
type registeredExtension struct {
index int
reflectedDefault func() reflect.Value
}
// registeredExtensions is used to keep track of the indexes of each registered extension.
var registeredExtensions = map[string]registeredExtension{}
// limitsExtensionsFields is the list of the extension fields to be added to the reflection-crafted Limits struct.
var limitsExtensionsFields []reflect.StructField
// plainLimits is used to prevent an infinite loop of calling UnmarshalJSON/UnmarshalYAML by hiding behind type indirection.
type plainLimits Limits
// plainLimitsStructField is the last field in the struct crafted by newLimitsWithExtensions.
var plainLimitsStructField = reflect.StructField{
Name: "PlainLimits",
Type: reflect.TypeOf(new(plainLimits)),
Tag: `yaml:",inline"`,
Anonymous: true,
}
// newLimitsWithExtensions returns an interface{} value of a pointer to a struct of type:
//
// struct {
// EXTNAME1 T1 `yaml:"extname1" json:"extname1"`
// // ...
// EXTNAMEN TN `yaml:"extnameN" json:"extnameN"`
//
// PlainLimits map[string]interface{} `yaml:",inline"`
// }
//