github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/msg/consumer/consumer.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 consumer
    22  
    23  import (
    24  	"net"
    25  	"sync"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/msg/generated/proto/msgpb"
    29  	"github.com/m3db/m3/src/msg/protocol/proto"
    30  	"github.com/m3db/m3/src/x/clock"
    31  	xio "github.com/m3db/m3/src/x/io"
    32  	xtime "github.com/m3db/m3/src/x/time"
    33  
    34  	"github.com/uber-go/tally"
    35  	"go.uber.org/zap"
    36  )
    37  
    38  type listener struct {
    39  	net.Listener
    40  
    41  	opts    Options
    42  	msgPool *messagePool
    43  	m       metrics
    44  }
    45  
    46  // NewListener creates a consumer listener.
    47  func NewListener(addr string, opts Options) (Listener, error) {
    48  	if opts == nil {
    49  		opts = NewOptions()
    50  	}
    51  	lis, err := net.Listen("tcp", addr)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	mPool := newMessagePool(opts.MessagePoolOptions())
    56  	mPool.Init()
    57  	return &listener{
    58  		Listener: lis,
    59  		opts:     opts,
    60  		msgPool:  mPool,
    61  		m:        newConsumerMetrics(opts.InstrumentOptions().MetricsScope()),
    62  	}, nil
    63  }
    64  
    65  func (l *listener) Accept() (Consumer, error) {
    66  	conn, err := l.Listener.Accept()
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	return newConsumer(conn, l.msgPool, l.opts, l.m, NewNoOpMessageProcessor()), nil
    72  }
    73  
    74  type metrics struct {
    75  	messageReceived    tally.Counter
    76  	messageDecodeError tally.Counter
    77  	ackSent            tally.Counter
    78  	ackEncodeError     tally.Counter
    79  	ackWriteError      tally.Counter
    80  	// the duration between the producer sending the message and the consumer reading the message.
    81  	receiveLatency tally.Histogram
    82  	// the duration between the consumer reading the message and sending an ack to the producer.
    83  	handleLatency tally.Histogram
    84  }
    85  
    86  func newConsumerMetrics(scope tally.Scope) metrics {
    87  	return metrics{
    88  		messageReceived:    scope.Counter("message-received"),
    89  		messageDecodeError: scope.Counter("message-decode-error"),
    90  		ackSent:            scope.Counter("ack-sent"),
    91  		ackEncodeError:     scope.Counter("ack-encode-error"),
    92  		ackWriteError:      scope.Counter("ack-write-error"),
    93  		receiveLatency: scope.Histogram("receive-latency",
    94  			// 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.2s, 2.4s, 4.8s, 9.6s
    95  			tally.MustMakeExponentialDurationBuckets(time.Millisecond*10, 2, 11)),
    96  		handleLatency: scope.Histogram("handle-latency",
    97  			// 10ms, 20ms, 40ms, 80ms, 160ms, 320ms, 640ms, 1.2s, 2.4s, 4.8s, 9.6s
    98  			tally.MustMakeExponentialDurationBuckets(time.Millisecond*10, 2, 11)),
    99  	}
   100  }
   101  
   102  type consumer struct {
   103  	sync.Mutex
   104  
   105  	opts    Options
   106  	mPool   *messagePool
   107  	encoder proto.Encoder
   108  	decoder proto.Decoder
   109  	w       xio.ResettableWriter
   110  	conn    net.Conn
   111  
   112  	ackPb            msgpb.Ack
   113  	closed           bool
   114  	doneCh           chan struct{}
   115  	wg               sync.WaitGroup
   116  	m                metrics
   117  	messageProcessor MessageProcessor
   118  }
   119  
   120  func newConsumer(
   121  	conn net.Conn,
   122  	mPool *messagePool,
   123  	opts Options,
   124  	m metrics,
   125  	mp MessageProcessor,
   126  ) *consumer {
   127  	var (
   128  		wOpts = xio.ResettableWriterOptions{
   129  			WriteBufferSize: opts.ConnectionWriteBufferSize(),
   130  		}
   131  
   132  		rwOpts   = opts.DecoderOptions().RWOptions()
   133  		writerFn = rwOpts.ResettableWriterFn()
   134  	)
   135  
   136  	return &consumer{
   137  		opts:    opts,
   138  		mPool:   mPool,
   139  		encoder: proto.NewEncoder(opts.EncoderOptions()),
   140  		decoder: proto.NewDecoder(
   141  			conn, opts.DecoderOptions(), opts.ConnectionReadBufferSize(),
   142  		),
   143  		w:                writerFn(newConnWithTimeout(conn, opts.ConnectionWriteTimeout(), time.Now), wOpts),
   144  		conn:             conn,
   145  		closed:           false,
   146  		doneCh:           make(chan struct{}),
   147  		m:                m,
   148  		messageProcessor: mp,
   149  	}
   150  }
   151  
   152  func (c *consumer) Init() {
   153  	c.wg.Add(1)
   154  	go func() {
   155  		c.ackUntilClose()
   156  		c.wg.Done()
   157  	}()
   158  }
   159  func (c *consumer) process(m Message) {
   160  	c.messageProcessor.Process(m)
   161  }
   162  
   163  func (c *consumer) Message() (Message, error) {
   164  	m := c.mPool.Get()
   165  	m.reset(c)
   166  	if err := c.decoder.Decode(m); err != nil {
   167  		c.mPool.Put(m)
   168  		c.m.messageDecodeError.Inc(1)
   169  		return nil, err
   170  	}
   171  	if m.Metadata.SentAtNanos > 0 {
   172  		c.m.receiveLatency.RecordDuration(xtime.Since(xtime.UnixNano(m.Metadata.SentAtNanos)))
   173  	}
   174  	c.m.messageReceived.Inc(1)
   175  	return m, nil
   176  }
   177  
   178  // This function could be called concurrently if messages are being
   179  // processed concurrently.
   180  func (c *consumer) tryAck(m msgpb.Metadata) {
   181  	c.Lock()
   182  	if c.closed {
   183  		c.Unlock()
   184  		return
   185  	}
   186  	c.ackPb.Metadata = append(c.ackPb.Metadata, m)
   187  	ackLen := len(c.ackPb.Metadata)
   188  	if ackLen < c.opts.AckBufferSize() {
   189  		c.Unlock()
   190  		return
   191  	}
   192  	c.trySendAcksWithLock(ackLen)
   193  	c.Unlock()
   194  }
   195  
   196  func (c *consumer) ackUntilClose() {
   197  	flushTicker := time.NewTicker(c.opts.AckFlushInterval())
   198  	defer flushTicker.Stop()
   199  
   200  	for {
   201  		select {
   202  		case <-flushTicker.C:
   203  			c.tryAckAndFlush()
   204  		case <-c.doneCh:
   205  			c.tryAckAndFlush()
   206  			return
   207  		}
   208  	}
   209  }
   210  
   211  func (c *consumer) tryAckAndFlush() {
   212  	c.Lock()
   213  	if ackLen := len(c.ackPb.Metadata); ackLen > 0 {
   214  		c.trySendAcksWithLock(ackLen)
   215  	}
   216  	c.w.Flush()
   217  	c.Unlock()
   218  }
   219  
   220  // if acks fail to send the client will retry sending the messages.
   221  func (c *consumer) trySendAcksWithLock(ackLen int) {
   222  	err := c.encoder.Encode(&c.ackPb)
   223  	log := c.opts.InstrumentOptions().Logger()
   224  	c.ackPb.Metadata = c.ackPb.Metadata[:0]
   225  	if err != nil {
   226  		c.m.ackEncodeError.Inc(1)
   227  		log.Error("failed to encode ack. client will retry sending message.", zap.Error(err))
   228  		return
   229  	}
   230  	_, err = c.w.Write(c.encoder.Bytes())
   231  	if err != nil {
   232  		c.m.ackWriteError.Inc(1)
   233  		log.Error("failed to write ack. client will retry sending message.", zap.Error(err))
   234  		c.tryCloseConn()
   235  		return
   236  	}
   237  	if err := c.w.Flush(); err != nil {
   238  		c.m.ackWriteError.Inc(1)
   239  		log.Error("failed to flush ack. client will retry sending message.", zap.Error(err))
   240  		c.tryCloseConn()
   241  		return
   242  	}
   243  	c.m.ackSent.Inc(int64(ackLen))
   244  }
   245  
   246  func (c *consumer) tryCloseConn() {
   247  	if err := c.conn.Close(); err != nil {
   248  		c.opts.InstrumentOptions().Logger().Error("failed to close connection.", zap.Error(err))
   249  	}
   250  }
   251  
   252  func (c *consumer) Close() {
   253  	c.Lock()
   254  	if c.closed {
   255  		c.Unlock()
   256  		return
   257  	}
   258  	c.closed = true
   259  	c.Unlock()
   260  
   261  	close(c.doneCh)
   262  	c.wg.Wait()
   263  	c.conn.Close()
   264  }
   265  
   266  type message struct {
   267  	msgpb.Message
   268  
   269  	mPool *messagePool
   270  	c     *consumer
   271  }
   272  
   273  func newMessage(p *messagePool) *message {
   274  	return &message{mPool: p}
   275  }
   276  
   277  func (m *message) Bytes() []byte {
   278  	return m.Value
   279  }
   280  
   281  func (m *message) Ack() {
   282  	m.c.tryAck(m.Metadata)
   283  	if m.mPool != nil {
   284  		m.mPool.Put(m)
   285  	}
   286  }
   287  
   288  func (m *message) reset(c *consumer) {
   289  	m.c = c
   290  	resetProto(&m.Message)
   291  }
   292  
   293  func (m *message) ShardID() uint64 {
   294  	return m.Metadata.Shard
   295  }
   296  
   297  func (m *message) SentAtNanos() uint64 {
   298  	return m.Metadata.SentAtNanos
   299  }
   300  
   301  func resetProto(m *msgpb.Message) {
   302  	m.Metadata.Id = 0
   303  	m.Metadata.Shard = 0
   304  	m.Value = m.Value[:0]
   305  }
   306  
   307  type connWithTimeout struct {
   308  	net.Conn
   309  
   310  	timeout time.Duration
   311  	nowFn   clock.NowFn
   312  }
   313  
   314  func newConnWithTimeout(conn net.Conn, timeout time.Duration, nowFn clock.NowFn) connWithTimeout {
   315  	return connWithTimeout{
   316  		Conn:    conn,
   317  		timeout: timeout,
   318  		nowFn:   nowFn,
   319  	}
   320  }
   321  
   322  func (conn connWithTimeout) Write(p []byte) (int, error) {
   323  	if conn.timeout > 0 {
   324  		conn.SetWriteDeadline(conn.nowFn().Add(conn.timeout))
   325  	}
   326  	return conn.Conn.Write(p)
   327  }