2018-08-22 13:08:52 +03:00
|
|
|
// Copyright 2018 The Prometheus Authors
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2014-12-11 02:01:46 +03:00
|
|
|
package prometheus
|
|
|
|
|
|
|
|
import (
|
2015-05-05 02:20:11 +03:00
|
|
|
"runtime"
|
2014-12-11 02:01:46 +03:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2015-02-27 18:12:59 +03:00
|
|
|
dto "github.com/prometheus/client_model/go"
|
2014-12-11 02:01:46 +03:00
|
|
|
)
|
|
|
|
|
2019-05-06 00:44:22 +03:00
|
|
|
func TestGoCollectorGoroutines(t *testing.T) {
|
2014-12-11 02:01:46 +03:00
|
|
|
var (
|
2019-05-06 00:44:22 +03:00
|
|
|
c = NewGoCollector()
|
|
|
|
metricCh = make(chan Metric)
|
|
|
|
waitCh = make(chan struct{})
|
|
|
|
endGoroutineCh = make(chan struct{})
|
|
|
|
endCollectionCh = make(chan struct{})
|
|
|
|
old = -1
|
2014-12-11 02:01:46 +03:00
|
|
|
)
|
2019-05-06 00:44:22 +03:00
|
|
|
defer func() {
|
|
|
|
close(endGoroutineCh)
|
|
|
|
// Drain the collect channel to prevent goroutine leak.
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-metricCh:
|
|
|
|
case <-endCollectionCh:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2014-12-11 02:01:46 +03:00
|
|
|
|
|
|
|
go func() {
|
2019-05-06 00:44:22 +03:00
|
|
|
c.Collect(metricCh)
|
2019-10-14 20:44:28 +03:00
|
|
|
for i := 1; i <= 10; i++ {
|
|
|
|
// Start 10 goroutines to be sure we'll detect an
|
|
|
|
// increase even if unrelated goroutines happen to
|
|
|
|
// terminate during this test.
|
|
|
|
go func(c <-chan struct{}) {
|
|
|
|
<-c
|
|
|
|
}(endGoroutineCh)
|
|
|
|
}
|
2019-05-06 00:44:22 +03:00
|
|
|
<-waitCh
|
|
|
|
c.Collect(metricCh)
|
|
|
|
close(endCollectionCh)
|
2014-12-11 02:01:46 +03:00
|
|
|
}()
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
2019-05-06 00:44:22 +03:00
|
|
|
case m := <-metricCh:
|
2017-02-15 08:06:22 +03:00
|
|
|
// m can be Gauge or Counter,
|
|
|
|
// currently just test the go_goroutines Gauge
|
|
|
|
// and ignore others.
|
|
|
|
if m.Desc().fqName != "go_goroutines" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
pb := &dto.Metric{}
|
|
|
|
m.Write(pb)
|
|
|
|
if pb.GetGauge() == nil {
|
|
|
|
continue
|
|
|
|
}
|
2014-12-11 02:01:46 +03:00
|
|
|
|
2017-02-15 08:06:22 +03:00
|
|
|
if old == -1 {
|
|
|
|
old = int(pb.GetGauge().GetValue())
|
2019-05-06 00:44:22 +03:00
|
|
|
close(waitCh)
|
2017-02-15 08:06:22 +03:00
|
|
|
continue
|
|
|
|
}
|
2014-12-11 02:01:46 +03:00
|
|
|
|
2019-10-14 20:44:28 +03:00
|
|
|
if diff := old - int(pb.GetGauge().GetValue()); diff > -1 {
|
|
|
|
t.Errorf("want at least one new goroutine, got %d fewer", diff)
|
2015-04-30 07:27:14 +03:00
|
|
|
}
|
|
|
|
case <-time.After(1 * time.Second):
|
|
|
|
t.Fatalf("expected collect timed out")
|
|
|
|
}
|
2019-05-06 00:44:22 +03:00
|
|
|
break
|
2015-04-30 07:27:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-06 00:44:22 +03:00
|
|
|
func TestGoCollectorGC(t *testing.T) {
|
2015-04-30 07:27:14 +03:00
|
|
|
var (
|
2019-05-06 00:44:22 +03:00
|
|
|
c = NewGoCollector()
|
|
|
|
metricCh = make(chan Metric)
|
|
|
|
waitCh = make(chan struct{})
|
|
|
|
endCollectionCh = make(chan struct{})
|
|
|
|
oldGC uint64
|
|
|
|
oldPause float64
|
2015-04-30 07:27:14 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
go func() {
|
2019-05-06 00:44:22 +03:00
|
|
|
c.Collect(metricCh)
|
2015-04-30 07:27:14 +03:00
|
|
|
// force GC
|
2015-05-05 02:20:11 +03:00
|
|
|
runtime.GC()
|
2019-05-06 00:44:22 +03:00
|
|
|
<-waitCh
|
|
|
|
c.Collect(metricCh)
|
|
|
|
close(endCollectionCh)
|
|
|
|
}()
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
// Drain the collect channel to prevent goroutine leak.
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-metricCh:
|
|
|
|
case <-endCollectionCh:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2015-04-30 07:27:14 +03:00
|
|
|
}()
|
|
|
|
|
|
|
|
first := true
|
|
|
|
for {
|
|
|
|
select {
|
2019-05-06 00:44:22 +03:00
|
|
|
case metric := <-metricCh:
|
2018-01-19 18:21:07 +03:00
|
|
|
pb := &dto.Metric{}
|
|
|
|
metric.Write(pb)
|
|
|
|
if pb.GetSummary() == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if len(pb.GetSummary().Quantile) != 5 {
|
|
|
|
t.Errorf("expected 4 buckets, got %d", len(pb.GetSummary().Quantile))
|
|
|
|
}
|
|
|
|
for idx, want := range []float64{0.0, 0.25, 0.5, 0.75, 1.0} {
|
|
|
|
if *pb.GetSummary().Quantile[idx].Quantile != want {
|
|
|
|
t.Errorf("bucket #%d is off, got %f, want %f", idx, *pb.GetSummary().Quantile[idx].Quantile, want)
|
2015-04-30 07:27:14 +03:00
|
|
|
}
|
2014-12-11 02:01:46 +03:00
|
|
|
}
|
2018-01-19 18:21:07 +03:00
|
|
|
if first {
|
|
|
|
first = false
|
|
|
|
oldGC = *pb.GetSummary().SampleCount
|
|
|
|
oldPause = *pb.GetSummary().SampleSum
|
2019-05-06 00:44:22 +03:00
|
|
|
close(waitCh)
|
2018-01-19 18:21:07 +03:00
|
|
|
continue
|
|
|
|
}
|
2019-05-26 15:11:51 +03:00
|
|
|
if diff := *pb.GetSummary().SampleCount - oldGC; diff < 1 {
|
|
|
|
t.Errorf("want at least 1 new garbage collection run, got %d", diff)
|
2018-01-19 18:21:07 +03:00
|
|
|
}
|
|
|
|
if diff := *pb.GetSummary().SampleSum - oldPause; diff <= 0 {
|
2019-05-26 15:11:51 +03:00
|
|
|
t.Errorf("want an increase in pause time, got a change of %f", diff)
|
2018-01-19 18:21:07 +03:00
|
|
|
}
|
2014-12-11 02:01:46 +03:00
|
|
|
case <-time.After(1 * time.Second):
|
|
|
|
t.Fatalf("expected collect timed out")
|
|
|
|
}
|
2019-05-06 00:44:22 +03:00
|
|
|
break
|
2014-12-11 02:01:46 +03:00
|
|
|
}
|
|
|
|
}
|
2022-01-25 10:43:45 +03:00
|
|
|
|
|
|
|
func BenchmarkGoCollector(b *testing.B) {
|
|
|
|
c := NewGoCollector().(*goCollector)
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
ch := make(chan Metric, 8)
|
|
|
|
go func() {
|
|
|
|
// Drain all metrics received until the
|
|
|
|
// channel is closed.
|
|
|
|
for range ch {
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
c.Collect(ch)
|
|
|
|
close(ch)
|
|
|
|
}
|
|
|
|
}
|