// 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 . package crypto import ( "math/rand" "time" ) // default retry configuration const ( retryWaitMin = 100 * time.Millisecond // minimum retry limit. retryWaitMax = 1500 * time.Millisecond // 1.5 secs worth of max retry. ) // LinearJitterBackoff provides the time.Duration for a caller to // perform linear backoff based on the attempt number and with jitter to // prevent a thundering herd. // // min and max here are *not* absolute values. The number to be multiplied by // the attempt number will be chosen at random from between them, thus they are // bounding the jitter. // // For instance: // * To get strictly linear backoff of one second increasing each retry, set // both to one second (1s, 2s, 3s, 4s, ...) // * To get a small amount of jitter centered around one second increasing each // retry, set to around one second, such as a min of 800ms and max of 1200ms // (892ms, 2102ms, 2945ms, 4312ms, ...) // * To get extreme jitter, set to a very wide spread, such as a min of 100ms // and a max of 20s (15382ms, 292ms, 51321ms, 35234ms, ...) func LinearJitterBackoff(min, max time.Duration, attemptNum int) time.Duration { // attemptNum always starts at zero but we want to start at 1 for multiplication attemptNum++ if max <= min { // Unclear what to do here, or they are the same, so return min * // attemptNum return min * time.Duration(attemptNum) } // Seed rand; doing this every time is fine rand := rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) // Pick a random number that lies somewhere between the min and max and // multiply by the attemptNum. attemptNum starts at zero so we always // increment here. We first get a random percentage, then apply that to the // difference between min and max, and add to min. jitter := rand.Float64() * float64(max-min) jitterMin := int64(jitter) + int64(min) return time.Duration(jitterMin * int64(attemptNum)) }