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  }