storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/dynamic-timeouts_test.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2017 MinIO, Inc.
     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 cmd
    18  
    19  import (
    20  	"math/rand"
    21  	"runtime"
    22  	"sync"
    23  	"testing"
    24  	"time"
    25  )
    26  
    27  func TestDynamicTimeoutSingleIncrease(t *testing.T) {
    28  
    29  	timeout := newDynamicTimeout(time.Minute, time.Second)
    30  
    31  	initial := timeout.Timeout()
    32  
    33  	for i := 0; i < dynamicTimeoutLogSize; i++ {
    34  		timeout.LogFailure()
    35  	}
    36  
    37  	adjusted := timeout.Timeout()
    38  
    39  	if initial >= adjusted {
    40  		t.Errorf("Failure to increase timeout, expected %v to be more than %v", adjusted, initial)
    41  	}
    42  }
    43  
    44  func TestDynamicTimeoutDualIncrease(t *testing.T) {
    45  
    46  	timeout := newDynamicTimeout(time.Minute, time.Second)
    47  
    48  	initial := timeout.Timeout()
    49  
    50  	for i := 0; i < dynamicTimeoutLogSize; i++ {
    51  		timeout.LogFailure()
    52  	}
    53  
    54  	adjusted := timeout.Timeout()
    55  
    56  	for i := 0; i < dynamicTimeoutLogSize; i++ {
    57  		timeout.LogFailure()
    58  	}
    59  
    60  	adjustedAgain := timeout.Timeout()
    61  
    62  	if initial >= adjusted || adjusted >= adjustedAgain {
    63  		t.Errorf("Failure to increase timeout multiple times")
    64  	}
    65  }
    66  
    67  func TestDynamicTimeoutSingleDecrease(t *testing.T) {
    68  
    69  	timeout := newDynamicTimeout(time.Minute, time.Second)
    70  
    71  	initial := timeout.Timeout()
    72  
    73  	for i := 0; i < dynamicTimeoutLogSize; i++ {
    74  		timeout.LogSuccess(20 * time.Second)
    75  	}
    76  
    77  	adjusted := timeout.Timeout()
    78  
    79  	if initial <= adjusted {
    80  		t.Errorf("Failure to decrease timeout, expected %v to be less than %v", adjusted, initial)
    81  	}
    82  }
    83  
    84  func TestDynamicTimeoutDualDecrease(t *testing.T) {
    85  
    86  	timeout := newDynamicTimeout(time.Minute, time.Second)
    87  
    88  	initial := timeout.Timeout()
    89  
    90  	for i := 0; i < dynamicTimeoutLogSize; i++ {
    91  		timeout.LogSuccess(20 * time.Second)
    92  	}
    93  
    94  	adjusted := timeout.Timeout()
    95  
    96  	for i := 0; i < dynamicTimeoutLogSize; i++ {
    97  		timeout.LogSuccess(20 * time.Second)
    98  	}
    99  
   100  	adjustedAgain := timeout.Timeout()
   101  
   102  	if initial <= adjusted || adjusted <= adjustedAgain {
   103  		t.Errorf("Failure to decrease timeout multiple times, initial: %v, adjusted: %v, again: %v", initial, adjusted, adjustedAgain)
   104  	}
   105  }
   106  
   107  func TestDynamicTimeoutManyDecreases(t *testing.T) {
   108  
   109  	timeout := newDynamicTimeout(time.Minute, time.Second)
   110  
   111  	initial := timeout.Timeout()
   112  
   113  	const successTimeout = 20 * time.Second
   114  	for l := 0; l < 100; l++ {
   115  		for i := 0; i < dynamicTimeoutLogSize; i++ {
   116  			timeout.LogSuccess(successTimeout)
   117  		}
   118  
   119  	}
   120  
   121  	adjusted := timeout.Timeout()
   122  	// Check whether eventual timeout is between initial value and success timeout
   123  	if initial <= adjusted || adjusted <= successTimeout {
   124  		t.Errorf("Failure to decrease timeout appropriately")
   125  	}
   126  }
   127  
   128  func TestDynamicTimeoutConcurrent(t *testing.T) {
   129  	// Race test.
   130  	timeout := newDynamicTimeout(time.Second, time.Millisecond)
   131  	var wg sync.WaitGroup
   132  	for i := 0; i < runtime.GOMAXPROCS(0); i++ {
   133  		wg.Add(1)
   134  		rng := rand.New(rand.NewSource(int64(i)))
   135  		go func() {
   136  			defer wg.Done()
   137  			for i := 0; i < 100; i++ {
   138  				timeout.LogFailure()
   139  				for j := 0; j < 100; j++ {
   140  					timeout.LogSuccess(time.Duration(float64(time.Second) * rng.Float64()))
   141  				}
   142  				to := timeout.Timeout()
   143  				if to < time.Millisecond || to > time.Second {
   144  					panic(to)
   145  				}
   146  			}
   147  		}()
   148  	}
   149  	wg.Wait()
   150  }
   151  
   152  func TestDynamicTimeoutHitMinimum(t *testing.T) {
   153  
   154  	const minimum = 30 * time.Second
   155  	timeout := newDynamicTimeout(time.Minute, minimum)
   156  
   157  	initial := timeout.Timeout()
   158  
   159  	const successTimeout = 20 * time.Second
   160  	for l := 0; l < 100; l++ {
   161  		for i := 0; i < dynamicTimeoutLogSize; i++ {
   162  			timeout.LogSuccess(successTimeout)
   163  		}
   164  	}
   165  
   166  	adjusted := timeout.Timeout()
   167  	// Check whether eventual timeout has hit the minimum value
   168  	if initial <= adjusted || adjusted != minimum {
   169  		t.Errorf("Failure to decrease timeout appropriately")
   170  	}
   171  }
   172  
   173  func testDynamicTimeoutAdjust(t *testing.T, timeout *dynamicTimeout, f func() float64) {
   174  
   175  	const successTimeout = 20 * time.Second
   176  
   177  	for i := 0; i < dynamicTimeoutLogSize; i++ {
   178  
   179  		rnd := f()
   180  		duration := time.Duration(float64(successTimeout) * rnd)
   181  
   182  		if duration < 100*time.Millisecond {
   183  			duration = 100 * time.Millisecond
   184  		}
   185  		if duration >= time.Minute {
   186  			timeout.LogFailure()
   187  		} else {
   188  			timeout.LogSuccess(duration)
   189  		}
   190  	}
   191  }
   192  
   193  func TestDynamicTimeoutAdjustExponential(t *testing.T) {
   194  
   195  	timeout := newDynamicTimeout(time.Minute, time.Second)
   196  
   197  	rand.Seed(0)
   198  
   199  	initial := timeout.Timeout()
   200  
   201  	for try := 0; try < 10; try++ {
   202  
   203  		testDynamicTimeoutAdjust(t, timeout, rand.ExpFloat64)
   204  
   205  	}
   206  
   207  	adjusted := timeout.Timeout()
   208  	if initial <= adjusted {
   209  		t.Errorf("Failure to decrease timeout, expected %v to be less than %v", adjusted, initial)
   210  	}
   211  }
   212  
   213  func TestDynamicTimeoutAdjustNormalized(t *testing.T) {
   214  
   215  	timeout := newDynamicTimeout(time.Minute, time.Second)
   216  
   217  	rand.Seed(0)
   218  
   219  	initial := timeout.Timeout()
   220  
   221  	for try := 0; try < 10; try++ {
   222  
   223  		testDynamicTimeoutAdjust(t, timeout, func() float64 {
   224  			return 1.0 + rand.NormFloat64()
   225  		})
   226  
   227  	}
   228  
   229  	adjusted := timeout.Timeout()
   230  	if initial <= adjusted {
   231  		t.Errorf("Failure to decrease timeout, expected %v to be less than %v", adjusted, initial)
   232  	}
   233  }