mirror of
https://github.com/kopia/kopia.git
synced 2026-01-25 14:58:00 -05: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>
79 lines
2.0 KiB
Go
79 lines
2.0 KiB
Go
//go:build testing
|
|
// +build testing
|
|
|
|
package clock
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const refreshServerTimeEvery = 3 * time.Second
|
|
|
|
// Now is overridable function that returns current wall clock time.
|
|
var Now = func() time.Time {
|
|
return discardMonotonicTime(time.Now()) // nolint:forbidigo
|
|
}
|
|
|
|
func init() {
|
|
fakeTimeServer := os.Getenv("KOPIA_FAKE_CLOCK_ENDPOINT")
|
|
if fakeTimeServer == "" {
|
|
return
|
|
}
|
|
|
|
Now = getTimeFromServer(fakeTimeServer)
|
|
}
|
|
|
|
// getTimeFromServer returns a function that will return timestamp as returned by the server
|
|
// increasing it client-side by certain inteval until maximum is reached, at which point
|
|
// it will ask the server again for new timestamp.
|
|
//
|
|
// The server endpoint must be HTTP and be set using KOPIA_FAKE_CLOCK_ENDPOINT environment
|
|
// variable.
|
|
func getTimeFromServer(endpoint string) func() time.Time {
|
|
var mu sync.Mutex
|
|
|
|
var timeInfo struct {
|
|
Time time.Time `json:"time"`
|
|
ValidFor time.Duration `json:"validFor"`
|
|
}
|
|
|
|
var (
|
|
nextRefreshRealTime time.Time // nolint:forbidigo
|
|
localTimeOffset time.Duration // offset to be added to time.Now() to produce server time
|
|
)
|
|
|
|
return func() time.Time {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
|
|
localTime := time.Now() // nolint:forbidigo
|
|
if localTime.After(nextRefreshRealTime) {
|
|
resp, err := http.Get(endpoint) //nolint:gosec,noctx
|
|
if err != nil {
|
|
log.Fatalf("unable to get fake time from server: %v", err)
|
|
}
|
|
defer resp.Body.Close() //nolint:errcheck
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
log.Fatalf("unable to get fake time from server: %v", resp.Status)
|
|
}
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&timeInfo); err != nil {
|
|
log.Fatalf("invalid time received from fake time server: %v", err)
|
|
}
|
|
|
|
nextRefreshRealTime = localTime.Add(timeInfo.ValidFor) // nolint:forbidigo
|
|
|
|
// compute offset such that localTime + localTimeOffset == serverTime
|
|
localTimeOffset = timeInfo.Time.Sub(localTime)
|
|
}
|
|
|
|
return discardMonotonicTime(localTime.Add(localTimeOffset))
|
|
}
|
|
}
|