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 }