github.com/m3db/m3@v1.5.0/src/cmd/services/m3coordinator/ingest/m3msg/ingest_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 ingestm3msg 22 23 import ( 24 "context" 25 "errors" 26 "sync" 27 "testing" 28 "time" 29 30 "github.com/m3db/m3/src/cmd/services/m3coordinator/server/m3msg" 31 "github.com/m3db/m3/src/metrics/encoding/protobuf" 32 "github.com/m3db/m3/src/metrics/policy" 33 "github.com/m3db/m3/src/msg/consumer" 34 "github.com/m3db/m3/src/query/models" 35 "github.com/m3db/m3/src/query/storage" 36 "github.com/m3db/m3/src/query/storage/m3/storagemetadata" 37 "github.com/m3db/m3/src/query/ts" 38 xerrors "github.com/m3db/m3/src/x/errors" 39 "github.com/m3db/m3/src/x/ident" 40 "github.com/m3db/m3/src/x/instrument" 41 "github.com/m3db/m3/src/x/pool" 42 "github.com/m3db/m3/src/x/serialize" 43 xtime "github.com/m3db/m3/src/x/time" 44 45 "github.com/golang/mock/gomock" 46 "github.com/stretchr/testify/require" 47 "github.com/uber-go/tally" 48 ) 49 50 func TestIngest(t *testing.T) { 51 ctrl := gomock.NewController(t) 52 defer ctrl.Finish() 53 54 cfg := Configuration{ 55 WorkerPoolSize: 2, 56 OpPool: pool.ObjectPoolConfiguration{ 57 Size: 1, 58 }, 59 } 60 appender := &mockAppender{} 61 ingester, err := cfg.NewIngester(appender, models.NewTagOptions(), 62 instrument.NewOptions()) 63 require.NoError(t, err) 64 65 id := newTestID(t, "__name__", "foo", "app", "bar") 66 metricNanos := int64(1234) 67 val := float64(1) 68 sp := policy.MustParseStoragePolicy("1m:40d") 69 m := consumer.NewMockMessage(ctrl) 70 var wg sync.WaitGroup 71 wg.Add(1) 72 callback := m3msg.NewProtobufCallback(m, protobuf.NewAggregatedDecoder(nil), &wg) 73 74 m.EXPECT().Ack() 75 ingester.Ingest(context.TODO(), id, metricNanos, 0, val, nil, sp, callback) 76 77 for appender.cnt() != 1 { 78 time.Sleep(100 * time.Millisecond) 79 } 80 81 expected, err := storage.NewWriteQuery(storage.WriteQueryOptions{ 82 Annotation: nil, 83 Attributes: storagemetadata.Attributes{ 84 MetricsType: storagemetadata.AggregatedMetricsType, 85 Resolution: time.Minute, 86 Retention: 40 * 24 * time.Hour, 87 }, 88 Datapoints: ts.Datapoints{ 89 ts.Datapoint{ 90 Timestamp: xtime.UnixNano(metricNanos), 91 Value: val, 92 }, 93 }, 94 Tags: models.NewTags(2, nil).AddTags( 95 []models.Tag{ 96 { 97 Name: []byte("__name__"), 98 Value: []byte("foo"), 99 }, 100 { 101 Name: []byte("app"), 102 Value: []byte("bar"), 103 }, 104 }, 105 ), 106 Unit: xtime.Second, 107 }) 108 require.NoError(t, err) 109 110 require.Equal(t, *expected, *appender.received[0]) 111 112 // Make sure the op is put back to pool. 113 op := ingester.p.Get().(*ingestOp) 114 require.Equal(t, id, op.id) 115 } 116 117 func TestIngestNonRetryableError(t *testing.T) { 118 ctrl := gomock.NewController(t) 119 defer ctrl.Finish() 120 121 cfg := Configuration{ 122 WorkerPoolSize: 2, 123 OpPool: pool.ObjectPoolConfiguration{ 124 Size: 1, 125 }, 126 } 127 128 scope := tally.NewTestScope("", nil) 129 instrumentOpts := instrument.NewOptions().SetMetricsScope(scope) 130 131 nonRetryableError := xerrors.NewNonRetryableError(errors.New("bad request error")) 132 appender := &mockAppender{expectErr: nonRetryableError} 133 ingester, err := cfg.NewIngester(appender, models.NewTagOptions(), 134 instrumentOpts) 135 require.NoError(t, err) 136 137 id := newTestID(t, "__name__", "foo", "app", "bar") 138 metricNanos := int64(1234) 139 val := float64(1) 140 sp := policy.MustParseStoragePolicy("1m:40d") 141 m := consumer.NewMockMessage(ctrl) 142 var wg sync.WaitGroup 143 wg.Add(1) 144 callback := m3msg.NewProtobufCallback(m, protobuf.NewAggregatedDecoder(nil), &wg) 145 146 m.EXPECT().Ack() 147 ingester.Ingest(context.TODO(), id, metricNanos, 0, val, nil, sp, callback) 148 149 for appender.cntErr() != 1 { 150 time.Sleep(100 * time.Millisecond) 151 } 152 153 // Make non-retryable error marked. 154 counters := scope.Snapshot().Counters() 155 156 counter, ok := counters["errors+component=ingester,type=not-retryable"] 157 require.True(t, ok) 158 require.Equal(t, int64(1), counter.Value()) 159 } 160 161 type mockAppender struct { 162 sync.RWMutex 163 164 expectErr error 165 receivedErr []*storage.WriteQuery 166 received []*storage.WriteQuery 167 } 168 169 func (m *mockAppender) Write(ctx context.Context, query *storage.WriteQuery) error { 170 m.Lock() 171 defer m.Unlock() 172 173 if m.expectErr != nil { 174 m.receivedErr = append(m.receivedErr, query) 175 return m.expectErr 176 } 177 m.received = append(m.received, query) 178 return nil 179 } 180 181 func (m *mockAppender) cnt() int { 182 m.Lock() 183 defer m.Unlock() 184 185 return len(m.received) 186 } 187 188 func (m *mockAppender) cntErr() int { 189 m.Lock() 190 defer m.Unlock() 191 192 return len(m.receivedErr) 193 } 194 195 func newTestID(t *testing.T, tags ...string) []byte { 196 tagEncoderPool := serialize.NewTagEncoderPool(serialize.NewTagEncoderOptions(), 197 pool.NewObjectPoolOptions().SetSize(1)) 198 tagEncoderPool.Init() 199 200 tagsIter := ident.MustNewTagStringsIterator(tags...) 201 tagEncoder := tagEncoderPool.Get() 202 err := tagEncoder.Encode(tagsIter) 203 require.NoError(t, err) 204 205 data, ok := tagEncoder.Data() 206 require.True(t, ok) 207 return data.Bytes() 208 }