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 }