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  }