New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactored limits to be able to define them dynamically when Cortex is vendored in another project #1549
Refactored limits to be able to define them dynamically when Cortex is vendored in another project #1549
Changes from 4 commits
f9f0770
a15fc8c
735da12
298be80
963cb83
5afdcb3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,10 @@ package validation | |
|
||
import ( | ||
"flag" | ||
"os" | ||
"time" | ||
|
||
"gopkg.in/yaml.v2" | ||
) | ||
|
||
// Limits describe all the limits for users; can be used to describe global default | ||
|
@@ -73,7 +76,177 @@ func (l *Limits) UnmarshalYAML(unmarshal func(interface{}) error) error { | |
// We want to set c to the defaults and then overwrite it with the input. | ||
// To make unmarshal fill the plain data struct rather than calling UnmarshalYAML | ||
// again, we have to hide it using a type indirection. See prometheus/config. | ||
*l = defaultLimits | ||
|
||
// During startup we wont have a default value so we don't want to overwrite them | ||
if defaultLimits != nil { | ||
*l = *defaultLimits | ||
} | ||
type plain Limits | ||
return unmarshal((*plain)(l)) | ||
} | ||
|
||
// 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 | ||
|
||
// Overrides periodically fetch a set of per-user overrides, and provides convenience | ||
// functions for fetching the correct value. | ||
type Overrides struct { | ||
overridesManager *OverridesManager | ||
} | ||
|
||
// NewOverrides makes a new Overrides. | ||
// We store the supplied limits in a global variable to ensure per-tenant limits | ||
// are defaulted to those values. As such, the last call to NewOverrides will | ||
// become the new global defaults. | ||
func NewOverrides(defaults Limits) (*Overrides, error) { | ||
defaultLimits = &defaults | ||
|
||
overridesManager, err := NewOverridesManager(defaults.PerTenantOverridePeriod, defaults.PerTenantOverrideConfig, | ||
loadOverrides, &defaults) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Overrides{ | ||
overridesManager: overridesManager, | ||
}, nil | ||
} | ||
|
||
// Stop background reloading of overrides. | ||
func (o *Overrides) Stop() { | ||
o.overridesManager.Stop() | ||
} | ||
|
||
// IngestionRate returns the limit on ingester rate (samples per second). | ||
func (o *Overrides) IngestionRate(userID string) float64 { | ||
return o.overridesManager.GetLimits(userID).(*Limits).IngestionRate | ||
} | ||
|
||
// IngestionBurstSize returns the burst size for ingestion rate. | ||
func (o *Overrides) IngestionBurstSize(userID string) int { | ||
return o.overridesManager.GetLimits(userID).(*Limits).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.overridesManager.GetLimits(userID).(*Limits).AcceptHASamples | ||
} | ||
|
||
// 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.overridesManager.GetLimits(userID).(*Limits).HAReplicaLabel | ||
} | ||
|
||
// 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.overridesManager.GetLimits(userID).(*Limits).HAClusterLabel | ||
} | ||
|
||
// MaxLabelNameLength returns maximum length a label name can be. | ||
func (o *Overrides) MaxLabelNameLength(userID string) int { | ||
return o.overridesManager.GetLimits(userID).(*Limits).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.overridesManager.GetLimits(userID).(*Limits).MaxLabelValueLength | ||
} | ||
|
||
// MaxLabelNamesPerSeries returns maximum number of label/value pairs timeseries. | ||
func (o *Overrides) MaxLabelNamesPerSeries(userID string) int { | ||
return o.overridesManager.GetLimits(userID).(*Limits).MaxLabelNamesPerSeries | ||
} | ||
|
||
// RejectOldSamples returns true when we should reject samples older than certain | ||
// age. | ||
func (o *Overrides) RejectOldSamples(userID string) bool { | ||
return o.overridesManager.GetLimits(userID).(*Limits).RejectOldSamples | ||
} | ||
|
||
// RejectOldSamplesMaxAge returns the age at which samples should be rejected. | ||
func (o *Overrides) RejectOldSamplesMaxAge(userID string) time.Duration { | ||
return o.overridesManager.GetLimits(userID).(*Limits).RejectOldSamplesMaxAge | ||
} | ||
|
||
// CreationGracePeriod is misnamed, and actually returns how far into the future | ||
// we should accept samples. | ||
func (o *Overrides) CreationGracePeriod(userID string) time.Duration { | ||
return o.overridesManager.GetLimits(userID).(*Limits).CreationGracePeriod | ||
} | ||
|
||
// MaxSeriesPerQuery returns the maximum number of series a query is allowed to hit. | ||
func (o *Overrides) MaxSeriesPerQuery(userID string) int { | ||
return o.overridesManager.GetLimits(userID).(*Limits).MaxSeriesPerQuery | ||
} | ||
|
||
// MaxSamplesPerQuery returns the maximum number of samples in a query (from the ingester). | ||
func (o *Overrides) MaxSamplesPerQuery(userID string) int { | ||
return o.overridesManager.GetLimits(userID).(*Limits).MaxSamplesPerQuery | ||
} | ||
|
||
// MaxSeriesPerUser returns the maximum number of series a user is allowed to store. | ||
func (o *Overrides) MaxSeriesPerUser(userID string) int { | ||
return o.overridesManager.GetLimits(userID).(*Limits).MaxSeriesPerUser | ||
} | ||
|
||
// MaxSeriesPerMetric returns the maximum number of series allowed per metric. | ||
func (o *Overrides) MaxSeriesPerMetric(userID string) int { | ||
return o.overridesManager.GetLimits(userID).(*Limits).MaxSeriesPerMetric | ||
} | ||
|
||
// MaxChunksPerQuery returns the maximum number of chunks allowed per query. | ||
func (o *Overrides) MaxChunksPerQuery(userID string) int { | ||
return o.overridesManager.GetLimits(userID).(*Limits).MaxChunksPerQuery | ||
} | ||
|
||
// MaxQueryLength returns the limit of the length (in time) of a query. | ||
func (o *Overrides) MaxQueryLength(userID string) time.Duration { | ||
return o.overridesManager.GetLimits(userID).(*Limits).MaxQueryLength | ||
} | ||
|
||
// MaxQueryParallelism returns the limit to the number of sub-queries the | ||
// frontend will process in parallel. | ||
func (o *Overrides) MaxQueryParallelism(userID string) int { | ||
return o.overridesManager.GetLimits(userID).(*Limits).MaxQueryParallelism | ||
} | ||
|
||
// EnforceMetricName whether to enforce the presence of a metric name. | ||
func (o *Overrides) EnforceMetricName(userID string) bool { | ||
return o.overridesManager.GetLimits(userID).(*Limits).EnforceMetricName | ||
} | ||
|
||
// CardinalityLimit whether to enforce the presence of a metric name. | ||
func (o *Overrides) CardinalityLimit(userID string) int { | ||
return o.overridesManager.GetLimits(userID).(*Limits).CardinalityLimit | ||
} | ||
|
||
// Loads overrides and returns the limits as an interface to store them in OverridesManager. | ||
// We need to implement it here since OverridesManager must store type Limits in an interface but | ||
// it doesn't know its definition to initialize it. | ||
func loadOverrides(filename string) (map[string]interface{}, error) { | ||
f, err := os.Open(filename) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var overrides struct { | ||
Overrides map[string]*Limits `yaml:"overrides"` | ||
} | ||
|
||
decoder := yaml.NewDecoder(f) | ||
decoder.SetStrict(true) | ||
if err := decoder.Decode(&overrides); err != nil { | ||
return nil, err | ||
} | ||
|
||
overridesAsInterface := map[string]interface{}{} | ||
for userID := range overrides.Overrides { | ||
overridesAsInterface[userID] = overrides.Overrides[userID] | ||
} | ||
|
||
return overridesAsInterface, nil | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we make this take a "factory" for the I think this is possible with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As discussed offline, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without this values were getting reset to 0 while loading cortex config which had just per tenant overrides config path set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did this not happen before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sandlis explained offline:
cortex
pkg.defaultLimits
has not been inited.defaultLimits
to the half-zero'd struct.