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 }