github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/cmd/services/m3query/config/config.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package config
    22  
    23  import (
    24  	"errors"
    25  	"math"
    26  	"time"
    27  
    28  	etcdclient "github.com/m3db/m3/src/cluster/client/etcd"
    29  	"github.com/m3db/m3/src/cluster/placement"
    30  	"github.com/m3db/m3/src/cmd/services/m3coordinator/downsample"
    31  	ingestm3msg "github.com/m3db/m3/src/cmd/services/m3coordinator/ingest/m3msg"
    32  	"github.com/m3db/m3/src/cmd/services/m3coordinator/server/m3msg"
    33  	"github.com/m3db/m3/src/metrics/aggregation"
    34  	"github.com/m3db/m3/src/query/api/v1/handler/prometheus/handleroptions"
    35  	"github.com/m3db/m3/src/query/graphite/graphite"
    36  	"github.com/m3db/m3/src/query/models"
    37  	"github.com/m3db/m3/src/query/storage"
    38  	"github.com/m3db/m3/src/query/storage/m3"
    39  	"github.com/m3db/m3/src/query/storage/m3/consolidators"
    40  	"github.com/m3db/m3/src/query/storage/m3/storagemetadata"
    41  	xconfig "github.com/m3db/m3/src/x/config"
    42  	"github.com/m3db/m3/src/x/debug/config"
    43  	"github.com/m3db/m3/src/x/instrument"
    44  	xlog "github.com/m3db/m3/src/x/log"
    45  	"github.com/m3db/m3/src/x/opentracing"
    46  	xtime "github.com/m3db/m3/src/x/time"
    47  )
    48  
    49  // BackendStorageType is an enum for different backends.
    50  type BackendStorageType string
    51  
    52  const (
    53  	// GRPCStorageType is for backends which only support grpc endpoints.
    54  	GRPCStorageType BackendStorageType = "grpc"
    55  	// M3DBStorageType is for m3db backend.
    56  	M3DBStorageType BackendStorageType = "m3db"
    57  	// NoopEtcdStorageType is for a noop backend which returns empty results for
    58  	// any query and blackholes any writes, but requires that a valid etcd cluster
    59  	// is defined and can be connected to. Primarily used for standalone
    60  	// coordinators used only to serve m3admin APIs.
    61  	NoopEtcdStorageType BackendStorageType = "noop-etcd"
    62  
    63  	// PromRemoteStorageType is a type of storage that is backed by Prometheus Remote Write compatible API.
    64  	PromRemoteStorageType BackendStorageType = "prom-remote"
    65  
    66  	defaultListenAddress = "0.0.0.0:7201"
    67  
    68  	defaultCarbonIngesterListenAddress = "0.0.0.0:7204"
    69  
    70  	defaultQueryTimeout = 30 * time.Second
    71  
    72  	defaultPrometheusMaxSamplesPerQuery = 100000000
    73  )
    74  
    75  var (
    76  	defaultLogging = xlog.Configuration{
    77  		Level: "info",
    78  	}
    79  	defaultMetricsSanitization        = instrument.PrometheusMetricSanitization
    80  	defaultMetricsExtendedMetricsType = instrument.NoExtendedMetrics
    81  	defaultMetrics                    = instrument.MetricsConfiguration{
    82  		RootScope: &instrument.ScopeConfiguration{
    83  			Prefix: "coordinator",
    84  		},
    85  		PrometheusReporter: &instrument.PrometheusConfiguration{
    86  			HandlerPath: "/metrics",
    87  			// Default to coordinator (until https://github.com/m3db/m3/issues/682 is resolved)
    88  			ListenAddress: "0.0.0.0:7203",
    89  		},
    90  		Sanitization:    &defaultMetricsSanitization,
    91  		SamplingRate:    1.0,
    92  		ExtendedMetrics: &defaultMetricsExtendedMetricsType,
    93  	}
    94  
    95  	// 5m is the default lookback in Prometheus.
    96  	defaultLookbackDuration = 5 * time.Minute
    97  
    98  	defaultCarbonIngesterAggregationType = aggregation.Mean
    99  
   100  	// By default, cap total series to prevent results of
   101  	// extremely large sizes consuming too much memory.
   102  	defaultStorageQuerySeriesLimit = 100_000
   103  	defaultStorageQueryDocsLimit   = 0 // Default OFF.
   104  
   105  	// By default, raise errors instead of truncating results so
   106  	// users do not experience see unexpected results.
   107  	defaultRequireExhaustive = true
   108  
   109  	defaultWriteWorkerPool = xconfig.WorkerPoolPolicy{
   110  		GrowOnDemand:          true,
   111  		Size:                  4096,
   112  		KillWorkerProbability: 0.001,
   113  	}
   114  
   115  	// By default, return up to 4 metric metadata stats per request.
   116  	defaultMaxMetricMetadataStats = 4
   117  )
   118  
   119  // Configuration is the configuration for the query service.
   120  type Configuration struct {
   121  	// Metrics configuration.
   122  	Metrics *instrument.MetricsConfiguration `yaml:"metrics"`
   123  
   124  	// Logging configuration.
   125  	Logging *xlog.Configuration `yaml:"logging"`
   126  
   127  	// Tracing configures opentracing. If not provided, tracing is disabled.
   128  	Tracing opentracing.TracingConfiguration `yaml:"tracing"`
   129  
   130  	// Clusters is the DB cluster configurations for read, write and
   131  	// query endpoints.
   132  	Clusters m3.ClustersStaticConfiguration `yaml:"clusters"`
   133  
   134  	// LocalConfiguration is the local embedded configuration if running
   135  	// coordinator embedded in the DB.
   136  	Local *LocalConfiguration `yaml:"local"`
   137  
   138  	// ClusterManagement for placement, namespaces and database management
   139  	// endpoints.
   140  	ClusterManagement ClusterManagementConfiguration `yaml:"clusterManagement"`
   141  
   142  	// PrometheusRemoteBackend configures prometheus remote write backend.
   143  	// Used only when backend property is "prom-remote"
   144  	PrometheusRemoteBackend *PrometheusRemoteBackendConfiguration `yaml:"prometheusRemoteBackend"`
   145  
   146  	// ListenAddress is the server listen address.
   147  	ListenAddress *string `yaml:"listenAddress"`
   148  
   149  	// Filter is the read/write/complete tags filter configuration.
   150  	Filter FilterConfiguration `yaml:"filter"`
   151  
   152  	// RPC is the RPC configuration.
   153  	RPC *RPCConfiguration `yaml:"rpc"`
   154  
   155  	// HTTP is the HTTP configuration.
   156  	HTTP HTTPConfiguration `yaml:"http"`
   157  
   158  	// Backend is the backend store for query service.
   159  	Backend BackendStorageType `yaml:"backend"`
   160  
   161  	// TagOptions is the tag configuration options.
   162  	TagOptions TagOptionsConfiguration `yaml:"tagOptions"`
   163  
   164  	// ReadWorkerPool is the worker pool policy for read requests.
   165  	ReadWorkerPool xconfig.WorkerPoolPolicy `yaml:"readWorkerPoolPolicy"`
   166  
   167  	// WriteWorkerPool is the worker pool policy for write requests.
   168  	WriteWorkerPool *xconfig.WorkerPoolPolicy `yaml:"writeWorkerPoolPolicy"`
   169  
   170  	// WriteForwarding is the write forwarding options.
   171  	WriteForwarding WriteForwardingConfiguration `yaml:"writeForwarding"`
   172  
   173  	// Downsample configures how the metrics should be downsampled.
   174  	Downsample downsample.Configuration `yaml:"downsample"`
   175  
   176  	// Ingest is the ingest server.
   177  	Ingest *IngestConfiguration `yaml:"ingest"`
   178  
   179  	// Carbon is the carbon configuration.
   180  	Carbon *CarbonConfiguration `yaml:"carbon"`
   181  
   182  	// Middleware is middleware-specific configuration.
   183  	Middleware MiddlewareConfiguration `yaml:"middleware"`
   184  
   185  	// Query is the query configuration.
   186  	Query QueryConfiguration `yaml:"query"`
   187  
   188  	// Limits specifies limits on per-query resource usage.
   189  	Limits LimitsConfiguration `yaml:"limits"`
   190  
   191  	// LookbackDuration determines the lookback duration for queries
   192  	LookbackDuration *time.Duration `yaml:"lookbackDuration"`
   193  
   194  	// ResultOptions are the results options for query.
   195  	ResultOptions ResultOptions `yaml:"resultOptions"`
   196  
   197  	// DeprecatedExperimental is the configuration for the experimental API group. It is not used anymore
   198  	// and only kept for backwards-support with older configuration files.
   199  	DeprecatedExperimental ExperimentalAPIConfiguration `yaml:"experimental"`
   200  
   201  	// StoreMetricsType controls if metrics type is stored or not.
   202  	StoreMetricsType *bool `yaml:"storeMetricsType"`
   203  
   204  	// MultiProcess is the multi-process configuration.
   205  	MultiProcess MultiProcessConfiguration `yaml:"multiProcess"`
   206  
   207  	// Debug configuration.
   208  	Debug config.DebugConfiguration `yaml:"debug"`
   209  }
   210  
   211  // ListenAddressOrDefault returns the listen address or default.
   212  func (c *Configuration) ListenAddressOrDefault() string {
   213  	if c.ListenAddress != nil {
   214  		return *c.ListenAddress
   215  	}
   216  
   217  	return defaultListenAddress
   218  }
   219  
   220  // LoggingOrDefault returns the logging config or default.
   221  func (c *Configuration) LoggingOrDefault() xlog.Configuration {
   222  	if c.Logging != nil {
   223  		return *c.Logging
   224  	}
   225  
   226  	return defaultLogging
   227  }
   228  
   229  // MetricsOrDefault returns the metrics config or default.
   230  func (c *Configuration) MetricsOrDefault() *instrument.MetricsConfiguration {
   231  	if c.Metrics != nil {
   232  		return c.Metrics
   233  	}
   234  
   235  	return &defaultMetrics
   236  }
   237  
   238  // WriteWorkerPoolOrDefault returns the write worker pool config or default.
   239  func (c *Configuration) WriteWorkerPoolOrDefault() xconfig.WorkerPoolPolicy {
   240  	if c.WriteWorkerPool != nil {
   241  		return *c.WriteWorkerPool
   242  	}
   243  
   244  	return defaultWriteWorkerPool
   245  }
   246  
   247  // WriteForwardingConfiguration is the write forwarding configuration.
   248  type WriteForwardingConfiguration struct {
   249  	PromRemoteWrite handleroptions.PromWriteHandlerForwardingOptions `yaml:"promRemoteWrite"`
   250  }
   251  
   252  // Filter is a query filter type.
   253  type Filter string
   254  
   255  const (
   256  	// FilterLocalOnly is a filter that specifies local only storage should be used.
   257  	FilterLocalOnly Filter = "local_only"
   258  	// FilterRemoteOnly is a filter that specifies remote only storage should be used.
   259  	FilterRemoteOnly Filter = "remote_only"
   260  	// FilterAllowAll is a filter that specifies all storages should be used.
   261  	FilterAllowAll Filter = "allow_all"
   262  	// FilterAllowNone is a filter that specifies no storages should be used.
   263  	FilterAllowNone Filter = "allow_none"
   264  )
   265  
   266  // FilterConfiguration is the filters for write/read/complete tags storage filters.
   267  type FilterConfiguration struct {
   268  	Read         Filter `yaml:"read"`
   269  	Write        Filter `yaml:"write"`
   270  	CompleteTags Filter `yaml:"completeTags"`
   271  }
   272  
   273  // ResultOptions are the result options for query.
   274  type ResultOptions struct {
   275  	// KeepNaNs keeps NaNs before returning query results.
   276  	// The default is false, which matches Prometheus
   277  	KeepNaNs bool `yaml:"keepNans"`
   278  }
   279  
   280  // QueryConfiguration is the query configuration.
   281  type QueryConfiguration struct {
   282  	// Timeout is the query timeout.
   283  	Timeout *time.Duration `yaml:"timeout"`
   284  	// DefaultEngine is the default query engine.
   285  	DefaultEngine string `yaml:"defaultEngine"`
   286  	// ConsolidationConfiguration are configs for consolidating fetched queries.
   287  	ConsolidationConfiguration ConsolidationConfiguration `yaml:"consolidation"`
   288  	// Prometheus is prometheus client configuration.
   289  	Prometheus PrometheusQueryConfiguration `yaml:"prometheus"`
   290  	// RestrictTags is an optional configuration that can be set to restrict
   291  	// all queries with certain tags by.
   292  	RestrictTags *RestrictTagsConfiguration `yaml:"restrictTags"`
   293  	// RequireLabelsEndpointStartEndTime requires requests to /label(s) endpoints
   294  	// to specify a start and end time to prevent unbounded queries.
   295  	RequireLabelsEndpointStartEndTime bool `yaml:"requireLabelsEndpointStartEndTime"`
   296  	// RequireSeriesEndpointStartEndTime requires requests to /series endpoint
   297  	// to specify a start and end time to prevent unbounded queries.
   298  	RequireSeriesEndpointStartEndTime bool `yaml:"requireSeriesEndpointStartEndTime"`
   299  }
   300  
   301  // TimeoutOrDefault returns the configured timeout or default value.
   302  func (c QueryConfiguration) TimeoutOrDefault() time.Duration {
   303  	if v := c.Timeout; v != nil {
   304  		return *v
   305  	}
   306  	return defaultQueryTimeout
   307  }
   308  
   309  // RestrictTagsAsStorageRestrictByTag returns restrict tags as
   310  // storage options to restrict all queries by default.
   311  func (c QueryConfiguration) RestrictTagsAsStorageRestrictByTag() (*storage.RestrictByTag, bool, error) {
   312  	if c.RestrictTags == nil {
   313  		return nil, false, nil
   314  	}
   315  
   316  	var (
   317  		cfg    = *c.RestrictTags
   318  		result = handleroptions.StringTagOptions{
   319  			Restrict: make([]handleroptions.StringMatch, 0, len(cfg.Restrict)),
   320  			Strip:    cfg.Strip,
   321  		}
   322  	)
   323  	for _, elem := range cfg.Restrict {
   324  		value := handleroptions.StringMatch(elem)
   325  		result.Restrict = append(result.Restrict, value)
   326  	}
   327  
   328  	opts, err := result.StorageOptions()
   329  	if err != nil {
   330  		return nil, false, err
   331  	}
   332  
   333  	return opts, true, nil
   334  }
   335  
   336  // RestrictTagsConfiguration applies tag restriction to all queries.
   337  type RestrictTagsConfiguration struct {
   338  	Restrict []StringMatch `yaml:"match"`
   339  	Strip    []string      `yaml:"strip"`
   340  }
   341  
   342  // StringMatch is an easy to use representation of models.Matcher.
   343  type StringMatch struct {
   344  	Name  string `yaml:"name"`
   345  	Type  string `yaml:"type"`
   346  	Value string `yaml:"value"`
   347  }
   348  
   349  // ConsolidationConfiguration are configs for consolidating fetched queries.
   350  type ConsolidationConfiguration struct {
   351  	// MatchType determines the options by which series should match.
   352  	MatchType consolidators.MatchType `yaml:"matchType"`
   353  }
   354  
   355  // PrometheusQueryConfiguration is the prometheus query engine configuration.
   356  type PrometheusQueryConfiguration struct {
   357  	// MaxSamplesPerQuery is the limit on fetched samples per query.
   358  	MaxSamplesPerQuery *int `yaml:"maxSamplesPerQuery"`
   359  
   360  	// Convert configures Prometheus time series conversions.
   361  	Convert *PrometheusConvertConfiguration `yaml:"convert"`
   362  }
   363  
   364  // ConvertOptionsOrDefault creates storage.PromConvertOptions based on the given configuration.
   365  func (c PrometheusQueryConfiguration) ConvertOptionsOrDefault() storage.PromConvertOptions {
   366  	opts := storage.NewPromConvertOptions()
   367  
   368  	if v := c.Convert; v != nil {
   369  		if value := v.ResolutionThresholdForCounterNormalization; value != nil {
   370  			opts = opts.SetResolutionThresholdForCounterNormalization(*value)
   371  		}
   372  
   373  		opts = opts.SetValueDecreaseTolerance(v.ValueDecreaseTolerance)
   374  
   375  		// Default to max time so that it's always applicable if value
   376  		// decrease tolerance is non-zero.
   377  		toleranceUntil := xtime.UnixNano(math.MaxInt64)
   378  		if value := v.ValueDecreaseToleranceUntil; value != nil {
   379  			toleranceUntil = xtime.ToUnixNano(*value)
   380  		}
   381  		opts = opts.SetValueDecreaseToleranceUntil(toleranceUntil)
   382  	}
   383  
   384  	return opts
   385  }
   386  
   387  // PrometheusConvertConfiguration configures Prometheus time series conversions.
   388  type PrometheusConvertConfiguration struct {
   389  	// ResolutionThresholdForCounterNormalization sets the resolution threshold starting from which
   390  	// Prometheus counter normalization is performed in order to avoid Prometheus counter
   391  	// extrapolation artifacts.
   392  	ResolutionThresholdForCounterNormalization *time.Duration `yaml:"resolutionThresholdForCounterNormalization"`
   393  
   394  	// ValueDecreaseTolerance allows for setting a specific amount of tolerance
   395  	// to avoid returning a decrease if it's below a certain tolerance.
   396  	// This is useful for applications that have precision issues emitting
   397  	// monotonic increasing data and will accidentally make it seem like the
   398  	// counter value decreases when it hasn't changed.
   399  	ValueDecreaseTolerance float64 `yaml:"valueDecreaseTolerance"`
   400  
   401  	// ValueDecreaseToleranceUntil allows for setting a time threshold on
   402  	// which to apply the conditional value decrease threshold.
   403  	ValueDecreaseToleranceUntil *time.Time `yaml:"valueDecreaseToleranceUntil"`
   404  }
   405  
   406  // MaxSamplesPerQueryOrDefault returns the max samples per query or default.
   407  func (c PrometheusQueryConfiguration) MaxSamplesPerQueryOrDefault() int {
   408  	if v := c.MaxSamplesPerQuery; v != nil {
   409  		return *v
   410  	}
   411  
   412  	return defaultPrometheusMaxSamplesPerQuery
   413  }
   414  
   415  // LimitsConfiguration represents limitations on resource usage in the query
   416  // instance. Limits are split between per-query and global limits.
   417  type LimitsConfiguration struct {
   418  	// PerQuery configures limits which apply to each query individually.
   419  	PerQuery PerQueryLimitsConfiguration `yaml:"perQuery"`
   420  }
   421  
   422  // PerQueryLimitsConfiguration represents limits on resource usage within a
   423  // single query. Zero or negative values imply no limit.
   424  type PerQueryLimitsConfiguration struct {
   425  	// MaxFetchedSeries limits the number of time series returned for any given
   426  	// individual storage node per query, before returning result to query
   427  	// service.
   428  	MaxFetchedSeries int `yaml:"maxFetchedSeries"`
   429  
   430  	// InstanceMultiple increases the per database instance series limit.
   431  	// The series limit per database instance is calculated as:
   432  	//
   433  	// InstanceSeriesLimit = MaxFetchesSeries / (instances per replica) * InstanceMultiple.
   434  	//
   435  	// A value > 1 allows a buffer in case data is not uniformly sharded across instances in a replica.
   436  	// If set to 0 the feature is disabled and the MaxFetchedSeries is used as the limit for database instance.
   437  	// For large clusters, enabling this feature can dramatically decrease the amount of wasted series read from a
   438  	// single database instance.
   439  	InstanceMultiple float32 `yaml:"instanceMultiple"`
   440  
   441  	// MaxFetchedDocs limits the number of index documents matched for any given
   442  	// individual storage node per query, before returning result to query
   443  	// service.
   444  	MaxFetchedDocs int `yaml:"maxFetchedDocs"`
   445  
   446  	// MaxFetchedRange limits the time range of index documents matched for any given
   447  	// individual storage node per query, before returning result to query
   448  	// service.
   449  	MaxFetchedRange time.Duration `yaml:"maxFetchedRange"`
   450  
   451  	// RequireExhaustive results in an error if the query exceeds any limit.
   452  	RequireExhaustive *bool `yaml:"requireExhaustive"`
   453  
   454  	// MaxMetricMetadataStats limits the number of metric metadata stats to return
   455  	// as a response header after a query. If unset, defaults to 4. If set to zero,
   456  	// no metric metadata stats will be returned as a response header.
   457  	MaxMetricMetadataStats *int `yaml:"maxMetricMetadataStats"`
   458  }
   459  
   460  // AsFetchOptionsBuilderLimitsOptions converts this configuration to
   461  // handleroptions.FetchOptionsBuilderLimitsOptions.
   462  func (l *PerQueryLimitsConfiguration) AsFetchOptionsBuilderLimitsOptions() handleroptions.FetchOptionsBuilderLimitsOptions {
   463  	seriesLimit := defaultStorageQuerySeriesLimit
   464  	if v := l.MaxFetchedSeries; v > 0 {
   465  		seriesLimit = v
   466  	}
   467  
   468  	docsLimit := defaultStorageQueryDocsLimit
   469  	if v := l.MaxFetchedDocs; v > 0 {
   470  		docsLimit = v
   471  	}
   472  
   473  	requireExhaustive := defaultRequireExhaustive
   474  	if r := l.RequireExhaustive; r != nil {
   475  		requireExhaustive = *r
   476  	}
   477  
   478  	maxMetricMetadataStats := defaultMaxMetricMetadataStats
   479  	if v := l.MaxMetricMetadataStats; v != nil {
   480  		maxMetricMetadataStats = *v
   481  	}
   482  
   483  	return handleroptions.FetchOptionsBuilderLimitsOptions{
   484  		SeriesLimit:            seriesLimit,
   485  		InstanceMultiple:       l.InstanceMultiple,
   486  		DocsLimit:              docsLimit,
   487  		RangeLimit:             l.MaxFetchedRange,
   488  		RequireExhaustive:      requireExhaustive,
   489  		MaxMetricMetadataStats: maxMetricMetadataStats,
   490  	}
   491  }
   492  
   493  // IngestConfiguration is the configuration for ingestion server.
   494  type IngestConfiguration struct {
   495  	// Ingester is the configuration for storage based ingester.
   496  	Ingester ingestm3msg.Configuration `yaml:"ingester"`
   497  
   498  	// M3Msg is the configuration for m3msg server.
   499  	M3Msg m3msg.Configuration `yaml:"m3msg"`
   500  }
   501  
   502  // CarbonConfiguration is the configuration for the carbon server.
   503  type CarbonConfiguration struct {
   504  	// Ingester if set defines an ingester to run for carbon.
   505  	Ingester *CarbonIngesterConfiguration `yaml:"ingester"`
   506  	// LimitsFind sets the limits configuration for find queries.
   507  	LimitsFind *LimitsConfiguration `yaml:"limitsFind"`
   508  	// LimitsRender sets the limits configuration for render queries.
   509  	LimitsRender *LimitsConfiguration `yaml:"limitsRender"`
   510  	// AggregateNamespacesAllData configures whether all aggregate
   511  	// namespaces contain entire copies of the data set.
   512  	// This affects whether queries can be optimized or not, if false
   513  	// they cannot be since it's unclear if data matching an expression
   514  	// sits in one or many or none of the aggregate namespaces so all
   515  	// must be queried, but if true then it can be determined based
   516  	// on the query range whether a single namespace can fulfill the
   517  	// entire query and if so to only fetch from that one aggregated namespace.
   518  	AggregateNamespacesAllData bool `yaml:"aggregateNamespacesAllData"`
   519  	// ShiftTimeStart sets a constant time to shift start by.
   520  	ShiftTimeStart time.Duration `yaml:"shiftTimeStart"`
   521  	// ShiftTimeEnd sets a constant time to shift end by.
   522  	ShiftTimeEnd time.Duration `yaml:"shiftTimeEnd"`
   523  	// ShiftStepsStart sets a constant set of steps to shift start by.
   524  	ShiftStepsStart int `yaml:"shiftStepsStart"`
   525  	// ShiftStepsEnd sets a constant set of steps to shift end by.
   526  	ShiftStepsEnd int `yaml:"shiftStepsEnd"`
   527  	// ShiftStepsStartWhenAtResolutionBoundary sets a constant set of steps to
   528  	// shift start by if and only if the start is an exact match to the
   529  	// resolution boundary of a query.
   530  	ShiftStepsStartWhenAtResolutionBoundary *int `yaml:"shiftStepsStartWhenAtResolutionBoundary"`
   531  	// ShiftStepsEndWhenAtResolutionBoundary sets a constant set of steps to
   532  	// shift end by if and only if the end is an exact match to the
   533  	// resolution boundary of a query.
   534  	ShiftStepsEndWhenAtResolutionBoundary *int `yaml:"shiftStepsEndWhenAtResolutionBoundary"`
   535  	// ShiftStepsStartWhenEndAtResolutionBoundary sets a constant set of steps to
   536  	// shift start by if and only if the end is an exact match to the resolution boundary
   537  	// of a query AND the start is not an exact match to the resolution boundary.
   538  	ShiftStepsStartWhenEndAtResolutionBoundary *int `yaml:"shiftStepsStartWhenEndAtResolutionBoundary"`
   539  	// ShiftStepsEndWhenStartAtResolutionBoundary sets a constant set of steps to
   540  	// shift end by if and only if the start is an exact match to the resolution boundary
   541  	// of a query AND the end is not an exact match to the resolution boundary.
   542  	ShiftStepsEndWhenStartAtResolutionBoundary *int `yaml:"shiftStepsEndWhenStartAtResolutionBoundary"`
   543  	// RenderPartialStart sets whether to render partial datapoints when
   544  	// the start time is between a datapoint's resolution step size.
   545  	RenderPartialStart bool `yaml:"renderPartialStart"`
   546  	// RenderPartialEnd sets whether to render partial datapoints when
   547  	// the end time is between a datapoint's resolution step size.
   548  	RenderPartialEnd bool `yaml:"renderPartialEnd"`
   549  	// RenderSeriesAllNaNs will render series that have only NaNs for entire
   550  	// output instead of returning an empty array of datapoints.
   551  	RenderSeriesAllNaNs bool `yaml:"renderSeriesAllNaNs"`
   552  	// CompileEscapeAllNotOnlyQuotes will escape all characters when using a backslash
   553  	// in a quoted string rather than just reserving for escaping quotes.
   554  	CompileEscapeAllNotOnlyQuotes bool `yaml:"compileEscapeAllNotOnlyQuotes"`
   555  	// FindResultsIncludeBothExpandableAndLeaf will include both an expandable
   556  	// node and a leaf node if there is a duplicate path node that is both an
   557  	// expandable node and a leaf node.
   558  	FindResultsIncludeBothExpandableAndLeaf bool `yaml:"findResultsIncludeBothExpandableAndLeaf"`
   559  }
   560  
   561  // MiddlewareConfiguration is middleware-specific configuration.
   562  type MiddlewareConfiguration struct {
   563  	// Logging configures the logging middleware.
   564  	Logging LoggingMiddlewareConfiguration `yaml:"logging"`
   565  	// Metrics configures the metrics middleware.
   566  	Metrics MetricsMiddlewareConfiguration `yaml:"metrics"`
   567  	// Prometheus configures prometheus-related middleware.
   568  	Prometheus PrometheusMiddlewareConfiguration `yaml:"prometheus"`
   569  }
   570  
   571  // LoggingMiddlewareConfiguration configures the logging middleware.
   572  type LoggingMiddlewareConfiguration struct {
   573  	// Threshold defines the latency threshold for logging the response. If zero, the default of 1s is used. To disable
   574  	// response logging set Disabled.
   575  	Threshold time.Duration
   576  	// Disabled turns off response logging by default for endpoints.
   577  	Disabled bool
   578  }
   579  
   580  // MetricsMiddlewareConfiguration configures the metrics middleware.
   581  type MetricsMiddlewareConfiguration struct {
   582  	// QueryEndpointsClassification contains the configuration for sizing queries to
   583  	// the query and query_range Prometheus endpoints.
   584  	QueryEndpointsClassification QueryClassificationConfig `yaml:"queryEndpointsClassification"`
   585  	// LabelEndpointsClassification contains the configuration for sizing queries to
   586  	// the label names and label values Prometheus endpoints.
   587  	LabelEndpointsClassification QueryClassificationConfig `yaml:"labelEndpointsClassification"`
   588  	// AddStatusToLatencies will add a tag with the query's response code to
   589  	// middleware latency metrics.
   590  	// NB: Setting this to true will increase cardinality by the number of
   591  	// expected response codes (likely around ~10).
   592  	AddStatusToLatencies bool `yaml:"addStatusToLatencies"`
   593  }
   594  
   595  // QueryClassificationConfig contains the buckets used to group a query into a bucket for
   596  // the sake of understanding the size of the query based on a specific dimension. Currently,
   597  // we have two sets of buckets: results and duration. The results buckets help us understand
   598  // the size of the query based on the number of results returned whereas the duration buckets help
   599  // us understand the size of the query based on the time range of the query. Dimension values are
   600  // rounded down to the nearest bucket. If the value is smaller than all buckets, then it is
   601  // allocated to the first bucket. Buckets are expected to be ordered in ascending order.
   602  type QueryClassificationConfig struct {
   603  	// ResultsBuckets contains the buckets to be compared with the number of results (e.g. number of
   604  	// time series or labels) returned by a specific endpoint.
   605  	ResultsBuckets []int `yaml:"resultsBuckets"`
   606  	// DurationBuckets contains the buckets to be compared with time range of a query for a
   607  	// specific endpoint.
   608  	DurationBuckets []time.Duration `yaml:"durationBuckets"`
   609  }
   610  
   611  // Enabled returns true if classification buckets were specified.
   612  func (q *QueryClassificationConfig) Enabled() bool {
   613  	return len(q.DurationBuckets) > 0 || len(q.ResultsBuckets) > 0
   614  }
   615  
   616  // PrometheusMiddlewareConfiguration configures the range rewriting middleware.
   617  type PrometheusMiddlewareConfiguration struct {
   618  	// ResolutionMultiplier is the multiple that will be applied to the range if it's determined
   619  	// that it needs to be updated. If this value is greater than 0, the range in a query will be
   620  	// updated if the namespaces used to service the request have resolution(s)
   621  	// that are greater than the range. The range will be updated to the largest resolution
   622  	// of the namespaces to service the request * the multiplier specified here. If this multiplier
   623  	// is 0, then this feature is disabled.
   624  	ResolutionMultiplier int `yaml:"resolutionMultiplier"`
   625  }
   626  
   627  // CarbonIngesterConfiguration is the configuration struct for carbon ingestion.
   628  type CarbonIngesterConfiguration struct {
   629  	ListenAddress  string                             `yaml:"listenAddress"`
   630  	MaxConcurrency int                                `yaml:"maxConcurrency"`
   631  	Rewrite        CarbonIngesterRewriteConfiguration `yaml:"rewrite"`
   632  	Rules          []CarbonIngesterRuleConfiguration  `yaml:"rules"`
   633  }
   634  
   635  // CarbonIngesterRewriteConfiguration is the configuration for rewriting
   636  // metrics at ingestion.
   637  type CarbonIngesterRewriteConfiguration struct {
   638  	// Cleanup will perform:
   639  	// - Trailing/leading dot elimination.
   640  	// - Double dot elimination.
   641  	// - Irregular char replacement with underscores (_), currently irregular
   642  	//   is defined as not being in [0-9a-zA-Z-_:#].
   643  	Cleanup bool `yaml:"cleanup"`
   644  }
   645  
   646  // LookbackDurationOrDefault validates the LookbackDuration
   647  func (c Configuration) LookbackDurationOrDefault() (time.Duration, error) {
   648  	if c.LookbackDuration == nil {
   649  		return defaultLookbackDuration, nil
   650  	}
   651  
   652  	v := *c.LookbackDuration
   653  	if v < 0 {
   654  		return 0, errors.New("lookbackDuration must be > 0")
   655  	}
   656  
   657  	return v, nil
   658  }
   659  
   660  // ListenAddressOrDefault returns the specified carbon ingester listen address if provided, or the
   661  // default value if not.
   662  func (c *CarbonIngesterConfiguration) ListenAddressOrDefault() string {
   663  	if c.ListenAddress != "" {
   664  		return c.ListenAddress
   665  	}
   666  
   667  	return defaultCarbonIngesterListenAddress
   668  }
   669  
   670  // RulesOrDefault returns the specified carbon ingester rules if provided, or generates reasonable
   671  // defaults using the provided aggregated namespaces if not.
   672  func (c *CarbonIngesterConfiguration) RulesOrDefault(namespaces m3.ClusterNamespaces) []CarbonIngesterRuleConfiguration {
   673  	if len(c.Rules) > 0 {
   674  		return c.Rules
   675  	}
   676  
   677  	if namespaces.NumAggregatedClusterNamespaces() == 0 {
   678  		return nil
   679  	}
   680  
   681  	// Default to fanning out writes for all metrics to all aggregated namespaces if any exists.
   682  	policies := make([]CarbonIngesterStoragePolicyConfiguration, 0, len(namespaces))
   683  	for _, ns := range namespaces {
   684  		if ns.Options().Attributes().MetricsType == storagemetadata.AggregatedMetricsType {
   685  			policies = append(policies, CarbonIngesterStoragePolicyConfiguration{
   686  				Resolution: ns.Options().Attributes().Resolution,
   687  				Retention:  ns.Options().Attributes().Retention,
   688  			})
   689  		}
   690  	}
   691  
   692  	if len(policies) == 0 {
   693  		return nil
   694  	}
   695  
   696  	// Create a single catch-all rule with a policy for each of the aggregated namespaces we
   697  	// enumerated above.
   698  	aggregationEnabled := true
   699  	return []CarbonIngesterRuleConfiguration{
   700  		{
   701  			Pattern: graphite.MatchAllPattern,
   702  			Aggregation: CarbonIngesterAggregationConfiguration{
   703  				Enabled: &aggregationEnabled,
   704  				Type:    &defaultCarbonIngesterAggregationType,
   705  			},
   706  			Policies: policies,
   707  		},
   708  	}
   709  }
   710  
   711  // CarbonIngesterRuleConfiguration is the configuration struct for a carbon
   712  // ingestion rule.
   713  type CarbonIngesterRuleConfiguration struct {
   714  	Pattern     string                                     `yaml:"pattern"`
   715  	Contains    string                                     `yaml:"contains"`
   716  	Continue    bool                                       `yaml:"continue"`
   717  	Aggregation CarbonIngesterAggregationConfiguration     `yaml:"aggregation"`
   718  	Policies    []CarbonIngesterStoragePolicyConfiguration `yaml:"policies"`
   719  }
   720  
   721  // CarbonIngesterAggregationConfiguration is the configuration struct
   722  // for the aggregation for a carbon ingest rule's storage policy.
   723  type CarbonIngesterAggregationConfiguration struct {
   724  	Enabled *bool             `yaml:"enabled"`
   725  	Type    *aggregation.Type `yaml:"type"`
   726  }
   727  
   728  // EnabledOrDefault returns whether aggregation should be enabled based on the provided configuration,
   729  // or a default value otherwise.
   730  func (c *CarbonIngesterAggregationConfiguration) EnabledOrDefault() bool {
   731  	if c.Enabled != nil {
   732  		return *c.Enabled
   733  	}
   734  
   735  	return true
   736  }
   737  
   738  // TypeOrDefault returns the aggregation type that should be used based on the provided configuration,
   739  // or a default value otherwise.
   740  func (c *CarbonIngesterAggregationConfiguration) TypeOrDefault() aggregation.Type {
   741  	if c.Type != nil {
   742  		return *c.Type
   743  	}
   744  
   745  	return defaultCarbonIngesterAggregationType
   746  }
   747  
   748  // CarbonIngesterStoragePolicyConfiguration is the configuration struct for
   749  // a carbon rule's storage policies.
   750  type CarbonIngesterStoragePolicyConfiguration struct {
   751  	Resolution time.Duration `yaml:"resolution" validate:"nonzero"`
   752  	Retention  time.Duration `yaml:"retention" validate:"nonzero"`
   753  }
   754  
   755  // LocalConfiguration is the local embedded configuration if running
   756  // coordinator embedded in the DB.
   757  type LocalConfiguration struct {
   758  	// Namespaces is the list of namespaces that the local embedded DB has.
   759  	Namespaces []m3.ClusterStaticNamespaceConfiguration `yaml:"namespaces"`
   760  }
   761  
   762  // ClusterManagementConfiguration is configuration for the placement,
   763  // namespaces and database management endpoints (optional).
   764  type ClusterManagementConfiguration struct {
   765  	// Etcd is the client configuration for etcd.
   766  	Etcd *etcdclient.Configuration `yaml:"etcd"`
   767  
   768  	// Placement is the cluster placement configuration.
   769  	Placement placement.Configuration `yaml:"placement"`
   770  }
   771  
   772  // RemoteConfigurations is a set of remote host configurations.
   773  type RemoteConfigurations []RemoteConfiguration
   774  
   775  // RemoteConfiguration is the configuration for a single remote host.
   776  type RemoteConfiguration struct {
   777  	// Name is the name for the remote zone.
   778  	Name string `yaml:"name"`
   779  	// RemoteListenAddresses is the remote listen addresses to call for remote
   780  	// coordinator calls in the remote zone.
   781  	RemoteListenAddresses []string `yaml:"remoteListenAddresses"`
   782  	// ErrorBehavior overrides the default error behavior for this host.
   783  	//
   784  	// NB: defaults to warning on error.
   785  	ErrorBehavior *storage.ErrorBehavior `yaml:"errorBehavior"`
   786  }
   787  
   788  // RPCConfiguration is the RPC configuration for the coordinator for
   789  // the GRPC server used for remote coordinator to coordinator calls.
   790  type RPCConfiguration struct {
   791  	// Enabled determines if coordinator RPC is enabled for remote calls.
   792  	//
   793  	// NB: this is no longer necessary to set to true if RPC is desired; enabled
   794  	// status is inferred based on which other options are provided;
   795  	// this remains for back-compat, and for disabling any existing RPC options.
   796  	Enabled *bool `yaml:"enabled"`
   797  
   798  	// ListenAddress is the RPC server listen address.
   799  	ListenAddress string `yaml:"listenAddress"`
   800  
   801  	// Remotes are the configuration settings for remote coordinator zones.
   802  	Remotes RemoteConfigurations `yaml:"remotes"`
   803  
   804  	// RemoteListenAddresses is the remote listen addresses to call for
   805  	// remote coordinator calls.
   806  	//
   807  	// NB: this is deprecated in favor of using RemoteZones, as setting
   808  	// RemoteListenAddresses will only allow for a single remote zone to be used.
   809  	RemoteListenAddresses []string `yaml:"remoteListenAddresses"`
   810  
   811  	// ErrorBehavior overrides the default error behavior for all rpc hosts.
   812  	//
   813  	// NB: defaults to warning on error.
   814  	ErrorBehavior *storage.ErrorBehavior `yaml:"errorBehavior"`
   815  
   816  	// ReflectionEnabled will enable reflection on the GRPC server, useful
   817  	// for testing connectivity with grpcurl, etc.
   818  	ReflectionEnabled bool `yaml:"reflectionEnabled"`
   819  }
   820  
   821  // PrometheusRemoteBackendConfiguration configures prometheus remote write backend.
   822  type PrometheusRemoteBackendConfiguration struct {
   823  	Endpoints       []PrometheusRemoteBackendEndpointConfiguration `yaml:"endpoints"`
   824  	RequestTimeout  *time.Duration                                 `yaml:"requestTimeout"`
   825  	ConnectTimeout  *time.Duration                                 `yaml:"connectTimeout"`
   826  	KeepAlive       *time.Duration                                 `yaml:"keepAlive"`
   827  	IdleConnTimeout *time.Duration                                 `yaml:"idleConnTimeout"`
   828  	MaxIdleConns    *int                                           `yaml:"maxIdleConns"`
   829  }
   830  
   831  // PrometheusRemoteBackendEndpointConfiguration configures single endpoint.
   832  type PrometheusRemoteBackendEndpointConfiguration struct {
   833  	Name    string `yaml:"name"`
   834  	Address string `yaml:"address"`
   835  	// When nil all unaggregated data will be sent to this endpoint.
   836  	StoragePolicy *PrometheusRemoteBackendStoragePolicyConfiguration `yaml:"storagePolicy"`
   837  }
   838  
   839  // PrometheusRemoteBackendStoragePolicyConfiguration configures storage policy for single endpoint.
   840  type PrometheusRemoteBackendStoragePolicyConfiguration struct {
   841  	Resolution time.Duration `yaml:"resolution" validate:"nonzero"`
   842  	Retention  time.Duration `yaml:"retention" validate:"nonzero"`
   843  
   844  	// Downsample is downsampling options to be used with this storage policy.
   845  	Downsample *m3.DownsampleClusterStaticNamespaceConfiguration `yaml:"downsample"`
   846  }
   847  
   848  // HTTPConfiguration is the HTTP configuration for configuring
   849  // the HTTP server used by the coordinator to serve incoming requests.
   850  type HTTPConfiguration struct {
   851  	// EnableH2C enables support for the HTTP/2 cleartext protocol. H2C
   852  	// enables the use of HTTP/2 without requiring TLS.
   853  	EnableH2C bool `yaml:"enableH2C"`
   854  }
   855  
   856  // TagOptionsConfiguration is the configuration for shared tag options
   857  // Currently only name, but can expand to cover deduplication settings, or other
   858  // relevant options.
   859  type TagOptionsConfiguration struct {
   860  	// MetricName specifies the tag name that corresponds to the metric's name tag
   861  	// If not provided, defaults to `__name__`.
   862  	MetricName string `yaml:"metricName"`
   863  
   864  	// BucketName specifies the tag name that corresponds to the metric's bucket.
   865  	// If not provided, defaults to `le`.
   866  	BucketName string `yaml:"bucketName"`
   867  
   868  	// Scheme determines the default ID generation scheme. Defaults to TypeQuoted.
   869  	Scheme models.IDSchemeType `yaml:"idScheme"`
   870  
   871  	// Filters are optional tag filters, removing all series with tags
   872  	// matching the filter from computations.
   873  	Filters []TagFilter `yaml:"filters"`
   874  
   875  	// AllowTagNameDuplicates allows for duplicate tags to appear on series.
   876  	AllowTagNameDuplicates bool `yaml:"allowTagNameDuplicates"`
   877  
   878  	// AllowTagValueEmpty allows for empty tags to appear on series.
   879  	AllowTagValueEmpty bool `yaml:"allowTagValueEmpty"`
   880  }
   881  
   882  // TagFilter is a tag filter.
   883  type TagFilter struct {
   884  	// Values are the values to filter.
   885  	//
   886  	// NB:If this is unset, all series containing
   887  	// a tag with given `Name` are filtered.
   888  	Values []string `yaml:"values"`
   889  	// Name is the tag name.
   890  	Name string `yaml:"name"`
   891  }
   892  
   893  // TagOptionsFromConfig translates tag option configuration into tag options.
   894  func TagOptionsFromConfig(cfg TagOptionsConfiguration) (models.TagOptions, error) {
   895  	opts := models.NewTagOptions()
   896  	name := cfg.MetricName
   897  	if name != "" {
   898  		opts = opts.SetMetricName([]byte(name))
   899  	}
   900  
   901  	bucket := cfg.BucketName
   902  	if bucket != "" {
   903  		opts = opts.SetBucketName([]byte(bucket))
   904  	}
   905  
   906  	if cfg.Scheme == models.TypeDefault {
   907  		// Default to quoted if unspecified.
   908  		cfg.Scheme = models.TypeQuoted
   909  	}
   910  
   911  	opts = opts.SetIDSchemeType(cfg.Scheme)
   912  	if err := opts.Validate(); err != nil {
   913  		return nil, err
   914  	}
   915  
   916  	if cfg.Filters != nil {
   917  		filters := make([]models.Filter, 0, len(cfg.Filters))
   918  		for _, filter := range cfg.Filters {
   919  			values := make([][]byte, 0, len(filter.Values))
   920  			for _, strVal := range filter.Values {
   921  				values = append(values, []byte(strVal))
   922  			}
   923  
   924  			filters = append(filters, models.Filter{
   925  				Name:   []byte(filter.Name),
   926  				Values: values,
   927  			})
   928  		}
   929  
   930  		opts = opts.SetFilters(filters)
   931  	}
   932  
   933  	opts = opts.SetAllowTagNameDuplicates(cfg.AllowTagNameDuplicates)
   934  	opts = opts.SetAllowTagValueEmpty(cfg.AllowTagValueEmpty)
   935  
   936  	return opts, nil
   937  }
   938  
   939  // ExperimentalAPIConfiguration is the configuration for the experimental API group.
   940  type ExperimentalAPIConfiguration struct {
   941  	Enabled bool `yaml:"enabled"`
   942  }
   943  
   944  // MultiProcessConfiguration is the multi-process configuration which
   945  // allows running multiple sub-processes of an instance reusing the
   946  // same listen ports.
   947  type MultiProcessConfiguration struct {
   948  	// Enabled is whether to enable multi-process execution.
   949  	Enabled bool `yaml:"enabled"`
   950  	// Count is the number of sub-processes to run, leave zero
   951  	// to auto-detect based on number of CPUs.
   952  	Count int `yaml:"count" validate:"min=0"`
   953  	// PerCPU is the factor of processes to run per CPU, leave
   954  	// zero to use the default of 0.5 per CPU (i.e. one process for
   955  	// every two CPUs).
   956  	PerCPU float64 `yaml:"perCPU" validate:"min=0.0, max=1.0"`
   957  	// GoMaxProcs if set will explicitly set the child GOMAXPROCs env var.
   958  	GoMaxProcs int `yaml:"goMaxProcs"`
   959  }