k8s.io/client-go@v0.22.2/util/workqueue/metrics_test.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package workqueue
    18  
    19  import (
    20  	"sync"
    21  	"testing"
    22  	"time"
    23  
    24  	"k8s.io/apimachinery/pkg/util/clock"
    25  )
    26  
    27  type testMetrics struct {
    28  	added, gotten, finished int64
    29  
    30  	updateCalled chan<- struct{}
    31  }
    32  
    33  func (m *testMetrics) add(item t)            { m.added++ }
    34  func (m *testMetrics) get(item t)            { m.gotten++ }
    35  func (m *testMetrics) done(item t)           { m.finished++ }
    36  func (m *testMetrics) updateUnfinishedWork() { m.updateCalled <- struct{}{} }
    37  
    38  func TestMetricShutdown(t *testing.T) {
    39  	ch := make(chan struct{})
    40  	m := &testMetrics{
    41  		updateCalled: ch,
    42  	}
    43  	c := clock.NewFakeClock(time.Now())
    44  	q := newQueue(c, m, time.Millisecond)
    45  	for !c.HasWaiters() {
    46  		// Wait for the go routine to call NewTicker()
    47  		time.Sleep(time.Millisecond)
    48  	}
    49  
    50  	c.Step(time.Millisecond)
    51  	<-ch
    52  	q.ShutDown()
    53  
    54  	c.Step(time.Hour)
    55  	select {
    56  	default:
    57  		return
    58  	case <-ch:
    59  		t.Errorf("Unexpected update after shutdown was called.")
    60  	}
    61  }
    62  
    63  type testMetric struct {
    64  	inc int64
    65  	dec int64
    66  	set float64
    67  
    68  	observedValue float64
    69  	observedCount int
    70  
    71  	notifyCh chan<- struct{}
    72  
    73  	lock sync.Mutex
    74  }
    75  
    76  func (m *testMetric) Inc() {
    77  	m.lock.Lock()
    78  	defer m.lock.Unlock()
    79  	m.inc++
    80  	m.notify()
    81  }
    82  
    83  func (m *testMetric) Dec() {
    84  	m.lock.Lock()
    85  	defer m.lock.Unlock()
    86  	m.dec++
    87  	m.notify()
    88  }
    89  
    90  func (m *testMetric) Set(f float64) {
    91  	m.lock.Lock()
    92  	defer m.lock.Unlock()
    93  	m.set = f
    94  	m.notify()
    95  }
    96  
    97  func (m *testMetric) Observe(f float64) {
    98  	m.lock.Lock()
    99  	defer m.lock.Unlock()
   100  	m.observedValue = f
   101  	m.observedCount++
   102  	m.notify()
   103  }
   104  
   105  func (m *testMetric) gaugeValue() float64 {
   106  	m.lock.Lock()
   107  	defer m.lock.Unlock()
   108  	if m.set != 0 {
   109  		return m.set
   110  	}
   111  	return float64(m.inc - m.dec)
   112  }
   113  
   114  func (m *testMetric) observationValue() float64 {
   115  	m.lock.Lock()
   116  	defer m.lock.Unlock()
   117  	return m.observedValue
   118  }
   119  
   120  func (m *testMetric) observationCount() int {
   121  	m.lock.Lock()
   122  	defer m.lock.Unlock()
   123  	return m.observedCount
   124  }
   125  
   126  func (m *testMetric) notify() {
   127  	if m.notifyCh != nil {
   128  		m.notifyCh <- struct{}{}
   129  	}
   130  }
   131  
   132  type testMetricsProvider struct {
   133  	depth      testMetric
   134  	adds       testMetric
   135  	latency    testMetric
   136  	duration   testMetric
   137  	unfinished testMetric
   138  	longest    testMetric
   139  	retries    testMetric
   140  }
   141  
   142  func (m *testMetricsProvider) NewDepthMetric(name string) GaugeMetric {
   143  	return &m.depth
   144  }
   145  
   146  func (m *testMetricsProvider) NewAddsMetric(name string) CounterMetric {
   147  	return &m.adds
   148  }
   149  
   150  func (m *testMetricsProvider) NewLatencyMetric(name string) HistogramMetric {
   151  	return &m.latency
   152  }
   153  
   154  func (m *testMetricsProvider) NewWorkDurationMetric(name string) HistogramMetric {
   155  	return &m.duration
   156  }
   157  
   158  func (m *testMetricsProvider) NewUnfinishedWorkSecondsMetric(name string) SettableGaugeMetric {
   159  	return &m.unfinished
   160  }
   161  
   162  func (m *testMetricsProvider) NewLongestRunningProcessorSecondsMetric(name string) SettableGaugeMetric {
   163  	return &m.longest
   164  }
   165  
   166  func (m *testMetricsProvider) NewRetriesMetric(name string) CounterMetric {
   167  	return &m.retries
   168  }
   169  
   170  func TestMetrics(t *testing.T) {
   171  	mp := testMetricsProvider{}
   172  	t0 := time.Unix(0, 0)
   173  	c := clock.NewFakeClock(t0)
   174  	mf := queueMetricsFactory{metricsProvider: &mp}
   175  	m := mf.newQueueMetrics("test", c)
   176  	q := newQueue(c, m, time.Millisecond)
   177  	defer q.ShutDown()
   178  	for !c.HasWaiters() {
   179  		// Wait for the go routine to call NewTicker()
   180  		time.Sleep(time.Millisecond)
   181  	}
   182  
   183  	q.Add("foo")
   184  	if e, a := 1.0, mp.adds.gaugeValue(); e != a {
   185  		t.Errorf("expected %v, got %v", e, a)
   186  	}
   187  
   188  	if e, a := 1.0, mp.depth.gaugeValue(); e != a {
   189  		t.Errorf("expected %v, got %v", e, a)
   190  	}
   191  
   192  	c.Step(50 * time.Microsecond)
   193  
   194  	// Start processing
   195  	i, _ := q.Get()
   196  	if i != "foo" {
   197  		t.Errorf("Expected %v, got %v", "foo", i)
   198  	}
   199  
   200  	if e, a := 5e-05, mp.latency.observationValue(); e != a {
   201  		t.Errorf("expected %v, got %v", e, a)
   202  	}
   203  	if e, a := 1, mp.latency.observationCount(); e != a {
   204  		t.Errorf("expected %v, got %v", e, a)
   205  	}
   206  
   207  	// Add it back while processing; multiple adds of the same item are
   208  	// de-duped.
   209  	q.Add(i)
   210  	q.Add(i)
   211  	q.Add(i)
   212  	q.Add(i)
   213  	q.Add(i)
   214  	if e, a := 2.0, mp.adds.gaugeValue(); e != a {
   215  		t.Errorf("expected %v, got %v", e, a)
   216  	}
   217  	// One thing remains in the queue
   218  	if e, a := 1.0, mp.depth.gaugeValue(); e != a {
   219  		t.Errorf("expected %v, got %v", e, a)
   220  	}
   221  
   222  	c.Step(25 * time.Microsecond)
   223  
   224  	// Finish it up
   225  	q.Done(i)
   226  
   227  	if e, a := 2.5e-05, mp.duration.observationValue(); e != a {
   228  		t.Errorf("expected %v, got %v", e, a)
   229  	}
   230  	if e, a := 1, mp.duration.observationCount(); e != a {
   231  		t.Errorf("expected %v, got %v", e, a)
   232  	}
   233  
   234  	// One thing remains in the queue
   235  	if e, a := 1.0, mp.depth.gaugeValue(); e != a {
   236  		t.Errorf("expected %v, got %v", e, a)
   237  	}
   238  
   239  	// It should be back on the queue
   240  	i, _ = q.Get()
   241  	if i != "foo" {
   242  		t.Errorf("Expected %v, got %v", "foo", i)
   243  	}
   244  
   245  	if e, a := 2.5e-05, mp.latency.observationValue(); e != a {
   246  		t.Errorf("expected %v, got %v", e, a)
   247  	}
   248  	if e, a := 2, mp.latency.observationCount(); e != a {
   249  		t.Errorf("expected %v, got %v", e, a)
   250  	}
   251  
   252  	// use a channel to ensure we don't look at the metric before it's
   253  	// been set.
   254  	ch := make(chan struct{}, 1)
   255  	longestCh := make(chan struct{}, 1)
   256  	mp.unfinished.notifyCh = ch
   257  	mp.longest.notifyCh = longestCh
   258  	c.Step(time.Millisecond)
   259  	<-ch
   260  	mp.unfinished.notifyCh = nil
   261  	if e, a := .001, mp.unfinished.gaugeValue(); e != a {
   262  		t.Errorf("expected %v, got %v", e, a)
   263  	}
   264  	<-longestCh
   265  	mp.longest.notifyCh = nil
   266  	if e, a := .001, mp.longest.gaugeValue(); e != a {
   267  		t.Errorf("expected %v, got %v", e, a)
   268  	}
   269  
   270  	// Finish that one up
   271  	q.Done(i)
   272  	if e, a := .001, mp.duration.observationValue(); e != a {
   273  		t.Errorf("expected %v, got %v", e, a)
   274  	}
   275  	if e, a := 2, mp.duration.observationCount(); e != a {
   276  		t.Errorf("expected %v, got %v", e, a)
   277  	}
   278  }