github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/fs/accounting/accounting.go (about) 1 // Package accounting providers an accounting and limiting reader 2 package accounting 3 4 import ( 5 "fmt" 6 "io" 7 "sync" 8 "time" 9 "unicode/utf8" 10 11 "github.com/ncw/rclone/fs" 12 "github.com/ncw/rclone/fs/asyncreader" 13 "github.com/ncw/rclone/fs/fserrors" 14 "github.com/pkg/errors" 15 ) 16 17 // ErrorMaxTransferLimitReached is returned from Read when the max 18 // transfer limit is reached. 19 var ErrorMaxTransferLimitReached = fserrors.FatalError(errors.New("Max transfer limit reached as set by --max-transfer")) 20 21 // Account limits and accounts for one transfer 22 type Account struct { 23 // The mutex is to make sure Read() and Close() aren't called 24 // concurrently. Unfortunately the persistent connection loop 25 // in http transport calls Read() after Do() returns on 26 // CancelRequest so this race can happen when it apparently 27 // shouldn't. 28 mu sync.Mutex 29 in io.Reader 30 origIn io.ReadCloser 31 close io.Closer 32 size int64 33 name string 34 statmu sync.Mutex // Separate mutex for stat values. 35 bytes int64 // Total number of bytes read 36 max int64 // if >=0 the max number of bytes to transfer 37 start time.Time // Start time of first read 38 lpTime time.Time // Time of last average measurement 39 lpBytes int // Number of bytes read since last measurement 40 avg float64 // Moving average of last few measurements in bytes/s 41 closed bool // set if the file is closed 42 exit chan struct{} // channel that will be closed when transfer is finished 43 withBuf bool // is using a buffered in 44 } 45 46 const averagePeriod = 16 // period to do exponentially weighted averages over 47 48 // NewAccountSizeName makes a Account reader for an io.ReadCloser of 49 // the given size and name 50 func NewAccountSizeName(in io.ReadCloser, size int64, name string) *Account { 51 acc := &Account{ 52 in: in, 53 close: in, 54 origIn: in, 55 size: size, 56 name: name, 57 exit: make(chan struct{}), 58 avg: 0, 59 lpTime: time.Now(), 60 max: int64(fs.Config.MaxTransfer), 61 } 62 go acc.averageLoop() 63 Stats.inProgress.set(acc.name, acc) 64 return acc 65 } 66 67 // NewAccount makes a Account reader for an object 68 func NewAccount(in io.ReadCloser, obj fs.Object) *Account { 69 return NewAccountSizeName(in, obj.Size(), obj.Remote()) 70 } 71 72 // WithBuffer - If the file is above a certain size it adds an Async reader 73 func (acc *Account) WithBuffer() *Account { 74 acc.withBuf = true 75 var buffers int 76 if acc.size >= int64(fs.Config.BufferSize) || acc.size == -1 { 77 buffers = int(int64(fs.Config.BufferSize) / asyncreader.BufferSize) 78 } else { 79 buffers = int(acc.size / asyncreader.BufferSize) 80 } 81 // On big files add a buffer 82 if buffers > 0 { 83 rc, err := asyncreader.New(acc.origIn, buffers) 84 if err != nil { 85 fs.Errorf(acc.name, "Failed to make buffer: %v", err) 86 } else { 87 acc.in = rc 88 acc.close = rc 89 } 90 } 91 return acc 92 } 93 94 // GetReader returns the underlying io.ReadCloser under any Buffer 95 func (acc *Account) GetReader() io.ReadCloser { 96 acc.mu.Lock() 97 defer acc.mu.Unlock() 98 return acc.origIn 99 } 100 101 // GetAsyncReader returns the current AsyncReader or nil if Account is unbuffered 102 func (acc *Account) GetAsyncReader() *asyncreader.AsyncReader { 103 acc.mu.Lock() 104 defer acc.mu.Unlock() 105 if asyncIn, ok := acc.in.(*asyncreader.AsyncReader); ok { 106 return asyncIn 107 } 108 return nil 109 } 110 111 // StopBuffering stops the async buffer doing any more buffering 112 func (acc *Account) StopBuffering() { 113 if asyncIn, ok := acc.in.(*asyncreader.AsyncReader); ok { 114 asyncIn.Abandon() 115 } 116 } 117 118 // UpdateReader updates the underlying io.ReadCloser stopping the 119 // async buffer (if any) and re-adding it 120 func (acc *Account) UpdateReader(in io.ReadCloser) { 121 acc.mu.Lock() 122 acc.StopBuffering() 123 acc.in = in 124 acc.close = in 125 acc.origIn = in 126 acc.WithBuffer() 127 acc.mu.Unlock() 128 } 129 130 // averageLoop calculates averages for the stats in the background 131 func (acc *Account) averageLoop() { 132 tick := time.NewTicker(time.Second) 133 var period float64 134 defer tick.Stop() 135 for { 136 select { 137 case now := <-tick.C: 138 acc.statmu.Lock() 139 // Add average of last second. 140 elapsed := now.Sub(acc.lpTime).Seconds() 141 avg := float64(acc.lpBytes) / elapsed 142 // Soft start the moving average 143 if period < averagePeriod { 144 period++ 145 } 146 acc.avg = (avg + (period-1)*acc.avg) / period 147 acc.lpBytes = 0 148 acc.lpTime = now 149 // Unlock stats 150 acc.statmu.Unlock() 151 case <-acc.exit: 152 return 153 } 154 } 155 } 156 157 // Check the read is valid 158 func (acc *Account) checkRead() (err error) { 159 acc.statmu.Lock() 160 if acc.max >= 0 && Stats.GetBytes() >= acc.max { 161 acc.statmu.Unlock() 162 return ErrorMaxTransferLimitReached 163 } 164 // Set start time. 165 if acc.start.IsZero() { 166 acc.start = time.Now() 167 } 168 acc.statmu.Unlock() 169 return nil 170 } 171 172 // Account the read and limit bandwidth 173 func (acc *Account) accountRead(n int) { 174 // Update Stats 175 acc.statmu.Lock() 176 acc.lpBytes += n 177 acc.bytes += int64(n) 178 acc.statmu.Unlock() 179 180 Stats.Bytes(int64(n)) 181 182 limitBandwidth(n) 183 } 184 185 // read bytes from the io.Reader passed in and account them 186 func (acc *Account) read(in io.Reader, p []byte) (n int, err error) { 187 err = acc.checkRead() 188 if err == nil { 189 n, err = in.Read(p) 190 acc.accountRead(n) 191 } 192 return n, err 193 } 194 195 // Read bytes from the object - see io.Reader 196 func (acc *Account) Read(p []byte) (n int, err error) { 197 acc.mu.Lock() 198 defer acc.mu.Unlock() 199 return acc.read(acc.in, p) 200 } 201 202 // AccountRead account having read n bytes 203 func (acc *Account) AccountRead(n int) (err error) { 204 acc.mu.Lock() 205 defer acc.mu.Unlock() 206 err = acc.checkRead() 207 if err == nil { 208 acc.accountRead(n) 209 } 210 return err 211 } 212 213 // Close the object 214 func (acc *Account) Close() error { 215 acc.mu.Lock() 216 defer acc.mu.Unlock() 217 if acc.closed { 218 return nil 219 } 220 acc.closed = true 221 close(acc.exit) 222 Stats.inProgress.clear(acc.name) 223 if acc.close == nil { 224 return nil 225 } 226 return acc.close.Close() 227 } 228 229 // progress returns bytes read as well as the size. 230 // Size can be <= 0 if the size is unknown. 231 func (acc *Account) progress() (bytes, size int64) { 232 if acc == nil { 233 return 0, 0 234 } 235 acc.statmu.Lock() 236 bytes, size = acc.bytes, acc.size 237 acc.statmu.Unlock() 238 return bytes, size 239 } 240 241 // speed returns the speed of the current file transfer 242 // in bytes per second, as well a an exponentially weighted moving average 243 // If no read has completed yet, 0 is returned for both values. 244 func (acc *Account) speed() (bps, current float64) { 245 if acc == nil { 246 return 0, 0 247 } 248 acc.statmu.Lock() 249 defer acc.statmu.Unlock() 250 if acc.bytes == 0 { 251 return 0, 0 252 } 253 // Calculate speed from first read. 254 total := float64(time.Now().Sub(acc.start)) / float64(time.Second) 255 bps = float64(acc.bytes) / total 256 current = acc.avg 257 return 258 } 259 260 // eta returns the ETA of the current operation, 261 // rounded to full seconds. 262 // If the ETA cannot be determined 'ok' returns false. 263 func (acc *Account) eta() (etaDuration time.Duration, ok bool) { 264 if acc == nil { 265 return 0, false 266 } 267 acc.statmu.Lock() 268 defer acc.statmu.Unlock() 269 return eta(acc.bytes, acc.size, acc.avg) 270 } 271 272 // shortenName shortens in to size runes long 273 // If size <= 0 then in is left untouched 274 func shortenName(in string, size int) string { 275 if size <= 0 { 276 return in 277 } 278 if utf8.RuneCountInString(in) <= size { 279 return in 280 } 281 name := []rune(in) 282 size-- // don't count elipsis rune 283 suffixLength := size / 2 284 prefixLength := size - suffixLength 285 suffixStart := len(name) - suffixLength 286 name = append(append(name[:prefixLength], '…'), name[suffixStart:]...) 287 return string(name) 288 } 289 290 // String produces stats for this file 291 func (acc *Account) String() string { 292 a, b := acc.progress() 293 _, cur := acc.speed() 294 eta, etaok := acc.eta() 295 etas := "-" 296 if etaok { 297 if eta > 0 { 298 etas = fmt.Sprintf("%v", eta) 299 } else { 300 etas = "0s" 301 } 302 } 303 304 if fs.Config.DataRateUnit == "bits" { 305 cur = cur * 8 306 } 307 308 percentageDone := 0 309 if b > 0 { 310 percentageDone = int(100 * float64(a) / float64(b)) 311 } 312 313 return fmt.Sprintf("%*s:%3d%% /%s, %s/s, %s", 314 fs.Config.StatsFileNameLength, 315 shortenName(acc.name, fs.Config.StatsFileNameLength), 316 percentageDone, 317 fs.SizeSuffix(b), 318 fs.SizeSuffix(cur), 319 etas, 320 ) 321 } 322 323 // RemoteStats produces stats for this file 324 func (acc *Account) RemoteStats() (out map[string]interface{}) { 325 out = make(map[string]interface{}) 326 a, b := acc.progress() 327 out["bytes"] = a 328 out["size"] = b 329 spd, cur := acc.speed() 330 out["speed"] = spd 331 out["speedAvg"] = cur 332 333 eta, etaok := acc.eta() 334 out["eta"] = nil 335 if etaok { 336 if eta > 0 { 337 out["eta"] = eta.Seconds() 338 } else { 339 out["eta"] = 0 340 } 341 } 342 out["name"] = acc.name 343 344 percentageDone := 0 345 if b > 0 { 346 percentageDone = int(100 * float64(a) / float64(b)) 347 } 348 out["percentage"] = percentageDone 349 350 return out 351 } 352 353 // OldStream returns the top io.Reader 354 func (acc *Account) OldStream() io.Reader { 355 acc.mu.Lock() 356 defer acc.mu.Unlock() 357 return acc.in 358 } 359 360 // SetStream updates the top io.Reader 361 func (acc *Account) SetStream(in io.Reader) { 362 acc.mu.Lock() 363 acc.in = in 364 acc.mu.Unlock() 365 } 366 367 // WrapStream wraps an io Reader so it will be accounted in the same 368 // way as account 369 func (acc *Account) WrapStream(in io.Reader) io.Reader { 370 return &accountStream{ 371 acc: acc, 372 in: in, 373 } 374 } 375 376 // accountStream accounts a single io.Reader into a parent *Account 377 type accountStream struct { 378 acc *Account 379 in io.Reader 380 } 381 382 // OldStream return the underlying stream 383 func (a *accountStream) OldStream() io.Reader { 384 return a.in 385 } 386 387 // SetStream set the underlying stream 388 func (a *accountStream) SetStream(in io.Reader) { 389 a.in = in 390 } 391 392 // WrapStream wrap in in an accounter 393 func (a *accountStream) WrapStream(in io.Reader) io.Reader { 394 return a.acc.WrapStream(in) 395 } 396 397 // Read bytes from the object - see io.Reader 398 func (a *accountStream) Read(p []byte) (n int, err error) { 399 return a.acc.read(a.in, p) 400 } 401 402 // Accounter accounts a stream allowing the accounting to be removed and re-added 403 type Accounter interface { 404 io.Reader 405 OldStream() io.Reader 406 SetStream(io.Reader) 407 WrapStream(io.Reader) io.Reader 408 } 409 410 // WrapFn wraps an io.Reader (for accounting purposes usually) 411 type WrapFn func(io.Reader) io.Reader 412 413 // UnWrap unwraps a reader returning unwrapped and wrap, a function to 414 // wrap it back up again. If `in` is an Accounter then this function 415 // will take the accounting unwrapped and wrap will put it back on 416 // again the new Reader passed in. 417 // 418 // This allows functions which wrap io.Readers to move the accounting 419 // to the end of the wrapped chain of readers. This is very important 420 // if buffering is being introduced and if the Reader might be wrapped 421 // again. 422 func UnWrap(in io.Reader) (unwrapped io.Reader, wrap WrapFn) { 423 acc, ok := in.(Accounter) 424 if !ok { 425 return in, func(r io.Reader) io.Reader { return r } 426 } 427 return acc.OldStream(), acc.WrapStream 428 }