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