github.com/m3db/m3@v1.5.0/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  func (c *Configuration) newClientOptions(
   100  	kvClient m3clusterclient.Client,
   101  	clockOpts clock.Options,
   102  	instrumentOpts instrument.Options,
   103  	rwOpts xio.Options,
   104  ) (Options, error) {
   105  	opts := NewOptions().
   106  		SetAggregatorClientType(c.Type).
   107  		SetClockOptions(clockOpts).
   108  		SetInstrumentOptions(instrumentOpts).
   109  		SetRWOptions(rwOpts)
   110  
   111  	switch c.Type {
   112  	case M3MsgAggregatorClient:
   113  		m3msgCfg := c.M3Msg
   114  		if m3msgCfg == nil {
   115  			return nil, errNoM3MsgOptions
   116  		}
   117  
   118  		m3msgOpts, err := m3msgCfg.NewM3MsgOptions(kvClient, instrumentOpts, rwOpts)
   119  		if err != nil {
   120  			return nil, err
   121  		}
   122  
   123  		// Allow M3Msg options to override the timer options for instrument options.
   124  		opts = opts.SetInstrumentOptions(
   125  			opts.InstrumentOptions().SetTimerOptions(m3msgOpts.TimerOptions()))
   126  
   127  		// Set the M3Msg options configured.
   128  		opts = opts.SetM3MsgOptions(m3msgOpts)
   129  	case LegacyAggregatorClient:
   130  		placementKV := c.PlacementKV
   131  		if placementKV == nil {
   132  			return nil, errLegacyClientNoPlacementKVConfig
   133  		}
   134  
   135  		placementWatcher := c.Watcher
   136  		if placementWatcher == nil {
   137  			return nil, errLegacyClientNoWatcherConfig
   138  		}
   139  
   140  		scope := instrumentOpts.MetricsScope()
   141  		connectionOpts := c.Connection.NewConnectionOptions(scope.SubScope("connection"))
   142  		kvOpts, err := placementKV.NewOverrideOptions()
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  
   147  		placementStore, err := kvClient.Store(kvOpts)
   148  		if err != nil {
   149  			return nil, err
   150  		}
   151  
   152  		iOpts := instrumentOpts.SetMetricsScope(scope.SubScope("encoder"))
   153  		encoderOpts := c.Encoder.NewEncoderOptions(iOpts)
   154  
   155  		iOpts = instrumentOpts.SetMetricsScope(scope.SubScope("placement-watcher"))
   156  		watcherOpts := placementWatcher.NewOptions(placementStore, iOpts)
   157  
   158  		// Get the shard fn.
   159  		hashType := sharding.DefaultHash
   160  		if c.HashType != nil {
   161  			hashType = *c.HashType
   162  		}
   163  		shardFn, err := hashType.ShardFn()
   164  		if err != nil {
   165  			return nil, err
   166  		}
   167  
   168  		opts = opts.SetWatcherOptions(watcherOpts).
   169  			SetShardFn(shardFn).
   170  			SetEncoderOptions(encoderOpts).
   171  			SetConnectionOptions(connectionOpts)
   172  
   173  		if c.ShardCutoverWarmupDuration != nil {
   174  			opts = opts.SetShardCutoverWarmupDuration(*c.ShardCutoverWarmupDuration)
   175  		}
   176  		if c.ShardCutoffLingerDuration != nil {
   177  			opts = opts.SetShardCutoffLingerDuration(*c.ShardCutoffLingerDuration)
   178  		}
   179  		if c.FlushWorkerCount != 0 {
   180  			opts = opts.SetFlushWorkerCount(c.FlushWorkerCount)
   181  		}
   182  		if c.ForceFlushEvery != 0 {
   183  			opts = opts.SetForceFlushEvery(c.ForceFlushEvery)
   184  		}
   185  		if c.MaxBatchSize != 0 {
   186  			opts = opts.SetMaxBatchSize(c.MaxBatchSize)
   187  		}
   188  		if c.MaxTimerBatchSize != 0 {
   189  			opts = opts.SetMaxTimerBatchSize(c.MaxTimerBatchSize)
   190  		}
   191  		if c.QueueSize != 0 {
   192  			opts = opts.SetInstanceQueueSize(c.QueueSize)
   193  		}
   194  		if c.QueueDropType != nil {
   195  			opts = opts.SetQueueDropType(*c.QueueDropType)
   196  		}
   197  	default:
   198  		return nil, fmt.Errorf("unknown client type: %v", c.Type)
   199  	}
   200  
   201  	// Validate the options.
   202  	if err := opts.Validate(); err != nil {
   203  		return nil, err
   204  	}
   205  	return opts, nil
   206  }
   207  
   208  // ConnectionConfiguration contains the connection configuration.
   209  type ConnectionConfiguration struct {
   210  	ConnectionTimeout            time.Duration        `yaml:"connectionTimeout"`
   211  	ConnectionKeepAlive          *bool                `yaml:"connectionKeepAlive"`
   212  	WriteTimeout                 time.Duration        `yaml:"writeTimeout"`
   213  	InitReconnectThreshold       int                  `yaml:"initReconnectThreshold"`
   214  	MaxReconnectThreshold        int                  `yaml:"maxReconnectThreshold"`
   215  	ReconnectThresholdMultiplier int                  `yaml:"reconnectThresholdMultiplier"`
   216  	MaxReconnectDuration         *time.Duration       `yaml:"maxReconnectDuration"`
   217  	WriteRetries                 *retry.Configuration `yaml:"writeRetries"`
   218  }
   219  
   220  // NewConnectionOptions creates new connection options.
   221  func (c *ConnectionConfiguration) NewConnectionOptions(scope tally.Scope) ConnectionOptions {
   222  	opts := NewConnectionOptions()
   223  	if c.ConnectionTimeout != 0 {
   224  		opts = opts.SetConnectionTimeout(c.ConnectionTimeout)
   225  	}
   226  	if c.ConnectionKeepAlive != nil {
   227  		opts = opts.SetConnectionKeepAlive(*c.ConnectionKeepAlive)
   228  	}
   229  	if c.WriteTimeout != 0 {
   230  		opts = opts.SetWriteTimeout(c.WriteTimeout)
   231  	}
   232  	if c.InitReconnectThreshold != 0 {
   233  		opts = opts.SetInitReconnectThreshold(c.InitReconnectThreshold)
   234  	}
   235  	if c.MaxReconnectThreshold != 0 {
   236  		opts = opts.SetMaxReconnectThreshold(c.MaxReconnectThreshold)
   237  	}
   238  	if c.ReconnectThresholdMultiplier != 0 {
   239  		opts = opts.SetReconnectThresholdMultiplier(c.ReconnectThresholdMultiplier)
   240  	}
   241  	if c.MaxReconnectDuration != nil {
   242  		opts = opts.SetMaxReconnectDuration(*c.MaxReconnectDuration)
   243  	}
   244  	if c.WriteRetries != nil {
   245  		retryOpts := c.WriteRetries.NewOptions(scope)
   246  		opts = opts.SetWriteRetryOptions(retryOpts)
   247  	}
   248  	return opts
   249  }
   250  
   251  // EncoderConfiguration configures the encoder.
   252  type EncoderConfiguration struct {
   253  	InitBufferSize *int                              `yaml:"initBufferSize"`
   254  	MaxMessageSize *int                              `yaml:"maxMessageSize"`
   255  	BytesPool      *pool.BucketizedPoolConfiguration `yaml:"bytesPool"`
   256  }
   257  
   258  // NewEncoderOptions create a new set of encoder options.
   259  func (c *EncoderConfiguration) NewEncoderOptions(
   260  	instrumentOpts instrument.Options,
   261  ) protobuf.UnaggregatedOptions {
   262  	opts := protobuf.NewUnaggregatedOptions()
   263  	if c.InitBufferSize != nil {
   264  		opts = opts.SetInitBufferSize(*c.InitBufferSize)
   265  	}
   266  	if c.MaxMessageSize != nil {
   267  		opts = opts.SetMaxMessageSize(*c.MaxMessageSize)
   268  	}
   269  	if c.BytesPool != nil {
   270  		sizeBuckets := c.BytesPool.NewBuckets()
   271  		objectPoolOpts := c.BytesPool.NewObjectPoolOptions(instrumentOpts)
   272  		bytesPool := pool.NewBytesPool(sizeBuckets, objectPoolOpts)
   273  		opts = opts.SetBytesPool(bytesPool)
   274  		bytesPool.Init()
   275  	}
   276  	return opts
   277  }
   278  
   279  // M3MsgConfiguration contains the M3Msg client configuration, required
   280  // if using M3Msg client type.
   281  type M3MsgConfiguration struct {
   282  	Producer producerconfig.ProducerConfiguration `yaml:"producer"`
   283  }
   284  
   285  // NewM3MsgOptions returns new M3Msg options from configuration.
   286  func (c *M3MsgConfiguration) NewM3MsgOptions(
   287  	kvClient m3clusterclient.Client,
   288  	instrumentOpts instrument.Options,
   289  	rwOpts xio.Options,
   290  ) (M3MsgOptions, error) {
   291  	opts := NewM3MsgOptions()
   292  
   293  	// For M3Msg clients we want to use the default timer options
   294  	// as defined by the default M3Msg options for low overhead
   295  	// timers.
   296  	instrumentOpts = instrumentOpts.SetTimerOptions(opts.TimerOptions())
   297  
   298  	producer, err := c.Producer.NewProducer(kvClient, instrumentOpts, rwOpts)
   299  	if err != nil {
   300  		return nil, err
   301  	}
   302  
   303  	opts = opts.SetProducer(producer)
   304  
   305  	// Validate the options.
   306  	if err := opts.Validate(); err != nil {
   307  		return nil, err
   308  	}
   309  	return opts, nil
   310  }