github.com/MetalBlockchain/metalgo@v1.11.9/utils/timer/adaptive_timeout_manager_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 timer
     5  
     6  import (
     7  	"sync"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/prometheus/client_golang/prometheus"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/MetalBlockchain/metalgo/ids"
    15  )
    16  
    17  // Test that Initialize works
    18  func TestAdaptiveTimeoutManagerInit(t *testing.T) {
    19  	type test struct {
    20  		config      AdaptiveTimeoutConfig
    21  		expectedErr error
    22  	}
    23  
    24  	tests := []*test{
    25  		{
    26  			config: AdaptiveTimeoutConfig{
    27  				InitialTimeout:     time.Second,
    28  				MinimumTimeout:     2 * time.Second,
    29  				MaximumTimeout:     3 * time.Second,
    30  				TimeoutCoefficient: 2,
    31  				TimeoutHalflife:    5 * time.Minute,
    32  			},
    33  			expectedErr: errInitialTimeoutBelowMinimum,
    34  		},
    35  		{
    36  			config: AdaptiveTimeoutConfig{
    37  				InitialTimeout:     5 * time.Second,
    38  				MinimumTimeout:     2 * time.Second,
    39  				MaximumTimeout:     3 * time.Second,
    40  				TimeoutCoefficient: 2,
    41  				TimeoutHalflife:    5 * time.Minute,
    42  			},
    43  			expectedErr: errInitialTimeoutAboveMaximum,
    44  		},
    45  		{
    46  			config: AdaptiveTimeoutConfig{
    47  				InitialTimeout:     2 * time.Second,
    48  				MinimumTimeout:     2 * time.Second,
    49  				MaximumTimeout:     3 * time.Second,
    50  				TimeoutCoefficient: 0.9,
    51  				TimeoutHalflife:    5 * time.Minute,
    52  			},
    53  			expectedErr: errTooSmallTimeoutCoefficient,
    54  		},
    55  		{
    56  			config: AdaptiveTimeoutConfig{
    57  				InitialTimeout:     2 * time.Second,
    58  				MinimumTimeout:     2 * time.Second,
    59  				MaximumTimeout:     3 * time.Second,
    60  				TimeoutCoefficient: 1,
    61  			},
    62  			expectedErr: errNonPositiveHalflife,
    63  		},
    64  		{
    65  			config: AdaptiveTimeoutConfig{
    66  				InitialTimeout:     2 * time.Second,
    67  				MinimumTimeout:     2 * time.Second,
    68  				MaximumTimeout:     3 * time.Second,
    69  				TimeoutCoefficient: 1,
    70  				TimeoutHalflife:    -1 * time.Second,
    71  			},
    72  			expectedErr: errNonPositiveHalflife,
    73  		},
    74  		{
    75  			config: AdaptiveTimeoutConfig{
    76  				InitialTimeout:     2 * time.Second,
    77  				MinimumTimeout:     2 * time.Second,
    78  				MaximumTimeout:     3 * time.Second,
    79  				TimeoutCoefficient: 1,
    80  				TimeoutHalflife:    5 * time.Minute,
    81  			},
    82  		},
    83  	}
    84  
    85  	for _, test := range tests {
    86  		_, err := NewAdaptiveTimeoutManager(&test.config, prometheus.NewRegistry())
    87  		require.ErrorIs(t, err, test.expectedErr)
    88  	}
    89  }
    90  
    91  func TestAdaptiveTimeoutManager(t *testing.T) {
    92  	tm, err := NewAdaptiveTimeoutManager(
    93  		&AdaptiveTimeoutConfig{
    94  			InitialTimeout:     time.Millisecond,
    95  			MinimumTimeout:     time.Millisecond,
    96  			MaximumTimeout:     time.Hour,
    97  			TimeoutHalflife:    5 * time.Minute,
    98  			TimeoutCoefficient: 1.25,
    99  		},
   100  		prometheus.NewRegistry(),
   101  	)
   102  	require.NoError(t, err)
   103  	go tm.Dispatch()
   104  
   105  	var lock sync.Mutex
   106  
   107  	numSuccessful := 5
   108  
   109  	wg := sync.WaitGroup{}
   110  	wg.Add(numSuccessful)
   111  
   112  	callback := new(func())
   113  	*callback = func() {
   114  		lock.Lock()
   115  		defer lock.Unlock()
   116  
   117  		numSuccessful--
   118  		if numSuccessful > 0 {
   119  			tm.Put(ids.RequestID{Op: byte(numSuccessful)}, true, *callback)
   120  		}
   121  		if numSuccessful >= 0 {
   122  			wg.Done()
   123  		}
   124  		if numSuccessful%2 == 0 {
   125  			tm.Remove(ids.RequestID{Op: byte(numSuccessful)})
   126  			tm.Put(ids.RequestID{Op: byte(numSuccessful)}, true, *callback)
   127  		}
   128  	}
   129  	(*callback)()
   130  	(*callback)()
   131  
   132  	wg.Wait()
   133  }