Fix handling of time ranges in timed store.

The current logic assumes that entries are added to the store in
monotonically increasing order for time. This is not true when
we add creation events for existing containers.
This commit is contained in:
Rohit Jnagal 2015-05-07 16:44:22 +00:00
parent 5bd6f601a4
commit 06c9dcd3bb
2 changed files with 34 additions and 20 deletions

View File

@ -170,8 +170,8 @@ func TestGetEventsForOneEvent(t *testing.T) {
func TestGetEventsForTimePeriod(t *testing.T) { func TestGetEventsForTimePeriod(t *testing.T) {
myEventHolder, myRequest, fakeEvent, fakeEvent2 := initializeScenario(t) myEventHolder, myRequest, fakeEvent, fakeEvent2 := initializeScenario(t)
myRequest.StartTime = createOldTime(t).Add(-1 * time.Second * 10) myRequest.StartTime = time.Now().Add(-1 * time.Second * 10)
myRequest.EndTime = createOldTime(t).Add(time.Second * 10) myRequest.EndTime = time.Now().Add(time.Second * 10)
myRequest.EventType[info.EventOom] = true myRequest.EventType[info.EventOom] = true
myEventHolder.AddEvent(fakeEvent) myEventHolder.AddEvent(fakeEvent)
@ -181,7 +181,7 @@ func TestGetEventsForTimePeriod(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
checkNumberOfEvents(t, 1, len(receivedEvents)) checkNumberOfEvents(t, 1, len(receivedEvents))
ensureProperEventReturned(t, fakeEvent, receivedEvents[0]) ensureProperEventReturned(t, fakeEvent2, receivedEvents[0])
} }
func TestGetEventsForNoTypeRequested(t *testing.T) { func TestGetEventsForNoTypeRequested(t *testing.T) {

View File

@ -19,10 +19,24 @@ import (
"time" "time"
) )
type timedStoreDataSlice []timedStoreData
func (t timedStoreDataSlice) Less(i, j int) bool {
return t[i].timestamp.Before(t[j].timestamp)
}
func (t timedStoreDataSlice) Len() int {
return len(t)
}
func (t timedStoreDataSlice) Swap(i, j int) {
t[i], t[j] = t[j], t[i]
}
// A time-based buffer for ContainerStats. // A time-based buffer for ContainerStats.
// Holds information for a specific time period and/or a max number of items. // Holds information for a specific time period and/or a max number of items.
type TimedStore struct { type TimedStore struct {
buffer []timedStoreData buffer timedStoreDataSlice
age time.Duration age time.Duration
maxItems int maxItems int
} }
@ -36,7 +50,7 @@ type timedStoreData struct {
// A maxItems value of -1 means no limit. // A maxItems value of -1 means no limit.
func NewTimedStore(age time.Duration, maxItems int) *TimedStore { func NewTimedStore(age time.Duration, maxItems int) *TimedStore {
return &TimedStore{ return &TimedStore{
buffer: make([]timedStoreData, 0), buffer: make(timedStoreDataSlice, 0),
age: age, age: age,
maxItems: maxItems, maxItems: maxItems,
} }
@ -44,7 +58,21 @@ func NewTimedStore(age time.Duration, maxItems int) *TimedStore {
// Adds an element to the start of the buffer (removing one from the end if necessary). // Adds an element to the start of the buffer (removing one from the end if necessary).
func (self *TimedStore) Add(timestamp time.Time, item interface{}) { func (self *TimedStore) Add(timestamp time.Time, item interface{}) {
// Remove any elements before the eviction time. // Remove any elements if over our max size.
if self.maxItems >= 0 && (len(self.buffer)+1) > self.maxItems {
startIndex := len(self.buffer) + 1 - self.maxItems
self.buffer = self.buffer[startIndex:]
}
// Add the new element first and sort. We can then remove an expired element, if required.
copied := item
self.buffer = append(self.buffer, timedStoreData{
timestamp: timestamp,
data: copied,
})
sort.Sort(self.buffer)
// Remove any elements before eviction time.
// TODO(rjnagal): This is assuming that the added entry has timestamp close to now.
evictTime := timestamp.Add(-self.age) evictTime := timestamp.Add(-self.age)
index := sort.Search(len(self.buffer), func(index int) bool { index := sort.Search(len(self.buffer), func(index int) bool {
return self.buffer[index].timestamp.After(evictTime) return self.buffer[index].timestamp.After(evictTime)
@ -53,17 +81,6 @@ func (self *TimedStore) Add(timestamp time.Time, item interface{}) {
self.buffer = self.buffer[index:] self.buffer = self.buffer[index:]
} }
// Remove any elements if over our max size.
if self.maxItems >= 0 && (len(self.buffer)+1) > self.maxItems {
startIndex := len(self.buffer) + 1 - self.maxItems
self.buffer = self.buffer[startIndex:]
}
copied := item
self.buffer = append(self.buffer, timedStoreData{
timestamp: timestamp,
data: copied,
})
} }
// Returns up to maxResult elements in the specified time period (inclusive). // Returns up to maxResult elements in the specified time period (inclusive).
@ -80,9 +97,6 @@ func (self *TimedStore) InTimeRange(start, end time.Time, maxResults int) []inte
maxResults = -1 maxResults = -1
} }
// NOTE: Since we store the elments in descending timestamp order "start" will
// be a higher index than "end".
var startIndex int var startIndex int
if start.IsZero() { if start.IsZero() {
// None specified, start at the beginning. // None specified, start at the beginning.