github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/aggregator/client/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 client
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/aggregator/sharding"
    29  	m3clusterclient "github.com/m3db/m3/src/cluster/client"
    30  	"github.com/m3db/m3/src/cluster/kv"
    31  	"github.com/m3db/m3/src/cluster/placement"
    32  	"github.com/m3db/m3/src/metrics/encoding/protobuf"
    33  	producerconfig "github.com/m3db/m3/src/msg/producer/config"
    34  	"github.com/m3db/m3/src/x/clock"
    35  	"github.com/m3db/m3/src/x/instrument"
    36  	xio "github.com/m3db/m3/src/x/io"
    37  	"github.com/m3db/m3/src/x/pool"
    38  	"github.com/m3db/m3/src/x/retry"
    39  
    40  	"github.com/uber-go/tally"
    41  )
    42  
    43  var errNoM3MsgOptions = errors.New("m3msg aggregator client: missing m3msg options")
    44  
    45  // Configuration contains client configuration.
    46  type Configuration struct {
    47  	Type                       AggregatorClientType            `yaml:"type"`
    48  	M3Msg                      *M3MsgConfiguration             `yaml:"m3msg"`
    49  	PlacementKV                *kv.OverrideConfiguration       `yaml:"placementKV"`
    50  	Watcher                    *placement.WatcherConfiguration `yaml:"placementWatcher"`
    51  	HashType                   *sharding.HashType              `yaml:"hashType"`
    52  	ShardCutoverWarmupDuration *time.Duration                  `yaml:"shardCutoverWarmupDuration"`
    53  	ShardCutoffLingerDuration  *time.Duration                  `yaml:"shardCutoffLingerDuration"`
    54  	Encoder                    EncoderConfiguration            `yaml:"encoder"`
    55  	FlushSize                  int                             `yaml:"flushSize,omitempty"` // FlushSize is deprecated
    56  	FlushWorkerCount           int                             `yaml:"flushWorkerCount"`
    57  	ForceFlushEvery            time.Duration                   `yaml:"forceFlushEvery"`
    58  	MaxBatchSize               int                             `yaml:"maxBatchSize"`
    59  	MaxTimerBatchSize          int                             `yaml:"maxTimerBatchSize"`
    60  	QueueSize                  int                             `yaml:"queueSize"`
    61  	QueueDropType              *DropType                       `yaml:"queueDropType"`
    62  	Connection                 ConnectionConfiguration         `yaml:"connection"`
    63  }
    64  
    65  // NewAdminClient creates a new admin client.
    66  func (c *Configuration) NewAdminClient(
    67  	kvClient m3clusterclient.Client,
    68  	clockOpts clock.Options,
    69  	instrumentOpts instrument.Options,
    70  	rwOpts xio.Options,
    71  ) (AdminClient, error) {
    72  	client, err := c.NewClient(kvClient, clockOpts, instrumentOpts, rwOpts)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	return client.(AdminClient), nil
    77  }
    78  
    79  // NewClient creates a new client.
    80  func (c *Configuration) NewClient(
    81  	kvClient m3clusterclient.Client,
    82  	clockOpts clock.Options,
    83  	instrumentOpts instrument.Options,
    84  	rwOpts xio.Options,
    85  ) (Client, error) {
    86  	opts, err := c.NewClientOptions(kvClient, clockOpts, instrumentOpts, rwOpts)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	return NewClient(opts)
    92  }
    93  
    94  var (
    95  	errLegacyClientNoPlacementKVConfig = errors.New("no placement KV config set")
    96  	errLegacyClientNoWatcherConfig     = errors.New("no placement watcher config set")
    97  )
    98  
    99  // NewClientOptions creates Options that can be used to
   100  // create a New Aggregator Client.
   101  func (c *Configuration) NewClientOptions(
   102  	kvClient m3clusterclient.Client,
   103  	clockOpts clock.Options,
   104  	instrumentOpts instrument.Options,
   105  	rwOpts xio.Options,
   106  ) (Options, error) {
   107  	opts := NewOptions().
   108  		SetAggregatorClientType(c.Type).
   109  		SetClockOptions(clockOpts).
   110  		SetInstrumentOptions(instrumentOpts).
   111  		SetRWOptions(rwOpts)
   112  
   113  	switch c.Type {
   114  	case M3MsgAggregatorClient:
   115  		m3msgCfg := c.M3Msg
   116  		if m3msgCfg == nil {
   117  			return nil, errNoM3MsgOptions
   118  		}
   119  
   120  		m3msgOpts, err := m3msgCfg.NewM3MsgOptions(kvClient, instrumentOpts, rwOpts)
   121  		if err != nil {
   122  			return nil, err
   123  		}
   124  
   125  		// Allow M3Msg options to override the timer options for instrument options.
   126  		opts = opts.SetInstrumentOptions(
   127  			opts.InstrumentOptions().SetTimerOptions(m3msgOpts.TimerOptions()))
   128  
   129  		// Set the M3Msg options configured.
   130  		opts = opts.SetM3MsgOptions(m3msgOpts)
   131  	case LegacyAggregatorClient:
   132  		placementKV := c.PlacementKV
   133  		if placementKV == nil {
   134  			return nil, errLegacyClientNoPlacementKVConfig
   135  		}
   136  
   137  		placementWatcher := c.Watcher
   138  		if placementWatcher == nil {
   139  			return nil, errLegacyClientNoWatcherConfig
   140  		}
   141  
   142  		scope := instrumentOpts.MetricsScope()
   143  		connectionOpts := c.Connection.NewConnectionOptions(scope.SubScope("connection"))
   144  		kvOpts, err := placementKV.NewOverrideOptions()
   145  		if err != nil {
   146  			return nil, err
   147  		}
   148  
   149  		placementStore, err := kvClient.Store(kvOpts)
   150  		if err != nil {
   151  			return nil, err
   152  		}
   153  
   154  		iOpts := instrumentOpts.SetMetricsScope(scope.SubScope("encoder"))
   155  		encoderOpts := c.Encoder.NewEncoderOptions(iOpts)
   156  
   157  		iOpts = instrumentOpts.SetMetricsScope(scope.SubScope("placement-watcher"))
   158  		watcherOpts := placementWatcher.NewOptions(placementStore, iOpts)
   159  
   160  		// Get the shard fn.
   161  		hashType := sharding.DefaultHash
   162  		if c.HashType != nil {
   163  			hashType = *c.HashType
   164  		}
   165  		shardFn, err := hashType.ShardFn()
   166  		if err != nil {
   167  			return nil, err
   168  		}
   169  
   170  		opts = opts.SetWatcherOptions(watcherOpts).
   171  			SetShardFn(shardFn).
   172  			SetEncoderOptions(encoderOpts).
   173  			SetConnectionOptions(connectionOpts)
   174  
   175  		if c.ShardCutoverWarmupDuration != nil {
   176  			opts = opts.SetShardCutoverWarmupDuration(*c.ShardCutoverWarmupDuration)
   177  		}
   178  		if c.ShardCutoffLingerDuration != nil {
   179  			opts = opts.SetShardCutoffLingerDuration(*c.ShardCutoffLingerDuration)
   180  		}
   181  		if c.FlushWorkerCount != 0 {
   182  			opts = opts.SetFlushWorkerCount(c.FlushWorkerCount)
   183  		}
   184  		if c.ForceFlushEvery != 0 {
   185  			opts = opts.SetForceFlushEvery(c.ForceFlushEvery)
   186  		}
   187  		if c.MaxBatchSize != 0 {
   188  			opts = opts.SetMaxBatchSize(c.MaxBatchSize)
   189  		}
   190  		if c.MaxTimerBatchSize != 0 {
   191  			opts = opts.SetMaxTimerBatchSize(c.MaxTimerBatchSize)
   192  		}
   193  		if c.QueueSize != 0 {
   194  			opts = opts.SetInstanceQueueSize(c.QueueSize)
   195  		}
   196  		if c.QueueDropType != nil {
   197  			opts = opts.SetQueueDropType(*c.QueueDropType)
   198  		}
   199  	default:
   200  		return nil, fmt.Errorf("unknown client type: %v", c.Type)
   201  	}
   202  
   203  	// Validate the options.
   204  	if err := opts.Validate(); err != nil {
   205  		return nil, err
   206  	}
   207  	return opts, nil
   208  }
   209  
   210  // ConnectionConfiguration contains the connection configuration.
   211  type ConnectionConfiguration struct {
   212  	ConnectionTimeout            time.Duration        `yaml:"connectionTimeout"`
   213  	ConnectionKeepAlive          *bool                `yaml:"connectionKeepAlive"`
   214  	WriteTimeout                 time.Duration        `yaml:"writeTimeout"`
   215  	InitReconnectThreshold       int                  `yaml:"initReconnectThreshold"`
   216  	MaxReconnectThreshold        int                  `yaml:"maxReconnectThreshold"`
   217  	ReconnectThresholdMultiplier int                  `yaml:"reconnectThresholdMultiplier"`
   218  	MaxReconnectDuration         *time.Duration       `yaml:"maxReconnectDuration"`
   219  	WriteRetries                 *retry.Configuration `yaml:"writeRetries"`
   220  }
   221  
   222  // NewConnectionOptions creates new connection options.
   223  func (c *ConnectionConfiguration) NewConnectionOptions(scope tally.Scope) ConnectionOptions {
   224  	opts := NewConnectionOptions()
   225  	if c.ConnectionTimeout != 0 {
   226  		opts = opts.SetConnectionTimeout(c.ConnectionTimeout)
   227  	}
   228  	if c.ConnectionKeepAlive != nil {
   229  		opts = opts.SetConnectionKeepAlive(*c.ConnectionKeepAlive)
   230  	}
   231  	if c.WriteTimeout != 0 {
   232  		opts = opts.SetWriteTimeout(c.WriteTimeout)
   233  	}
   234  	if c.InitReconnectThreshold != 0 {
   235  		opts = opts.SetInitReconnectThreshold(c.InitReconnectThreshold)
   236  	}
   237  	if c.MaxReconnectThreshold != 0 {
   238  		opts = opts.SetMaxReconnectThreshold(c.MaxReconnectThreshold)
   239  	}
   240  	if c.ReconnectThresholdMultiplier != 0 {
   241  		opts = opts.SetReconnectThresholdMultiplier(c.ReconnectThresholdMultiplier)
   242  	}
   243  	if c.MaxReconnectDuration != nil {
   244  		opts = opts.SetMaxReconnectDuration(*c.MaxReconnectDuration)
   245  	}
   246  	if c.WriteRetries != nil {
   247  		retryOpts := c.WriteRetries.NewOptions(scope)
   248  		opts = opts.SetWriteRetryOptions(retryOpts)
   249  	}
   250  	return opts
   251  }
   252  
   253  // EncoderConfiguration configures the encoder.
   254  type EncoderConfiguration struct {
   255  	InitBufferSize *int                              `yaml:"initBufferSize"`
   256  	MaxMessageSize *int                              `yaml:"maxMessageSize"`
   257  	BytesPool      *pool.BucketizedPoolConfiguration `yaml:"bytesPool"`
   258  }
   259  
   260  // NewEncoderOptions create a new set of encoder options.
   261  func (c *EncoderConfiguration) NewEncoderOptions(
   262  	instrumentOpts instrument.Options,
   263  ) protobuf.UnaggregatedOptions {
   264  	opts := protobuf.NewUnaggregatedOptions()
   265  	if c.InitBufferSize != nil {
   266  		opts = opts.SetInitBufferSize(*c.InitBufferSize)
   267  	}
   268  	if c.MaxMessageSize != nil {
   269  		opts = opts.SetMaxMessageSize(*c.MaxMessageSize)
   270  	}
   271  	if c.BytesPool != nil {
   272  		sizeBuckets := c.BytesPool.NewBuckets()
   273  		objectPoolOpts := c.BytesPool.NewObjectPoolOptions(instrumentOpts)
   274  		bytesPool := pool.NewBytesPool(sizeBuckets, objectPoolOpts)
   275  		opts = opts.SetBytesPool(bytesPool)
   276  		bytesPool.Init()
   277  	}
   278  	return opts
   279  }
   280  
   281  // M3MsgConfiguration contains the M3Msg client configuration, required
   282  // if using M3Msg client type.
   283  type M3MsgConfiguration struct {
   284  	Producer producerconfig.ProducerConfiguration `yaml:"producer"`
   285  }
   286  
   287  // NewM3MsgOptions returns new M3Msg options from configuration.
   288  func (c *M3MsgConfiguration) NewM3MsgOptions(
   289  	kvClient m3clusterclient.Client,
   290  	instrumentOpts instrument.Options,
   291  	rwOpts xio.Options,
   292  ) (M3MsgOptions, error) {
   293  	opts := NewM3MsgOptions()
   294  
   295  	// For M3Msg clients we want to use the default timer options
   296  	// as defined by the default M3Msg options for low overhead
   297  	// timers.
   298  	instrumentOpts = instrumentOpts.SetTimerOptions(opts.TimerOptions())
   299  
   300  	producer, err := c.Producer.NewProducer(kvClient, instrumentOpts, rwOpts)
   301  	if err != nil {
   302  		return nil, err
   303  	}
   304  
   305  	opts = opts.SetProducer(producer)
   306  
   307  	// Validate the options.
   308  	if err := opts.Validate(); err != nil {
   309  		return nil, err
   310  	}
   311  	return opts, nil
   312  }