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 }