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