2021-04-18 15:41:13 -04:00
|
|
|
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
|
|
//
|
|
|
|
// This file is part of MinIO Object Storage stack
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2017-08-31 14:29:22 -04:00
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math/rand"
|
2020-09-18 12:18:18 -04:00
|
|
|
"runtime"
|
|
|
|
"sync"
|
2017-08-31 14:29:22 -04:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestDynamicTimeoutSingleIncrease(t *testing.T) {
|
|
|
|
|
|
|
|
timeout := newDynamicTimeout(time.Minute, time.Second)
|
|
|
|
|
|
|
|
initial := timeout.Timeout()
|
|
|
|
|
|
|
|
for i := 0; i < dynamicTimeoutLogSize; i++ {
|
|
|
|
timeout.LogFailure()
|
|
|
|
}
|
|
|
|
|
|
|
|
adjusted := timeout.Timeout()
|
|
|
|
|
|
|
|
if initial >= adjusted {
|
|
|
|
t.Errorf("Failure to increase timeout, expected %v to be more than %v", adjusted, initial)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDynamicTimeoutDualIncrease(t *testing.T) {
|
|
|
|
|
|
|
|
timeout := newDynamicTimeout(time.Minute, time.Second)
|
|
|
|
|
|
|
|
initial := timeout.Timeout()
|
|
|
|
|
|
|
|
for i := 0; i < dynamicTimeoutLogSize; i++ {
|
|
|
|
timeout.LogFailure()
|
|
|
|
}
|
|
|
|
|
|
|
|
adjusted := timeout.Timeout()
|
|
|
|
|
|
|
|
for i := 0; i < dynamicTimeoutLogSize; i++ {
|
|
|
|
timeout.LogFailure()
|
|
|
|
}
|
|
|
|
|
|
|
|
adjustedAgain := timeout.Timeout()
|
|
|
|
|
|
|
|
if initial >= adjusted || adjusted >= adjustedAgain {
|
|
|
|
t.Errorf("Failure to increase timeout multiple times")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDynamicTimeoutSingleDecrease(t *testing.T) {
|
|
|
|
|
|
|
|
timeout := newDynamicTimeout(time.Minute, time.Second)
|
|
|
|
|
|
|
|
initial := timeout.Timeout()
|
|
|
|
|
|
|
|
for i := 0; i < dynamicTimeoutLogSize; i++ {
|
|
|
|
timeout.LogSuccess(20 * time.Second)
|
|
|
|
}
|
|
|
|
|
|
|
|
adjusted := timeout.Timeout()
|
|
|
|
|
|
|
|
if initial <= adjusted {
|
|
|
|
t.Errorf("Failure to decrease timeout, expected %v to be less than %v", adjusted, initial)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDynamicTimeoutDualDecrease(t *testing.T) {
|
|
|
|
|
|
|
|
timeout := newDynamicTimeout(time.Minute, time.Second)
|
|
|
|
|
|
|
|
initial := timeout.Timeout()
|
|
|
|
|
|
|
|
for i := 0; i < dynamicTimeoutLogSize; i++ {
|
|
|
|
timeout.LogSuccess(20 * time.Second)
|
|
|
|
}
|
|
|
|
|
|
|
|
adjusted := timeout.Timeout()
|
|
|
|
|
|
|
|
for i := 0; i < dynamicTimeoutLogSize; i++ {
|
|
|
|
timeout.LogSuccess(20 * time.Second)
|
|
|
|
}
|
|
|
|
|
|
|
|
adjustedAgain := timeout.Timeout()
|
|
|
|
|
|
|
|
if initial <= adjusted || adjusted <= adjustedAgain {
|
2020-09-18 12:18:18 -04:00
|
|
|
t.Errorf("Failure to decrease timeout multiple times, initial: %v, adjusted: %v, again: %v", initial, adjusted, adjustedAgain)
|
2017-08-31 14:29:22 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDynamicTimeoutManyDecreases(t *testing.T) {
|
|
|
|
|
|
|
|
timeout := newDynamicTimeout(time.Minute, time.Second)
|
|
|
|
|
|
|
|
initial := timeout.Timeout()
|
|
|
|
|
|
|
|
const successTimeout = 20 * time.Second
|
|
|
|
for l := 0; l < 100; l++ {
|
|
|
|
for i := 0; i < dynamicTimeoutLogSize; i++ {
|
|
|
|
timeout.LogSuccess(successTimeout)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
adjusted := timeout.Timeout()
|
|
|
|
// Check whether eventual timeout is between initial value and success timeout
|
|
|
|
if initial <= adjusted || adjusted <= successTimeout {
|
|
|
|
t.Errorf("Failure to decrease timeout appropriately")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-18 12:18:18 -04:00
|
|
|
func TestDynamicTimeoutConcurrent(t *testing.T) {
|
|
|
|
// Race test.
|
|
|
|
timeout := newDynamicTimeout(time.Second, time.Millisecond)
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for i := 0; i < runtime.GOMAXPROCS(0); i++ {
|
|
|
|
wg.Add(1)
|
|
|
|
rng := rand.New(rand.NewSource(int64(i)))
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
for i := 0; i < 100; i++ {
|
|
|
|
timeout.LogFailure()
|
|
|
|
for j := 0; j < 100; j++ {
|
|
|
|
timeout.LogSuccess(time.Duration(float64(time.Second) * rng.Float64()))
|
|
|
|
}
|
|
|
|
to := timeout.Timeout()
|
|
|
|
if to < time.Millisecond || to > time.Second {
|
|
|
|
panic(to)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
}
|
|
|
|
|
2017-08-31 14:29:22 -04:00
|
|
|
func TestDynamicTimeoutHitMinimum(t *testing.T) {
|
|
|
|
|
|
|
|
const minimum = 30 * time.Second
|
|
|
|
timeout := newDynamicTimeout(time.Minute, minimum)
|
|
|
|
|
|
|
|
initial := timeout.Timeout()
|
|
|
|
|
|
|
|
const successTimeout = 20 * time.Second
|
|
|
|
for l := 0; l < 100; l++ {
|
|
|
|
for i := 0; i < dynamicTimeoutLogSize; i++ {
|
|
|
|
timeout.LogSuccess(successTimeout)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
adjusted := timeout.Timeout()
|
|
|
|
// Check whether eventual timeout has hit the minimum value
|
|
|
|
if initial <= adjusted || adjusted != minimum {
|
|
|
|
t.Errorf("Failure to decrease timeout appropriately")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func testDynamicTimeoutAdjust(t *testing.T, timeout *dynamicTimeout, f func() float64) {
|
|
|
|
|
|
|
|
const successTimeout = 20 * time.Second
|
|
|
|
|
|
|
|
for i := 0; i < dynamicTimeoutLogSize; i++ {
|
|
|
|
|
|
|
|
rnd := f()
|
|
|
|
duration := time.Duration(float64(successTimeout) * rnd)
|
|
|
|
|
|
|
|
if duration < 100*time.Millisecond {
|
|
|
|
duration = 100 * time.Millisecond
|
|
|
|
}
|
|
|
|
if duration >= time.Minute {
|
|
|
|
timeout.LogFailure()
|
|
|
|
} else {
|
|
|
|
timeout.LogSuccess(duration)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDynamicTimeoutAdjustExponential(t *testing.T) {
|
|
|
|
|
|
|
|
timeout := newDynamicTimeout(time.Minute, time.Second)
|
|
|
|
|
2020-09-18 12:18:18 -04:00
|
|
|
rand.Seed(0)
|
2017-08-31 14:29:22 -04:00
|
|
|
|
|
|
|
initial := timeout.Timeout()
|
|
|
|
|
|
|
|
for try := 0; try < 10; try++ {
|
|
|
|
|
|
|
|
testDynamicTimeoutAdjust(t, timeout, rand.ExpFloat64)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
adjusted := timeout.Timeout()
|
|
|
|
if initial <= adjusted {
|
|
|
|
t.Errorf("Failure to decrease timeout, expected %v to be less than %v", adjusted, initial)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDynamicTimeoutAdjustNormalized(t *testing.T) {
|
|
|
|
|
|
|
|
timeout := newDynamicTimeout(time.Minute, time.Second)
|
|
|
|
|
2020-09-18 12:18:18 -04:00
|
|
|
rand.Seed(0)
|
2017-08-31 14:29:22 -04:00
|
|
|
|
|
|
|
initial := timeout.Timeout()
|
|
|
|
|
|
|
|
for try := 0; try < 10; try++ {
|
|
|
|
|
|
|
|
testDynamicTimeoutAdjust(t, timeout, func() float64 {
|
|
|
|
return 1.0 + rand.NormFloat64()
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
adjusted := timeout.Timeout()
|
|
|
|
if initial <= adjusted {
|
|
|
|
t.Errorf("Failure to decrease timeout, expected %v to be less than %v", adjusted, initial)
|
|
|
|
}
|
|
|
|
}
|