mirror of
https://github.com/kopia/kopia.git
synced 2026-01-06 05:27:59 -05:00
- upgrade to golangci-lint 2.6.1 - updates for gosec - updates for govet - updates for perfsprint - updates modernize Leaves out modernize:omitempty due to conflicts with tests
221 lines
6.2 KiB
Go
221 lines
6.2 KiB
Go
//go:build darwin || (linux && amd64)
|
|
|
|
package engine
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/kopia/kopia/internal/clock"
|
|
)
|
|
|
|
var (
|
|
repoBuildTime = "unknown"
|
|
repoGitRevision = "unknown"
|
|
repoGitBranch = "unknown"
|
|
testBuildTime = "unknown"
|
|
testGitRevision = "unknown"
|
|
testGitBranch = "unknown"
|
|
)
|
|
|
|
// Stats prints the engine stats, cumulative and from the current run.
|
|
func (e *Engine) Stats() string {
|
|
b := &strings.Builder{}
|
|
|
|
fmt.Fprintln(b, "==================================")
|
|
fmt.Fprintln(b, "Build Info")
|
|
fmt.Fprintln(b, "==================================")
|
|
fmt.Fprintf(b, " Repo build time: %25v\n", repoBuildTime)
|
|
fmt.Fprintf(b, " Repo git revision: %25v\n", repoGitRevision)
|
|
fmt.Fprintf(b, " Repo git branch: %25v\n", repoGitBranch)
|
|
fmt.Fprintln(b, "")
|
|
fmt.Fprintf(b, " Engine build time: %25v\n", testBuildTime)
|
|
fmt.Fprintf(b, " Engine git revision: %25v\n", testGitRevision)
|
|
fmt.Fprintf(b, " Engine git branch: %25v\n", testGitBranch)
|
|
fmt.Fprintln(b, "")
|
|
fmt.Fprintln(b, "==================================")
|
|
fmt.Fprintln(b, "Engine Action Summary (Cumulative)")
|
|
fmt.Fprintln(b, "==================================")
|
|
fmt.Fprintf(b, " Engine runtime: %10vs\n", e.getRuntimeSeconds())
|
|
fmt.Fprintln(b, "")
|
|
fmt.Fprint(b, e.CumulativeStats.Stats())
|
|
fmt.Fprintln(b, "")
|
|
|
|
fmt.Fprintln(b, "==================================")
|
|
fmt.Fprintln(b, "Engine Action Summary (This Run)")
|
|
fmt.Fprintln(b, "==================================")
|
|
fmt.Fprint(b, e.RunStats.Stats())
|
|
fmt.Fprintln(b, "")
|
|
|
|
return b.String()
|
|
}
|
|
|
|
// Stats tracks statistics during engine runtime.
|
|
type Stats struct {
|
|
RunCounter int64
|
|
ActionCounter int64
|
|
CreationTime time.Time
|
|
RunTime time.Duration
|
|
PerActionStats map[ActionKey]*ActionStats
|
|
|
|
DataRestoreCount int64
|
|
DataPurgeCount int64
|
|
ErrorRecoveryCount int64
|
|
NoOpCount int64
|
|
}
|
|
|
|
// Stats returns a string report of the engine's stats.
|
|
func (stats *Stats) Stats() string {
|
|
b := &strings.Builder{}
|
|
|
|
fmt.Fprintln(b, "=============")
|
|
fmt.Fprintln(b, "Stat summary")
|
|
fmt.Fprintln(b, "=============")
|
|
fmt.Fprintf(b, " Number of runs: %10v\n", stats.RunCounter)
|
|
fmt.Fprintf(b, " Engine lifetime: %10vs\n", stats.getLifetimeSeconds())
|
|
fmt.Fprintf(b, " Actions run: %10v\n", stats.ActionCounter)
|
|
fmt.Fprintf(b, " Errors recovered: %10v\n", stats.ErrorRecoveryCount)
|
|
fmt.Fprintf(b, " Data dir restores: %10v\n", stats.DataRestoreCount)
|
|
fmt.Fprintf(b, " Data dir purges: %10v\n", stats.DataPurgeCount)
|
|
fmt.Fprintf(b, " NoOp count: %10v\n", stats.NoOpCount)
|
|
fmt.Fprintln(b, "")
|
|
fmt.Fprintln(b, "=============")
|
|
fmt.Fprintln(b, "Action stats")
|
|
fmt.Fprintln(b, "=============")
|
|
|
|
for actionKey, actionStat := range stats.PerActionStats {
|
|
fmt.Fprintf(b, "%s:\n", actionKey)
|
|
fmt.Fprintf(b, " Count: %10d\n", actionStat.Count)
|
|
fmt.Fprintf(b, " Avg Runtime: %10v\n", actionStat.avgRuntimeString())
|
|
fmt.Fprintf(b, " Max Runtime: %10vs\n", durationToSec(actionStat.MaxRuntime))
|
|
fmt.Fprintf(b, " Min Runtime: %10vs\n", durationToSec(actionStat.MinRuntime))
|
|
fmt.Fprintf(b, " Error Count: %10v\n", actionStat.ErrorCount)
|
|
fmt.Fprintln(b, "")
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
// ActionStats tracks runtime statistics for an action.
|
|
type ActionStats struct {
|
|
Count int64
|
|
TotalRuntime time.Duration
|
|
MinRuntime time.Duration
|
|
MaxRuntime time.Duration
|
|
ErrorCount int64
|
|
}
|
|
|
|
// AverageRuntime returns the average run time for the action.
|
|
func (s *ActionStats) AverageRuntime() time.Duration {
|
|
return time.Duration(int64(s.TotalRuntime) / s.Count)
|
|
}
|
|
|
|
// Record records the current time against the provided start time
|
|
// and updates the stats accordingly.
|
|
func (s *ActionStats) Record(st time.Time, err error) {
|
|
thisRuntime := clock.Now().Sub(st)
|
|
s.TotalRuntime += thisRuntime
|
|
|
|
if thisRuntime > s.MaxRuntime {
|
|
s.MaxRuntime = thisRuntime
|
|
}
|
|
|
|
if s.Count == 0 || thisRuntime < s.MinRuntime {
|
|
s.MinRuntime = thisRuntime
|
|
}
|
|
|
|
s.Count++
|
|
|
|
if err != nil {
|
|
s.ErrorCount++
|
|
}
|
|
}
|
|
|
|
func (stats *Stats) getLifetimeSeconds() int64 {
|
|
return durationToSec(clock.Now().Sub(stats.CreationTime))
|
|
}
|
|
|
|
func durationToSec(dur time.Duration) int64 {
|
|
return int64(dur.Round(time.Second).Seconds())
|
|
}
|
|
|
|
func (s *ActionStats) avgRuntimeString() string {
|
|
if s.Count == 0 {
|
|
return "--"
|
|
}
|
|
|
|
return fmt.Sprintf("%vs", durationToSec(s.AverageRuntime()))
|
|
}
|
|
|
|
func (e *Engine) getTimestampS() int64 {
|
|
return e.getRuntimeSeconds()
|
|
}
|
|
|
|
func (e *Engine) getRuntimeSeconds() int64 {
|
|
return durationToSec(e.CumulativeStats.RunTime + clock.Now().Sub(e.RunStats.CreationTime))
|
|
}
|
|
|
|
type statsUpdatetype int
|
|
|
|
const (
|
|
statsIncrNoOp statsUpdatetype = iota
|
|
statsIncrDataPurge
|
|
statsIncrDataRestore
|
|
statsIncrErrorRecovery
|
|
)
|
|
|
|
func (e *Engine) statsUpdateCounters(updateType statsUpdatetype) {
|
|
e.statsMux.Lock()
|
|
defer e.statsMux.Unlock()
|
|
|
|
switch updateType {
|
|
case statsIncrNoOp:
|
|
e.RunStats.NoOpCount++
|
|
e.CumulativeStats.NoOpCount++
|
|
case statsIncrDataPurge:
|
|
e.RunStats.DataPurgeCount++
|
|
e.CumulativeStats.DataPurgeCount++
|
|
case statsIncrDataRestore:
|
|
e.RunStats.DataRestoreCount++
|
|
e.CumulativeStats.DataRestoreCount++
|
|
case statsIncrErrorRecovery:
|
|
e.RunStats.ErrorRecoveryCount++
|
|
e.CumulativeStats.ErrorRecoveryCount++
|
|
}
|
|
}
|
|
|
|
func (e *Engine) statsIncrActionCountAndLog(actionKey ActionKey) {
|
|
e.statsMux.Lock()
|
|
defer e.statsMux.Unlock()
|
|
|
|
e.RunStats.ActionCounter++
|
|
e.CumulativeStats.ActionCounter++
|
|
|
|
log.Printf(
|
|
"Engine executing ACTION: name=%q actionCount=%v totActCount=%v t=%vs (%vs)",
|
|
actionKey,
|
|
e.RunStats.ActionCounter,
|
|
e.CumulativeStats.ActionCounter,
|
|
e.RunStats.getLifetimeSeconds(),
|
|
e.getRuntimeSeconds(),
|
|
)
|
|
}
|
|
|
|
func (e *Engine) statsUpdatePerAction(actionKey ActionKey, st time.Time, err error) {
|
|
e.statsMux.Lock()
|
|
defer e.statsMux.Unlock()
|
|
|
|
if e.RunStats.PerActionStats != nil && e.RunStats.PerActionStats[actionKey] == nil {
|
|
e.RunStats.PerActionStats[actionKey] = new(ActionStats)
|
|
}
|
|
|
|
if e.CumulativeStats.PerActionStats != nil && e.CumulativeStats.PerActionStats[actionKey] == nil {
|
|
e.CumulativeStats.PerActionStats[actionKey] = new(ActionStats)
|
|
}
|
|
|
|
e.RunStats.PerActionStats[actionKey].Record(st, err)
|
|
e.CumulativeStats.PerActionStats[actionKey].Record(st, err)
|
|
}
|