mirror of
https://github.com/rclone/rclone.git
synced 2025-12-06 00:03:32 +00:00
pacer: make pacer more flexible
Make the pacer package more flexible by extracting the pace calculation functions into a separate interface. This also allows to move features that require the fs package like logging and custom errors into the fs package. Also add a RetryAfterError sentinel error that can be used to signal a desired retry time to the Calculator.
This commit is contained in:
committed by
Nick Craig-Wood
parent
9ed721a3f6
commit
61616ba864
89
fs/fs.go
89
fs/fs.go
@@ -16,8 +16,10 @@ import (
|
||||
|
||||
"github.com/ncw/rclone/fs/config/configmap"
|
||||
"github.com/ncw/rclone/fs/config/configstruct"
|
||||
"github.com/ncw/rclone/fs/fserrors"
|
||||
"github.com/ncw/rclone/fs/fspath"
|
||||
"github.com/ncw/rclone/fs/hash"
|
||||
"github.com/ncw/rclone/lib/pacer"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@@ -1112,3 +1114,90 @@ func GetModifyWindow(fss ...Info) time.Duration {
|
||||
}
|
||||
return window
|
||||
}
|
||||
|
||||
// Pacer is a simple wrapper around a pacer.Pacer with logging.
|
||||
type Pacer struct {
|
||||
*pacer.Pacer
|
||||
}
|
||||
|
||||
type logCalculator struct {
|
||||
pacer.Calculator
|
||||
}
|
||||
|
||||
// NewPacer creates a Pacer for the given Fs and Calculator.
|
||||
func NewPacer(c pacer.Calculator) *Pacer {
|
||||
p := &Pacer{
|
||||
Pacer: pacer.New(
|
||||
pacer.InvokerOption(pacerInvoker),
|
||||
pacer.MaxConnectionsOption(Config.Checkers+Config.Transfers),
|
||||
pacer.RetriesOption(Config.LowLevelRetries),
|
||||
pacer.CalculatorOption(c),
|
||||
),
|
||||
}
|
||||
p.SetCalculator(c)
|
||||
return p
|
||||
}
|
||||
|
||||
func (d *logCalculator) Calculate(state pacer.State) time.Duration {
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
if c, ok := state.LastError.(causer); ok {
|
||||
state.LastError = c.Cause()
|
||||
} else {
|
||||
Logf("pacer", "Invalid error in fs.Pacer: %t", state.LastError)
|
||||
}
|
||||
oldSleepTime := state.SleepTime
|
||||
newSleepTime := d.Calculator.Calculate(state)
|
||||
if state.ConsecutiveRetries > 0 {
|
||||
if newSleepTime != oldSleepTime {
|
||||
Debugf("pacer", "Rate limited, increasing sleep to %v", newSleepTime)
|
||||
}
|
||||
} else {
|
||||
if newSleepTime != oldSleepTime {
|
||||
Debugf("pacer", "Reducing sleep to %v", newSleepTime)
|
||||
}
|
||||
}
|
||||
return newSleepTime
|
||||
}
|
||||
|
||||
// SetCalculator sets the pacing algorithm. Don't modify the Calculator object
|
||||
// afterwards, use the ModifyCalculator method when needed.
|
||||
//
|
||||
// It will choose the default algorithm if nil is passed in.
|
||||
func (p *Pacer) SetCalculator(c pacer.Calculator) {
|
||||
switch c.(type) {
|
||||
case *logCalculator:
|
||||
Logf("pacer", "Invalid Calculator in fs.Pacer.SetCalculator")
|
||||
case nil:
|
||||
c = &logCalculator{pacer.NewDefault()}
|
||||
default:
|
||||
c = &logCalculator{c}
|
||||
}
|
||||
|
||||
p.Pacer.SetCalculator(c)
|
||||
}
|
||||
|
||||
// ModifyCalculator calls the given function with the currently configured
|
||||
// Calculator and the Pacer lock held.
|
||||
func (p *Pacer) ModifyCalculator(f func(pacer.Calculator)) {
|
||||
p.ModifyCalculator(func(c pacer.Calculator) {
|
||||
switch _c := c.(type) {
|
||||
case *logCalculator:
|
||||
f(_c.Calculator)
|
||||
default:
|
||||
Logf("pacer", "Invalid Calculator in fs.Pacer: %t", c)
|
||||
f(c)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func pacerInvoker(try, retries int, f pacer.Paced) (retry bool, err error) {
|
||||
retry, err = f()
|
||||
if retry {
|
||||
Debugf("pacer", "low level retry %d/%d (error %v)", try, retries, err)
|
||||
err = fserrors.RetryError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user