github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/cmd/services/m3coordinator/server/m3msg/protobuf_handler_test.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  	"net"
    26  	"sync"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/m3db/m3/src/metrics/encoding/protobuf"
    31  	"github.com/m3db/m3/src/metrics/metric"
    32  	"github.com/m3db/m3/src/metrics/metric/aggregated"
    33  	"github.com/m3db/m3/src/metrics/policy"
    34  	"github.com/m3db/m3/src/msg/consumer"
    35  	"github.com/m3db/m3/src/msg/generated/proto/msgpb"
    36  	"github.com/m3db/m3/src/msg/protocol/proto"
    37  	"github.com/m3db/m3/src/x/instrument"
    38  	"github.com/m3db/m3/src/x/server"
    39  	xtime "github.com/m3db/m3/src/x/time"
    40  
    41  	"github.com/stretchr/testify/require"
    42  )
    43  
    44  var (
    45  	testID = "stats.foo1.gauges.m3+some-name+dc=foo1,env=production,service=foo,type=gauge"
    46  
    47  	// baseStoragePolicy represents what we typically define in config for SP.
    48  	// precisionStoragePolicy is the same retention/resolution, but includes the
    49  	// precision (which is often included with incoming writes).
    50  	baseStoragePolicy      = policy.MustParseStoragePolicy("1m:40d")
    51  	precisionStoragePolicy = policy.NewStoragePolicy(time.Minute, xtime.Second, 40*24*time.Hour)
    52  )
    53  
    54  func TestM3MsgServerWithProtobufHandler(t *testing.T) {
    55  	l, err := net.Listen("tcp", "127.0.0.1:0")
    56  	require.NoError(t, err)
    57  
    58  	w := &mockWriter{m: make(map[string]payload)}
    59  	hOpts := Options{
    60  		WriteFn:           w.write,
    61  		InstrumentOptions: instrument.NewOptions(),
    62  	}
    63  	opts := consumer.NewOptions().
    64  		SetAckBufferSize(1).
    65  		SetConnectionWriteBufferSize(1)
    66  
    67  	s := server.NewServer(
    68  		"a",
    69  		consumer.NewMessageHandler(consumer.SingletonMessageProcessor(newProtobufProcessor(hOpts)), opts),
    70  		server.NewOptions(),
    71  	)
    72  	s.Serve(l)
    73  
    74  	conn, err := net.Dial("tcp", l.Addr().String())
    75  	require.NoError(t, err)
    76  	m1 := aggregated.MetricWithStoragePolicy{
    77  		Metric: aggregated.Metric{
    78  			ID:        []byte(testID),
    79  			TimeNanos: 1000,
    80  			Value:     1,
    81  			Type:      metric.GaugeType,
    82  		},
    83  		StoragePolicy: precisionStoragePolicy,
    84  	}
    85  
    86  	encoder := protobuf.NewAggregatedEncoder(nil)
    87  	require.NoError(t, encoder.Encode(m1))
    88  	enc := proto.NewEncoder(opts.EncoderOptions())
    89  	require.NoError(t, enc.Encode(&msgpb.Message{
    90  		Value: encoder.Buffer().Bytes(),
    91  	}))
    92  	_, err = conn.Write(enc.Bytes())
    93  	require.NoError(t, err)
    94  
    95  	var a msgpb.Ack
    96  	dec := proto.NewDecoder(conn, opts.DecoderOptions(), 10)
    97  	require.NoError(t, dec.Decode(&a))
    98  	require.Equal(t, 1, w.ingested())
    99  
   100  	m2 := aggregated.MetricWithStoragePolicy{
   101  		Metric: aggregated.Metric{
   102  			ID:        []byte{},
   103  			TimeNanos: 0,
   104  			Value:     0,
   105  			Type:      metric.UnknownType,
   106  		},
   107  		StoragePolicy: precisionStoragePolicy,
   108  	}
   109  	require.NoError(t, encoder.Encode(m2))
   110  	enc = proto.NewEncoder(opts.EncoderOptions())
   111  	require.NoError(t, enc.Encode(&msgpb.Message{
   112  		Value: encoder.Buffer().Bytes(),
   113  	}))
   114  	_, err = conn.Write(enc.Bytes())
   115  	require.NoError(t, err)
   116  	require.NoError(t, dec.Decode(&a))
   117  	require.Equal(t, 2, w.ingested())
   118  
   119  	payload, ok := w.m[string(m1.ID)] //nolint:govet
   120  	require.True(t, ok)
   121  	require.Equal(t, string(m1.ID), payload.id)
   122  	require.Equal(t, m1.TimeNanos, payload.metricNanos)
   123  	require.Equal(t, m1.Value, payload.value)
   124  	require.Equal(t, m1.StoragePolicy, payload.sp)
   125  
   126  	payload, ok = w.m[string(m2.ID)]
   127  	require.True(t, ok)
   128  	require.Equal(t, string(m2.ID), payload.id)
   129  	require.Equal(t, m2.TimeNanos, payload.metricNanos)
   130  	require.Equal(t, m2.Value, payload.value)
   131  	require.Equal(t, m2.StoragePolicy, payload.sp)
   132  }
   133  
   134  func TestM3MsgServerWithProtobufHandler_Blackhole(t *testing.T) {
   135  	l, err := net.Listen("tcp", "127.0.0.1:0")
   136  	require.NoError(t, err)
   137  
   138  	w := &mockWriter{m: make(map[string]payload)}
   139  	hOpts := Options{
   140  		WriteFn:           w.write,
   141  		InstrumentOptions: instrument.NewOptions(),
   142  		BlockholePolicies: []policy.StoragePolicy{baseStoragePolicy},
   143  	}
   144  	opts := consumer.NewOptions().
   145  		SetAckBufferSize(1).
   146  		SetConnectionWriteBufferSize(1)
   147  
   148  	s := server.NewServer(
   149  		"a",
   150  		consumer.NewMessageHandler(consumer.SingletonMessageProcessor(newProtobufProcessor(hOpts)), opts),
   151  		server.NewOptions(),
   152  	)
   153  	s.Serve(l)
   154  
   155  	conn, err := net.Dial("tcp", l.Addr().String())
   156  	require.NoError(t, err)
   157  	m1 := aggregated.MetricWithStoragePolicy{
   158  		Metric: aggregated.Metric{
   159  			ID:        []byte(testID),
   160  			TimeNanos: 1000,
   161  			Value:     1,
   162  			Type:      metric.GaugeType,
   163  		},
   164  		StoragePolicy: precisionStoragePolicy,
   165  	}
   166  
   167  	encoder := protobuf.NewAggregatedEncoder(nil)
   168  	require.NoError(t, encoder.Encode(m1))
   169  	enc := proto.NewEncoder(opts.EncoderOptions())
   170  	require.NoError(t, enc.Encode(&msgpb.Message{
   171  		Value: encoder.Buffer().Bytes(),
   172  	}))
   173  	_, err = conn.Write(enc.Bytes())
   174  	require.NoError(t, err)
   175  
   176  	var a msgpb.Ack
   177  	dec := proto.NewDecoder(conn, opts.DecoderOptions(), 10)
   178  	require.NoError(t, dec.Decode(&a))
   179  	require.Equal(t, 0, w.ingested())
   180  
   181  	// Ensure a metric with a different policy still gets ingested.
   182  	m2 := aggregated.MetricWithStoragePolicy{
   183  		Metric: aggregated.Metric{
   184  			ID:        []byte{},
   185  			TimeNanos: 0,
   186  			Value:     0,
   187  			Type:      metric.UnknownType,
   188  		},
   189  		StoragePolicy: policy.MustParseStoragePolicy("5m:180d"),
   190  	}
   191  	require.NoError(t, encoder.Encode(m2))
   192  	enc = proto.NewEncoder(opts.EncoderOptions())
   193  	require.NoError(t, enc.Encode(&msgpb.Message{
   194  		Value: encoder.Buffer().Bytes(),
   195  	}))
   196  	_, err = conn.Write(enc.Bytes())
   197  	require.NoError(t, err)
   198  	require.NoError(t, dec.Decode(&a))
   199  	require.Equal(t, 1, w.ingested())
   200  
   201  	// Ensure a metric with base policy (equivalent but default precision) is
   202  	// still ignored.
   203  	m3 := aggregated.MetricWithStoragePolicy{
   204  		Metric: aggregated.Metric{
   205  			ID:        []byte(testID),
   206  			TimeNanos: 1000,
   207  			Value:     1,
   208  			Type:      metric.GaugeType,
   209  		},
   210  		StoragePolicy: baseStoragePolicy,
   211  	}
   212  	require.NoError(t, encoder.Encode(m3))
   213  	enc = proto.NewEncoder(opts.EncoderOptions())
   214  	require.NoError(t, enc.Encode(&msgpb.Message{
   215  		Value: encoder.Buffer().Bytes(),
   216  	}))
   217  	_, err = conn.Write(enc.Bytes())
   218  	require.NoError(t, err)
   219  	require.NoError(t, dec.Decode(&a))
   220  	require.Equal(t, 1, w.ingested())
   221  }
   222  
   223  type mockWriter struct {
   224  	sync.Mutex
   225  
   226  	m map[string]payload
   227  	n int
   228  }
   229  
   230  func (m *mockWriter) write(
   231  	ctx context.Context,
   232  	name []byte,
   233  	metricNanos, encodeNanos int64,
   234  	value float64,
   235  	annotation []byte,
   236  	sp policy.StoragePolicy,
   237  	callbackable Callbackable,
   238  ) {
   239  	m.Lock()
   240  	m.n++
   241  	payload := payload{
   242  		id:          string(name),
   243  		metricNanos: metricNanos,
   244  		encodeNanos: encodeNanos,
   245  		value:       value,
   246  		sp:          sp,
   247  	}
   248  	m.m[payload.id] = payload
   249  	m.Unlock()
   250  	callbackable.Callback(OnSuccess)
   251  }
   252  
   253  func (m *mockWriter) ingested() int {
   254  	m.Lock()
   255  	defer m.Unlock()
   256  
   257  	return m.n
   258  }
   259  
   260  type payload struct {
   261  	id          string
   262  	metricNanos int64
   263  	encodeNanos int64
   264  	value       float64
   265  	sp          policy.StoragePolicy
   266  }