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 }