github.com/m3db/m3@v1.5.0/src/cmd/services/m3coordinator/downsample/options.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 downsample
    22  
    23  import (
    24  	"bytes"
    25  	"errors"
    26  	"fmt"
    27  	"runtime"
    28  	"time"
    29  
    30  	"github.com/prometheus/common/model"
    31  
    32  	"github.com/m3db/m3/src/aggregator/aggregator"
    33  	"github.com/m3db/m3/src/aggregator/aggregator/handler"
    34  	"github.com/m3db/m3/src/aggregator/client"
    35  	clusterclient "github.com/m3db/m3/src/cluster/client"
    36  	"github.com/m3db/m3/src/cluster/kv"
    37  	"github.com/m3db/m3/src/cluster/kv/mem"
    38  	"github.com/m3db/m3/src/cluster/placement"
    39  	placementservice "github.com/m3db/m3/src/cluster/placement/service"
    40  	placementstorage "github.com/m3db/m3/src/cluster/placement/storage"
    41  	"github.com/m3db/m3/src/cluster/services"
    42  	"github.com/m3db/m3/src/metrics/aggregation"
    43  	"github.com/m3db/m3/src/metrics/filters"
    44  	"github.com/m3db/m3/src/metrics/generated/proto/aggregationpb"
    45  	"github.com/m3db/m3/src/metrics/generated/proto/pipelinepb"
    46  	"github.com/m3db/m3/src/metrics/generated/proto/rulepb"
    47  	"github.com/m3db/m3/src/metrics/generated/proto/transformationpb"
    48  	"github.com/m3db/m3/src/metrics/matcher"
    49  	"github.com/m3db/m3/src/metrics/matcher/cache"
    50  	"github.com/m3db/m3/src/metrics/matcher/namespace"
    51  	"github.com/m3db/m3/src/metrics/metadata"
    52  	"github.com/m3db/m3/src/metrics/metric"
    53  	"github.com/m3db/m3/src/metrics/metric/aggregated"
    54  	"github.com/m3db/m3/src/metrics/metric/id"
    55  	"github.com/m3db/m3/src/metrics/metric/unaggregated"
    56  	"github.com/m3db/m3/src/metrics/pipeline"
    57  	"github.com/m3db/m3/src/metrics/policy"
    58  	"github.com/m3db/m3/src/metrics/rules"
    59  	ruleskv "github.com/m3db/m3/src/metrics/rules/store/kv"
    60  	"github.com/m3db/m3/src/metrics/rules/view"
    61  	"github.com/m3db/m3/src/metrics/transformation"
    62  	"github.com/m3db/m3/src/query/models"
    63  	"github.com/m3db/m3/src/query/storage"
    64  	"github.com/m3db/m3/src/query/storage/m3"
    65  	"github.com/m3db/m3/src/query/storage/m3/storagemetadata"
    66  	"github.com/m3db/m3/src/x/clock"
    67  	"github.com/m3db/m3/src/x/ident"
    68  	"github.com/m3db/m3/src/x/instrument"
    69  	xio "github.com/m3db/m3/src/x/io"
    70  	"github.com/m3db/m3/src/x/pool"
    71  	"github.com/m3db/m3/src/x/serialize"
    72  	xsync "github.com/m3db/m3/src/x/sync"
    73  	xtime "github.com/m3db/m3/src/x/time"
    74  
    75  	"github.com/pborman/uuid"
    76  )
    77  
    78  const (
    79  	instanceID                     = "downsampler_local"
    80  	placementKVKey                 = "/placement"
    81  	defaultConfigInMemoryNamespace = "default"
    82  	replicationFactor              = 1
    83  	defaultStorageFlushConcurrency = 20000
    84  	defaultOpenTimeout             = 10 * time.Second
    85  	defaultBufferFutureTimedMetric = time.Minute
    86  	defaultVerboseErrors           = true
    87  	// defaultMatcherCacheCapacity sets the default matcher cache
    88  	// capacity to zero so that the cache is turned off.
    89  	// This is due to discovering that there is a lot of contention
    90  	// used by the cache and the fact that most coordinators are used
    91  	// in a stateless manner with a central deployment which in turn
    92  	// leads to an extremely low cache hit ratio anyway.
    93  	defaultMatcherCacheCapacity = 0
    94  )
    95  
    96  var (
    97  	defaultMetricNameTagName    = []byte(model.MetricNameLabel)
    98  	numShards                   = runtime.GOMAXPROCS(0)
    99  	defaultNamespaceTag         = metric.M3MetricsPrefixString + "_namespace__"
   100  	defaultFilterOutTagPrefixes = [][]byte{
   101  		metric.M3MetricsPrefix,
   102  	}
   103  
   104  	errNoStorage                    = errors.New("downsampling enabled with storage not set")
   105  	errNoClusterClient              = errors.New("downsampling enabled with cluster client not set")
   106  	errNoRulesStore                 = errors.New("downsampling enabled with rules store not set")
   107  	errNoClockOptions               = errors.New("downsampling enabled with clock options not set")
   108  	errNoInstrumentOptions          = errors.New("downsampling enabled with instrument options not set")
   109  	errNoTagEncoderOptions          = errors.New("downsampling enabled with tag encoder options not set")
   110  	errNoTagDecoderOptions          = errors.New("downsampling enabled with tag decoder options not set")
   111  	errNoTagEncoderPoolOptions      = errors.New("downsampling enabled with tag encoder pool options not set")
   112  	errNoTagDecoderPoolOptions      = errors.New("downsampling enabled with tag decoder pool options not set")
   113  	errNoMetricsAppenderPoolOptions = errors.New("downsampling enabled with metrics appender pool options not set")
   114  	errRollupRuleNoTransforms       = errors.New("rollup rule has no transforms set")
   115  )
   116  
   117  // CustomRuleStoreFn is a function to swap the backend used for the rule stores.
   118  type CustomRuleStoreFn func(clusterclient.Client, instrument.Options) (kv.TxnStore, error)
   119  
   120  // DownsamplerOptions is a set of required downsampler options.
   121  type DownsamplerOptions struct {
   122  	Storage                    storage.Appender
   123  	StorageFlushConcurrency    int
   124  	ClusterClient              clusterclient.Client
   125  	RulesKVStore               kv.Store
   126  	ClusterNamespacesWatcher   m3.ClusterNamespacesWatcher
   127  	NameTag                    string
   128  	ClockOptions               clock.Options
   129  	InstrumentOptions          instrument.Options
   130  	TagEncoderOptions          serialize.TagEncoderOptions
   131  	TagDecoderOptions          serialize.TagDecoderOptions
   132  	TagEncoderPoolOptions      pool.ObjectPoolOptions
   133  	TagDecoderPoolOptions      pool.ObjectPoolOptions
   134  	OpenTimeout                time.Duration
   135  	TagOptions                 models.TagOptions
   136  	MetricsAppenderPoolOptions pool.ObjectPoolOptions
   137  	RWOptions                  xio.Options
   138  	InterruptedCh              <-chan struct{}
   139  }
   140  
   141  // NameTagOrDefault returns the configured name tag or the default if one is not set.
   142  func (o DownsamplerOptions) NameTagOrDefault() []byte {
   143  	if o.NameTag == "" {
   144  		return defaultMetricNameTagName
   145  	}
   146  	return []byte(o.NameTag)
   147  }
   148  
   149  // AutoMappingRule is a mapping rule to apply to metrics.
   150  type AutoMappingRule struct {
   151  	Aggregations []aggregation.Type
   152  	Policies     policy.StoragePolicies
   153  }
   154  
   155  // NewAutoMappingRules generates mapping rules from cluster namespaces.
   156  func NewAutoMappingRules(namespaces []m3.ClusterNamespace) ([]AutoMappingRule, error) {
   157  	autoMappingRules := make([]AutoMappingRule, 0, len(namespaces))
   158  	for _, namespace := range namespaces {
   159  		opts := namespace.Options()
   160  		attrs := opts.Attributes()
   161  		if attrs.MetricsType != storagemetadata.AggregatedMetricsType {
   162  			continue
   163  		}
   164  
   165  		if opts.ReadOnly() {
   166  			continue
   167  		}
   168  
   169  		downsampleOpts, err := opts.DownsampleOptions()
   170  		if err != nil {
   171  			errFmt := "unable to resolve downsample options for namespace: %v"
   172  			return nil, fmt.Errorf(errFmt, namespace.NamespaceID().String())
   173  		}
   174  		if downsampleOpts.All {
   175  			storagePolicy := policy.NewStoragePolicy(attrs.Resolution,
   176  				xtime.Second, attrs.Retention)
   177  			autoMappingRules = append(autoMappingRules, AutoMappingRule{
   178  				// NB(r): By default we will apply just keep all last values
   179  				// since coordinator only uses downsampling with Prometheus
   180  				// remote write endpoint.
   181  				// More rich static configuration mapping rules can be added
   182  				// in the future but they are currently not required.
   183  				Aggregations: []aggregation.Type{aggregation.Last},
   184  				Policies:     policy.StoragePolicies{storagePolicy},
   185  			})
   186  		}
   187  	}
   188  	return autoMappingRules, nil
   189  }
   190  
   191  // StagedMetadatas returns the corresponding staged metadatas for this mapping rule.
   192  func (r AutoMappingRule) StagedMetadatas() (metadata.StagedMetadatas, error) {
   193  	aggID, err := aggregation.CompressTypes(r.Aggregations...)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	return metadata.StagedMetadatas{
   199  		metadata.StagedMetadata{
   200  			Metadata: metadata.Metadata{
   201  				Pipelines: metadata.PipelineMetadatas{
   202  					metadata.PipelineMetadata{
   203  						AggregationID:   aggID,
   204  						StoragePolicies: r.Policies,
   205  					},
   206  				},
   207  			},
   208  		},
   209  	}, nil
   210  }
   211  
   212  // Validate validates the dynamic downsampling options.
   213  func (o DownsamplerOptions) validate() error {
   214  	if o.Storage == nil {
   215  		return errNoStorage
   216  	}
   217  	if o.ClusterClient == nil {
   218  		return errNoClusterClient
   219  	}
   220  	if o.RulesKVStore == nil {
   221  		return errNoRulesStore
   222  	}
   223  	if o.ClockOptions == nil {
   224  		return errNoClockOptions
   225  	}
   226  	if o.InstrumentOptions == nil {
   227  		return errNoInstrumentOptions
   228  	}
   229  	if o.TagEncoderOptions == nil {
   230  		return errNoTagEncoderOptions
   231  	}
   232  	if o.TagDecoderOptions == nil {
   233  		return errNoTagDecoderOptions
   234  	}
   235  	if o.TagEncoderPoolOptions == nil {
   236  		return errNoTagEncoderPoolOptions
   237  	}
   238  	if o.TagDecoderPoolOptions == nil {
   239  		return errNoTagDecoderPoolOptions
   240  	}
   241  	if o.MetricsAppenderPoolOptions == nil {
   242  		return errNoMetricsAppenderPoolOptions
   243  	}
   244  	return nil
   245  }
   246  
   247  // agg will have one of aggregator or clientRemote set, the
   248  // rest of the fields must not be nil.
   249  type agg struct {
   250  	aggregator   aggregator.Aggregator
   251  	clientRemote client.Client
   252  
   253  	clockOpts      clock.Options
   254  	matcher        matcher.Matcher
   255  	pools          aggPools
   256  	untimedRollups bool
   257  }
   258  
   259  // Configuration configurates a downsampler.
   260  type Configuration struct {
   261  	// Matcher is the configuration for the downsampler matcher.
   262  	Matcher MatcherConfiguration `yaml:"matcher"`
   263  
   264  	// Rules is a set of downsample rules. If set, this overrides any rules set
   265  	// in the KV store (and the rules in KV store are not evaluated at all).
   266  	Rules *RulesConfiguration `yaml:"rules"`
   267  
   268  	// RemoteAggregator specifies that downsampling should be done remotely
   269  	// by sending values to a remote m3aggregator cluster which then
   270  	// can forward the aggregated values to stateless m3coordinator backends.
   271  	RemoteAggregator *RemoteAggregatorConfiguration `yaml:"remoteAggregator"`
   272  
   273  	// AggregationTypes configs the aggregation types.
   274  	AggregationTypes *aggregation.TypesConfiguration `yaml:"aggregationTypes"`
   275  
   276  	// Pool of counter elements.
   277  	CounterElemPool pool.ObjectPoolConfiguration `yaml:"counterElemPool"`
   278  
   279  	// Pool of timer elements.
   280  	TimerElemPool pool.ObjectPoolConfiguration `yaml:"timerElemPool"`
   281  
   282  	// Pool of gauge elements.
   283  	GaugeElemPool pool.ObjectPoolConfiguration `yaml:"gaugeElemPool"`
   284  
   285  	// BufferPastLimits specifies the buffer past limits.
   286  	BufferPastLimits []BufferPastLimitConfiguration `yaml:"bufferPastLimits"`
   287  
   288  	// EntryTTL determines how long an entry remains alive before it may be
   289  	// expired due to inactivity.
   290  	EntryTTL time.Duration `yaml:"entryTTL"`
   291  
   292  	// UntimedRollups indicates rollup rules should be untimed.
   293  	UntimedRollups bool `yaml:"untimedRollups"`
   294  }
   295  
   296  // MatcherConfiguration is the configuration for the rule matcher.
   297  type MatcherConfiguration struct {
   298  	// Cache if non-zero will set the capacity of the rules matching cache.
   299  	Cache MatcherCacheConfiguration `yaml:"cache"`
   300  	// NamespaceTag defines the namespace tag to use to select rules
   301  	// namespace to evaluate against. Default is "__m3_namespace__".
   302  	NamespaceTag string `yaml:"namespaceTag"`
   303  	// RequireNamespaceWatchOnInit returns the flag to ensure matcher is initialized with a loaded namespace watch.
   304  	// This only makes sense to use if the corresponding namespace / ruleset values are properly seeded.
   305  	RequireNamespaceWatchOnInit bool `yaml:"requireNamespaceWatchOnInit"`
   306  }
   307  
   308  // MatcherCacheConfiguration is the configuration for the rule matcher cache.
   309  type MatcherCacheConfiguration struct {
   310  	// Capacity if set the capacity of the rules matching cache.
   311  	Capacity *int `yaml:"capacity"`
   312  }
   313  
   314  // RulesConfiguration is a set of rules configuration to use for downsampling.
   315  type RulesConfiguration struct {
   316  	// MappingRules are mapping rules that set retention and resolution
   317  	// for metrics given a filter to match metrics against.
   318  	MappingRules []MappingRuleConfiguration `yaml:"mappingRules"`
   319  
   320  	// RollupRules are rollup rules that sets specific aggregations for sets
   321  	// of metrics given a filter to match metrics against.
   322  	RollupRules []RollupRuleConfiguration `yaml:"rollupRules"`
   323  }
   324  
   325  // MappingRuleConfiguration is a mapping rule configuration.
   326  type MappingRuleConfiguration struct {
   327  	// Filter is a string separated filter of label name to label value
   328  	// glob patterns to filter the mapping rule to.
   329  	// e.g. "app:*nginx* foo:bar baz:qux*qaz*"
   330  	Filter string `yaml:"filter"`
   331  
   332  	// Aggregations is the aggregations to apply to the set of metrics.
   333  	// One of:
   334  	// - "Last"
   335  	// - "Min"
   336  	// - "Max"
   337  	// - "Mean"
   338  	// - "Median"
   339  	// - "Count"
   340  	// - "Sum"
   341  	// - "SumSq"
   342  	// - "Stdev"
   343  	// - "P10"
   344  	// - "P20"
   345  	// - "P30"
   346  	// - "P40"
   347  	// - "P50"
   348  	// - "P60"
   349  	// - "P70"
   350  	// - "P80"
   351  	// - "P90"
   352  	// - "P95"
   353  	// - "P99"
   354  	// - "P999"
   355  	// - "P9999"
   356  	Aggregations []aggregation.Type `yaml:"aggregations"`
   357  
   358  	// StoragePolicies are retention/resolution storage policies at which to
   359  	// keep matched metrics.
   360  	StoragePolicies []StoragePolicyConfiguration `yaml:"storagePolicies"`
   361  
   362  	// Drop specifies to drop any metrics that match the filter rather than
   363  	// keeping them with a storage policy.
   364  	Drop bool `yaml:"drop"`
   365  
   366  	// Tags are the tags to be added to the metric while applying the mapping
   367  	// rule. Users are free to add name/value combinations to the metric. The
   368  	// coordinator also supports certain first class tags which will augment
   369  	// the metric with coordinator generated tag values.
   370  	// __m3_graphite_aggregation__ as a tag will augment the metric with an
   371  	// aggregation tag which is required for graphite. If a metric is of the
   372  	// form {__g0__:stats __g1__:metric __g2__:timer} and we have configured
   373  	// a P95 aggregation, this option will add __g3__:P95 to the metric.
   374  	// __m3_graphite_prefix__ as a tag will add the provided value as a prefix
   375  	// to graphite metrics.
   376  	// __m3_drop_timestamp__ as a tag will drop the timestamp from while
   377  	// writing the metric out. So effectively treat it as an untimed metric.
   378  	Tags []Tag `yaml:"tags"`
   379  
   380  	// Optional fields follow.
   381  
   382  	// Name is optional.
   383  	Name string `yaml:"name"`
   384  }
   385  
   386  // Tag is structure describing tags as used by mapping rule configuration.
   387  type Tag struct {
   388  	// Name is the tag name.
   389  	Name string `yaml:"name"`
   390  	// Value is the tag value.
   391  	Value string `yaml:"value"`
   392  }
   393  
   394  // Rule returns the mapping rule for the mapping rule configuration.
   395  func (r MappingRuleConfiguration) Rule() (view.MappingRule, error) {
   396  	id := uuid.New()
   397  	name := r.Name
   398  	if name == "" {
   399  		name = id
   400  	}
   401  	filter := r.Filter
   402  
   403  	aggID, err := aggregation.CompressTypes(r.Aggregations...)
   404  	if err != nil {
   405  		return view.MappingRule{}, err
   406  	}
   407  
   408  	storagePolicies, err := StoragePolicyConfigurations(r.StoragePolicies).StoragePolicies()
   409  	if err != nil {
   410  		return view.MappingRule{}, err
   411  	}
   412  
   413  	var drop policy.DropPolicy
   414  	if r.Drop {
   415  		drop = policy.DropIfOnlyMatch
   416  	}
   417  
   418  	tags := make([]models.Tag, 0, len(r.Tags))
   419  	for _, tag := range r.Tags {
   420  		tags = append(tags, models.Tag{
   421  			Name:  []byte(tag.Name),
   422  			Value: []byte(tag.Value),
   423  		})
   424  	}
   425  
   426  	return view.MappingRule{
   427  		ID:              id,
   428  		Name:            name,
   429  		Filter:          filter,
   430  		AggregationID:   aggID,
   431  		StoragePolicies: storagePolicies,
   432  		DropPolicy:      drop,
   433  		Tags:            tags,
   434  	}, nil
   435  }
   436  
   437  // StoragePolicyConfiguration is the storage policy to apply to a set of metrics.
   438  type StoragePolicyConfiguration struct {
   439  	Resolution time.Duration `yaml:"resolution"`
   440  	Retention  time.Duration `yaml:"retention"`
   441  }
   442  
   443  // StoragePolicy returns the corresponding storage policy.
   444  func (p StoragePolicyConfiguration) StoragePolicy() (policy.StoragePolicy, error) {
   445  	return policy.ParseStoragePolicy(p.String())
   446  }
   447  
   448  func (p StoragePolicyConfiguration) String() string {
   449  	return fmt.Sprintf("%s:%s", p.Resolution.String(), p.Retention.String())
   450  }
   451  
   452  // StoragePolicyConfigurations are a set of storage policy configurations.
   453  type StoragePolicyConfigurations []StoragePolicyConfiguration
   454  
   455  // StoragePolicies returns storage policies.
   456  func (p StoragePolicyConfigurations) StoragePolicies() (policy.StoragePolicies, error) {
   457  	storagePolicies := make(policy.StoragePolicies, 0, len(p))
   458  	for _, policy := range p {
   459  		value, err := policy.StoragePolicy()
   460  		if err != nil {
   461  			return nil, err
   462  		}
   463  		storagePolicies = append(storagePolicies, value)
   464  	}
   465  	return storagePolicies, nil
   466  }
   467  
   468  // RollupRuleConfiguration is a rollup rule configuration.
   469  type RollupRuleConfiguration struct {
   470  	// Filter is a space separated filter of label name to label value glob
   471  	// patterns to which to filter the mapping rule.
   472  	// e.g. "app:*nginx* foo:bar baz:qux*qaz*"
   473  	Filter string `yaml:"filter"`
   474  
   475  	// Transforms are a set of of rollup rule transforms.
   476  	Transforms []TransformConfiguration `yaml:"transforms"`
   477  
   478  	// StoragePolicies are retention/resolution storage policies at which to keep
   479  	// the matched metrics.
   480  	StoragePolicies []StoragePolicyConfiguration `yaml:"storagePolicies"`
   481  
   482  	// Optional fields follow.
   483  
   484  	// Name is optional.
   485  	Name string `yaml:"name"`
   486  
   487  	// Tags are the tags to be added to the metric while applying the rollup
   488  	// rule. Users are free to add name/value combinations to the metric.
   489  	Tags []Tag `yaml:"tags"`
   490  }
   491  
   492  // Rule returns the rollup rule for the rollup rule configuration.
   493  func (r RollupRuleConfiguration) Rule() (view.RollupRule, error) {
   494  	id := uuid.New()
   495  	name := r.Name
   496  	if name == "" {
   497  		name = id
   498  	}
   499  	filter := r.Filter
   500  
   501  	storagePolicies, err := StoragePolicyConfigurations(r.StoragePolicies).
   502  		StoragePolicies()
   503  	if err != nil {
   504  		return view.RollupRule{}, err
   505  	}
   506  
   507  	ops := make([]pipeline.OpUnion, 0, len(r.Transforms))
   508  	for _, elem := range r.Transforms {
   509  		// TODO: make sure only one of "Rollup" or "Aggregate" or "Transform" is not nil
   510  		switch {
   511  		case elem.Rollup != nil:
   512  			cfg := elem.Rollup
   513  			if len(cfg.GroupBy) > 0 && len(cfg.ExcludeBy) > 0 {
   514  				return view.RollupRule{}, fmt.Errorf(
   515  					"must specify group by or exclude by tags for rollup operation not both: "+
   516  						"groupBy=%d, excludeBy=%d", len(cfg.GroupBy), len(cfg.ExcludeBy))
   517  			}
   518  
   519  			rollupType := pipelinepb.RollupOp_GROUP_BY
   520  			tags := cfg.GroupBy
   521  			if len(cfg.ExcludeBy) > 0 {
   522  				rollupType = pipelinepb.RollupOp_EXCLUDE_BY
   523  				tags = cfg.ExcludeBy
   524  			}
   525  
   526  			aggregationTypes, err := AggregationTypes(cfg.Aggregations).Proto()
   527  			if err != nil {
   528  				return view.RollupRule{}, err
   529  			}
   530  
   531  			op, err := pipeline.NewOpUnionFromProto(pipelinepb.PipelineOp{
   532  				Type: pipelinepb.PipelineOp_ROLLUP,
   533  				Rollup: &pipelinepb.RollupOp{
   534  					Type:             rollupType,
   535  					NewName:          cfg.MetricName,
   536  					Tags:             tags,
   537  					AggregationTypes: aggregationTypes,
   538  				},
   539  			})
   540  			if err != nil {
   541  				return view.RollupRule{}, err
   542  			}
   543  			ops = append(ops, op)
   544  		case elem.Aggregate != nil:
   545  			cfg := elem.Aggregate
   546  			aggregationType, err := cfg.Type.Proto()
   547  			if err != nil {
   548  				return view.RollupRule{}, err
   549  			}
   550  			op, err := pipeline.NewOpUnionFromProto(pipelinepb.PipelineOp{
   551  				Type: pipelinepb.PipelineOp_AGGREGATION,
   552  				Aggregation: &pipelinepb.AggregationOp{
   553  					Type: aggregationType,
   554  				},
   555  			})
   556  			if err != nil {
   557  				return view.RollupRule{}, err
   558  			}
   559  			ops = append(ops, op)
   560  		case elem.Transform != nil:
   561  			cfg := elem.Transform
   562  			var transformType transformationpb.TransformationType
   563  			err := cfg.Type.ToProto(&transformType)
   564  			if err != nil {
   565  				return view.RollupRule{}, err
   566  			}
   567  			op, err := pipeline.NewOpUnionFromProto(pipelinepb.PipelineOp{
   568  				Type: pipelinepb.PipelineOp_TRANSFORMATION,
   569  				Transformation: &pipelinepb.TransformationOp{
   570  					Type: transformType,
   571  				},
   572  			})
   573  			if err != nil {
   574  				return view.RollupRule{}, err
   575  			}
   576  			ops = append(ops, op)
   577  		}
   578  	}
   579  
   580  	if len(ops) == 0 {
   581  		return view.RollupRule{}, errRollupRuleNoTransforms
   582  	}
   583  
   584  	targetPipeline := pipeline.NewPipeline(ops)
   585  
   586  	targets := []view.RollupTarget{
   587  		{
   588  			Pipeline:        targetPipeline,
   589  			StoragePolicies: storagePolicies,
   590  		},
   591  	}
   592  
   593  	tags := make([]models.Tag, 0, len(r.Tags))
   594  	for _, tag := range r.Tags {
   595  		tags = append(tags, models.Tag{
   596  			Name:  []byte(tag.Name),
   597  			Value: []byte(tag.Value),
   598  		})
   599  	}
   600  
   601  	return view.RollupRule{
   602  		ID:      id,
   603  		Name:    name,
   604  		Filter:  filter,
   605  		Targets: targets,
   606  		Tags:    tags,
   607  	}, nil
   608  }
   609  
   610  // TransformConfiguration is a rollup rule transform operation, only one
   611  // single operation is allowed to be specified on any one transform configuration.
   612  type TransformConfiguration struct {
   613  	Rollup    *RollupOperationConfiguration    `yaml:"rollup"`
   614  	Aggregate *AggregateOperationConfiguration `yaml:"aggregate"`
   615  	Transform *TransformOperationConfiguration `yaml:"transform"`
   616  }
   617  
   618  // RollupOperationConfiguration is a rollup operation.
   619  type RollupOperationConfiguration struct {
   620  	// MetricName is the name of the new metric that is emitted after
   621  	// the rollup is applied with its aggregations and group by's.
   622  	MetricName string `yaml:"metricName"`
   623  
   624  	// GroupBy is a set of labels to group by, only these remain on the
   625  	// new metric name produced by the rollup operation.
   626  	// Note: Can only use either groupBy or excludeBy, not both, use the
   627  	// rollup operation "type" to specify which is used.
   628  	GroupBy []string `yaml:"groupBy"`
   629  
   630  	// ExcludeBy is a set of labels to exclude by, only these tags are removed
   631  	// from the resulting rolled up metric.
   632  	// Note: Can only use either groupBy or excludeBy, not both, use the
   633  	// rollup operation "type" to specify which is used.
   634  	ExcludeBy []string `yaml:"excludeBy"`
   635  
   636  	// Aggregations is a set of aggregate operations to perform.
   637  	Aggregations []aggregation.Type `yaml:"aggregations"`
   638  }
   639  
   640  // AggregateOperationConfiguration is an aggregate operation.
   641  type AggregateOperationConfiguration struct {
   642  	// Type is an aggregation operation type.
   643  	Type aggregation.Type `yaml:"type"`
   644  }
   645  
   646  // TransformOperationConfiguration is a transform operation.
   647  type TransformOperationConfiguration struct {
   648  	// Type is a transformation operation type.
   649  	Type transformation.Type `yaml:"type"`
   650  }
   651  
   652  // AggregationTypes is a set of aggregation types.
   653  type AggregationTypes []aggregation.Type
   654  
   655  // Proto returns a set of aggregation types as their protobuf value.
   656  func (t AggregationTypes) Proto() ([]aggregationpb.AggregationType, error) {
   657  	result := make([]aggregationpb.AggregationType, 0, len(t))
   658  	for _, elem := range t {
   659  		value, err := elem.Proto()
   660  		if err != nil {
   661  			return nil, err
   662  		}
   663  		result = append(result, value)
   664  	}
   665  	return result, nil
   666  }
   667  
   668  // RemoteAggregatorConfiguration specifies a remote aggregator
   669  // to use for downsampling.
   670  type RemoteAggregatorConfiguration struct {
   671  	// Client is the remote aggregator client.
   672  	Client client.Configuration `yaml:"client"`
   673  	// clientOverride can be used in tests to test initializing a mock client.
   674  	clientOverride client.Client
   675  }
   676  
   677  func (c RemoteAggregatorConfiguration) newClient(
   678  	kvClient clusterclient.Client,
   679  	clockOpts clock.Options,
   680  	instrumentOpts instrument.Options,
   681  	rwOpts xio.Options,
   682  ) (client.Client, error) {
   683  	if c.clientOverride != nil {
   684  		return c.clientOverride, nil
   685  	}
   686  
   687  	return c.Client.NewClient(kvClient, clockOpts, instrumentOpts, rwOpts)
   688  }
   689  
   690  // BufferPastLimitConfiguration specifies a custom buffer past limit
   691  // for aggregation tiles.
   692  type BufferPastLimitConfiguration struct {
   693  	Resolution time.Duration `yaml:"resolution"`
   694  	BufferPast time.Duration `yaml:"bufferPast"`
   695  }
   696  
   697  // NewDownsampler returns a new downsampler.
   698  func (cfg Configuration) NewDownsampler(
   699  	opts DownsamplerOptions,
   700  ) (Downsampler, error) {
   701  	agg, err := cfg.newAggregator(opts)
   702  	if err != nil {
   703  		return nil, err
   704  	}
   705  
   706  	return newDownsampler(downsamplerOptions{
   707  		opts: opts,
   708  		agg:  agg,
   709  	})
   710  }
   711  
   712  func (cfg Configuration) newAggregator(o DownsamplerOptions) (agg, error) {
   713  	// Validate options first.
   714  	if err := o.validate(); err != nil {
   715  		return agg{}, err
   716  	}
   717  
   718  	var (
   719  		storageFlushConcurrency = defaultStorageFlushConcurrency
   720  		clockOpts               = o.ClockOptions
   721  		instrumentOpts          = o.InstrumentOptions
   722  		scope                   = instrumentOpts.MetricsScope()
   723  		logger                  = instrumentOpts.Logger()
   724  		openTimeout             = defaultOpenTimeout
   725  		namespaceTag            = defaultNamespaceTag
   726  	)
   727  	if o.StorageFlushConcurrency > 0 {
   728  		storageFlushConcurrency = o.StorageFlushConcurrency
   729  	}
   730  	if o.OpenTimeout > 0 {
   731  		openTimeout = o.OpenTimeout
   732  	}
   733  	if cfg.Matcher.NamespaceTag != "" {
   734  		namespaceTag = cfg.Matcher.NamespaceTag
   735  	}
   736  
   737  	pools := o.newAggregatorPools()
   738  	ruleSetOpts := o.newAggregatorRulesOptions(pools)
   739  
   740  	matcherOpts := matcher.NewOptions().
   741  		SetClockOptions(clockOpts).
   742  		SetInstrumentOptions(instrumentOpts).
   743  		SetRuleSetOptions(ruleSetOpts).
   744  		SetKVStore(o.RulesKVStore).
   745  		SetNamespaceResolver(namespace.NewResolver([]byte(namespaceTag), nil)).
   746  		SetRequireNamespaceWatchOnInit(cfg.Matcher.RequireNamespaceWatchOnInit).
   747  		SetInterruptedCh(o.InterruptedCh)
   748  
   749  	// NB(r): If rules are being explicitly set in config then we are
   750  	// going to use an in memory KV store for rules and explicitly set them up.
   751  	if cfg.Rules != nil {
   752  		logger.Debug("registering downsample rules from config, not using KV")
   753  		kvTxnMemStore := mem.NewStore()
   754  
   755  		// Initialize the namespaces
   756  		if err := initStoreNamespaces(kvTxnMemStore, matcherOpts.NamespacesKey()); err != nil {
   757  			return agg{}, err
   758  		}
   759  
   760  		rulesetKeyFmt := matcherOpts.RuleSetKeyFn()([]byte("%s"))
   761  		rulesStoreOpts := ruleskv.NewStoreOptions(matcherOpts.NamespacesKey(),
   762  			rulesetKeyFmt, nil)
   763  		rulesStore := ruleskv.NewStore(kvTxnMemStore, rulesStoreOpts)
   764  
   765  		ruleNamespaces, err := rulesStore.ReadNamespaces()
   766  		if err != nil {
   767  			return agg{}, err
   768  		}
   769  
   770  		updateMetadata := rules.NewRuleSetUpdateHelper(0).
   771  			NewUpdateMetadata(time.Now().UnixNano(), "config")
   772  
   773  		// Create the default namespace, always not present since in-memory.
   774  		_, err = ruleNamespaces.AddNamespace(defaultConfigInMemoryNamespace,
   775  			updateMetadata)
   776  		if err != nil {
   777  			return agg{}, err
   778  		}
   779  
   780  		// Create the ruleset in the default namespace.
   781  		rs := rules.NewEmptyRuleSet(defaultConfigInMemoryNamespace,
   782  			updateMetadata)
   783  		for _, mappingRule := range cfg.Rules.MappingRules {
   784  			rule, err := mappingRule.Rule()
   785  			if err != nil {
   786  				return agg{}, err
   787  			}
   788  
   789  			_, err = rs.AddMappingRule(rule, updateMetadata)
   790  			if err != nil {
   791  				return agg{}, err
   792  			}
   793  		}
   794  
   795  		for _, rollupRule := range cfg.Rules.RollupRules {
   796  			rule, err := rollupRule.Rule()
   797  			if err != nil {
   798  				return agg{}, err
   799  			}
   800  
   801  			_, err = rs.AddRollupRule(rule, updateMetadata)
   802  			if err != nil {
   803  				return agg{}, err
   804  			}
   805  		}
   806  
   807  		if err := rulesStore.WriteAll(ruleNamespaces, rs); err != nil {
   808  			return agg{}, err
   809  		}
   810  
   811  		// Set the rules KV store to the in-memory one we created to
   812  		// store the rules we created from config.
   813  		// This makes sure that other components using rules KV store points to
   814  		// the in-memory store that has the rules created from config.
   815  		matcherOpts = matcherOpts.SetKVStore(kvTxnMemStore)
   816  	}
   817  
   818  	matcherCacheCapacity := defaultMatcherCacheCapacity
   819  	if v := cfg.Matcher.Cache.Capacity; v != nil {
   820  		matcherCacheCapacity = *v
   821  	}
   822  
   823  	kvStore, err := o.ClusterClient.KV()
   824  	if err != nil {
   825  		return agg{}, err
   826  	}
   827  
   828  	// NB(antanas): matcher registers watcher on namespaces key. Making sure it is set, otherwise watcher times out.
   829  	// With RequireNamespaceWatchOnInit being true we expect namespaces to be set upfront
   830  	// so we do not initialize them here at all because it might potentially hide human error.
   831  	if !matcherOpts.RequireNamespaceWatchOnInit() {
   832  		if err := initStoreNamespaces(kvStore, matcherOpts.NamespacesKey()); err != nil {
   833  			return agg{}, err
   834  		}
   835  	}
   836  
   837  	matcher, err := o.newAggregatorMatcher(matcherOpts, matcherCacheCapacity)
   838  	if err != nil {
   839  		return agg{}, err
   840  	}
   841  
   842  	if remoteAgg := cfg.RemoteAggregator; remoteAgg != nil {
   843  		// If downsampling setup to use a remote aggregator instead of local
   844  		// aggregator, set that up instead.
   845  		scope := instrumentOpts.MetricsScope().SubScope("remote-aggregator-client")
   846  		iOpts := instrumentOpts.SetMetricsScope(scope)
   847  		rwOpts := o.RWOptions
   848  		if rwOpts == nil {
   849  			logger.Info("no rw options set, using default")
   850  			rwOpts = xio.NewOptions()
   851  		}
   852  
   853  		client, err := remoteAgg.newClient(o.ClusterClient, clockOpts, iOpts, rwOpts)
   854  		if err != nil {
   855  			err = fmt.Errorf("could not create remote aggregator client: %v", err)
   856  			return agg{}, err
   857  		}
   858  		if err := client.Init(); err != nil {
   859  			return agg{}, fmt.Errorf("could not initialize remote aggregator client: %v", err)
   860  		}
   861  
   862  		return agg{
   863  			clientRemote:   client,
   864  			matcher:        matcher,
   865  			pools:          pools,
   866  			untimedRollups: cfg.UntimedRollups,
   867  		}, nil
   868  	}
   869  
   870  	serviceID := services.NewServiceID().
   871  		SetEnvironment("production").
   872  		SetName("downsampler").
   873  		SetZone("embedded")
   874  
   875  	localKVStore := kvStore
   876  	// NB(antanas): to protect against running with real Etcd and overriding existing placements.
   877  	if !mem.IsMem(localKVStore) {
   878  		localKVStore = mem.NewStore()
   879  	}
   880  
   881  	placementManager, err := o.newAggregatorPlacementManager(serviceID, localKVStore)
   882  	if err != nil {
   883  		return agg{}, err
   884  	}
   885  
   886  	flushTimesManager := aggregator.NewFlushTimesManager(
   887  		aggregator.NewFlushTimesManagerOptions().
   888  			SetFlushTimesStore(localKVStore))
   889  
   890  	electionManager, err := o.newAggregatorElectionManager(serviceID,
   891  		placementManager, flushTimesManager, clockOpts)
   892  	if err != nil {
   893  		return agg{}, err
   894  	}
   895  
   896  	flushManager, flushHandler := o.newAggregatorFlushManagerAndHandler(
   897  		placementManager, flushTimesManager, electionManager, o.ClockOptions, instrumentOpts,
   898  		storageFlushConcurrency, pools)
   899  
   900  	bufferPastLimits := defaultBufferPastLimits
   901  	if numLimitsCfg := len(cfg.BufferPastLimits); numLimitsCfg > 0 {
   902  		// Allow overrides from config.
   903  		bufferPastLimits = make([]bufferPastLimit, 0, numLimitsCfg)
   904  		for _, limit := range cfg.BufferPastLimits {
   905  			bufferPastLimits = append(bufferPastLimits, bufferPastLimit{
   906  				upperBound: limit.Resolution,
   907  				bufferPast: limit.BufferPast,
   908  			})
   909  		}
   910  	}
   911  
   912  	bufferForPastTimedMetricFn := func(tile time.Duration) time.Duration {
   913  		return bufferForPastTimedMetric(bufferPastLimits, tile)
   914  	}
   915  
   916  	maxAllowedForwardingDelayFn := func(tile time.Duration, numForwardedTimes int) time.Duration {
   917  		return maxAllowedForwardingDelay(bufferPastLimits, tile, numForwardedTimes)
   918  	}
   919  
   920  	// Finally construct all options.
   921  	aggregatorOpts := aggregator.NewOptions(clockOpts).
   922  		SetInstrumentOptions(instrumentOpts).
   923  		SetDefaultStoragePolicies(nil).
   924  		SetMetricPrefix(nil).
   925  		SetCounterPrefix(nil).
   926  		SetGaugePrefix(nil).
   927  		SetTimerPrefix(nil).
   928  		SetPlacementManager(placementManager).
   929  		SetFlushTimesManager(flushTimesManager).
   930  		SetElectionManager(electionManager).
   931  		SetFlushManager(flushManager).
   932  		SetFlushHandler(flushHandler).
   933  		SetBufferForPastTimedMetricFn(bufferForPastTimedMetricFn).
   934  		SetBufferForFutureTimedMetric(defaultBufferFutureTimedMetric).
   935  		SetMaxAllowedForwardingDelayFn(maxAllowedForwardingDelayFn).
   936  		SetVerboseErrors(defaultVerboseErrors)
   937  
   938  	if cfg.EntryTTL != 0 {
   939  		aggregatorOpts = aggregatorOpts.SetEntryTTL(cfg.EntryTTL)
   940  	}
   941  
   942  	if cfg.AggregationTypes != nil {
   943  		aggTypeOpts, err := cfg.AggregationTypes.NewOptions(instrumentOpts)
   944  		if err != nil {
   945  			return agg{}, err
   946  		}
   947  		aggregatorOpts = aggregatorOpts.SetAggregationTypesOptions(aggTypeOpts)
   948  	}
   949  
   950  	// Set counter elem pool.
   951  	counterElemPoolOpts := cfg.CounterElemPool.NewObjectPoolOptions(
   952  		instrumentOpts.SetMetricsScope(scope.SubScope("counter-elem-pool")),
   953  	)
   954  	counterElemPool := aggregator.NewCounterElemPool(counterElemPoolOpts)
   955  	aggregatorOpts = aggregatorOpts.SetCounterElemPool(counterElemPool)
   956  	// use a singleton ElemOptions to avoid allocs per elem.
   957  	elemOpts := aggregator.NewElemOptions(aggregatorOpts)
   958  	counterElemPool.Init(func() *aggregator.CounterElem {
   959  		return aggregator.MustNewCounterElem(aggregator.ElemData{}, elemOpts)
   960  	})
   961  
   962  	// Set timer elem pool.
   963  	timerElemPoolOpts := cfg.TimerElemPool.NewObjectPoolOptions(
   964  		instrumentOpts.SetMetricsScope(scope.SubScope("timer-elem-pool")),
   965  	)
   966  	timerElemPool := aggregator.NewTimerElemPool(timerElemPoolOpts)
   967  	aggregatorOpts = aggregatorOpts.SetTimerElemPool(timerElemPool)
   968  	timerElemPool.Init(func() *aggregator.TimerElem {
   969  		return aggregator.MustNewTimerElem(aggregator.ElemData{}, elemOpts)
   970  	})
   971  
   972  	// Set gauge elem pool.
   973  	gaugeElemPoolOpts := cfg.GaugeElemPool.NewObjectPoolOptions(
   974  		instrumentOpts.SetMetricsScope(scope.SubScope("gauge-elem-pool")),
   975  	)
   976  	gaugeElemPool := aggregator.NewGaugeElemPool(gaugeElemPoolOpts)
   977  	aggregatorOpts = aggregatorOpts.SetGaugeElemPool(gaugeElemPool)
   978  	gaugeElemPool.Init(func() *aggregator.GaugeElem {
   979  		return aggregator.MustNewGaugeElem(aggregator.ElemData{}, elemOpts)
   980  	})
   981  
   982  	adminAggClient := newAggregatorLocalAdminClient()
   983  	aggregatorOpts = aggregatorOpts.SetAdminClient(adminAggClient)
   984  
   985  	aggregatorInstance := aggregator.NewAggregator(aggregatorOpts)
   986  	if err := aggregatorInstance.Open(); err != nil {
   987  		return agg{}, err
   988  	}
   989  
   990  	// Update the local aggregator client with the active aggregator instance.
   991  	// NB: Can't do this at construction time since needs to be passed as an
   992  	// option to the aggregator constructor.
   993  	adminAggClient.setAggregator(aggregatorInstance)
   994  
   995  	// Wait until the aggregator becomes leader so we don't miss datapoints
   996  	deadline := time.Now().Add(openTimeout)
   997  	for {
   998  		if !time.Now().Before(deadline) {
   999  			return agg{}, fmt.Errorf("aggregator not promoted to leader after: %s",
  1000  				openTimeout.String())
  1001  		}
  1002  		if electionManager.ElectionState() == aggregator.LeaderState {
  1003  			break
  1004  		}
  1005  		time.Sleep(10 * time.Millisecond)
  1006  	}
  1007  
  1008  	return agg{
  1009  		aggregator:     aggregatorInstance,
  1010  		matcher:        matcher,
  1011  		pools:          pools,
  1012  		untimedRollups: cfg.UntimedRollups,
  1013  	}, nil
  1014  }
  1015  
  1016  func initStoreNamespaces(store kv.Store, nsKey string) error {
  1017  	_, err := store.SetIfNotExists(nsKey, &rulepb.Namespaces{})
  1018  	if errors.Is(err, kv.ErrAlreadyExists) {
  1019  		return nil
  1020  	}
  1021  	return err
  1022  }
  1023  
  1024  type aggPools struct {
  1025  	tagEncoderPool         serialize.TagEncoderPool
  1026  	tagDecoderPool         serialize.TagDecoderPool
  1027  	metricTagsIteratorPool serialize.MetricTagsIteratorPool
  1028  	metricsAppenderPool    *metricsAppenderPool
  1029  }
  1030  
  1031  func (o DownsamplerOptions) newAggregatorPools() aggPools {
  1032  	tagEncoderPool := serialize.NewTagEncoderPool(o.TagEncoderOptions,
  1033  		o.TagEncoderPoolOptions)
  1034  	tagEncoderPool.Init()
  1035  
  1036  	tagDecoderPool := serialize.NewTagDecoderPool(o.TagDecoderOptions,
  1037  		o.TagDecoderPoolOptions)
  1038  	tagDecoderPool.Init()
  1039  
  1040  	metricTagsIteratorPool := serialize.NewMetricTagsIteratorPool(tagDecoderPool,
  1041  		o.TagDecoderPoolOptions)
  1042  	metricTagsIteratorPool.Init()
  1043  
  1044  	metricsAppenderPool := newMetricsAppenderPool(
  1045  		o.MetricsAppenderPoolOptions,
  1046  		o.TagDecoderOptions.TagSerializationLimits(),
  1047  		o.NameTagOrDefault())
  1048  
  1049  	return aggPools{
  1050  		tagEncoderPool:         tagEncoderPool,
  1051  		tagDecoderPool:         tagDecoderPool,
  1052  		metricTagsIteratorPool: metricTagsIteratorPool,
  1053  		metricsAppenderPool:    metricsAppenderPool,
  1054  	}
  1055  }
  1056  
  1057  func (o DownsamplerOptions) newAggregatorRulesOptions(pools aggPools) rules.Options {
  1058  	nameTag := o.NameTagOrDefault()
  1059  	tagsFilterOpts := filters.TagsFilterOptions{
  1060  		NameTagKey: nameTag,
  1061  	}
  1062  
  1063  	isRollupIDFn := func(name []byte, tags []byte) bool {
  1064  		return isRollupID(tags, pools.metricTagsIteratorPool)
  1065  	}
  1066  
  1067  	newRollupIDProviderPool := newRollupIDProviderPool(pools.tagEncoderPool,
  1068  		o.TagEncoderPoolOptions, ident.BytesID(nameTag))
  1069  	newRollupIDProviderPool.Init()
  1070  
  1071  	newRollupIDFn := func(newName []byte, tagPairs []id.TagPair) []byte {
  1072  		// First filter out any tags that have a prefix that
  1073  		// are not included in output metric IDs (such as metric
  1074  		// type tags that are just used for filtering like __m3_type__).
  1075  		filtered := tagPairs[:0]
  1076  	TagPairsFilterLoop:
  1077  		for i := range tagPairs {
  1078  			for _, filter := range defaultFilterOutTagPrefixes {
  1079  				if bytes.HasPrefix(tagPairs[i].Name, filter) {
  1080  					continue TagPairsFilterLoop
  1081  				}
  1082  			}
  1083  			filtered = append(filtered, tagPairs[i])
  1084  		}
  1085  
  1086  		// Create the rollup using filtered tag pairs.
  1087  		rollupIDProvider := newRollupIDProviderPool.Get()
  1088  		id, err := rollupIDProvider.provide(newName, filtered)
  1089  		if err != nil {
  1090  			panic(err) // Encoding should never fail
  1091  		}
  1092  		rollupIDProvider.finalize()
  1093  		return id
  1094  	}
  1095  
  1096  	return rules.NewOptions().
  1097  		SetTagsFilterOptions(tagsFilterOpts).
  1098  		SetNewRollupIDFn(newRollupIDFn).
  1099  		SetIsRollupIDFn(isRollupIDFn)
  1100  }
  1101  
  1102  func (o DownsamplerOptions) newAggregatorMatcher(
  1103  	opts matcher.Options,
  1104  	capacity int,
  1105  ) (matcher.Matcher, error) {
  1106  	var matcherCache cache.Cache
  1107  	if capacity > 0 {
  1108  		scope := opts.InstrumentOptions().MetricsScope().SubScope("matcher-cache")
  1109  		instrumentOpts := opts.InstrumentOptions().
  1110  			SetMetricsScope(scope)
  1111  		cacheOpts := cache.NewOptions().
  1112  			SetCapacity(capacity).
  1113  			SetNamespaceResolver(opts.NamespaceResolver()).
  1114  			SetClockOptions(opts.ClockOptions()).
  1115  			SetInstrumentOptions(instrumentOpts)
  1116  		matcherCache = cache.NewCache(cacheOpts)
  1117  	}
  1118  
  1119  	return matcher.NewMatcher(matcherCache, opts)
  1120  }
  1121  
  1122  func (o DownsamplerOptions) newAggregatorPlacementManager(
  1123  	serviceID services.ServiceID,
  1124  	localKVStore kv.Store,
  1125  ) (aggregator.PlacementManager, error) {
  1126  	instance := placement.NewInstance().
  1127  		SetID(instanceID).
  1128  		SetWeight(1).
  1129  		SetEndpoint(instanceID)
  1130  
  1131  	placementOpts := placement.NewOptions().
  1132  		SetIsStaged(true).
  1133  		SetShardStateMode(placement.StableShardStateOnly)
  1134  
  1135  	placementSvc := placementservice.NewPlacementService(
  1136  		placementstorage.NewPlacementStorage(localKVStore, placementKVKey, placementOpts),
  1137  		placementservice.WithPlacementOptions(placementOpts))
  1138  
  1139  	_, err := placementSvc.BuildInitialPlacement([]placement.Instance{instance}, numShards,
  1140  		replicationFactor)
  1141  	if err != nil {
  1142  		return nil, err
  1143  	}
  1144  
  1145  	placementWatcherOpts := placement.NewWatcherOptions().
  1146  		SetStagedPlacementKey(placementKVKey).
  1147  		SetStagedPlacementStore(localKVStore)
  1148  	placementManagerOpts := aggregator.NewPlacementManagerOptions().
  1149  		SetInstanceID(instanceID).
  1150  		SetWatcherOptions(placementWatcherOpts)
  1151  
  1152  	return aggregator.NewPlacementManager(placementManagerOpts), nil
  1153  }
  1154  
  1155  func (o DownsamplerOptions) newAggregatorElectionManager(
  1156  	serviceID services.ServiceID,
  1157  	placementManager aggregator.PlacementManager,
  1158  	flushTimesManager aggregator.FlushTimesManager,
  1159  	clockOpts clock.Options,
  1160  ) (aggregator.ElectionManager, error) {
  1161  	leaderValue := instanceID
  1162  	campaignOpts, err := services.NewCampaignOptions()
  1163  	if err != nil {
  1164  		return nil, err
  1165  	}
  1166  
  1167  	campaignOpts = campaignOpts.SetLeaderValue(leaderValue)
  1168  
  1169  	leaderService := newLocalLeaderService(serviceID)
  1170  
  1171  	electionManagerOpts := aggregator.NewElectionManagerOptions().
  1172  		SetClockOptions(clockOpts).
  1173  		SetCampaignOptions(campaignOpts).
  1174  		SetLeaderService(leaderService).
  1175  		SetPlacementManager(placementManager).
  1176  		SetFlushTimesManager(flushTimesManager)
  1177  
  1178  	return aggregator.NewElectionManager(electionManagerOpts), nil
  1179  }
  1180  
  1181  func (o DownsamplerOptions) newAggregatorFlushManagerAndHandler(
  1182  	placementManager aggregator.PlacementManager,
  1183  	flushTimesManager aggregator.FlushTimesManager,
  1184  	electionManager aggregator.ElectionManager,
  1185  	clockOpts clock.Options,
  1186  	instrumentOpts instrument.Options,
  1187  	storageFlushConcurrency int,
  1188  	pools aggPools,
  1189  ) (aggregator.FlushManager, handler.Handler) {
  1190  	flushManagerOpts := aggregator.NewFlushManagerOptions().
  1191  		SetClockOptions(clockOpts).
  1192  		SetPlacementManager(placementManager).
  1193  		SetFlushTimesManager(flushTimesManager).
  1194  		SetElectionManager(electionManager).
  1195  		SetJitterEnabled(false)
  1196  	flushManager := aggregator.NewFlushManager(flushManagerOpts)
  1197  
  1198  	flushWorkers := xsync.NewWorkerPool(storageFlushConcurrency)
  1199  	flushWorkers.Init()
  1200  	handler := newDownsamplerFlushHandler(o.Storage, pools.metricTagsIteratorPool,
  1201  		flushWorkers, o.TagOptions, instrumentOpts)
  1202  
  1203  	return flushManager, handler
  1204  }
  1205  
  1206  // Force the local aggregator client to implement client.Client.
  1207  var _ client.AdminClient = (*aggregatorLocalAdminClient)(nil)
  1208  
  1209  type aggregatorLocalAdminClient struct {
  1210  	agg aggregator.Aggregator
  1211  }
  1212  
  1213  func newAggregatorLocalAdminClient() *aggregatorLocalAdminClient {
  1214  	return &aggregatorLocalAdminClient{}
  1215  }
  1216  
  1217  func (c *aggregatorLocalAdminClient) setAggregator(agg aggregator.Aggregator) {
  1218  	c.agg = agg
  1219  }
  1220  
  1221  // Init initializes the client.
  1222  func (c *aggregatorLocalAdminClient) Init() error {
  1223  	return fmt.Errorf("always initialized")
  1224  }
  1225  
  1226  // WriteUntimedCounter writes untimed counter metrics.
  1227  func (c *aggregatorLocalAdminClient) WriteUntimedCounter(
  1228  	counter unaggregated.Counter,
  1229  	metadatas metadata.StagedMetadatas,
  1230  ) error {
  1231  	return c.agg.AddUntimed(counter.ToUnion(), metadatas)
  1232  }
  1233  
  1234  // WriteUntimedBatchTimer writes untimed batch timer metrics.
  1235  func (c *aggregatorLocalAdminClient) WriteUntimedBatchTimer(
  1236  	batchTimer unaggregated.BatchTimer,
  1237  	metadatas metadata.StagedMetadatas,
  1238  ) error {
  1239  	return c.agg.AddUntimed(batchTimer.ToUnion(), metadatas)
  1240  }
  1241  
  1242  // WriteUntimedGauge writes untimed gauge metrics.
  1243  func (c *aggregatorLocalAdminClient) WriteUntimedGauge(
  1244  	gauge unaggregated.Gauge,
  1245  	metadatas metadata.StagedMetadatas,
  1246  ) error {
  1247  	return c.agg.AddUntimed(gauge.ToUnion(), metadatas)
  1248  }
  1249  
  1250  // WriteTimed writes timed metrics.
  1251  func (c *aggregatorLocalAdminClient) WriteTimed(
  1252  	metric aggregated.Metric,
  1253  	metadata metadata.TimedMetadata,
  1254  ) error {
  1255  	return c.agg.AddTimed(metric, metadata)
  1256  }
  1257  
  1258  // WriteTimedWithStagedMetadatas writes timed metrics with staged metadatas.
  1259  func (c *aggregatorLocalAdminClient) WriteTimedWithStagedMetadatas(
  1260  	metric aggregated.Metric,
  1261  	metadatas metadata.StagedMetadatas,
  1262  ) error {
  1263  	return c.agg.AddTimedWithStagedMetadatas(metric, metadatas)
  1264  }
  1265  
  1266  // WriteForwarded writes forwarded metrics.
  1267  func (c *aggregatorLocalAdminClient) WriteForwarded(
  1268  	metric aggregated.ForwardedMetric,
  1269  	metadata metadata.ForwardMetadata,
  1270  ) error {
  1271  	return c.agg.AddForwarded(metric, metadata)
  1272  }
  1273  
  1274  // WritePassthrough writes passthrough metrics.
  1275  func (c *aggregatorLocalAdminClient) WritePassthrough(
  1276  	metric aggregated.Metric,
  1277  	storagePolicy policy.StoragePolicy,
  1278  ) error {
  1279  	return c.agg.AddPassthrough(metric, storagePolicy)
  1280  }
  1281  
  1282  // Flush flushes any remaining data buffered by the client.
  1283  func (c *aggregatorLocalAdminClient) Flush() error {
  1284  	return nil
  1285  }
  1286  
  1287  // Close closes the client.
  1288  func (c *aggregatorLocalAdminClient) Close() error {
  1289  	return nil
  1290  }
  1291  
  1292  type bufferPastLimit struct {
  1293  	upperBound time.Duration
  1294  	bufferPast time.Duration
  1295  }
  1296  
  1297  var defaultBufferPastLimits = []bufferPastLimit{
  1298  	{upperBound: 0, bufferPast: 15 * time.Second},
  1299  	{upperBound: 30 * time.Second, bufferPast: 30 * time.Second},
  1300  	{upperBound: time.Minute, bufferPast: time.Minute},
  1301  	{upperBound: 2 * time.Minute, bufferPast: 2 * time.Minute},
  1302  }
  1303  
  1304  func bufferForPastTimedMetric(
  1305  	limits []bufferPastLimit,
  1306  	tile time.Duration,
  1307  ) time.Duration {
  1308  	bufferPast := limits[0].bufferPast
  1309  	for _, limit := range limits {
  1310  		if tile < limit.upperBound {
  1311  			return bufferPast
  1312  		}
  1313  		bufferPast = limit.bufferPast
  1314  	}
  1315  	return bufferPast
  1316  }
  1317  
  1318  func maxAllowedForwardingDelay(
  1319  	limits []bufferPastLimit,
  1320  	tile time.Duration,
  1321  	numForwardedTimes int,
  1322  ) time.Duration {
  1323  	resolutionForwardDelay := tile * time.Duration(numForwardedTimes)
  1324  	bufferPast := limits[0].bufferPast
  1325  	for _, limit := range limits {
  1326  		if tile < limit.upperBound {
  1327  			return bufferPast + resolutionForwardDelay
  1328  		}
  1329  		bufferPast = limit.bufferPast
  1330  	}
  1331  	return bufferPast + resolutionForwardDelay
  1332  }