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  }