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  }