github.com/lastbackend/toolkit@v0.0.0-20241020043710-cafa37b95aad/pkg/util/backoff/backoff.go (about)

     1  /*
     2  Copyright [2014] - [2023] The Last.Backend authors.
     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 backoff
    18  
    19  import (
    20  	"math"
    21  	"sync/atomic"
    22  	"time"
    23  )
    24  
    25  const maxInt64 = float64(math.MaxInt64 - 512)
    26  
    27  type Backoff struct {
    28  	attempt uint64
    29  	// Factor are the multiplication factor
    30  	Factor float64
    31  	// Min and Max are the minimum and maximum counter values.
    32  	Min, Max time.Duration
    33  }
    34  
    35  func (b *Backoff) Duration() time.Duration {
    36  	d := b.ForAttempt(float64(atomic.AddUint64(&b.attempt, 1) - 1))
    37  	return d
    38  }
    39  
    40  func (b *Backoff) ForAttempt(attempt float64) time.Duration {
    41  	min := b.Min
    42  	if min <= 0 {
    43  		min = 100 * time.Millisecond
    44  	}
    45  	max := b.Max
    46  	if max <= 0 {
    47  		max = 10 * time.Second
    48  	}
    49  	if min >= max {
    50  		return max
    51  	}
    52  
    53  	factor := b.Factor
    54  	if factor <= 0 {
    55  		factor = 2
    56  	}
    57  
    58  	duration := float64(min) * math.Pow(factor, attempt)
    59  
    60  	if duration > maxInt64 {
    61  		return max
    62  	}
    63  
    64  	d := time.Duration(duration)
    65  	if d < min {
    66  		return min
    67  	}
    68  	if d > max {
    69  		return max
    70  	}
    71  	return d
    72  }
    73  
    74  func (b *Backoff) Reset() {
    75  	atomic.StoreUint64(&b.attempt, 0)
    76  }
    77  
    78  func (b *Backoff) Attempt() float64 {
    79  	return float64(atomic.LoadUint64(&b.attempt))
    80  }