diff --git a/storage/memory/stats_buffer.go b/storage/memory/stats_buffer.go new file mode 100644 index 00000000..a10ea188 --- /dev/null +++ b/storage/memory/stats_buffer.go @@ -0,0 +1,56 @@ +package memory + +import ( + "github.com/google/cadvisor/info" +) + +// 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) + self.buffer[self.index] = *item +} + +// Returns the first N elements in the buffer. If N > size of buffer, size of buffer elements are returned. +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 +} + +func (self *StatsBuffer) Size() int { + return self.size +} diff --git a/storage/memory/stats_buffer_test.go b/storage/memory/stats_buffer_test.go new file mode 100644 index 00000000..780b15ba --- /dev/null +++ b/storage/memory/stats_buffer_test.go @@ -0,0 +1,63 @@ +package memory + +import ( + "testing" + + "github.com/google/cadvisor/info" +) + +func createStats(id int32) *info.ContainerStats { + return &info.ContainerStats{ + Cpu: info.CpuStats{ + Load: id, + }, + } +} + +func expectSize(t *testing.T, sb *StatsBuffer, expectedSize int) { + if sb.Size() != expectedSize { + t.Errorf("Expected size %v, got %v", expectedSize, sb.Size()) + } +} + +func expectElements(t *testing.T, sb *StatsBuffer, expected []int32) { + res := sb.FirstN(sb.Size()) + if len(res) != len(expected) { + t.Errorf("Expected elements %v, got %v", expected, res) + return + } + for i, el := range res { + if el.Cpu.Load != expected[i] { + t.Errorf("Expected elements %v, got %v", expected, res) + } + } +} + +func TestAddAndFirstN(t *testing.T) { + sb := NewStatsBuffer(5) + + // Add 1. + sb.Add(createStats(1)) + expectSize(t, sb, 1) + expectElements(t, sb, []int32{1}) + + // Fill the buffer. + for i := 1; i <= 5; i++ { + expectSize(t, sb, i) + sb.Add(createStats(int32(i))) + } + expectSize(t, sb, 5) + expectElements(t, sb, []int32{1, 2, 3, 4, 5}) + + // Add more than is available in the buffer + sb.Add(createStats(6)) + expectSize(t, sb, 5) + expectElements(t, sb, []int32{2, 3, 4, 5, 6}) + + // Replace all elements. + for i := 7; i <= 10; i++ { + sb.Add(createStats(int32(i))) + } + expectSize(t, sb, 5) + expectElements(t, sb, []int32{6, 7, 8, 9, 10}) +}