mirror of
https://github.com/kopia/kopia.git
synced 2026-03-27 18:42:26 -04:00
The dual time measurement is described in https://go.googlesource.com/proposal/+/master/design/12914-monotonic.md The fix is to discard hidden monotonic time component of time.Time by converting to unix time and back. Reviewed usage of clock.Now() and replaced with timetrack.StartTimer() when measuring time. The problem in #1402 was that passage of time was measured using the monotonic time and not wall clock time. When the computer goes to sleep, monotonic time is still monotonic while wall clock time makes a leap when the computer wakes up. This is the behavior that epoch manager (and most other compontents in Kopia) rely upon. Fixes #1402 Co-authored-by: Julio Lopez <julio+gh@kasten.io>
72 lines
1.7 KiB
Go
72 lines
1.7 KiB
Go
// Package timetrack tracks the progress and estimates completion of a task.
|
|
package timetrack
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/kopia/kopia/internal/clock"
|
|
)
|
|
|
|
// Estimator estimates the completion of a task.
|
|
type Estimator struct {
|
|
startTime time.Time
|
|
}
|
|
|
|
// Timings returns task progress and timings.
|
|
type Timings struct {
|
|
PercentComplete float64
|
|
EstimatedEndTime time.Time
|
|
Remaining time.Duration
|
|
SpeedPerSecond float64
|
|
}
|
|
|
|
// Estimate estimates the completion of a task given the current progress as indicated
|
|
// by ration of completed/total.
|
|
func (v Estimator) Estimate(completed, total float64) (Timings, bool) {
|
|
now := time.Now() //nolint:forbidigo
|
|
elapsed := now.Sub(v.startTime)
|
|
|
|
if elapsed > 1*time.Second && total > 0 && completed > 0 {
|
|
completedRatio := completed / total
|
|
if completedRatio > 1 {
|
|
completedRatio = 1
|
|
}
|
|
|
|
if completedRatio < 0 {
|
|
completedRatio = 0
|
|
}
|
|
|
|
predictedSeconds := elapsed.Seconds() / completedRatio
|
|
predictedEndTime := v.startTime.Add(time.Duration(predictedSeconds) * time.Second)
|
|
|
|
dt := predictedEndTime.Sub(clock.Now()).Truncate(time.Second)
|
|
if dt < 0 {
|
|
dt = 0
|
|
}
|
|
|
|
return Timings{
|
|
PercentComplete: 100 * completed / total,
|
|
EstimatedEndTime: now.Add(dt),
|
|
Remaining: dt,
|
|
SpeedPerSecond: completed / elapsed.Seconds(),
|
|
}, true
|
|
}
|
|
|
|
return Timings{}, false
|
|
}
|
|
|
|
// Completed computes the duration and speed (total per second).
|
|
func (v Estimator) Completed(total float64) (totalTime time.Duration, speed float64) {
|
|
dur := time.Since(v.startTime) //nolint:forbidigo
|
|
if dur <= 0 {
|
|
return 0, 0
|
|
}
|
|
|
|
return dur, total / dur.Seconds()
|
|
}
|
|
|
|
// Start returns an Estimator object.
|
|
func Start() Estimator {
|
|
return Estimator{startTime: time.Now()} //nolint:forbidigo
|
|
}
|