github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/cache/expiring.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes Authors.
     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  	"container/heap"
    21  	"sync"
    22  	"time"
    23  
    24  	"k8s.io/utils/clock"
    25  )
    26  
    27  // NewExpiring returns an initialized expiring cache.
    28  func NewExpiring() *Expiring {
    29  	return NewExpiringWithClock(clock.RealClock{})
    30  }
    31  
    32  // NewExpiringWithClock is like NewExpiring but allows passing in a custom
    33  // clock for testing.
    34  func NewExpiringWithClock(clock clock.Clock) *Expiring {
    35  	return &Expiring{
    36  		clock: clock,
    37  		cache: make(map[interface{}]entry),
    38  	}
    39  }
    40  
    41  // Expiring is a map whose entries expire after a per-entry timeout.
    42  type Expiring struct {
    43  	clock clock.Clock
    44  
    45  	// mu protects the below fields
    46  	mu sync.RWMutex
    47  	// cache is the internal map that backs the cache.
    48  	cache map[interface{}]entry
    49  	// generation is used as a cheap resource version for cache entries. Cleanups
    50  	// are scheduled with a key and generation. When the cleanup runs, it first
    51  	// compares its generation with the current generation of the entry. It
    52  	// deletes the entry iff the generation matches. This prevents cleanups
    53  	// scheduled for earlier versions of an entry from deleting later versions of
    54  	// an entry when Set() is called multiple times with the same key.
    55  	//
    56  	// The integer value of the generation of an entry is meaningless.
    57  	generation uint64
    58  
    59  	heap expiringHeap
    60  }
    61  
    62  type entry struct {
    63  	val        interface{}
    64  	expiry     time.Time
    65  	generation uint64
    66  }
    67  
    68  // Get looks up an entry in the cache.
    69  func (c *Expiring) Get(key interface{}) (val interface{}, ok bool) {
    70  	c.mu.RLock()
    71  	defer c.mu.RUnlock()
    72  	e, ok := c.cache[key]
    73  	if !ok || !c.clock.Now().Before(e.expiry) {
    74  		return nil, false
    75  	}
    76  	return e.val, true
    77  }
    78  
    79  // Set sets a key/value/expiry entry in the map, overwriting any previous entry
    80  // with the same key. The entry expires at the given expiry time, but its TTL
    81  // may be lengthened or shortened by additional calls to Set(). Garbage
    82  // collection of expired entries occurs during calls to Set(), however calls to
    83  // Get() will not return expired entries that have not yet been garbage
    84  // collected.
    85  func (c *Expiring) Set(key interface{}, val interface{}, ttl time.Duration) {
    86  	now := c.clock.Now()
    87  	expiry := now.Add(ttl)
    88  
    89  	c.mu.Lock()
    90  	defer c.mu.Unlock()
    91  
    92  	c.generation++
    93  
    94  	c.cache[key] = entry{
    95  		val:        val,
    96  		expiry:     expiry,
    97  		generation: c.generation,
    98  	}
    99  
   100  	// Run GC inline before pushing the new entry.
   101  	c.gc(now)
   102  
   103  	heap.Push(&c.heap, &expiringHeapEntry{
   104  		key:        key,
   105  		expiry:     expiry,
   106  		generation: c.generation,
   107  	})
   108  }
   109  
   110  // Delete deletes an entry in the map.
   111  func (c *Expiring) Delete(key interface{}) {
   112  	c.mu.Lock()
   113  	defer c.mu.Unlock()
   114  	c.del(key, 0)
   115  }
   116  
   117  // del deletes the entry for the given key. The generation argument is the
   118  // generation of the entry that should be deleted. If the generation has been
   119  // changed (e.g. if a set has occurred on an existing element but the old
   120  // cleanup still runs), this is a noop. If the generation argument is 0, the
   121  // entry's generation is ignored and the entry is deleted.
   122  //
   123  // del must be called under the write lock.
   124  func (c *Expiring) del(key interface{}, generation uint64) {
   125  	e, ok := c.cache[key]
   126  	if !ok {
   127  		return
   128  	}
   129  	if generation != 0 && generation != e.generation {
   130  		return
   131  	}
   132  	delete(c.cache, key)
   133  }
   134  
   135  // Len returns the number of items in the cache.
   136  func (c *Expiring) Len() int {
   137  	c.mu.RLock()
   138  	defer c.mu.RUnlock()
   139  	return len(c.cache)
   140  }
   141  
   142  func (c *Expiring) gc(now time.Time) {
   143  	for {
   144  		// Return from gc if the heap is empty or the next element is not yet
   145  		// expired.
   146  		//
   147  		// heap[0] is a peek at the next element in the heap, which is not obvious
   148  		// from looking at the (*expiringHeap).Pop() implementation below.
   149  		// heap.Pop() swaps the first entry with the last entry of the heap, then
   150  		// calls (*expiringHeap).Pop() which returns the last element.
   151  		if len(c.heap) == 0 || now.Before(c.heap[0].expiry) {
   152  			return
   153  		}
   154  		cleanup := heap.Pop(&c.heap).(*expiringHeapEntry)
   155  		c.del(cleanup.key, cleanup.generation)
   156  	}
   157  }
   158  
   159  type expiringHeapEntry struct {
   160  	key        interface{}
   161  	expiry     time.Time
   162  	generation uint64
   163  }
   164  
   165  // expiringHeap is a min-heap ordered by expiration time of its entries. The
   166  // expiring cache uses this as a priority queue to efficiently organize entries
   167  // which will be garbage collected once they expire.
   168  type expiringHeap []*expiringHeapEntry
   169  
   170  var _ heap.Interface = &expiringHeap{}
   171  
   172  func (cq expiringHeap) Len() int {
   173  	return len(cq)
   174  }
   175  
   176  func (cq expiringHeap) Less(i, j int) bool {
   177  	return cq[i].expiry.Before(cq[j].expiry)
   178  }
   179  
   180  func (cq expiringHeap) Swap(i, j int) {
   181  	cq[i], cq[j] = cq[j], cq[i]
   182  }
   183  
   184  func (cq *expiringHeap) Push(c interface{}) {
   185  	*cq = append(*cq, c.(*expiringHeapEntry))
   186  }
   187  
   188  func (cq *expiringHeap) Pop() interface{} {
   189  	c := (*cq)[cq.Len()-1]
   190  	*cq = (*cq)[:cq.Len()-1]
   191  	return c
   192  }