From 066e70fac24cd4233de0e45c8f2b145457ecbfc0 Mon Sep 17 00:00:00 2001 From: CJ Cullen Date: Sat, 14 May 2016 16:38:19 -0700 Subject: [PATCH] Add an LRU Expire Cache. --- pkg/util/cache/lruexpirecache.go | 66 +++++++++++++++++++++++++++ pkg/util/cache/lruexpirecache_test.go | 63 +++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 pkg/util/cache/lruexpirecache.go create mode 100644 pkg/util/cache/lruexpirecache_test.go diff --git a/pkg/util/cache/lruexpirecache.go b/pkg/util/cache/lruexpirecache.go new file mode 100644 index 00000000000..22f7f27679c --- /dev/null +++ b/pkg/util/cache/lruexpirecache.go @@ -0,0 +1,66 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cache + +import ( + "sync" + "time" + + "github.com/golang/groupcache/lru" +) + +type LRUExpireCache struct { + cache *lru.Cache + lock sync.RWMutex +} + +func NewLRUExpireCache(maxSize int) *LRUExpireCache { + return &LRUExpireCache{cache: lru.New(maxSize)} +} + +type cacheEntry struct { + value interface{} + expireTime time.Time +} + +func (c *LRUExpireCache) Add(key lru.Key, value interface{}, ttl time.Duration) { + c.lock.Lock() + defer c.lock.Unlock() + c.cache.Add(key, &cacheEntry{value, time.Now().Add(ttl)}) + // Remove entry from cache after ttl. + time.AfterFunc(ttl, func() { c.remove(key) }) +} + +func (c *LRUExpireCache) Get(key lru.Key) (interface{}, bool) { + c.lock.RLock() + defer c.lock.RUnlock() + e, ok := c.cache.Get(key) + if !ok { + return nil, false + } + if time.Now().After(e.(*cacheEntry).expireTime) { + go c.remove(key) + return nil, false + } + return e.(*cacheEntry).value, true +} + +func (c *LRUExpireCache) remove(key lru.Key) { + c.lock.Lock() + defer c.lock.Unlock() + c.cache.Remove(key) +} diff --git a/pkg/util/cache/lruexpirecache_test.go b/pkg/util/cache/lruexpirecache_test.go new file mode 100644 index 00000000000..07465ea5643 --- /dev/null +++ b/pkg/util/cache/lruexpirecache_test.go @@ -0,0 +1,63 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cache + +import ( + "testing" + "time" + + "github.com/golang/groupcache/lru" +) + +func expectEntry(t *testing.T, c *LRUExpireCache, key lru.Key, value interface{}) { + result, ok := c.Get(key) + if !ok || result != value { + t.Errorf("Expected cache[%v]: %v, got %v", key, value, result) + } +} + +func expectNotEntry(t *testing.T, c *LRUExpireCache, key lru.Key) { + if result, ok := c.Get(key); ok { + t.Errorf("Expected cache[%v] to be empty, got %v", key, result) + } +} + +func TestSimpleGet(t *testing.T) { + c := NewLRUExpireCache(10) + c.Add("long-lived", "12345", 10*time.Hour) + expectEntry(t, c, "long-lived", "12345") +} + +func TestExpiredGet(t *testing.T) { + c := NewLRUExpireCache(10) + c.Add("short-lived", "12345", 0*time.Second) + expectNotEntry(t, c, "short-lived") +} + +func TestLRUOverflow(t *testing.T) { + c := NewLRUExpireCache(4) + c.Add("elem1", "1", 10*time.Hour) + c.Add("elem2", "2", 10*time.Hour) + c.Add("elem3", "3", 10*time.Hour) + c.Add("elem4", "4", 10*time.Hour) + c.Add("elem5", "5", 10*time.Hour) + expectNotEntry(t, c, "elem1") + expectEntry(t, c, "elem2", "2") + expectEntry(t, c, "elem3", "3") + expectEntry(t, c, "elem4", "4") + expectEntry(t, c, "elem5", "5") +}