github.com/MetalBlockchain/metalgo@v1.11.9/snow/networking/tracker/resource_tracker_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package tracker
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/prometheus/client_golang/prometheus"
    11  	"github.com/stretchr/testify/require"
    12  	"go.uber.org/mock/gomock"
    13  
    14  	"github.com/MetalBlockchain/metalgo/ids"
    15  	"github.com/MetalBlockchain/metalgo/utils/math/meter"
    16  	"github.com/MetalBlockchain/metalgo/utils/resource"
    17  )
    18  
    19  func TestNewCPUTracker(t *testing.T) {
    20  	require := require.New(t)
    21  
    22  	reg := prometheus.NewRegistry()
    23  	halflife := 5 * time.Second
    24  	factory := &meter.ContinuousFactory{}
    25  
    26  	trackerIntf, err := NewResourceTracker(reg, resource.NoUsage, factory, halflife)
    27  	require.NoError(err)
    28  	require.IsType(&resourceTracker{}, trackerIntf)
    29  	tracker := trackerIntf.(*resourceTracker)
    30  	require.Equal(factory, tracker.factory)
    31  	require.NotNil(tracker.processingMeter)
    32  	require.Equal(halflife, tracker.halflife)
    33  	require.NotNil(tracker.meters)
    34  	require.NotNil(tracker.metrics)
    35  }
    36  
    37  func TestCPUTracker(t *testing.T) {
    38  	require := require.New(t)
    39  
    40  	halflife := 5 * time.Second
    41  
    42  	ctrl := gomock.NewController(t)
    43  	mockUser := resource.NewMockUser(ctrl)
    44  	mockUser.EXPECT().CPUUsage().Return(1.0).Times(3)
    45  
    46  	tracker, err := NewResourceTracker(prometheus.NewRegistry(), mockUser, meter.ContinuousFactory{}, time.Second)
    47  	require.NoError(err)
    48  
    49  	node1 := ids.BuildTestNodeID([]byte{1})
    50  	node2 := ids.BuildTestNodeID([]byte{2})
    51  
    52  	// Note that all the durations between start and end are [halflife].
    53  	startTime1 := time.Now()
    54  	endTime1 := startTime1.Add(halflife)
    55  	// Note that all CPU usage is attributed to at-large allocation.
    56  	tracker.StartProcessing(node1, startTime1)
    57  	tracker.StopProcessing(node1, endTime1)
    58  
    59  	startTime2 := endTime1
    60  	endTime2 := startTime2.Add(halflife)
    61  	// Note that all CPU usage is attributed to at-large allocation.
    62  	tracker.StartProcessing(node2, startTime2)
    63  	tracker.StopProcessing(node2, endTime2)
    64  
    65  	cpuTracker := tracker.CPUTracker()
    66  
    67  	node1Utilization := cpuTracker.Usage(node1, endTime2)
    68  	node2Utilization := cpuTracker.Usage(node2, endTime2)
    69  	require.Greater(node2Utilization, node1Utilization)
    70  
    71  	cumulative := cpuTracker.TotalUsage()
    72  	sum := node1Utilization + node2Utilization
    73  	require.Equal(sum, cumulative)
    74  
    75  	mockUser.EXPECT().CPUUsage().Return(.5).Times(3)
    76  
    77  	startTime3 := endTime2
    78  	endTime3 := startTime3.Add(halflife)
    79  	newNode1Utilization := cpuTracker.Usage(node1, endTime3)
    80  	require.Greater(node1Utilization, newNode1Utilization)
    81  	newCumulative := cpuTracker.TotalUsage()
    82  	require.Greater(cumulative, newCumulative)
    83  
    84  	startTime4 := endTime3
    85  	endTime4 := startTime4.Add(halflife)
    86  	// Note that only half of CPU usage is attributed to at-large allocation.
    87  	tracker.StartProcessing(node1, startTime4)
    88  	tracker.StopProcessing(node1, endTime4)
    89  
    90  	cumulative = cpuTracker.TotalUsage()
    91  	sum = node1Utilization + node2Utilization
    92  	require.Greater(sum, cumulative)
    93  }
    94  
    95  func TestCPUTrackerTimeUntilCPUUtilization(t *testing.T) {
    96  	require := require.New(t)
    97  
    98  	halflife := 5 * time.Second
    99  	tracker, err := NewResourceTracker(prometheus.NewRegistry(), resource.NoUsage, meter.ContinuousFactory{}, halflife)
   100  	require.NoError(err)
   101  	now := time.Now()
   102  	nodeID := ids.GenerateTestNodeID()
   103  	// Start the meter
   104  	tracker.StartProcessing(nodeID, now)
   105  	// One halflife passes; stop the meter
   106  	now = now.Add(halflife)
   107  	tracker.StopProcessing(nodeID, now)
   108  	cpuTracker := tracker.CPUTracker()
   109  	// Read the current value
   110  	currentVal := cpuTracker.Usage(nodeID, now)
   111  	// Suppose we want to wait for the value to be
   112  	// a third of its current value
   113  	desiredVal := currentVal / 3
   114  	// See when that should happen
   115  	timeUntilDesiredVal := cpuTracker.TimeUntilUsage(nodeID, now, desiredVal)
   116  	// Get the actual value at that time
   117  	now = now.Add(timeUntilDesiredVal)
   118  	actualVal := cpuTracker.Usage(nodeID, now)
   119  	// Make sure the actual/expected are close
   120  	require.InDelta(desiredVal, actualVal, .00001)
   121  	// Make sure TimeUntilUsage returns the zero duration if
   122  	// the value provided >= the current value
   123  	require.Zero(cpuTracker.TimeUntilUsage(nodeID, now, actualVal))
   124  	require.Zero(cpuTracker.TimeUntilUsage(nodeID, now, actualVal+.1))
   125  	// Make sure it returns the zero duration if the node isn't known
   126  	require.Zero(cpuTracker.TimeUntilUsage(ids.GenerateTestNodeID(), now, 0.0001))
   127  }