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:
Rohit Jnagal 2015-02-23 03:19:38 +00:00
parent 28984d7811
commit ed38123221
3 changed files with 56 additions and 11 deletions

View File

@ -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.

View File

@ -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)

View File

@ -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