github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/lib/pacer/pacers.go (about)

     1  package pacer
     2  
     3  import (
     4  	"math/rand"
     5  	"time"
     6  
     7  	"golang.org/x/time/rate"
     8  )
     9  
    10  type (
    11  	// MinSleep configures the minimum sleep time of a Calculator
    12  	MinSleep time.Duration
    13  	// MaxSleep configures the maximum sleep time of a Calculator
    14  	MaxSleep time.Duration
    15  	// DecayConstant configures the decay constant time of a Calculator
    16  	DecayConstant uint
    17  	// AttackConstant configures the attack constant of a Calculator
    18  	AttackConstant uint
    19  	// Burst configures the number of API calls to allow without sleeping
    20  	Burst int
    21  )
    22  
    23  // Default is a truncated exponential attack and decay.
    24  //
    25  // On retries the sleep time is doubled, on non errors then sleeptime decays
    26  // according to the decay constant as set with SetDecayConstant.
    27  //
    28  // The sleep never goes below that set with SetMinSleep or above that set
    29  // with SetMaxSleep.
    30  type Default struct {
    31  	minSleep       time.Duration // minimum sleep time
    32  	maxSleep       time.Duration // maximum sleep time
    33  	decayConstant  uint          // decay constant
    34  	attackConstant uint          // attack constant
    35  }
    36  
    37  // DefaultOption is the interface implemented by all options for the Default Calculator
    38  type DefaultOption interface {
    39  	ApplyDefault(*Default)
    40  }
    41  
    42  // NewDefault creates a Calculator used by Pacer as the default.
    43  func NewDefault(opts ...DefaultOption) *Default {
    44  	c := &Default{
    45  		minSleep:       10 * time.Millisecond,
    46  		maxSleep:       2 * time.Second,
    47  		decayConstant:  2,
    48  		attackConstant: 1,
    49  	}
    50  	c.Update(opts...)
    51  	return c
    52  }
    53  
    54  // Update applies the Calculator options.
    55  func (c *Default) Update(opts ...DefaultOption) {
    56  	for _, opt := range opts {
    57  		opt.ApplyDefault(c)
    58  	}
    59  }
    60  
    61  // ApplyDefault updates the value on the Calculator
    62  func (o MinSleep) ApplyDefault(c *Default) {
    63  	c.minSleep = time.Duration(o)
    64  }
    65  
    66  // ApplyDefault updates the value on the Calculator
    67  func (o MaxSleep) ApplyDefault(c *Default) {
    68  	c.maxSleep = time.Duration(o)
    69  }
    70  
    71  // ApplyDefault updates the value on the Calculator
    72  func (o DecayConstant) ApplyDefault(c *Default) {
    73  	c.decayConstant = uint(o)
    74  }
    75  
    76  // ApplyDefault updates the value on the Calculator
    77  func (o AttackConstant) ApplyDefault(c *Default) {
    78  	c.attackConstant = uint(o)
    79  }
    80  
    81  // Calculate takes the current Pacer state and return the wait time until the next try.
    82  func (c *Default) Calculate(state State) time.Duration {
    83  	if t, ok := IsRetryAfter(state.LastError); ok {
    84  		if t < c.minSleep {
    85  			return c.minSleep
    86  		}
    87  		return t
    88  	}
    89  
    90  	if state.ConsecutiveRetries > 0 {
    91  		sleepTime := c.maxSleep
    92  		if c.attackConstant != 0 {
    93  			sleepTime = (state.SleepTime << c.attackConstant) / ((1 << c.attackConstant) - 1)
    94  		}
    95  		if sleepTime > c.maxSleep {
    96  			sleepTime = c.maxSleep
    97  		}
    98  		return sleepTime
    99  	}
   100  	sleepTime := (state.SleepTime<<c.decayConstant - state.SleepTime) >> c.decayConstant
   101  	if sleepTime < c.minSleep {
   102  		sleepTime = c.minSleep
   103  	}
   104  	return sleepTime
   105  }
   106  
   107  // AmazonCloudDrive is a specialized pacer for Amazon Drive
   108  //
   109  // It implements a truncated exponential backoff strategy with randomization.
   110  // Normally operations are paced at the interval set with SetMinSleep. On errors
   111  // the sleep timer is set to 0..2**retries seconds.
   112  //
   113  // See https://developer.amazon.com/public/apis/experience/cloud-drive/content/restful-api-best-practices
   114  type AmazonCloudDrive struct {
   115  	minSleep time.Duration // minimum sleep time
   116  }
   117  
   118  // AmazonCloudDriveOption is the interface implemented by all options for the AmazonCloudDrive Calculator
   119  type AmazonCloudDriveOption interface {
   120  	ApplyAmazonCloudDrive(*AmazonCloudDrive)
   121  }
   122  
   123  // NewAmazonCloudDrive returns a new AmazonCloudDrive Calculator with default values
   124  func NewAmazonCloudDrive(opts ...AmazonCloudDriveOption) *AmazonCloudDrive {
   125  	c := &AmazonCloudDrive{
   126  		minSleep: 10 * time.Millisecond,
   127  	}
   128  	c.Update(opts...)
   129  	return c
   130  }
   131  
   132  // Update applies the Calculator options.
   133  func (c *AmazonCloudDrive) Update(opts ...AmazonCloudDriveOption) {
   134  	for _, opt := range opts {
   135  		opt.ApplyAmazonCloudDrive(c)
   136  	}
   137  }
   138  
   139  // ApplyAmazonCloudDrive updates the value on the Calculator
   140  func (o MinSleep) ApplyAmazonCloudDrive(c *AmazonCloudDrive) {
   141  	c.minSleep = time.Duration(o)
   142  }
   143  
   144  // Calculate takes the current Pacer state and return the wait time until the next try.
   145  func (c *AmazonCloudDrive) Calculate(state State) time.Duration {
   146  	if t, ok := IsRetryAfter(state.LastError); ok {
   147  		if t < c.minSleep {
   148  			return c.minSleep
   149  		}
   150  		return t
   151  	}
   152  
   153  	consecutiveRetries := state.ConsecutiveRetries
   154  	if consecutiveRetries == 0 {
   155  		return c.minSleep
   156  	}
   157  	if consecutiveRetries > 9 {
   158  		consecutiveRetries = 9
   159  	}
   160  	// consecutiveRetries starts at 1 so
   161  	// maxSleep is 2**(consecutiveRetries-1) seconds
   162  	maxSleep := time.Second << uint(consecutiveRetries-1)
   163  	// actual sleep is random from 0..maxSleep
   164  	sleepTime := time.Duration(rand.Int63n(int64(maxSleep)))
   165  	if sleepTime < c.minSleep {
   166  		sleepTime = c.minSleep
   167  	}
   168  	return sleepTime
   169  }
   170  
   171  // GoogleDrive is a specialized pacer for Google Drive
   172  //
   173  // It implements a truncated exponential backoff strategy with randomization.
   174  // Normally operations are paced at the interval set with SetMinSleep. On errors
   175  // the sleep timer is set to (2 ^ n) + random_number_milliseconds seconds.
   176  //
   177  // See https://developers.google.com/drive/v2/web/handle-errors#exponential-backoff
   178  type GoogleDrive struct {
   179  	minSleep time.Duration // minimum sleep time
   180  	burst    int           // number of requests without sleeping
   181  	limiter  *rate.Limiter // rate limiter for the minSleep
   182  }
   183  
   184  // GoogleDriveOption is the interface implemented by all options for the GoogleDrive Calculator
   185  type GoogleDriveOption interface {
   186  	ApplyGoogleDrive(*GoogleDrive)
   187  }
   188  
   189  // NewGoogleDrive returns a new GoogleDrive Calculator with default values
   190  func NewGoogleDrive(opts ...GoogleDriveOption) *GoogleDrive {
   191  	c := &GoogleDrive{
   192  		minSleep: 10 * time.Millisecond,
   193  		burst:    1,
   194  	}
   195  	c.Update(opts...)
   196  	return c
   197  }
   198  
   199  // Update applies the Calculator options.
   200  func (c *GoogleDrive) Update(opts ...GoogleDriveOption) {
   201  	for _, opt := range opts {
   202  		opt.ApplyGoogleDrive(c)
   203  	}
   204  	if c.burst <= 0 {
   205  		c.burst = 1
   206  	}
   207  	c.limiter = rate.NewLimiter(rate.Every(c.minSleep), c.burst)
   208  }
   209  
   210  // ApplyGoogleDrive updates the value on the Calculator
   211  func (o MinSleep) ApplyGoogleDrive(c *GoogleDrive) {
   212  	c.minSleep = time.Duration(o)
   213  }
   214  
   215  // ApplyGoogleDrive updates the value on the Calculator
   216  func (o Burst) ApplyGoogleDrive(c *GoogleDrive) {
   217  	c.burst = int(o)
   218  }
   219  
   220  // Calculate takes the current Pacer state and return the wait time until the next try.
   221  func (c *GoogleDrive) Calculate(state State) time.Duration {
   222  	if t, ok := IsRetryAfter(state.LastError); ok {
   223  		if t < c.minSleep {
   224  			return c.minSleep
   225  		}
   226  		return t
   227  	}
   228  
   229  	consecutiveRetries := state.ConsecutiveRetries
   230  	if consecutiveRetries == 0 {
   231  		return c.limiter.Reserve().Delay()
   232  	}
   233  	if consecutiveRetries > 5 {
   234  		consecutiveRetries = 5
   235  	}
   236  	// consecutiveRetries starts at 1 so go from 1,2,3,4,5,5 => 1,2,4,8,16,16
   237  	// maxSleep is 2**(consecutiveRetries-1) seconds + random milliseconds
   238  	return time.Second<<uint(consecutiveRetries-1) + time.Duration(rand.Int63n(int64(time.Second)))
   239  }
   240  
   241  // S3 implements a pacer compatible with our expectations of S3, where it tries to not
   242  // delay at all between successful calls, but backs off in the default fashion in response
   243  // to any errors.
   244  // The assumption is that errors should be exceedingly rare (S3 seems to have largely solved
   245  // the sort of stability questions rclone is likely to run into), and in the happy case
   246  // it can handle calls with no delays between them.
   247  //
   248  // Basically defaultPacer, but with some handling of sleepTime going to/from 0ms
   249  type S3 struct {
   250  	minSleep       time.Duration // minimum sleep time
   251  	maxSleep       time.Duration // maximum sleep time
   252  	decayConstant  uint          // decay constant
   253  	attackConstant uint          // attack constant
   254  }
   255  
   256  // S3Option is the interface implemented by all options for the S3 Calculator
   257  type S3Option interface {
   258  	ApplyS3(*S3)
   259  }
   260  
   261  // NewS3 returns a new S3 Calculator with default values
   262  func NewS3(opts ...S3Option) *S3 {
   263  	c := &S3{
   264  		maxSleep:       2 * time.Second,
   265  		decayConstant:  2,
   266  		attackConstant: 1,
   267  	}
   268  	c.Update(opts...)
   269  	return c
   270  }
   271  
   272  // Update applies the Calculator options.
   273  func (c *S3) Update(opts ...S3Option) {
   274  	for _, opt := range opts {
   275  		opt.ApplyS3(c)
   276  	}
   277  }
   278  
   279  // ApplyS3 updates the value on the Calculator
   280  func (o MaxSleep) ApplyS3(c *S3) {
   281  	c.maxSleep = time.Duration(o)
   282  }
   283  
   284  // ApplyS3 updates the value on the Calculator
   285  func (o MinSleep) ApplyS3(c *S3) {
   286  	c.minSleep = time.Duration(o)
   287  }
   288  
   289  // ApplyS3 updates the value on the Calculator
   290  func (o DecayConstant) ApplyS3(c *S3) {
   291  	c.decayConstant = uint(o)
   292  }
   293  
   294  // ApplyS3 updates the value on the Calculator
   295  func (o AttackConstant) ApplyS3(c *S3) {
   296  	c.attackConstant = uint(o)
   297  }
   298  
   299  // Calculate takes the current Pacer state and return the wait time until the next try.
   300  func (c *S3) Calculate(state State) time.Duration {
   301  	if t, ok := IsRetryAfter(state.LastError); ok {
   302  		if t < c.minSleep {
   303  			return c.minSleep
   304  		}
   305  		return t
   306  	}
   307  
   308  	if state.ConsecutiveRetries > 0 {
   309  		if c.attackConstant == 0 {
   310  			return c.maxSleep
   311  		}
   312  		if state.SleepTime == 0 {
   313  			return c.minSleep
   314  		}
   315  		sleepTime := (state.SleepTime << c.attackConstant) / ((1 << c.attackConstant) - 1)
   316  		if sleepTime > c.maxSleep {
   317  			sleepTime = c.maxSleep
   318  		}
   319  		return sleepTime
   320  	}
   321  	sleepTime := (state.SleepTime<<c.decayConstant - state.SleepTime) >> c.decayConstant
   322  	if sleepTime < c.minSleep {
   323  		sleepTime = 0
   324  	}
   325  	return sleepTime
   326  }