Add instant stat to derived result.
Before this change, we do not return anything meaningful till cadvisor has been up for a minute. Also, having instant usage in the same stat gives a more complete picture.
This commit is contained in:
parent
28984d7811
commit
ed38123221
@ -464,9 +464,19 @@ type Usage struct {
|
|||||||
Memory Percentiles `json:"memory"`
|
Memory Percentiles `json:"memory"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// latest sample collected for a container.
|
||||||
|
type InstantUsage struct {
|
||||||
|
// cpu rate in cpu milliseconds/second.
|
||||||
|
Cpu uint64 `json:"cpu"`
|
||||||
|
// Memory usage in bytes.
|
||||||
|
Memory uint64 `json:"memory"`
|
||||||
|
}
|
||||||
|
|
||||||
type DerivedStats struct {
|
type DerivedStats struct {
|
||||||
// Time of generation of these stats.
|
// Time of generation of these stats.
|
||||||
Timestamp time.Time `json:"timestamp"`
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
// Latest instantaneous sample.
|
||||||
|
LatestUsage InstantUsage `json:"latest_usage"`
|
||||||
// Percentiles in last observed minute.
|
// Percentiles in last observed minute.
|
||||||
MinuteUsage Usage `json:"minute_usage"`
|
MinuteUsage Usage `json:"minute_usage"`
|
||||||
// Percentile in last hour.
|
// Percentile in last hour.
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package summary
|
package summary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
@ -147,23 +148,31 @@ func getPercentComplete(stats []*secondSample) (percent int32) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate cpurate from two consecutive total cpu usage samples.
|
||||||
|
func getCpuRate(latest, previous secondSample) (uint64, error) {
|
||||||
|
var elapsed int64
|
||||||
|
elapsed = latest.Timestamp.Sub(previous.Timestamp).Nanoseconds()
|
||||||
|
if elapsed < 10*milliSecondsToNanoSeconds {
|
||||||
|
return 0, fmt.Errorf("elapsed time too small: %d ns: time now %s last %s", elapsed, latest.Timestamp.String(), previous.Timestamp.String())
|
||||||
|
}
|
||||||
|
if latest.Cpu < previous.Cpu {
|
||||||
|
return 0, fmt.Errorf("bad sample: cumulative cpu usage dropped from %d to %d", latest.Cpu, previous.Cpu)
|
||||||
|
}
|
||||||
|
// Cpurate is calculated in cpu-milliseconds per second.
|
||||||
|
cpuRate := (latest.Cpu - previous.Cpu) * secondsToMilliSeconds / uint64(elapsed)
|
||||||
|
return cpuRate, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a percentile sample for a minute by aggregating seconds samples.
|
// Returns a percentile sample for a minute by aggregating seconds samples.
|
||||||
func GetMinutePercentiles(stats []*secondSample) info.Usage {
|
func GetMinutePercentiles(stats []*secondSample) info.Usage {
|
||||||
lastSample := secondSample{}
|
lastSample := secondSample{}
|
||||||
cpu := NewResource(len(stats))
|
cpu := NewResource(len(stats))
|
||||||
memory := NewResource(len(stats))
|
memory := NewResource(len(stats))
|
||||||
for _, stat := range stats {
|
for _, stat := range stats {
|
||||||
var elapsed int64
|
|
||||||
if !lastSample.Timestamp.IsZero() {
|
if !lastSample.Timestamp.IsZero() {
|
||||||
elapsed = stat.Timestamp.Sub(lastSample.Timestamp).Nanoseconds()
|
cpuRate, err := getCpuRate(*stat, lastSample)
|
||||||
if elapsed < 10*milliSecondsToNanoSeconds {
|
if err != nil {
|
||||||
glog.Infof("Elapsed time too small: %d ns: time now %s last %s", elapsed, stat.Timestamp.String(), lastSample.Timestamp.String())
|
glog.V(2).Infof("Skipping sample, %v", err)
|
||||||
continue
|
|
||||||
}
|
|
||||||
glog.V(2).Infof("Read sample: cpu %d, memory %d", stat.Cpu, memory)
|
|
||||||
cpuRate := (stat.Cpu - lastSample.Cpu) * secondsToMilliSeconds / uint64(elapsed)
|
|
||||||
if cpuRate < 0 {
|
|
||||||
glog.Infof("cpu rate too small: %f ns", cpuRate)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
glog.V(2).Infof("Adding cpu rate sample : %d", cpuRate)
|
glog.V(2).Infof("Adding cpu rate sample : %d", cpuRate)
|
||||||
|
@ -47,7 +47,8 @@ type StatsSummary struct {
|
|||||||
secondSamples []*secondSample
|
secondSamples []*secondSample
|
||||||
// minute percentiles. We track 24 * 60 maximum samples.
|
// minute percentiles. We track 24 * 60 maximum samples.
|
||||||
minuteSamples *SamplesBuffer
|
minuteSamples *SamplesBuffer
|
||||||
// latest derived minute, hour, and day stats. Updated every minute.
|
// latest derived instant, minute, hour, and day stats. Instant sample updated every second.
|
||||||
|
// Others updated every minute.
|
||||||
derivedStats info.DerivedStats // Guarded by dataLock.
|
derivedStats info.DerivedStats // Guarded by dataLock.
|
||||||
dataLock sync.RWMutex
|
dataLock sync.RWMutex
|
||||||
}
|
}
|
||||||
@ -65,6 +66,7 @@ func (s *StatsSummary) AddSample(stat info.ContainerStats) error {
|
|||||||
sample.Memory = stat.Memory.WorkingSet
|
sample.Memory = stat.Memory.WorkingSet
|
||||||
}
|
}
|
||||||
s.secondSamples = append(s.secondSamples, &sample)
|
s.secondSamples = append(s.secondSamples, &sample)
|
||||||
|
s.updateLatestUsage()
|
||||||
// TODO(jnagal): Use 'available' to avoid unnecessary computation.
|
// TODO(jnagal): Use 'available' to avoid unnecessary computation.
|
||||||
if len(s.secondSamples) == 60 {
|
if len(s.secondSamples) == 60 {
|
||||||
// Make a minute stat.
|
// Make a minute stat.
|
||||||
@ -80,6 +82,29 @@ func (s *StatsSummary) AddSample(stat info.ContainerStats) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StatsSummary) updateLatestUsage() {
|
||||||
|
usage := info.InstantUsage{}
|
||||||
|
numStats := len(s.secondSamples)
|
||||||
|
if numStats < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
latest := s.secondSamples[numStats-1]
|
||||||
|
usage.Memory = latest.Memory
|
||||||
|
if numStats > 1 {
|
||||||
|
previous := s.secondSamples[numStats-2]
|
||||||
|
cpu, err := getCpuRate(*latest, *previous)
|
||||||
|
if err == nil {
|
||||||
|
usage.Cpu = cpu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.dataLock.Lock()
|
||||||
|
defer s.dataLock.Unlock()
|
||||||
|
s.derivedStats.LatestUsage = usage
|
||||||
|
s.derivedStats.Timestamp = latest.Timestamp
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Generate new derived stats based on current minute stats samples.
|
// Generate new derived stats based on current minute stats samples.
|
||||||
func (s *StatsSummary) updateDerivedStats() error {
|
func (s *StatsSummary) updateDerivedStats() error {
|
||||||
derived := info.DerivedStats{}
|
derived := info.DerivedStats{}
|
||||||
@ -102,6 +127,7 @@ func (s *StatsSummary) updateDerivedStats() error {
|
|||||||
|
|
||||||
s.dataLock.Lock()
|
s.dataLock.Lock()
|
||||||
defer s.dataLock.Unlock()
|
defer s.dataLock.Unlock()
|
||||||
|
derived.LatestUsage = s.derivedStats.LatestUsage
|
||||||
s.derivedStats = derived
|
s.derivedStats = derived
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
Reference in New Issue
Block a user