sigs.k8s.io/cluster-api-provider-azure@v1.14.3/util/cache/ttllru/ttllru.go (about)

     1  /*
     2  Copyright 2020 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 ttllru
    18  
    19  import (
    20  	"sync"
    21  	"time"
    22  
    23  	lru "github.com/hashicorp/golang-lru"
    24  	"github.com/pkg/errors"
    25  )
    26  
    27  type (
    28  	// Cache is a TTL LRU cache which caches items with a max time to live and with
    29  	// bounded length.
    30  	Cache struct {
    31  		Cacher
    32  		TimeToLive time.Duration
    33  		mu         sync.Mutex
    34  	}
    35  
    36  	// Cacher describes a basic cache.
    37  	Cacher interface {
    38  		Get(key interface{}) (value interface{}, ok bool)
    39  		Add(key interface{}, value interface{}) (evicted bool)
    40  		Remove(key interface{}) (ok bool)
    41  	}
    42  
    43  	// PeekingCacher describes a basic cache with the ability to peek.
    44  	PeekingCacher interface {
    45  		Cacher
    46  		Peek(key interface{}) (value interface{}, expiration time.Time, ok bool)
    47  	}
    48  
    49  	timeToLiveItem struct {
    50  		LastTouch time.Time
    51  		Value     interface{}
    52  	}
    53  )
    54  
    55  // New creates a new TTL LRU cache which caches items with a max time to live and with
    56  // bounded length.
    57  func New(size int, timeToLive time.Duration) (PeekingCacher, error) {
    58  	c, err := lru.New(size)
    59  	if err != nil {
    60  		return nil, errors.Wrap(err, "failed to build new LRU cache")
    61  	}
    62  
    63  	return newCache(timeToLive, c)
    64  }
    65  
    66  func newCache(timeToLive time.Duration, cache Cacher) (PeekingCacher, error) {
    67  	return &Cache{
    68  		Cacher:     cache,
    69  		TimeToLive: timeToLive,
    70  	}, nil
    71  }
    72  
    73  // Get returns a value and a bool indicating the value was found for a given key.
    74  func (ttlCache *Cache) Get(key interface{}) (value interface{}, ok bool) {
    75  	ttlItem, ok := ttlCache.peekItem(key)
    76  	if !ok {
    77  		return nil, false
    78  	}
    79  
    80  	ttlItem.LastTouch = time.Now()
    81  	return ttlItem.Value, true
    82  }
    83  
    84  // Add will add a value for a given key.
    85  func (ttlCache *Cache) Add(key interface{}, val interface{}) bool {
    86  	ttlCache.mu.Lock()
    87  	defer ttlCache.mu.Unlock()
    88  
    89  	return ttlCache.Cacher.Add(key, &timeToLiveItem{
    90  		Value:     val,
    91  		LastTouch: time.Now(),
    92  	})
    93  }
    94  
    95  // Peek will fetch an item from the cache, but will not update the expiration time.
    96  func (ttlCache *Cache) Peek(key interface{}) (value interface{}, expiration time.Time, ok bool) {
    97  	ttlItem, ok := ttlCache.peekItem(key)
    98  	if !ok {
    99  		return nil, time.Time{}, false
   100  	}
   101  
   102  	expirationTime := time.Now().Add(ttlCache.TimeToLive - time.Since(ttlItem.LastTouch))
   103  	return ttlItem.Value, expirationTime, true
   104  }
   105  
   106  func (ttlCache *Cache) peekItem(key interface{}) (value *timeToLiveItem, ok bool) {
   107  	ttlCache.mu.Lock()
   108  	defer ttlCache.mu.Unlock()
   109  
   110  	val, ok := ttlCache.Cacher.Get(key)
   111  	if !ok {
   112  		return nil, false
   113  	}
   114  
   115  	ttlItem, ok := val.(*timeToLiveItem)
   116  	if !ok {
   117  		return nil, false
   118  	}
   119  
   120  	if time.Since(ttlItem.LastTouch) > ttlCache.TimeToLive {
   121  		ttlCache.Cacher.Remove(key)
   122  		return nil, false
   123  	}
   124  
   125  	return ttlItem, true
   126  }