github.com/thanos-io/thanos@v0.32.5/pkg/receive/limiter_config.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package receive 5 6 import ( 7 "net/url" 8 9 "github.com/thanos-io/thanos/pkg/errors" 10 "github.com/thanos-io/thanos/pkg/httpconfig" 11 "gopkg.in/yaml.v2" 12 ) 13 14 // RootLimitsConfig is the root configuration for limits. 15 type RootLimitsConfig struct { 16 // WriteLimits hold the limits for writing data. 17 WriteLimits WriteLimitsConfig `yaml:"write"` 18 } 19 20 // ParseRootLimitConfig parses the root limit configuration. Even though 21 // the result is a pointer, it will only be nil if an error is returned. 22 func ParseRootLimitConfig(content []byte) (*RootLimitsConfig, error) { 23 var root RootLimitsConfig 24 if err := yaml.UnmarshalStrict(content, &root); err != nil { 25 return nil, errors.Wrapf(err, "parsing config YAML file") 26 } 27 28 if root.WriteLimits.GlobalLimits.MetaMonitoringURL != "" { 29 u, err := url.Parse(root.WriteLimits.GlobalLimits.MetaMonitoringURL) 30 if err != nil { 31 return nil, errors.Wrapf(err, "parsing meta-monitoring URL") 32 } 33 34 // url.Parse might pass a URL with only path, so need to check here for scheme and host. 35 // As per docs: https://pkg.go.dev/net/url#Parse. 36 if u.Host == "" || u.Scheme == "" { 37 return nil, errors.Newf("%s is not a valid meta-monitoring URL (scheme: %s,host: %s)", u, u.Scheme, u.Host) 38 } 39 root.WriteLimits.GlobalLimits.metaMonitoringURL = u 40 } 41 42 // Set default query if none specified. 43 if root.WriteLimits.GlobalLimits.MetaMonitoringLimitQuery == "" { 44 root.WriteLimits.GlobalLimits.MetaMonitoringLimitQuery = "sum(prometheus_tsdb_head_series) by (tenant)" 45 } 46 47 return &root, nil 48 } 49 50 func (r RootLimitsConfig) AreHeadSeriesLimitsConfigured() bool { 51 return r.WriteLimits.GlobalLimits.MetaMonitoringURL != "" && (len(r.WriteLimits.TenantsLimits) != 0 || r.WriteLimits.DefaultLimits.HeadSeriesLimit != 0) 52 } 53 54 type WriteLimitsConfig struct { 55 // GlobalLimits are limits that are shared across all tenants. 56 GlobalLimits GlobalLimitsConfig `yaml:"global"` 57 // DefaultLimits are the default limits for tenants without specified limits. 58 DefaultLimits DefaultLimitsConfig `yaml:"default"` 59 // TenantsLimits are the limits per tenant. 60 TenantsLimits TenantsWriteLimitsConfig `yaml:"tenants"` 61 } 62 63 type GlobalLimitsConfig struct { 64 // MaxConcurrency represents the maximum concurrency during write operations. 65 MaxConcurrency int64 `yaml:"max_concurrency"` 66 // MetaMonitoring options specify the query, url and client for Query API address used in head series limiting. 67 MetaMonitoringURL string `yaml:"meta_monitoring_url"` 68 MetaMonitoringHTTPClient *httpconfig.ClientConfig `yaml:"meta_monitoring_http_client"` 69 MetaMonitoringLimitQuery string `yaml:"meta_monitoring_limit_query"` 70 71 metaMonitoringURL *url.URL 72 } 73 74 type DefaultLimitsConfig struct { 75 // RequestLimits holds the difficult per-request limits. 76 RequestLimits requestLimitsConfig `yaml:"request"` 77 // HeadSeriesLimit specifies the maximum number of head series allowed for any tenant. 78 HeadSeriesLimit uint64 `yaml:"head_series_limit"` 79 } 80 81 // TenantsWriteLimitsConfig is a map of tenant IDs to their *WriteLimitConfig. 82 type TenantsWriteLimitsConfig map[string]*WriteLimitConfig 83 84 // A tenant might not always have limits configured, so things here must 85 // use pointers. 86 type WriteLimitConfig struct { 87 // RequestLimits holds the difficult per-request limits. 88 RequestLimits *requestLimitsConfig `yaml:"request"` 89 // HeadSeriesLimit specifies the maximum number of head series allowed for a tenant. 90 HeadSeriesLimit *uint64 `yaml:"head_series_limit"` 91 } 92 93 // Utils for initializing. 94 func NewEmptyWriteLimitConfig() *WriteLimitConfig { 95 return &WriteLimitConfig{} 96 } 97 98 func (w *WriteLimitConfig) SetRequestLimits(rl *requestLimitsConfig) *WriteLimitConfig { 99 w.RequestLimits = rl 100 return w 101 } 102 103 func (w *WriteLimitConfig) SetHeadSeriesLimit(val uint64) *WriteLimitConfig { 104 w.HeadSeriesLimit = &val 105 return w 106 } 107 108 type requestLimitsConfig struct { 109 SizeBytesLimit *int64 `yaml:"size_bytes_limit"` 110 SeriesLimit *int64 `yaml:"series_limit"` 111 SamplesLimit *int64 `yaml:"samples_limit"` 112 } 113 114 func NewEmptyRequestLimitsConfig() *requestLimitsConfig { 115 return &requestLimitsConfig{} 116 } 117 118 func (rl *requestLimitsConfig) SetSizeBytesLimit(value int64) *requestLimitsConfig { 119 rl.SizeBytesLimit = &value 120 return rl 121 } 122 123 func (rl *requestLimitsConfig) SetSeriesLimit(value int64) *requestLimitsConfig { 124 rl.SeriesLimit = &value 125 return rl 126 } 127 128 func (rl *requestLimitsConfig) SetSamplesLimit(value int64) *requestLimitsConfig { 129 rl.SamplesLimit = &value 130 return rl 131 } 132 133 // OverlayWith overlays the current configuration with another one. This means 134 // that limit values that are not set (have a nil value) will be overwritten in 135 // the caller. 136 func (rl *requestLimitsConfig) OverlayWith(other *requestLimitsConfig) *requestLimitsConfig { 137 if rl.SamplesLimit == nil { 138 rl.SamplesLimit = other.SamplesLimit 139 } 140 if rl.SeriesLimit == nil { 141 rl.SeriesLimit = other.SeriesLimit 142 } 143 if rl.SizeBytesLimit == nil { 144 rl.SizeBytesLimit = other.SizeBytesLimit 145 } 146 return rl 147 }