github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/cmd/services/m3coordinator/server/m3msg/protobuf_handler.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 m3msg
    22  
    23  import (
    24  	"context"
    25  	"sync"
    26  
    27  	"github.com/m3db/m3/src/metrics/encoding/protobuf"
    28  	"github.com/m3db/m3/src/metrics/policy"
    29  	"github.com/m3db/m3/src/msg/consumer"
    30  	"github.com/m3db/m3/src/x/instrument"
    31  	"github.com/m3db/m3/src/x/pool"
    32  
    33  	"github.com/uber-go/tally"
    34  	"go.uber.org/zap"
    35  )
    36  
    37  // Options for the ingest handler.
    38  type Options struct {
    39  	InstrumentOptions          instrument.Options
    40  	WriteFn                    WriteFn
    41  	ProtobufDecoderPoolOptions pool.ObjectPoolOptions
    42  	BlockholePolicies          []policy.StoragePolicy
    43  }
    44  
    45  type handlerMetrics struct {
    46  	metricAccepted               tally.Counter
    47  	droppedMetricBlackholePolicy tally.Counter
    48  	droppedMetricDecodeError     tally.Counter
    49  }
    50  
    51  func newHandlerMetrics(scope tally.Scope) handlerMetrics {
    52  	messageScope := scope.SubScope("metric")
    53  	return handlerMetrics{
    54  		metricAccepted:   messageScope.Counter("accepted"),
    55  		droppedMetricDecodeError: messageScope.Tagged(map[string]string{
    56  			"reason": "decode-error",
    57  		}).Counter("dropped"),
    58  		droppedMetricBlackholePolicy: messageScope.Tagged(map[string]string{
    59  			"reason": "blackhole-policy",
    60  		}).Counter("dropped"),
    61  	}
    62  }
    63  
    64  type pbHandler struct {
    65  	ctx     context.Context
    66  	writeFn WriteFn
    67  	pool    protobuf.AggregatedDecoderPool
    68  	wg      *sync.WaitGroup
    69  	logger  *zap.Logger
    70  	m       handlerMetrics
    71  
    72  	// Set of policies for which when we see a metric we drop it on the floor.
    73  	blackholePolicies []policy.StoragePolicy
    74  }
    75  
    76  func newProtobufProcessor(opts Options) consumer.MessageProcessor {
    77  	p := protobuf.NewAggregatedDecoderPool(opts.ProtobufDecoderPoolOptions)
    78  	p.Init()
    79  
    80  	h := &pbHandler{
    81  		ctx:               context.Background(),
    82  		writeFn:           opts.WriteFn,
    83  		pool:              p,
    84  		wg:                &sync.WaitGroup{},
    85  		logger:            opts.InstrumentOptions.Logger(),
    86  		m:                 newHandlerMetrics(opts.InstrumentOptions.MetricsScope()),
    87  		blackholePolicies: opts.BlockholePolicies,
    88  	}
    89  
    90  	if len(opts.BlockholePolicies) > 0 {
    91  		policyNames := make([]string, 0, len(opts.BlockholePolicies))
    92  		for _, sp := range h.blackholePolicies {
    93  			policyNames = append(policyNames, sp.String())
    94  		}
    95  		h.logger.Info("m3msg handler blackholing metrics for configured policies", zap.Strings("policyNames", policyNames))
    96  	}
    97  
    98  	return h
    99  }
   100  
   101  func (h *pbHandler) Process(msg consumer.Message) {
   102  	dec := h.pool.Get()
   103  	if err := dec.Decode(msg.Bytes()); err != nil {
   104  		h.logger.Error("could not decode metric from message", zap.Error(err))
   105  		h.m.droppedMetricDecodeError.Inc(1)
   106  		return
   107  	}
   108  	h.m.metricAccepted.Inc(1)
   109  
   110  	h.wg.Add(1)
   111  	r := NewProtobufCallback(msg, dec, h.wg)
   112  	sp := dec.StoragePolicy()
   113  	// If storage policy is blackholed, ack the message immediately and don't
   114  	// bother passing down the write path.
   115  	for _, blackholeSp := range h.blackholePolicies {
   116  		if sp.Equivalent(blackholeSp) {
   117  			h.m.droppedMetricBlackholePolicy.Inc(1)
   118  			r.Callback(OnSuccess)
   119  			return
   120  		}
   121  	}
   122  
   123  	h.writeFn(h.ctx, dec.ID(), dec.TimeNanos(), dec.EncodeNanos(), dec.Value(), dec.Annotation(), sp, r)
   124  }
   125  
   126  func (h *pbHandler) Close() { h.wg.Wait() }
   127  
   128  type protobufCallback struct {
   129  	msg consumer.Message
   130  	dec *protobuf.AggregatedDecoder
   131  	wg  *sync.WaitGroup
   132  }
   133  
   134  // NewProtobufCallback creates a callbackable.
   135  func NewProtobufCallback(
   136  	msg consumer.Message,
   137  	dec *protobuf.AggregatedDecoder,
   138  	wg *sync.WaitGroup,
   139  ) Callbackable {
   140  	return &protobufCallback{
   141  		msg: msg,
   142  		dec: dec,
   143  		wg:  wg,
   144  	}
   145  }
   146  
   147  func (c *protobufCallback) Callback(t CallbackType) {
   148  	switch t {
   149  	case OnSuccess, OnNonRetriableError:
   150  		c.msg.Ack()
   151  	}
   152  	c.wg.Done()
   153  	// Close the decoder, returns the underlying bytes to the pool.
   154  	c.dec.Close()
   155  }