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 }