github.com/matrixorigin/matrixone@v0.7.0/pkg/util/metric/metric_exporter_test.go (about) 1 // Copyright 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package metric 15 16 import ( 17 "context" 18 "sync" 19 "testing" 20 "time" 21 22 pb "github.com/matrixorigin/matrixone/pkg/pb/metric" 23 prom "github.com/prometheus/client_golang/prometheus" 24 "github.com/stretchr/testify/assert" 25 ) 26 27 type dummyCollect struct { 28 dummySwitch 29 sync.Mutex 30 mfs [][]*pb.MetricFamily 31 } 32 33 func (e *dummyCollect) SendMetrics(ctx context.Context, mfs []*pb.MetricFamily) error { 34 e.Lock() 35 defer e.Unlock() 36 e.mfs = append(e.mfs, mfs) 37 return nil 38 } 39 40 func (e *dummyCollect) sendCnt() int { 41 e.Lock() 42 defer e.Unlock() 43 return len(e.mfs) 44 } 45 46 func TestExporterCommonInfo(t *testing.T) { 47 exp := newMetricExporter(nil, nil, "node_uuid_42", "monolithic").(*metricExporter) 48 mfs := []*pb.MetricFamily{ 49 {Metric: []*pb.Metric{{}}}, 50 {Metric: []*pb.Metric{{Label: []*pb.LabelPair{{Name: "color", Value: "blue"}}}}}, 51 {Metric: []*pb.Metric{{Label: []*pb.LabelPair{{Name: "color", Value: "red"}, {Name: "zaxis", Value: "10"}}}}}, 52 {Metric: []*pb.Metric{ 53 {Label: []*pb.LabelPair{ 54 {Name: "color", Value: "red"}, 55 {Name: "zaxis", Value: "10"}, 56 {Name: "env", Value: "test"}, 57 }}, 58 {Label: []*pb.LabelPair{ 59 {Name: "color", Value: "gray"}, 60 {Name: "zaxis", Value: "20"}, 61 {Name: "env", Value: "dev"}, 62 }}, 63 }}, 64 } 65 exp.addCommonInfo(mfs) 66 if mfs[0].Metric[0].Collecttime == 0 { 67 t.Error("addCommonInfo adds no collecttime") 68 } 69 time := mfs[0].Metric[0].GetCollecttime() 70 71 names := []string{"color", "zaxis", "env"} 72 lblCnt := 0 73 for i, mf := range mfs { 74 assert.Equal(t, mf.GetNode(), "node_uuid_42") 75 assert.Equal(t, mf.GetRole(), "monolithic") 76 name := names[:lblCnt] 77 for _, m := range mf.Metric { 78 if m.GetCollecttime() != time { 79 t.Errorf("addCommonInfo adds different collecttime, group %d", i) 80 } 81 if len(m.Label) != lblCnt { 82 t.Errorf("addCommonInfo add wrong labels, group %d", i) 83 } 84 for i := range m.Label { 85 if m.Label[i].Name != name[i] { 86 t.Errorf("addCommonInfo add wrong labels, want %s@%d, got %s", name[i], i, m.Label[i]) 87 } 88 } 89 } 90 lblCnt += 1 91 } 92 } 93 94 func TestExporter(t *testing.T) { 95 dumCollect := &dummyCollect{} 96 dumClock := makeDummyClock(1) 97 var exp *metricExporter 98 99 withModifiedConfig(func() { 100 defer setGatherInterval(setGatherInterval(20 * time.Millisecond)) 101 defer setRawHistBufLimit(setRawHistBufLimit(5)) 102 defer setExportToProm(setExportToProm(false)) 103 reg := prom.NewRegistry() 104 iexp := newMetricExporter(reg, dumCollect, "node_uuid", "monolithic") 105 exp = iexp.(*metricExporter) 106 exp.Start(context.TODO()) 107 defer exp.Stop(false) 108 exp.now = dumClock 109 c := prom.NewCounter(prom.CounterOpts{Subsystem: "test", Name: "test_counter"}) 110 reg.MustRegister(c) 111 g := prom.NewGauge(prom.GaugeOpts{Subsystem: "test", Name: "test_gauge"}) 112 reg.MustRegister(g) 113 h := NewRawHist(prom.HistogramOpts{Subsystem: "test", Name: "test_hist"}) 114 h.exporter = &iexp 115 h.now = dumClock 116 reg.MustRegister(h) 117 118 wg := new(sync.WaitGroup) 119 wg.Add(1) 120 go func() { 121 // ~45ms, 2 rawHist full export 122 for i := 0; i < 14; i++ { 123 c.Add(1) 124 g.Add(2.0) 125 h.Observe(float64(i)) 126 } 127 // wait 2 Gather() 128 time.Sleep(50 * time.Millisecond) 129 wg.Done() 130 }() 131 wg.Wait() 132 }) 133 time.Sleep(50 * time.Millisecond) 134 sendCnt := dumCollect.sendCnt() 135 if sendCnt != 4 { 136 // test involving timer is not stable, if not matched just return. just bypass for now 137 t.Logf("[Metric TODO]: collector receive %d batch metrics, want 4", sendCnt) 138 return 139 } 140 141 // 14 Observe + 4 addCommonInfo 142 if dumClock()-1 != 14+4 { 143 t.Errorf("disorder time") 144 } 145 146 dumCollect.Lock() 147 148 counterCnt, gaugeCnt, sampleCnt := 0, 0, 0 149 for _, mfs := range dumCollect.mfs { 150 for _, mf := range mfs { 151 switch mf.GetType() { 152 case pb.MetricType_COUNTER: 153 counterCnt += len(mf.Metric) 154 case pb.MetricType_GAUGE: 155 gaugeCnt += len(mf.Metric) 156 case pb.MetricType_RAWHIST: 157 for _, m := range mf.Metric { 158 sampleCnt += len(m.RawHist.Samples) 159 } 160 } 161 } 162 } 163 if counterCnt != 2 || gaugeCnt != 2 { 164 t.Error("mismatched counters and gauges") 165 } 166 167 if sampleCnt != 14 { 168 t.Errorf("mismatched hist samples, want 14, got %d", sampleCnt) 169 } 170 dumCollect.Unlock() 171 172 func() { 173 exp.Lock() 174 defer exp.Unlock() 175 if cap(exp.histFamilies) != 1 { 176 t.Error("exporter buffer capacity should be 1") 177 } 178 }() 179 }