github.com/m3db/m3@v1.5.0/src/msg/producer/config/writer.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  	"time"
    26  
    27  	"github.com/uber-go/tally"
    28  
    29  	"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/cluster/services"
    33  	"github.com/m3db/m3/src/msg/producer/writer"
    34  	"github.com/m3db/m3/src/msg/protocol/proto"
    35  	"github.com/m3db/m3/src/msg/topic"
    36  	"github.com/m3db/m3/src/x/instrument"
    37  	xio "github.com/m3db/m3/src/x/io"
    38  	"github.com/m3db/m3/src/x/pool"
    39  	"github.com/m3db/m3/src/x/retry"
    40  )
    41  
    42  // ConnectionConfiguration configs the connection options.
    43  type ConnectionConfiguration struct {
    44  	NumConnections  *int                 `yaml:"numConnections"`
    45  	DialTimeout     *time.Duration       `yaml:"dialTimeout"`
    46  	WriteTimeout    *time.Duration       `yaml:"writeTimeout"`
    47  	KeepAlivePeriod *time.Duration       `yaml:"keepAlivePeriod"`
    48  	ResetDelay      *time.Duration       `yaml:"resetDelay"`
    49  	Retry           *retry.Configuration `yaml:"retry"`
    50  	FlushInterval   *time.Duration       `yaml:"flushInterval"`
    51  	WriteBufferSize *int                 `yaml:"writeBufferSize"`
    52  	ReadBufferSize  *int                 `yaml:"readBufferSize"`
    53  	// ContextDialer specifies a custom dialer to use when creating TCP connections to the consumer.
    54  	// See writer.ConnectionOptions.ContextDialer for details.
    55  	ContextDialer writer.ContextDialerFn `yaml:"-"` // not serializable
    56  }
    57  
    58  // NewOptions creates connection options.
    59  func (c *ConnectionConfiguration) NewOptions(iOpts instrument.Options) writer.ConnectionOptions {
    60  	opts := writer.NewConnectionOptions()
    61  	if c.NumConnections != nil {
    62  		opts = opts.SetNumConnections(*c.NumConnections)
    63  	}
    64  	if c.ContextDialer != nil {
    65  		opts = opts.SetContextDialer(c.ContextDialer)
    66  	}
    67  	if c.DialTimeout != nil {
    68  		opts = opts.SetDialTimeout(*c.DialTimeout)
    69  	}
    70  	if c.WriteTimeout != nil {
    71  		opts = opts.SetWriteTimeout(*c.WriteTimeout)
    72  	}
    73  	if c.KeepAlivePeriod != nil {
    74  		opts = opts.SetKeepAlivePeriod(*c.KeepAlivePeriod)
    75  	}
    76  	if c.ResetDelay != nil {
    77  		opts = opts.SetResetDelay(*c.ResetDelay)
    78  	}
    79  	if c.Retry != nil {
    80  		opts = opts.SetRetryOptions(c.Retry.NewOptions(iOpts.MetricsScope()))
    81  	}
    82  	if c.FlushInterval != nil {
    83  		opts = opts.SetFlushInterval(*c.FlushInterval)
    84  	}
    85  	if c.WriteBufferSize != nil {
    86  		opts = opts.SetWriteBufferSize(*c.WriteBufferSize)
    87  	}
    88  	if c.ReadBufferSize != nil {
    89  		opts = opts.SetReadBufferSize(*c.ReadBufferSize)
    90  	}
    91  	return opts.SetInstrumentOptions(iOpts)
    92  }
    93  
    94  // WriterConfiguration configs the writer options.
    95  type WriterConfiguration struct {
    96  	TopicName                         string                         `yaml:"topicName" validate:"nonzero"`
    97  	TopicServiceOverride              kv.OverrideConfiguration       `yaml:"topicServiceOverride"`
    98  	TopicWatchInitTimeout             *time.Duration                 `yaml:"topicWatchInitTimeout"`
    99  	PlacementOptions                  placement.Configuration        `yaml:"placement"`
   100  	PlacementServiceOverride          services.OverrideConfiguration `yaml:"placementServiceOverride"`
   101  	PlacementWatchInitTimeout         *time.Duration                 `yaml:"placementWatchInitTimeout"`
   102  	MessagePool                       *pool.ObjectPoolConfiguration  `yaml:"messagePool"`
   103  	MessageQueueNewWritesScanInterval *time.Duration                 `yaml:"messageQueueNewWritesScanInterval"`
   104  	MessageQueueFullScanInterval      *time.Duration                 `yaml:"messageQueueFullScanInterval"`
   105  	MessageQueueScanBatchSize         *int                           `yaml:"messageQueueScanBatchSize"`
   106  	InitialAckMapSize                 *int                           `yaml:"initialAckMapSize"`
   107  	CloseCheckInterval                *time.Duration                 `yaml:"closeCheckInterval"`
   108  	AckErrorRetry                     *retry.Configuration           `yaml:"ackErrorRetry"`
   109  	Encoder                           *proto.Configuration           `yaml:"encoder"`
   110  	Decoder                           *proto.Configuration           `yaml:"decoder"`
   111  	Connection                        *ConnectionConfiguration       `yaml:"connection"`
   112  
   113  	// StaticMessageRetry configs a static message retry policy.
   114  	StaticMessageRetry *StaticMessageRetryConfiguration `yaml:"staticMessageRetry"`
   115  	// MessageRetry configs a algorithmic retry policy.
   116  	// Only one of the retry configuration should be used.
   117  	MessageRetry *retry.Configuration `yaml:"messageRetry"`
   118  
   119  	// IgnoreCutoffCutover allows producing writes ignoring cutoff/cutover timestamp.
   120  	// Must be in sync with AggregatorConfiguration.WritesIgnoreCutoffCutover.
   121  	IgnoreCutoffCutover bool `yaml:"ignoreCutoffCutover"`
   122  	// WithoutConsumerScope drops the consumer tag from the metrics. For large m3msg deployments the consumer tag can
   123  	// add a lot of cardinality to the metrics.
   124  	WithoutConsumerScope bool `yaml:"withoutConsumerScope"`
   125  }
   126  
   127  // StaticMessageRetryConfiguration configs the static message retry policy.
   128  // When messageRetry config exists, messageRetry will override the static config.
   129  type StaticMessageRetryConfiguration struct {
   130  	Backoff []time.Duration `yaml:"backoff"`
   131  }
   132  
   133  // NewOptions creates writer options.
   134  func (c *WriterConfiguration) NewOptions(
   135  	cs client.Client,
   136  	iOpts instrument.Options,
   137  	rwOptions xio.Options,
   138  ) (writer.Options, error) {
   139  	opts := writer.NewOptions().
   140  		SetTopicName(c.TopicName).
   141  		SetPlacementOptions(c.PlacementOptions.NewOptions()).
   142  		SetInstrumentOptions(iOpts).
   143  		SetWithoutConsumerScope(c.WithoutConsumerScope)
   144  
   145  	kvOpts, err := c.TopicServiceOverride.NewOverrideOptions()
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	topicServiceOpts := topic.NewServiceOptions().
   151  		SetConfigService(cs).
   152  		SetKVOverrideOptions(kvOpts)
   153  	ts, err := topic.NewService(topicServiceOpts)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	opts = opts.SetTopicService(ts)
   159  
   160  	if c.TopicWatchInitTimeout != nil {
   161  		opts = opts.SetTopicWatchInitTimeout(*c.TopicWatchInitTimeout)
   162  	}
   163  	sd, err := cs.Services(c.PlacementServiceOverride.NewOptions())
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  
   168  	opts = opts.SetServiceDiscovery(sd)
   169  
   170  	if c.PlacementWatchInitTimeout != nil {
   171  		opts = opts.SetPlacementWatchInitTimeout(*c.PlacementWatchInitTimeout)
   172  	}
   173  	if c.MessagePool != nil {
   174  		opts = opts.SetMessagePoolOptions(c.MessagePool.NewObjectPoolOptions(iOpts))
   175  	}
   176  	opts, err = c.setRetryOptions(opts, iOpts)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  	if c.MessageQueueNewWritesScanInterval != nil {
   181  		opts = opts.SetMessageQueueNewWritesScanInterval(*c.MessageQueueNewWritesScanInterval)
   182  	}
   183  	if c.MessageQueueFullScanInterval != nil {
   184  		opts = opts.SetMessageQueueFullScanInterval(*c.MessageQueueFullScanInterval)
   185  	}
   186  	if c.MessageQueueScanBatchSize != nil {
   187  		opts = opts.SetMessageQueueScanBatchSize(*c.MessageQueueScanBatchSize)
   188  	}
   189  	if c.InitialAckMapSize != nil {
   190  		opts = opts.SetInitialAckMapSize(*c.InitialAckMapSize)
   191  	}
   192  	if c.CloseCheckInterval != nil {
   193  		opts = opts.SetCloseCheckInterval(*c.CloseCheckInterval)
   194  	}
   195  	if c.AckErrorRetry != nil {
   196  		opts = opts.SetAckErrorRetryOptions(c.AckErrorRetry.NewOptions(tally.NoopScope))
   197  	}
   198  	if c.Encoder != nil {
   199  		opts = opts.SetEncoderOptions(c.Encoder.NewOptions(iOpts))
   200  	}
   201  	if c.Decoder != nil {
   202  		opts = opts.SetDecoderOptions(c.Decoder.NewOptions(iOpts))
   203  	}
   204  	if c.Connection != nil {
   205  		opts = opts.SetConnectionOptions(c.Connection.NewOptions(iOpts))
   206  	}
   207  
   208  	opts = opts.SetIgnoreCutoffCutover(c.IgnoreCutoffCutover)
   209  
   210  	opts = opts.SetDecoderOptions(opts.DecoderOptions().SetRWOptions(rwOptions))
   211  	return opts, nil
   212  }
   213  
   214  func (c *WriterConfiguration) setRetryOptions(
   215  	opts writer.Options,
   216  	iOpts instrument.Options,
   217  ) (writer.Options, error) {
   218  	if c.StaticMessageRetry != nil && c.MessageRetry != nil {
   219  		return nil, errors.New("invalid writer config with both static and algorithmic retry config set")
   220  	}
   221  	if c.MessageRetry != nil {
   222  		return opts.SetMessageRetryNanosFn(
   223  			writer.NextRetryNanosFn(c.MessageRetry.NewOptions(iOpts.MetricsScope())),
   224  		), nil
   225  	}
   226  	if c.StaticMessageRetry != nil {
   227  		fn, err := writer.StaticRetryNanosFn(c.StaticMessageRetry.Backoff)
   228  		if err != nil {
   229  			return nil, err
   230  		}
   231  		return opts.SetMessageRetryNanosFn(fn), nil
   232  	}
   233  	return opts, nil
   234  }