152 lines
4.4 KiB
Go
152 lines
4.4 KiB
Go
// Copyright 2014 Google Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package memory
|
|
|
|
import (
|
|
"sort"
|
|
"time"
|
|
|
|
info "github.com/google/cadvisor/info/v1"
|
|
)
|
|
|
|
// A circular buffer for ContainerStats.
|
|
type StatsBuffer struct {
|
|
buffer []*info.ContainerStats
|
|
size int
|
|
index int
|
|
}
|
|
|
|
// Returns a new thread-compatible StatsBuffer.
|
|
func NewStatsBuffer(size int) *StatsBuffer {
|
|
return &StatsBuffer{
|
|
buffer: make([]*info.ContainerStats, size),
|
|
size: 0,
|
|
index: size - 1,
|
|
}
|
|
}
|
|
|
|
// Adds an element to the start of the buffer (removing one from the end if necessary).
|
|
func (self *StatsBuffer) Add(item *info.ContainerStats) {
|
|
if self.size < len(self.buffer) {
|
|
self.size++
|
|
}
|
|
self.index = (self.index + 1) % len(self.buffer)
|
|
copied := *item
|
|
self.buffer[self.index] = &copied
|
|
}
|
|
|
|
// Returns up to maxResult elements in the specified time period (inclusive).
|
|
// Results are from first to last. maxResults of -1 means no limit. When first
|
|
// and last are specified, maxResults is ignored.
|
|
func (self *StatsBuffer) InTimeRange(start, end time.Time, maxResults int) []*info.ContainerStats {
|
|
// No stats, return empty.
|
|
if self.size == 0 {
|
|
return []*info.ContainerStats{}
|
|
}
|
|
|
|
// Return all results in a time range if specified.
|
|
if !start.IsZero() && !end.IsZero() {
|
|
maxResults = -1
|
|
}
|
|
|
|
// NOTE: Since we store the elments in descending timestamp order "start" will
|
|
// be a higher index than "end".
|
|
|
|
var startIndex int
|
|
if start.IsZero() {
|
|
// None specified, start at the beginning.
|
|
startIndex = self.size - 1
|
|
} else {
|
|
// Start is the index before the elements smaller than it. We do this by
|
|
// finding the first element smaller than start and taking the index
|
|
// before that element
|
|
startIndex = sort.Search(self.size, func(index int) bool {
|
|
// buffer[index] < start
|
|
return self.Get(index).Timestamp.Before(start)
|
|
}) - 1
|
|
// Check if start is after all the data we have.
|
|
if startIndex < 0 {
|
|
return []*info.ContainerStats{}
|
|
}
|
|
}
|
|
|
|
var endIndex int
|
|
if end.IsZero() {
|
|
// None specified, end with the latest stats.
|
|
endIndex = 0
|
|
} else {
|
|
// End is the first index smaller than or equal to it (so, not larger).
|
|
endIndex = sort.Search(self.size, func(index int) bool {
|
|
// buffer[index] <= t -> !(buffer[index] > t)
|
|
return !self.Get(index).Timestamp.After(end)
|
|
})
|
|
// Check if end is before all the data we have.
|
|
if endIndex == self.size {
|
|
return []*info.ContainerStats{}
|
|
}
|
|
}
|
|
|
|
// Trim to maxResults size.
|
|
numResults := startIndex - endIndex + 1
|
|
if maxResults != -1 && numResults > maxResults {
|
|
startIndex -= numResults - maxResults
|
|
numResults = maxResults
|
|
}
|
|
|
|
// Return in sorted timestamp order so from the "back" to "front".
|
|
result := make([]*info.ContainerStats, numResults)
|
|
for i := 0; i < numResults; i++ {
|
|
result[i] = self.Get(startIndex - i)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// TODO(vmarmol): Remove this function as it will no longer be neededt.
|
|
// Returns the first N elements in the buffer. If N > size of buffer, size of buffer elements are returned.
|
|
// Returns the elements in ascending timestamp order.
|
|
func (self *StatsBuffer) FirstN(n int) []*info.ContainerStats {
|
|
// Cap n at the number of elements we have.
|
|
if n > self.size {
|
|
n = self.size
|
|
}
|
|
|
|
// index points to the latest element, get n before that one (keeping in mind we may have gone through 0).
|
|
start := self.index - (n - 1)
|
|
if start < 0 {
|
|
start += len(self.buffer)
|
|
}
|
|
|
|
// Copy the elements.
|
|
res := make([]*info.ContainerStats, n)
|
|
for i := 0; i < n; i++ {
|
|
index := (start + i) % len(self.buffer)
|
|
res[i] = self.buffer[index]
|
|
}
|
|
return res
|
|
}
|
|
|
|
// Gets the element at the specified index. Note that elements are stored in LIFO order.
|
|
func (self *StatsBuffer) Get(index int) *info.ContainerStats {
|
|
calculatedIndex := self.index - index
|
|
if calculatedIndex < 0 {
|
|
calculatedIndex += len(self.buffer)
|
|
}
|
|
return self.buffer[calculatedIndex]
|
|
}
|
|
|
|
func (self *StatsBuffer) Size() int {
|
|
return self.size
|
|
}
|