github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/client/cache/fifo.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cache 18 19 import ( 20 "sync" 21 ) 22 23 // Queue is exactly like a Store, but has a Pop() method too. 24 type Queue interface { 25 Store 26 27 // Pop blocks until it has something to return. 28 Pop() interface{} 29 30 // AddIfNotPresent adds a value previously 31 // returned by Pop back into the queue as long 32 // as nothing else (presumably more recent) 33 // has since been added. 34 AddIfNotPresent(interface{}) error 35 } 36 37 // FIFO receives adds and updates from a Reflector, and puts them in a queue for 38 // FIFO order processing. If multiple adds/updates of a single item happen while 39 // an item is in the queue before it has been processed, it will only be 40 // processed once, and when it is processed, the most recent version will be 41 // processed. This can't be done with a channel. 42 // 43 // FIFO solves this use case: 44 // * You want to process every object (exactly) once. 45 // * You want to process the most recent version of the object when you process it. 46 // * You do not want to process deleted objects, they should be removed from the queue. 47 // * You do not want to periodically reprocess objects. 48 // Compare with DeltaFIFO for other use cases. 49 type FIFO struct { 50 lock sync.RWMutex 51 cond sync.Cond 52 // We depend on the property that items in the set are in the queue and vice versa. 53 items map[string]interface{} 54 queue []string 55 // keyFunc is used to make the key used for queued item insertion and retrieval, and 56 // should be deterministic. 57 keyFunc KeyFunc 58 } 59 60 var ( 61 _ = Queue(&FIFO{}) // FIFO is a Queue 62 ) 63 64 // Add inserts an item, and puts it in the queue. The item is only enqueued 65 // if it doesn't already exist in the set. 66 func (f *FIFO) Add(obj interface{}) error { 67 id, err := f.keyFunc(obj) 68 if err != nil { 69 return KeyError{obj, err} 70 } 71 f.lock.Lock() 72 defer f.lock.Unlock() 73 if _, exists := f.items[id]; !exists { 74 f.queue = append(f.queue, id) 75 } 76 f.items[id] = obj 77 f.cond.Broadcast() 78 return nil 79 } 80 81 // AddIfNotPresent inserts an item, and puts it in the queue. If the item is already 82 // present in the set, it is neither enqueued nor added to the set. 83 // 84 // This is useful in a single producer/consumer scenario so that the consumer can 85 // safely retry items without contending with the producer and potentially enqueueing 86 // stale items. 87 func (f *FIFO) AddIfNotPresent(obj interface{}) error { 88 id, err := f.keyFunc(obj) 89 if err != nil { 90 return KeyError{obj, err} 91 } 92 f.lock.Lock() 93 defer f.lock.Unlock() 94 if _, exists := f.items[id]; exists { 95 return nil 96 } 97 98 f.queue = append(f.queue, id) 99 f.items[id] = obj 100 f.cond.Broadcast() 101 return nil 102 } 103 104 // Update is the same as Add in this implementation. 105 func (f *FIFO) Update(obj interface{}) error { 106 return f.Add(obj) 107 } 108 109 // Delete removes an item. It doesn't add it to the queue, because 110 // this implementation assumes the consumer only cares about the objects, 111 // not the order in which they were created/added. 112 func (f *FIFO) Delete(obj interface{}) error { 113 id, err := f.keyFunc(obj) 114 if err != nil { 115 return KeyError{obj, err} 116 } 117 f.lock.Lock() 118 defer f.lock.Unlock() 119 delete(f.items, id) 120 return err 121 } 122 123 // List returns a list of all the items. 124 func (f *FIFO) List() []interface{} { 125 f.lock.RLock() 126 defer f.lock.RUnlock() 127 list := make([]interface{}, 0, len(f.items)) 128 for _, item := range f.items { 129 list = append(list, item) 130 } 131 return list 132 } 133 134 // ListKeys returns a list of all the keys of the objects currently 135 // in the FIFO. 136 func (f *FIFO) ListKeys() []string { 137 f.lock.RLock() 138 defer f.lock.RUnlock() 139 list := make([]string, 0, len(f.items)) 140 for key := range f.items { 141 list = append(list, key) 142 } 143 return list 144 } 145 146 // Get returns the requested item, or sets exists=false. 147 func (f *FIFO) Get(obj interface{}) (item interface{}, exists bool, err error) { 148 key, err := f.keyFunc(obj) 149 if err != nil { 150 return nil, false, KeyError{obj, err} 151 } 152 return f.GetByKey(key) 153 } 154 155 // GetByKey returns the requested item, or sets exists=false. 156 func (f *FIFO) GetByKey(key string) (item interface{}, exists bool, err error) { 157 f.lock.RLock() 158 defer f.lock.RUnlock() 159 item, exists = f.items[key] 160 return item, exists, nil 161 } 162 163 // Pop waits until an item is ready and returns it. If multiple items are 164 // ready, they are returned in the order in which they were added/updated. 165 // The item is removed from the queue (and the store) before it is returned, 166 // so if you don't successfully process it, you need to add it back with 167 // AddIfNotPresent(). 168 func (f *FIFO) Pop() interface{} { 169 f.lock.Lock() 170 defer f.lock.Unlock() 171 for { 172 for len(f.queue) == 0 { 173 f.cond.Wait() 174 } 175 id := f.queue[0] 176 f.queue = f.queue[1:] 177 item, ok := f.items[id] 178 if !ok { 179 // Item may have been deleted subsequently. 180 continue 181 } 182 delete(f.items, id) 183 return item 184 } 185 } 186 187 // Replace will delete the contents of 'f', using instead the given map. 188 // 'f' takes ownership of the map, you should not reference the map again 189 // after calling this function. f's queue is reset, too; upon return, it 190 // will contain the items in the map, in no particular order. 191 func (f *FIFO) Replace(list []interface{}, resourceVersion string) error { 192 items := map[string]interface{}{} 193 for _, item := range list { 194 key, err := f.keyFunc(item) 195 if err != nil { 196 return KeyError{item, err} 197 } 198 items[key] = item 199 } 200 201 f.lock.Lock() 202 defer f.lock.Unlock() 203 f.items = items 204 f.queue = f.queue[:0] 205 for id := range items { 206 f.queue = append(f.queue, id) 207 } 208 if len(f.queue) > 0 { 209 f.cond.Broadcast() 210 } 211 return nil 212 } 213 214 // NewFIFO returns a Store which can be used to queue up items to 215 // process. 216 func NewFIFO(keyFunc KeyFunc) *FIFO { 217 f := &FIFO{ 218 items: map[string]interface{}{}, 219 queue: []string{}, 220 keyFunc: keyFunc, 221 } 222 f.cond.L = &f.lock 223 return f 224 }