| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2016, 2017 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"math/rand" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // lockedRandSource provides protected rand source, implements rand.Source interface.
 | 
					
						
							|  |  |  | type lockedRandSource struct { | 
					
						
							|  |  |  | 	lk  sync.Mutex | 
					
						
							|  |  |  | 	src rand.Source | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Int63 returns a non-negative pseudo-random 63-bit integer as an
 | 
					
						
							|  |  |  | // int64.
 | 
					
						
							|  |  |  | func (r *lockedRandSource) Int63() (n int64) { | 
					
						
							|  |  |  | 	r.lk.Lock() | 
					
						
							|  |  |  | 	n = r.src.Int63() | 
					
						
							|  |  |  | 	r.lk.Unlock() | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Seed uses the provided seed value to initialize the generator to a
 | 
					
						
							|  |  |  | // deterministic state.
 | 
					
						
							|  |  |  | func (r *lockedRandSource) Seed(seed int64) { | 
					
						
							|  |  |  | 	r.lk.Lock() | 
					
						
							|  |  |  | 	r.src.Seed(seed) | 
					
						
							|  |  |  | 	r.lk.Unlock() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MaxJitter will randomize over the full exponential backoff time
 | 
					
						
							|  |  |  | const MaxJitter = 1.0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-07 18:16:29 +08:00
										 |  |  | // NoJitter disables the use of jitter for randomizing the
 | 
					
						
							|  |  |  | // exponential backoff time
 | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | const NoJitter = 0.0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Global random source for fetching random values.
 | 
					
						
							|  |  |  | var globalRandomSource = rand.New(&lockedRandSource{ | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	src: rand.NewSource(UTCNow().UnixNano()), | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-07 18:16:29 +08:00
										 |  |  | // newRetryTimerJitter creates a timer with exponentially increasing delays
 | 
					
						
							|  |  |  | // until the maximum retry attempts are reached. - this function is a fully
 | 
					
						
							|  |  |  | // configurable version, meant for only advanced use cases. For the most part
 | 
					
						
							|  |  |  | // one should use newRetryTimerSimple and newRetryTimer.
 | 
					
						
							|  |  |  | func newRetryTimerWithJitter(unit time.Duration, cap time.Duration, jitter float64, doneCh chan struct{}) <-chan int { | 
					
						
							| 
									
										
										
										
											2016-10-27 07:09:06 +08:00
										 |  |  | 	attemptCh := make(chan int) | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-12 01:49:30 +08:00
										 |  |  | 	// normalize jitter to the range [0, 1.0]
 | 
					
						
							|  |  |  | 	if jitter < NoJitter { | 
					
						
							|  |  |  | 		jitter = NoJitter | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if jitter > MaxJitter { | 
					
						
							|  |  |  | 		jitter = MaxJitter | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	// computes the exponential backoff duration according to
 | 
					
						
							|  |  |  | 	// https://www.awsarchitectureblog.com/2015/03/backoff.html
 | 
					
						
							|  |  |  | 	exponentialBackoffWait := func(attempt int) time.Duration { | 
					
						
							| 
									
										
										
										
											2016-11-12 01:49:30 +08:00
										 |  |  | 		// 1<<uint(attempt) below could overflow, so limit the value of attempt
 | 
					
						
							|  |  |  | 		maxAttempt := 30 | 
					
						
							|  |  |  | 		if attempt > maxAttempt { | 
					
						
							|  |  |  | 			attempt = maxAttempt | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		//sleep = random_between(0, min(cap, base * 2 ** attempt))
 | 
					
						
							|  |  |  | 		sleep := unit * time.Duration(1<<uint(attempt)) | 
					
						
							|  |  |  | 		if sleep > cap { | 
					
						
							|  |  |  | 			sleep = cap | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if jitter != NoJitter { | 
					
						
							|  |  |  | 			sleep -= time.Duration(globalRandomSource.Float64() * float64(sleep) * jitter) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return sleep | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		defer close(attemptCh) | 
					
						
							| 
									
										
										
										
											2016-12-31 09:08:02 +08:00
										 |  |  | 		nextBackoff := 0 | 
					
						
							| 
									
										
										
										
											2017-02-02 01:17:32 +08:00
										 |  |  | 		// Channel used to signal after the expiry of backoff wait seconds.
 | 
					
						
							|  |  |  | 		var timer *time.Timer | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 		for { | 
					
						
							| 
									
										
										
										
											2017-02-02 01:17:32 +08:00
										 |  |  | 			select { // Attempts starts.
 | 
					
						
							| 
									
										
										
										
											2016-10-27 07:09:06 +08:00
										 |  |  | 			case attemptCh <- nextBackoff: | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 				nextBackoff++ | 
					
						
							|  |  |  | 			case <-doneCh: | 
					
						
							|  |  |  | 				// Stop the routine.
 | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-02-02 01:17:32 +08:00
										 |  |  | 			timer = time.NewTimer(exponentialBackoffWait(nextBackoff)) | 
					
						
							|  |  |  | 			// wait till next backoff time or till doneCh gets a message.
 | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-timer.C: | 
					
						
							|  |  |  | 			case <-doneCh: | 
					
						
							|  |  |  | 				// stop the timer and return.
 | 
					
						
							|  |  |  | 				timer.Stop() | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2017-02-07 18:16:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Start reading..
 | 
					
						
							| 
									
										
										
										
											2016-10-06 03:48:07 +08:00
										 |  |  | 	return attemptCh | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-02-07 18:16:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Default retry constants.
 | 
					
						
							| 
									
										
										
										
											2017-03-02 20:58:39 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2017-02-07 18:16:29 +08:00
										 |  |  | 	defaultRetryUnit = time.Second      // 1 second.
 | 
					
						
							|  |  |  | 	defaultRetryCap  = 30 * time.Second // 30 seconds.
 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newRetryTimerSimple creates a timer with exponentially increasing delays
 | 
					
						
							|  |  |  | // until the maximum retry attempts are reached. - this function is a
 | 
					
						
							|  |  |  | // simpler version with all default values.
 | 
					
						
							|  |  |  | func newRetryTimerSimple(doneCh chan struct{}) <-chan int { | 
					
						
							|  |  |  | 	return newRetryTimerWithJitter(defaultRetryUnit, defaultRetryCap, MaxJitter, doneCh) | 
					
						
							|  |  |  | } |