github.com/google/cadvisor@v0.49.1/utils/timed_store.go (about) 1 // Copyright 2014 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package utils 16 17 import ( 18 "sort" 19 "time" 20 ) 21 22 type timedStoreDataSlice []timedStoreData 23 24 func (t timedStoreDataSlice) Less(i, j int) bool { 25 return t[i].timestamp.Before(t[j].timestamp) 26 } 27 28 func (t timedStoreDataSlice) Len() int { 29 return len(t) 30 } 31 32 func (t timedStoreDataSlice) Swap(i, j int) { 33 t[i], t[j] = t[j], t[i] 34 } 35 36 // A time-based buffer for ContainerStats. 37 // Holds information for a specific time period and/or a max number of items. 38 type TimedStore struct { 39 buffer timedStoreDataSlice 40 age time.Duration 41 maxItems int 42 } 43 44 type timedStoreData struct { 45 timestamp time.Time 46 data interface{} 47 } 48 49 // Returns a new thread-compatible TimedStore. 50 // A maxItems value of -1 means no limit. 51 func NewTimedStore(age time.Duration, maxItems int) *TimedStore { 52 return &TimedStore{ 53 buffer: make(timedStoreDataSlice, 0), 54 age: age, 55 maxItems: maxItems, 56 } 57 } 58 59 // Adds an element to the start of the buffer (removing one from the end if necessary). 60 func (s *TimedStore) Add(timestamp time.Time, item interface{}) { 61 data := timedStoreData{ 62 timestamp: timestamp, 63 data: item, 64 } 65 // Common case: data is added in order. 66 if len(s.buffer) == 0 || !timestamp.Before(s.buffer[len(s.buffer)-1].timestamp) { 67 s.buffer = append(s.buffer, data) 68 } else { 69 // Data is out of order; insert it in the correct position. 70 index := sort.Search(len(s.buffer), func(index int) bool { 71 return s.buffer[index].timestamp.After(timestamp) 72 }) 73 s.buffer = append(s.buffer, timedStoreData{}) // Make room to shift the elements 74 copy(s.buffer[index+1:], s.buffer[index:]) // Shift the elements over 75 s.buffer[index] = data 76 } 77 78 // Remove any elements before eviction time. 79 // TODO(rjnagal): This is assuming that the added entry has timestamp close to now. 80 evictTime := timestamp.Add(-s.age) 81 index := sort.Search(len(s.buffer), func(index int) bool { 82 return s.buffer[index].timestamp.After(evictTime) 83 }) 84 if index < len(s.buffer) { 85 s.buffer = s.buffer[index:] 86 } 87 88 // Remove any elements if over our max size. 89 if s.maxItems >= 0 && len(s.buffer) > s.maxItems { 90 startIndex := len(s.buffer) - s.maxItems 91 s.buffer = s.buffer[startIndex:] 92 } 93 } 94 95 // Returns up to maxResult elements in the specified time period (inclusive). 96 // Results are from first to last. maxResults of -1 means no limit. 97 func (s *TimedStore) InTimeRange(start, end time.Time, maxResults int) []interface{} { 98 // No stats, return empty. 99 if len(s.buffer) == 0 { 100 return []interface{}{} 101 } 102 103 var startIndex int 104 if start.IsZero() { 105 // None specified, start at the beginning. 106 startIndex = len(s.buffer) - 1 107 } else { 108 // Start is the index before the elements smaller than it. We do this by 109 // finding the first element smaller than start and taking the index 110 // before that element 111 startIndex = sort.Search(len(s.buffer), func(index int) bool { 112 // buffer[index] < start 113 return s.getData(index).timestamp.Before(start) 114 }) - 1 115 // Check if start is after all the data we have. 116 if startIndex < 0 { 117 return []interface{}{} 118 } 119 } 120 121 var endIndex int 122 if end.IsZero() { 123 // None specified, end with the latest stats. 124 endIndex = 0 125 } else { 126 // End is the first index smaller than or equal to it (so, not larger). 127 endIndex = sort.Search(len(s.buffer), func(index int) bool { 128 // buffer[index] <= t -> !(buffer[index] > t) 129 return !s.getData(index).timestamp.After(end) 130 }) 131 // Check if end is before all the data we have. 132 if endIndex == len(s.buffer) { 133 return []interface{}{} 134 } 135 } 136 137 // Trim to maxResults size. 138 numResults := startIndex - endIndex + 1 139 if maxResults != -1 && numResults > maxResults { 140 startIndex -= numResults - maxResults 141 numResults = maxResults 142 } 143 144 // Return in sorted timestamp order so from the "back" to "front". 145 result := make([]interface{}, numResults) 146 for i := 0; i < numResults; i++ { 147 result[i] = s.Get(startIndex - i) 148 } 149 return result 150 } 151 152 // Gets the element at the specified index. Note that elements are output in LIFO order. 153 func (s *TimedStore) Get(index int) interface{} { 154 return s.getData(index).data 155 } 156 157 // Gets the data at the specified index. Note that elements are output in LIFO order. 158 func (s *TimedStore) getData(index int) timedStoreData { 159 return s.buffer[len(s.buffer)-index-1] 160 } 161 162 func (s *TimedStore) Size() int { 163 return len(s.buffer) 164 }