github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/accounting/accounting.go (about)

     1  // Package accounting providers an accounting and limiting reader
     2  package accounting
     3  
     4  import (
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"sync"
    10  	"time"
    11  	"unicode/utf8"
    12  
    13  	"github.com/rclone/rclone/fs/rc"
    14  
    15  	"github.com/rclone/rclone/fs"
    16  	"github.com/rclone/rclone/fs/asyncreader"
    17  	"github.com/rclone/rclone/fs/fserrors"
    18  )
    19  
    20  // ErrorMaxTransferLimitReached defines error when transfer limit is reached.
    21  // Used for checking on exit and matching to correct exit code.
    22  var ErrorMaxTransferLimitReached = errors.New("max transfer limit reached as set by --max-transfer")
    23  
    24  // ErrorMaxTransferLimitReachedFatal is returned from Read when the max
    25  // transfer limit is reached.
    26  var ErrorMaxTransferLimitReachedFatal = fserrors.FatalError(ErrorMaxTransferLimitReached)
    27  
    28  // ErrorMaxTransferLimitReachedGraceful is returned from operations.Copy when the max
    29  // transfer limit is reached and a graceful stop is required.
    30  var ErrorMaxTransferLimitReachedGraceful = fserrors.NoRetryError(ErrorMaxTransferLimitReached)
    31  
    32  // Start sets up the accounting, in particular the bandwidth limiting
    33  func Start(ctx context.Context) {
    34  	// Start the token bucket limiter
    35  	TokenBucket.StartTokenBucket(ctx)
    36  
    37  	// Start the bandwidth update ticker
    38  	TokenBucket.StartTokenTicker(ctx)
    39  
    40  	// Start the transactions per second limiter
    41  	StartLimitTPS(ctx)
    42  }
    43  
    44  // Account limits and accounts for one transfer
    45  type Account struct {
    46  	stats *StatsInfo
    47  	// The mutex is to make sure Read() and Close() aren't called
    48  	// concurrently.  Unfortunately the persistent connection loop
    49  	// in http transport calls Read() after Do() returns on
    50  	// CancelRequest so this race can happen when it apparently
    51  	// shouldn't.
    52  	mu       sync.Mutex // mutex protects these values
    53  	in       io.Reader
    54  	ctx      context.Context // current context for transfer - may change
    55  	ci       *fs.ConfigInfo
    56  	origIn   io.ReadCloser
    57  	close    io.Closer
    58  	size     int64
    59  	name     string
    60  	closed   bool          // set if the file is closed
    61  	exit     chan struct{} // channel that will be closed when transfer is finished
    62  	withBuf  bool          // is using a buffered in
    63  	checking bool          // set if attached transfer is checking
    64  
    65  	tokenBucket buckets // per file bandwidth limiter (may be nil)
    66  
    67  	values accountValues
    68  }
    69  
    70  // accountValues holds statistics for this Account
    71  type accountValues struct {
    72  	mu      sync.Mutex // Mutex for stat values.
    73  	bytes   int64      // Total number of bytes read
    74  	max     int64      // if >=0 the max number of bytes to transfer
    75  	start   time.Time  // Start time of first read
    76  	lpTime  time.Time  // Time of last average measurement
    77  	lpBytes int        // Number of bytes read since last measurement
    78  	avg     float64    // Moving average of last few measurements in Byte/s
    79  }
    80  
    81  const averagePeriod = 16 // period to do exponentially weighted averages over
    82  
    83  // newAccountSizeName makes an Account reader for an io.ReadCloser of
    84  // the given size and name
    85  func newAccountSizeName(ctx context.Context, stats *StatsInfo, in io.ReadCloser, size int64, name string) *Account {
    86  	acc := &Account{
    87  		stats:  stats,
    88  		in:     in,
    89  		ctx:    ctx,
    90  		ci:     fs.GetConfig(ctx),
    91  		close:  in,
    92  		origIn: in,
    93  		size:   size,
    94  		name:   name,
    95  		exit:   make(chan struct{}),
    96  		values: accountValues{
    97  			avg:    0,
    98  			lpTime: time.Now(),
    99  			max:    -1,
   100  		},
   101  	}
   102  	if acc.ci.CutoffMode == fs.CutoffModeHard {
   103  		acc.values.max = int64((acc.ci.MaxTransfer))
   104  	}
   105  	currLimit := acc.ci.BwLimitFile.LimitAt(time.Now())
   106  	if currLimit.Bandwidth.IsSet() {
   107  		fs.Debugf(acc.name, "Limiting file transfer to %v", currLimit.Bandwidth)
   108  		acc.tokenBucket = newTokenBucket(currLimit.Bandwidth)
   109  	}
   110  
   111  	go acc.averageLoop()
   112  	stats.inProgress.set(acc.name, acc)
   113  	return acc
   114  }
   115  
   116  // WithBuffer - If the file is above a certain size it adds an Async reader
   117  func (acc *Account) WithBuffer() *Account {
   118  	// if already have a buffer then just return
   119  	if acc.withBuf {
   120  		return acc
   121  	}
   122  	acc.withBuf = true
   123  	var buffers int
   124  	if acc.size >= int64(acc.ci.BufferSize) || acc.size == -1 {
   125  		buffers = int(int64(acc.ci.BufferSize) / asyncreader.BufferSize)
   126  	} else {
   127  		buffers = int(acc.size / asyncreader.BufferSize)
   128  	}
   129  	// On big files add a buffer
   130  	if buffers > 0 {
   131  		rc, err := asyncreader.New(acc.ctx, acc.origIn, buffers)
   132  		if err != nil {
   133  			fs.Errorf(acc.name, "Failed to make buffer: %v", err)
   134  		} else {
   135  			acc.in = rc
   136  			acc.close = rc
   137  		}
   138  	}
   139  	return acc
   140  }
   141  
   142  // HasBuffer - returns true if this Account has an AsyncReader with a buffer
   143  func (acc *Account) HasBuffer() bool {
   144  	acc.mu.Lock()
   145  	defer acc.mu.Unlock()
   146  	_, ok := acc.in.(*asyncreader.AsyncReader)
   147  	return ok
   148  }
   149  
   150  // GetReader returns the underlying io.ReadCloser under any Buffer
   151  func (acc *Account) GetReader() io.ReadCloser {
   152  	acc.mu.Lock()
   153  	defer acc.mu.Unlock()
   154  	return acc.origIn
   155  }
   156  
   157  // GetAsyncReader returns the current AsyncReader or nil if Account is unbuffered
   158  func (acc *Account) GetAsyncReader() *asyncreader.AsyncReader {
   159  	acc.mu.Lock()
   160  	defer acc.mu.Unlock()
   161  	if asyncIn, ok := acc.in.(*asyncreader.AsyncReader); ok {
   162  		return asyncIn
   163  	}
   164  	return nil
   165  }
   166  
   167  // StopBuffering stops the async buffer doing any more buffering
   168  func (acc *Account) StopBuffering() {
   169  	if asyncIn, ok := acc.in.(*asyncreader.AsyncReader); ok {
   170  		asyncIn.StopBuffering()
   171  	}
   172  }
   173  
   174  // Abandon stops the async buffer doing any more buffering
   175  func (acc *Account) Abandon() {
   176  	if asyncIn, ok := acc.in.(*asyncreader.AsyncReader); ok {
   177  		asyncIn.Abandon()
   178  	}
   179  }
   180  
   181  // UpdateReader updates the underlying io.ReadCloser stopping the
   182  // async buffer (if any) and re-adding it
   183  func (acc *Account) UpdateReader(ctx context.Context, in io.ReadCloser) {
   184  	acc.mu.Lock()
   185  	withBuf := acc.withBuf
   186  	if withBuf {
   187  		acc.Abandon()
   188  		acc.withBuf = false
   189  	}
   190  	acc.in = in
   191  	acc.ctx = ctx
   192  	acc.close = in
   193  	acc.origIn = in
   194  	acc.closed = false
   195  	if withBuf {
   196  		acc.WithBuffer()
   197  	}
   198  	acc.mu.Unlock()
   199  
   200  	// Reset counter to stop percentage going over 100%
   201  	acc.values.mu.Lock()
   202  	acc.values.lpBytes = 0
   203  	acc.values.bytes = 0
   204  	acc.values.mu.Unlock()
   205  }
   206  
   207  // averageLoop calculates averages for the stats in the background
   208  func (acc *Account) averageLoop() {
   209  	tick := time.NewTicker(time.Second)
   210  	var period float64
   211  	defer tick.Stop()
   212  	for {
   213  		select {
   214  		case now := <-tick.C:
   215  			acc.values.mu.Lock()
   216  			// Add average of last second.
   217  			elapsed := now.Sub(acc.values.lpTime).Seconds()
   218  			avg := 0.0
   219  			if elapsed > 0 {
   220  				avg = float64(acc.values.lpBytes) / elapsed
   221  			}
   222  			// Soft start the moving average
   223  			if period < averagePeriod {
   224  				period++
   225  			}
   226  			acc.values.avg = (avg + (period-1)*acc.values.avg) / period
   227  			acc.values.lpBytes = 0
   228  			acc.values.lpTime = now
   229  			// Unlock stats
   230  			acc.values.mu.Unlock()
   231  		case <-acc.exit:
   232  			return
   233  		}
   234  	}
   235  }
   236  
   237  // Check the read before it has happened is valid returning the number
   238  // of bytes remaining to read.
   239  func (acc *Account) checkReadBefore() (bytesUntilLimit int64, err error) {
   240  	// Check to see if context is cancelled
   241  	if err = acc.ctx.Err(); err != nil {
   242  		return 0, err
   243  	}
   244  	acc.values.mu.Lock()
   245  	if acc.values.max >= 0 {
   246  		bytesUntilLimit = acc.values.max - acc.stats.GetBytes()
   247  		if bytesUntilLimit < 0 {
   248  			acc.values.mu.Unlock()
   249  			return bytesUntilLimit, ErrorMaxTransferLimitReachedFatal
   250  		}
   251  	} else {
   252  		bytesUntilLimit = 1 << 62
   253  	}
   254  	// Set start time.
   255  	if acc.values.start.IsZero() {
   256  		acc.values.start = time.Now()
   257  	}
   258  	acc.values.mu.Unlock()
   259  	return bytesUntilLimit, nil
   260  }
   261  
   262  // Check the read call after the read has happened
   263  func (acc *Account) checkReadAfter(bytesUntilLimit int64, n int, err error) (outN int, outErr error) {
   264  	bytesUntilLimit -= int64(n)
   265  	if bytesUntilLimit < 0 {
   266  		// chop the overage off
   267  		n += int(bytesUntilLimit)
   268  		if n < 0 {
   269  			n = 0
   270  		}
   271  		err = ErrorMaxTransferLimitReachedFatal
   272  	}
   273  	return n, err
   274  }
   275  
   276  // ServerSideTransferStart should be called at the start of a server-side transfer
   277  //
   278  // This pretends a transfer has started
   279  func (acc *Account) ServerSideTransferStart() {
   280  	acc.values.mu.Lock()
   281  	// Set start time.
   282  	if acc.values.start.IsZero() {
   283  		acc.values.start = time.Now()
   284  	}
   285  	acc.values.mu.Unlock()
   286  }
   287  
   288  // ServerSideTransferEnd accounts for a read of n bytes in a sever
   289  // side transfer to be treated as a normal transfer.
   290  func (acc *Account) ServerSideTransferEnd(n int64) {
   291  	// Update Stats
   292  	acc.values.mu.Lock()
   293  	acc.values.bytes += n
   294  	acc.values.mu.Unlock()
   295  
   296  	acc.stats.Bytes(n)
   297  }
   298  
   299  // serverSideEnd accounts for non specific server side data
   300  func (acc *Account) serverSideEnd(n int64) {
   301  	// Account for bytes unless we are checking
   302  	if !acc.checking {
   303  		acc.stats.BytesNoNetwork(n)
   304  	}
   305  }
   306  
   307  // ServerSideCopyEnd accounts for a read of n bytes in a sever side copy
   308  func (acc *Account) ServerSideCopyEnd(n int64) {
   309  	acc.stats.AddServerSideCopy(n)
   310  	acc.serverSideEnd(n)
   311  }
   312  
   313  // ServerSideMoveEnd accounts for a read of n bytes in a sever side move
   314  func (acc *Account) ServerSideMoveEnd(n int64) {
   315  	acc.stats.AddServerSideMove(n)
   316  	acc.serverSideEnd(n)
   317  }
   318  
   319  // DryRun accounts for statistics without running the operation
   320  func (acc *Account) DryRun(n int64) {
   321  	acc.ServerSideTransferStart()
   322  	acc.ServerSideTransferEnd(n)
   323  }
   324  
   325  // Account for n bytes from the current file bandwidth limit (if any)
   326  func (acc *Account) limitPerFileBandwidth(n int) {
   327  	acc.values.mu.Lock()
   328  	tokenBucket := acc.tokenBucket[TokenBucketSlotAccounting]
   329  	acc.values.mu.Unlock()
   330  
   331  	if tokenBucket != nil {
   332  		err := tokenBucket.WaitN(context.Background(), n)
   333  		if err != nil {
   334  			fs.Errorf(nil, "Token bucket error: %v", err)
   335  		}
   336  	}
   337  }
   338  
   339  // Account the read and limit bandwidth
   340  func (acc *Account) accountRead(n int) {
   341  	// Update Stats
   342  	acc.values.mu.Lock()
   343  	acc.values.lpBytes += n
   344  	acc.values.bytes += int64(n)
   345  	acc.values.mu.Unlock()
   346  
   347  	acc.stats.Bytes(int64(n))
   348  
   349  	TokenBucket.LimitBandwidth(TokenBucketSlotAccounting, n)
   350  	acc.limitPerFileBandwidth(n)
   351  }
   352  
   353  // read bytes from the io.Reader passed in and account them
   354  func (acc *Account) read(in io.Reader, p []byte) (n int, err error) {
   355  	bytesUntilLimit, err := acc.checkReadBefore()
   356  	if err == nil {
   357  		n, err = in.Read(p)
   358  		acc.accountRead(n)
   359  		n, err = acc.checkReadAfter(bytesUntilLimit, n, err)
   360  	}
   361  	return n, err
   362  }
   363  
   364  // Read bytes from the object - see io.Reader
   365  func (acc *Account) Read(p []byte) (n int, err error) {
   366  	acc.mu.Lock()
   367  	defer acc.mu.Unlock()
   368  	return acc.read(acc.in, p)
   369  }
   370  
   371  // Thin wrapper for w
   372  type accountWriteTo struct {
   373  	w   io.Writer
   374  	acc *Account
   375  }
   376  
   377  // Write writes len(p) bytes from p to the underlying data stream. It
   378  // returns the number of bytes written from p (0 <= n <= len(p)) and
   379  // any error encountered that caused the write to stop early. Write
   380  // must return a non-nil error if it returns n < len(p). Write must
   381  // not modify the slice data, even temporarily.
   382  //
   383  // Implementations must not retain p.
   384  func (awt *accountWriteTo) Write(p []byte) (n int, err error) {
   385  	bytesUntilLimit, err := awt.acc.checkReadBefore()
   386  	if err == nil {
   387  		n, err = awt.w.Write(p)
   388  		n, err = awt.acc.checkReadAfter(bytesUntilLimit, n, err)
   389  		awt.acc.accountRead(n)
   390  	}
   391  	return n, err
   392  }
   393  
   394  // WriteTo writes data to w until there's no more data to write or
   395  // when an error occurs. The return value n is the number of bytes
   396  // written. Any error encountered during the write is also returned.
   397  func (acc *Account) WriteTo(w io.Writer) (n int64, err error) {
   398  	acc.mu.Lock()
   399  	in := acc.in
   400  	acc.mu.Unlock()
   401  	wrappedWriter := accountWriteTo{w: w, acc: acc}
   402  	if do, ok := in.(io.WriterTo); ok {
   403  		n, err = do.WriteTo(&wrappedWriter)
   404  	} else {
   405  		n, err = io.Copy(&wrappedWriter, in)
   406  	}
   407  	return
   408  }
   409  
   410  // AccountRead account having read n bytes
   411  func (acc *Account) AccountRead(n int) (err error) {
   412  	acc.mu.Lock()
   413  	defer acc.mu.Unlock()
   414  	bytesUntilLimit, err := acc.checkReadBefore()
   415  	if err == nil {
   416  		n, err = acc.checkReadAfter(bytesUntilLimit, n, err)
   417  		acc.accountRead(n)
   418  	}
   419  	return err
   420  }
   421  
   422  // Close the object
   423  func (acc *Account) Close() error {
   424  	acc.mu.Lock()
   425  	defer acc.mu.Unlock()
   426  	if acc.closed {
   427  		return nil
   428  	}
   429  	acc.closed = true
   430  	if acc.close == nil {
   431  		return nil
   432  	}
   433  	return acc.close.Close()
   434  }
   435  
   436  // Done with accounting - must be called to free accounting goroutine
   437  func (acc *Account) Done() {
   438  	acc.mu.Lock()
   439  	defer acc.mu.Unlock()
   440  	close(acc.exit)
   441  	acc.stats.inProgress.clear(acc.name)
   442  }
   443  
   444  // progress returns bytes read as well as the size.
   445  // Size can be <= 0 if the size is unknown.
   446  func (acc *Account) progress() (bytes, size int64) {
   447  	if acc == nil {
   448  		return 0, 0
   449  	}
   450  	acc.values.mu.Lock()
   451  	bytes, size = acc.values.bytes, acc.size
   452  	acc.values.mu.Unlock()
   453  	return bytes, size
   454  }
   455  
   456  // speed returns the speed of the current file transfer
   457  // in bytes per second, as well an exponentially weighted moving average
   458  // If no read has completed yet, 0 is returned for both values.
   459  func (acc *Account) speed() (bps, current float64) {
   460  	if acc == nil {
   461  		return 0, 0
   462  	}
   463  	acc.values.mu.Lock()
   464  	defer acc.values.mu.Unlock()
   465  	if acc.values.bytes == 0 {
   466  		return 0, 0
   467  	}
   468  	// Calculate speed from first read.
   469  	total := float64(time.Since(acc.values.start)) / float64(time.Second)
   470  	if total > 0 {
   471  		bps = float64(acc.values.bytes) / total
   472  	} else {
   473  		bps = 0.0
   474  	}
   475  	current = acc.values.avg
   476  	return
   477  }
   478  
   479  // eta returns the ETA of the current operation,
   480  // rounded to full seconds.
   481  // If the ETA cannot be determined 'ok' returns false.
   482  func (acc *Account) eta() (etaDuration time.Duration, ok bool) {
   483  	if acc == nil {
   484  		return 0, false
   485  	}
   486  	acc.values.mu.Lock()
   487  	defer acc.values.mu.Unlock()
   488  	return eta(acc.values.bytes, acc.size, acc.values.avg)
   489  }
   490  
   491  // shortenName shortens in to size runes long
   492  // If size <= 0 then in is left untouched
   493  func shortenName(in string, size int) string {
   494  	if size <= 0 {
   495  		return in
   496  	}
   497  	if utf8.RuneCountInString(in) <= size {
   498  		return in
   499  	}
   500  	name := []rune(in)
   501  	size-- // don't count ellipsis rune
   502  	suffixLength := size / 2
   503  	prefixLength := size - suffixLength
   504  	suffixStart := len(name) - suffixLength
   505  	name = append(append(name[:prefixLength], '…'), name[suffixStart:]...)
   506  	return string(name)
   507  }
   508  
   509  // String produces stats for this file
   510  func (acc *Account) String() string {
   511  	a, b := acc.progress()
   512  	_, cur := acc.speed()
   513  	eta, etaok := acc.eta()
   514  	etas := "-"
   515  	if etaok {
   516  		if eta > 0 {
   517  			etas = fmt.Sprintf("%v", eta)
   518  		} else {
   519  			etas = "0s"
   520  		}
   521  	}
   522  
   523  	if acc.ci.DataRateUnit == "bits" {
   524  		cur = cur * 8
   525  	}
   526  
   527  	percentageDone := 0
   528  	if b > 0 {
   529  		percentageDone = int(100 * float64(a) / float64(b))
   530  	}
   531  
   532  	return fmt.Sprintf("%*s:%3d%% /%s, %s/s, %s",
   533  		acc.ci.StatsFileNameLength,
   534  		shortenName(acc.name, acc.ci.StatsFileNameLength),
   535  		percentageDone,
   536  		fs.SizeSuffix(b),
   537  		fs.SizeSuffix(cur),
   538  		etas,
   539  	)
   540  }
   541  
   542  // rcStats adds remote control stats for this file
   543  func (acc *Account) rcStats(out rc.Params) {
   544  	a, b := acc.progress()
   545  	out["bytes"] = a
   546  	out["size"] = b
   547  	spd, cur := acc.speed()
   548  	out["speed"] = spd
   549  	out["speedAvg"] = cur
   550  
   551  	eta, etaOK := acc.eta()
   552  	if etaOK {
   553  		out["eta"] = eta.Seconds()
   554  	} else {
   555  		out["eta"] = nil
   556  	}
   557  	out["name"] = acc.name
   558  
   559  	percentageDone := 0
   560  	if b > 0 {
   561  		percentageDone = int(100 * float64(a) / float64(b))
   562  	}
   563  	out["percentage"] = percentageDone
   564  	out["group"] = acc.stats.group
   565  }
   566  
   567  // OldStream returns the top io.Reader
   568  func (acc *Account) OldStream() io.Reader {
   569  	acc.mu.Lock()
   570  	defer acc.mu.Unlock()
   571  	return acc.in
   572  }
   573  
   574  // SetStream updates the top io.Reader
   575  func (acc *Account) SetStream(in io.Reader) {
   576  	acc.mu.Lock()
   577  	acc.in = in
   578  	acc.mu.Unlock()
   579  }
   580  
   581  // WrapStream wraps an io Reader so it will be accounted in the same
   582  // way as account
   583  func (acc *Account) WrapStream(in io.Reader) io.Reader {
   584  	return &accountStream{
   585  		acc: acc,
   586  		in:  in,
   587  	}
   588  }
   589  
   590  // accountStream accounts a single io.Reader into a parent *Account
   591  type accountStream struct {
   592  	acc *Account
   593  	in  io.Reader
   594  }
   595  
   596  // OldStream return the underlying stream
   597  func (a *accountStream) OldStream() io.Reader {
   598  	return a.in
   599  }
   600  
   601  // SetStream set the underlying stream
   602  func (a *accountStream) SetStream(in io.Reader) {
   603  	a.in = in
   604  }
   605  
   606  // WrapStream wrap in an accounter
   607  func (a *accountStream) WrapStream(in io.Reader) io.Reader {
   608  	return a.acc.WrapStream(in)
   609  }
   610  
   611  // Read bytes from the object - see io.Reader
   612  func (a *accountStream) Read(p []byte) (n int, err error) {
   613  	return a.acc.read(a.in, p)
   614  }
   615  
   616  // Accounter accounts a stream allowing the accounting to be removed and re-added
   617  type Accounter interface {
   618  	io.Reader
   619  	OldStream() io.Reader
   620  	SetStream(io.Reader)
   621  	WrapStream(io.Reader) io.Reader
   622  }
   623  
   624  // WrapFn wraps an io.Reader (for accounting purposes usually)
   625  type WrapFn func(io.Reader) io.Reader
   626  
   627  // UnWrap unwraps a reader returning unwrapped and wrap, a function to
   628  // wrap it back up again.  If `in` is an Accounter then this function
   629  // will take the accounting unwrapped and wrap will put it back on
   630  // again the new Reader passed in.
   631  //
   632  // This allows functions which wrap io.Readers to move the accounting
   633  // to the end of the wrapped chain of readers.  This is very important
   634  // if buffering is being introduced and if the Reader might be wrapped
   635  // again.
   636  func UnWrap(in io.Reader) (unwrapped io.Reader, wrap WrapFn) {
   637  	acc, ok := in.(Accounter)
   638  	if !ok {
   639  		return in, func(r io.Reader) io.Reader { return r }
   640  	}
   641  	return acc.OldStream(), acc.WrapStream
   642  }
   643  
   644  // UnWrapAccounting unwraps a reader returning unwrapped and acc a
   645  // pointer to the accounting.
   646  //
   647  // The caller is expected to manage the accounting at this point.
   648  func UnWrapAccounting(in io.Reader) (unwrapped io.Reader, acc *Account) {
   649  	a, ok := in.(*accountStream)
   650  	if !ok {
   651  		return in, nil
   652  	}
   653  	return a.in, a.acc
   654  }