diff options
Diffstat (limited to 'vendor')
| -rw-r--r-- | vendor/github.com/ReneKroon/ttlcache/.travis.yml | 18 | ||||
| -rw-r--r-- | vendor/github.com/ReneKroon/ttlcache/LICENSE | 21 | ||||
| -rw-r--r-- | vendor/github.com/ReneKroon/ttlcache/Readme.md | 71 | ||||
| -rw-r--r-- | vendor/github.com/ReneKroon/ttlcache/cache.go | 307 | ||||
| -rw-r--r-- | vendor/github.com/ReneKroon/ttlcache/go.mod | 9 | ||||
| -rw-r--r-- | vendor/github.com/ReneKroon/ttlcache/go.sum | 11 | ||||
| -rw-r--r-- | vendor/github.com/ReneKroon/ttlcache/item.go | 46 | ||||
| -rw-r--r-- | vendor/github.com/ReneKroon/ttlcache/priority_queue.go | 71 | ||||
| -rw-r--r-- | vendor/modules.txt | 3 | 
9 files changed, 557 insertions, 0 deletions
| diff --git a/vendor/github.com/ReneKroon/ttlcache/.travis.yml b/vendor/github.com/ReneKroon/ttlcache/.travis.yml new file mode 100644 index 000000000..095be4ff3 --- /dev/null +++ b/vendor/github.com/ReneKroon/ttlcache/.travis.yml @@ -0,0 +1,18 @@ +language: go + +go: +  - "1.14" +  - "1.13" +git: +  depth: 1 + +install: +  - go install -race std +  - go install golang.org/x/tools/cmd/cover +  - go install golang.org/x/lint/golint +  - export PATH=$HOME/gopath/bin:$PATH + +script: +  - golint . +  - go test -cover -race -count=1 -timeout=30s -run . +  - cd bench; go test -run=Bench.* -bench=. -benchmem
\ No newline at end of file diff --git a/vendor/github.com/ReneKroon/ttlcache/LICENSE b/vendor/github.com/ReneKroon/ttlcache/LICENSE new file mode 100644 index 000000000..b3b587dce --- /dev/null +++ b/vendor/github.com/ReneKroon/ttlcache/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Rene Kroon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/ReneKroon/ttlcache/Readme.md b/vendor/github.com/ReneKroon/ttlcache/Readme.md new file mode 100644 index 000000000..9c537fbdc --- /dev/null +++ b/vendor/github.com/ReneKroon/ttlcache/Readme.md @@ -0,0 +1,71 @@ +## TTLCache - an in-memory cache with expiration + +TTLCache is a simple key/value cache in golang with the following functions: + +1. Thread-safe +2. Individual expiring time or global expiring time, you can choose +3. Auto-Extending expiration on `Get` -or- DNS style TTL, see `SkipTtlExtensionOnHit(bool)` +4. Fast and memory efficient +5. Can trigger callback on key expiration +6. Cleanup resources by calling `Close()` at end of lifecycle. + +Note (issue #25): by default, due to historic reasons, the TTL will be reset on each cache hit and you need to explicitly configure the cache to use a TTL that will not get extended. + +[](https://travis-ci.org/ReneKroon/ttlcache) + +#### Usage +```go +import ( +  "time" +  "fmt" + +  "github.com/ReneKroon/ttlcache" +) + +func main () { +  newItemCallback := func(key string, value interface{}) { +		fmt.Printf("New key(%s) added\n", key) +  } +  checkExpirationCallback := func(key string, value interface{}) bool { +		if key == "key1" { +		    // if the key equals "key1", the value +		    // will not be allowed to expire +		    return false +		} +		// all other values are allowed to expire +		return true +	} +  expirationCallback := func(key string, value interface{}) { +		fmt.Printf("This key(%s) has expired\n", key) +	} + +  cache := ttlcache.NewCache() +  defer cache.Close() +  cache.SetTTL(time.Duration(10 * time.Second)) +  cache.SetExpirationCallback(expirationCallback) + +  cache.Set("key", "value") +  cache.SetWithTTL("keyWithTTL", "value", 10 * time.Second) + +  value, exists := cache.Get("key") +  count := cache.Count() +  result := cache.Remove("key") +} +``` + +#### TTLCache - Some design considerations + +1. The complexity of the current cache is already quite high. Therefore i will not add 'convenience' features like an interface to supply a function to get missing keys.  +2. The locking should be done only in the functions of the Cache struct. Else data races can occur or recursive locks are needed, which are both unwanted. +3. I prefer correct functionality over fast tests. It's ok for new tests to take seconds to proof something. + +#### Original Project + +TTLCache was forked from [wunderlist/ttlcache](https://github.com/wunderlist/ttlcache) to add extra functions not avaiable in the original scope. +The main differences are: + +1. A item can store any kind of object, previously, only strings could be saved +2. Optionally, you can add callbacks too: check if a value should expire, be notified if a value expires, and be notified when new values are added to the cache +3. The expiration can be either global or per item +4. Can exist items without expiration time +5. Expirations and callbacks are realtime. Don't have a pooling time to check anymore, now it's done with a heap. diff --git a/vendor/github.com/ReneKroon/ttlcache/cache.go b/vendor/github.com/ReneKroon/ttlcache/cache.go new file mode 100644 index 000000000..f772d0c7c --- /dev/null +++ b/vendor/github.com/ReneKroon/ttlcache/cache.go @@ -0,0 +1,307 @@ +package ttlcache + +import ( +	"sync" +	"time" +) + +// CheckExpireCallback is used as a callback for an external check on item expiration +type checkExpireCallback func(key string, value interface{}) bool + +// ExpireCallback is used as a callback on item expiration or when notifying of an item new to the cache +type expireCallback func(key string, value interface{}) + +// Cache is a synchronized map of items that can auto-expire once stale +type Cache struct { +	mutex                  sync.Mutex +	ttl                    time.Duration +	items                  map[string]*item +	expireCallback         expireCallback +	checkExpireCallback    checkExpireCallback +	newItemCallback        expireCallback +	priorityQueue          *priorityQueue +	expirationNotification chan bool +	expirationTime         time.Time +	skipTTLExtension       bool +	shutdownSignal         chan (chan struct{}) +	isShutDown             bool +} + +func (cache *Cache) getItem(key string) (*item, bool, bool) { +	item, exists := cache.items[key] +	if !exists || item.expired() { +		return nil, false, false +	} + +	if item.ttl >= 0 && (item.ttl > 0 || cache.ttl > 0) { +		if cache.ttl > 0 && item.ttl == 0 { +			item.ttl = cache.ttl +		} + +		if !cache.skipTTLExtension { +			item.touch() +		} +		cache.priorityQueue.update(item) +	} + +	expirationNotification := false +	if cache.expirationTime.After(time.Now().Add(item.ttl)) { +		expirationNotification = true +	} +	return item, exists, expirationNotification +} + +func (cache *Cache) startExpirationProcessing() { +	timer := time.NewTimer(time.Hour) +	for { +		var sleepTime time.Duration +		cache.mutex.Lock() +		if cache.priorityQueue.Len() > 0 { +			sleepTime = time.Until(cache.priorityQueue.items[0].expireAt) +			if sleepTime < 0 && cache.priorityQueue.items[0].expireAt.IsZero() { +				sleepTime = time.Hour +			} else if sleepTime < 0 { +				sleepTime = time.Microsecond +			} +			if cache.ttl > 0 { +				sleepTime = min(sleepTime, cache.ttl) +			} + +		} else if cache.ttl > 0 { +			sleepTime = cache.ttl +		} else { +			sleepTime = time.Hour +		} + +		cache.expirationTime = time.Now().Add(sleepTime) +		cache.mutex.Unlock() + +		timer.Reset(sleepTime) +		select { +		case shutdownFeedback := <-cache.shutdownSignal: +			timer.Stop() +			cache.mutex.Lock() +			if cache.priorityQueue.Len() > 0 { +				cache.evictjob() +			} +			cache.mutex.Unlock() +			shutdownFeedback <- struct{}{} +			return +		case <-timer.C: +			timer.Stop() +			cache.mutex.Lock() +			if cache.priorityQueue.Len() == 0 { +				cache.mutex.Unlock() +				continue +			} + +			cache.cleanjob() +			cache.mutex.Unlock() + +		case <-cache.expirationNotification: +			timer.Stop() +			continue +		} +	} +} + +func (cache *Cache) evictjob() { +	// index will only be advanced if the current entry will not be evicted +	i := 0 +	for item := cache.priorityQueue.items[i]; ; item = cache.priorityQueue.items[i] { + +		cache.priorityQueue.remove(item) +		delete(cache.items, item.key) +		if cache.expireCallback != nil { +			go cache.expireCallback(item.key, item.data) +		} +		if cache.priorityQueue.Len() == 0 { +			return +		} +	} +} + +func (cache *Cache) cleanjob() { +	// index will only be advanced if the current entry will not be evicted +	i := 0 +	for item := cache.priorityQueue.items[i]; item.expired(); item = cache.priorityQueue.items[i] { + +		if cache.checkExpireCallback != nil { +			if !cache.checkExpireCallback(item.key, item.data) { +				item.touch() +				cache.priorityQueue.update(item) +				i++ +				if i == cache.priorityQueue.Len() { +					break +				} +				continue +			} +		} + +		cache.priorityQueue.remove(item) +		delete(cache.items, item.key) +		if cache.expireCallback != nil { +			go cache.expireCallback(item.key, item.data) +		} +		if cache.priorityQueue.Len() == 0 { +			return +		} +	} +} + +// Close calls Purge, and then stops the goroutine that does ttl checking, for a clean shutdown. +// The cache is no longer cleaning up after the first call to Close, repeated calls are safe though. +func (cache *Cache) Close() { + +	cache.mutex.Lock() +	if !cache.isShutDown { +		cache.isShutDown = true +		cache.mutex.Unlock() +		feedback := make(chan struct{}) +		cache.shutdownSignal <- feedback +		<-feedback +		close(cache.shutdownSignal) +	} else { +		cache.mutex.Unlock() +	} +	cache.Purge() +} + +// Set is a thread-safe way to add new items to the map +func (cache *Cache) Set(key string, data interface{}) { +	cache.SetWithTTL(key, data, ItemExpireWithGlobalTTL) +} + +// SetWithTTL is a thread-safe way to add new items to the map with individual ttl +func (cache *Cache) SetWithTTL(key string, data interface{}, ttl time.Duration) { +	cache.mutex.Lock() +	item, exists, _ := cache.getItem(key) + +	if exists { +		item.data = data +		item.ttl = ttl +	} else { +		item = newItem(key, data, ttl) +		cache.items[key] = item +	} + +	if item.ttl >= 0 && (item.ttl > 0 || cache.ttl > 0) { +		if cache.ttl > 0 && item.ttl == 0 { +			item.ttl = cache.ttl +		} +		item.touch() +	} + +	if exists { +		cache.priorityQueue.update(item) +	} else { +		cache.priorityQueue.push(item) +	} + +	cache.mutex.Unlock() +	if !exists && cache.newItemCallback != nil { +		cache.newItemCallback(key, data) +	} +	cache.expirationNotification <- true +} + +// Get is a thread-safe way to lookup items +// Every lookup, also touches the item, hence extending it's life +func (cache *Cache) Get(key string) (interface{}, bool) { +	cache.mutex.Lock() +	item, exists, triggerExpirationNotification := cache.getItem(key) + +	var dataToReturn interface{} +	if exists { +		dataToReturn = item.data +	} +	cache.mutex.Unlock() +	if triggerExpirationNotification { +		cache.expirationNotification <- true +	} +	return dataToReturn, exists +} + +func (cache *Cache) Remove(key string) bool { +	cache.mutex.Lock() +	object, exists := cache.items[key] +	if !exists { +		cache.mutex.Unlock() +		return false +	} +	delete(cache.items, object.key) +	cache.priorityQueue.remove(object) +	cache.mutex.Unlock() + +	return true +} + +// Count returns the number of items in the cache +func (cache *Cache) Count() int { +	cache.mutex.Lock() +	length := len(cache.items) +	cache.mutex.Unlock() +	return length +} + +func (cache *Cache) SetTTL(ttl time.Duration) { +	cache.mutex.Lock() +	cache.ttl = ttl +	cache.mutex.Unlock() +	cache.expirationNotification <- true +} + +// SetExpirationCallback sets a callback that will be called when an item expires +func (cache *Cache) SetExpirationCallback(callback expireCallback) { +	cache.expireCallback = callback +} + +// SetCheckExpirationCallback sets a callback that will be called when an item is about to expire +// in order to allow external code to decide whether the item expires or remains for another TTL cycle +func (cache *Cache) SetCheckExpirationCallback(callback checkExpireCallback) { +	cache.checkExpireCallback = callback +} + +// SetNewItemCallback sets a callback that will be called when a new item is added to the cache +func (cache *Cache) SetNewItemCallback(callback expireCallback) { +	cache.newItemCallback = callback +} + +// SkipTtlExtensionOnHit allows the user to change the cache behaviour. When this flag is set to true it will +// no longer extend TTL of items when they are retrieved using Get, or when their expiration condition is evaluated +// using SetCheckExpirationCallback. +func (cache *Cache) SkipTtlExtensionOnHit(value bool) { +	cache.skipTTLExtension = value +} + +// Purge will remove all entries +func (cache *Cache) Purge() { +	cache.mutex.Lock() +	cache.items = make(map[string]*item) +	cache.priorityQueue = newPriorityQueue() +	cache.mutex.Unlock() +} + +// NewCache is a helper to create instance of the Cache struct +func NewCache() *Cache { + +	shutdownChan := make(chan chan struct{}) + +	cache := &Cache{ +		items:                  make(map[string]*item), +		priorityQueue:          newPriorityQueue(), +		expirationNotification: make(chan bool), +		expirationTime:         time.Now(), +		shutdownSignal:         shutdownChan, +		isShutDown:             false, +	} +	go cache.startExpirationProcessing() +	return cache +} + +func min(duration time.Duration, second time.Duration) time.Duration { +	if duration < second { +		return duration +	} +	return second +} diff --git a/vendor/github.com/ReneKroon/ttlcache/go.mod b/vendor/github.com/ReneKroon/ttlcache/go.mod new file mode 100644 index 000000000..6806b2859 --- /dev/null +++ b/vendor/github.com/ReneKroon/ttlcache/go.mod @@ -0,0 +1,9 @@ +module github.com/ReneKroon/ttlcache + +go 1.14 + +require ( +	github.com/davecgh/go-spew v1.1.1 // indirect +	github.com/stretchr/testify v1.3.0 +	go.uber.org/goleak v0.10.0 +) diff --git a/vendor/github.com/ReneKroon/ttlcache/go.sum b/vendor/github.com/ReneKroon/ttlcache/go.sum new file mode 100644 index 000000000..5701e60f9 --- /dev/null +++ b/vendor/github.com/ReneKroon/ttlcache/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +go.uber.org/goleak v0.10.0 h1:G3eWbSNIskeRqtsN/1uI5B+eP73y3JUuBsv9AZjehb4= +go.uber.org/goleak v0.10.0/go.mod h1:VCZuO8V8mFPlL0F5J5GK1rtHV3DrFcQ1R8ryq7FK0aI= diff --git a/vendor/github.com/ReneKroon/ttlcache/item.go b/vendor/github.com/ReneKroon/ttlcache/item.go new file mode 100644 index 000000000..2f78f49cc --- /dev/null +++ b/vendor/github.com/ReneKroon/ttlcache/item.go @@ -0,0 +1,46 @@ +package ttlcache + +import ( +	"time" +) + +const ( +	// ItemNotExpire Will avoid the item being expired by TTL, but can still be exired by callback etc. +	ItemNotExpire time.Duration = -1 +	// ItemExpireWithGlobalTTL will use the global TTL when set. +	ItemExpireWithGlobalTTL time.Duration = 0 +) + +func newItem(key string, data interface{}, ttl time.Duration) *item { +	item := &item{ +		data: data, +		ttl:  ttl, +		key:  key, +	} +	// since nobody is aware yet of this item, it's safe to touch without lock here +	item.touch() +	return item +} + +type item struct { +	key        string +	data       interface{} +	ttl        time.Duration +	expireAt   time.Time +	queueIndex int +} + +// Reset the item expiration time +func (item *item) touch() { +	if item.ttl > 0 { +		item.expireAt = time.Now().Add(item.ttl) +	} +} + +// Verify if the item is expired +func (item *item) expired() bool { +	if item.ttl <= 0 { +		return false +	} +	return item.expireAt.Before(time.Now()) +} diff --git a/vendor/github.com/ReneKroon/ttlcache/priority_queue.go b/vendor/github.com/ReneKroon/ttlcache/priority_queue.go new file mode 100644 index 000000000..11b9c3140 --- /dev/null +++ b/vendor/github.com/ReneKroon/ttlcache/priority_queue.go @@ -0,0 +1,71 @@ +package ttlcache + +import ( +	"container/heap" +) + +func newPriorityQueue() *priorityQueue { +	queue := &priorityQueue{} +	heap.Init(queue) +	return queue +} + +type priorityQueue struct { +	items []*item +} + +func (pq *priorityQueue) update(item *item) { +	heap.Fix(pq, item.queueIndex) +} + +func (pq *priorityQueue) push(item *item) { +	heap.Push(pq, item) +} + +func (pq *priorityQueue) pop() *item { +	if pq.Len() == 0 { +		return nil +	} +	return heap.Pop(pq).(*item) +} + +func (pq *priorityQueue) remove(item *item) { +	heap.Remove(pq, item.queueIndex) +} + +func (pq priorityQueue) Len() int { +	length := len(pq.items) +	return length +} + +// Less will consider items with time.Time default value (epoch start) as more than set items. +func (pq priorityQueue) Less(i, j int) bool { +	if pq.items[i].expireAt.IsZero() { +		return false +	} +	if pq.items[j].expireAt.IsZero() { +		return true +	} +	return pq.items[i].expireAt.Before(pq.items[j].expireAt) +} + +func (pq priorityQueue) Swap(i, j int) { +	pq.items[i], pq.items[j] = pq.items[j], pq.items[i] +	pq.items[i].queueIndex = i +	pq.items[j].queueIndex = j +} + +func (pq *priorityQueue) Push(x interface{}) { +	item := x.(*item) +	item.queueIndex = len(pq.items) +	pq.items = append(pq.items, item) +} + +func (pq *priorityQueue) Pop() interface{} { +	old := pq.items +	n := len(old) +	item := old[n-1] +	item.queueIndex = -1 +	pq.items = old[0 : n-1] +	return item +} diff --git a/vendor/modules.txt b/vendor/modules.txt index dc9739bd9..043a62463 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,3 +1,6 @@ +# github.com/ReneKroon/ttlcache v1.7.0 +## explicit +github.com/ReneKroon/ttlcache  # github.com/aymerick/douceur v0.2.0  github.com/aymerick/douceur/css  github.com/aymerick/douceur/parser | 
