diff --git a/vendor/github.com/koding/cache/README.md b/vendor/github.com/koding/cache/README.md new file mode 100644 index 000000000..cc3f29920 --- /dev/null +++ b/vendor/github.com/koding/cache/README.md @@ -0,0 +1,32 @@ +# Cache [![GoDoc](https://godoc.org/github.com/koding/cache?status.svg)](https://godoc.org/github.com/koding/cache) [![Build Status](https://travis-ci.org/koding/cache.svg?branch=master)](https://travis-ci.org/koding/cache) + + +Cache is a backend provider for common use cases + +## Install and Usage + +Install the package with: + +```bash +go get github.com/koding/cache +``` + +Import it with: + +```go +import "github.com/koding/cache" +``` + + +Example +```go + +// create a cache with 2 second TTL +cache := NewMemoryWithTTL(2 * time.Second) +// start garbage collection for expired keys +cache.StartGC(time.Millisecond * 10) +// set item +err := cache.Set("test_key", "test_data") +// get item +data, err := cache.Get("test_key") +``` diff --git a/vendor/github.com/koding/cache/cache.go b/vendor/github.com/koding/cache/cache.go new file mode 100644 index 000000000..73ca69203 --- /dev/null +++ b/vendor/github.com/koding/cache/cache.go @@ -0,0 +1,15 @@ +package cache + +// Cache is the contract for all of the cache backends that are supported by +// this package +type Cache interface { + // Get returns single item from the backend if the requested item is not + // found, returns NotFound err + Get(key string) (interface{}, error) + + // Set sets a single item to the backend + Set(key string, value interface{}) error + + // Delete deletes single item from backend + Delete(key string) error +} diff --git a/vendor/github.com/koding/cache/doc.go b/vendor/github.com/koding/cache/doc.go new file mode 100644 index 000000000..2dcbc4b44 --- /dev/null +++ b/vendor/github.com/koding/cache/doc.go @@ -0,0 +1,9 @@ +// Package cache provides basic caching mechanisms for Go(lang) projects. +// +// Currently supported caching algorithms: +// MemoryNoTS: provides a non-thread safe in-memory caching system +// Memory : provides a thread safe in-memory caching system, built on top of MemoryNoTS cache +// LRUNoTS : provides a non-thread safe, fixed size in-memory caching system, built on top of MemoryNoTS cache +// LRU : provides a thread safe, fixed size in-memory caching system, built on top of LRUNoTS cache +// MemoryTTL : provides a thread safe, expiring in-memory caching system, built on top of MemoryNoTS cache +package cache diff --git a/vendor/github.com/koding/cache/errors.go b/vendor/github.com/koding/cache/errors.go new file mode 100644 index 000000000..9ffe436cc --- /dev/null +++ b/vendor/github.com/koding/cache/errors.go @@ -0,0 +1,8 @@ +package cache + +import "errors" + +var ( + // ErrNotFound holds exported `not found error` for not found items + ErrNotFound = errors.New("not found") +) diff --git a/vendor/github.com/koding/cache/helper_test.go b/vendor/github.com/koding/cache/helper_test.go new file mode 100644 index 000000000..be2c67994 --- /dev/null +++ b/vendor/github.com/koding/cache/helper_test.go @@ -0,0 +1,83 @@ +package cache + +import "testing" + +func testCacheGetSet(t *testing.T, cache Cache) { + err := cache.Set("test_key", "test_data") + if err != nil { + t.Fatal("should not give err while setting item") + } + + err = cache.Set("test_key2", "test_data2") + if err != nil { + t.Fatal("should not give err while setting item") + } + + data, err := cache.Get("test_key") + if err != nil { + t.Fatal("test_key should be in the cache") + } + + if data != "test_data" { + t.Fatal("data is not \"test_data\"") + } + + data, err = cache.Get("test_key2") + if err != nil { + t.Fatal("test_key2 should be in the cache") + } + + if data != "test_data2" { + t.Fatal("data is not \"test_data2\"") + } +} + +func testCacheNilValue(t *testing.T, cache Cache) { + err := cache.Set("test_key", nil) + if err != nil { + t.Fatal("should not give err while setting item") + } + + data, err := cache.Get("test_key") + if err != nil { + t.Fatal("test_key should be in the cache") + } + + if data != nil { + t.Fatal("data is not nil") + } + + err = cache.Delete("test_key") + if err != nil { + t.Fatal("should not give err while setting item") + } + + data, err = cache.Get("test_key") + if err == nil { + t.Fatal("test_key should not be in the cache") + } +} + +func testCacheDelete(t *testing.T, cache Cache) { + cache.Set("test_key", "test_data") + cache.Set("test_key2", "test_data2") + + err := cache.Delete("test_key3") + if err != nil { + t.Fatal("non-exiting item should not give error") + } + + err = cache.Delete("test_key") + if err != nil { + t.Fatal("exiting item should not give error") + } + + data, err := cache.Get("test_key") + if err != ErrNotFound { + t.Fatal("test_key should not be in the cache") + } + + if data != nil { + t.Fatal("data should be nil") + } +} diff --git a/vendor/github.com/koding/cache/lru.go b/vendor/github.com/koding/cache/lru.go new file mode 100644 index 000000000..2eefe5c4e --- /dev/null +++ b/vendor/github.com/koding/cache/lru.go @@ -0,0 +1,51 @@ +package cache + +import "sync" + +// LRU Discards the least recently used items first. This algorithm +// requires keeping track of what was used when. +type LRU struct { + // Mutex is used for handling the concurrent + // read/write requests for cache + sync.Mutex + + // cache holds the all cache values + cache Cache +} + +// NewLRU creates a thread-safe LRU cache +func NewLRU(size int) Cache { + return &LRU{ + cache: NewLRUNoTS(size), + } +} + +// Get returns the value of a given key if it exists, every get item will be +// moved to the head of the linked list for keeping track of least recent used +// item +func (l *LRU) Get(key string) (interface{}, error) { + l.Lock() + defer l.Unlock() + + return l.cache.Get(key) +} + +// Set sets or overrides the given key with the given value, every set item will +// be moved or prepended to the head of the linked list for keeping track of +// least recent used item. When the cache is full, last item of the linked list +// will be evicted from the cache +func (l *LRU) Set(key string, val interface{}) error { + l.Lock() + defer l.Unlock() + + return l.cache.Set(key, val) +} + +// Delete deletes the given key-value pair from cache, this function doesnt +// return an error if item is not in the cache +func (l *LRU) Delete(key string) error { + l.Lock() + defer l.Unlock() + + return l.cache.Delete(key) +} diff --git a/vendor/github.com/koding/cache/lru_nots.go b/vendor/github.com/koding/cache/lru_nots.go new file mode 100644 index 000000000..a6eebcd3d --- /dev/null +++ b/vendor/github.com/koding/cache/lru_nots.go @@ -0,0 +1,122 @@ +package cache + +import ( + "container/list" +) + +// LRUNoTS Discards the least recently used items first. This algorithm +// requires keeping track of what was used when. +type LRUNoTS struct { + // list holds all items in a linked list, for finding the `tail` of the list + list *list.List + + // cache holds the all cache values + cache Cache + + // size holds the limit of the LRU cache + size int +} + +// kv is an helper struct for keeping track of the key for the list item. Only +// place where we need the key of a value is while removing the last item from +// linked list, for other cases, all operations alread have the key +type kv struct { + k string + v interface{} +} + +// NewLRUNoTS creates a new LRU cache struct for further cache operations. Size +// is used for limiting the upper bound of the cache +func NewLRUNoTS(size int) Cache { + if size < 1 { + panic("invalid cache size") + } + + return &LRUNoTS{ + list: list.New(), + cache: NewMemoryNoTS(), + size: size, + } +} + +// Get returns the value of a given key if it exists, every get item will be +// moved to the head of the linked list for keeping track of least recent used +// item +func (l *LRUNoTS) Get(key string) (interface{}, error) { + res, err := l.cache.Get(key) + if err != nil { + return nil, err + } + + elem := res.(*list.Element) + // move found item to the head + l.list.MoveToFront(elem) + + return elem.Value.(*kv).v, nil +} + +// Set sets or overrides the given key with the given value, every set item will +// be moved or prepended to the head of the linked list for keeping track of +// least recent used item. When the cache is full, last item of the linked list +// will be evicted from the cache +func (l *LRUNoTS) Set(key string, val interface{}) error { + // try to get item + res, err := l.cache.Get(key) + if err != nil && err != ErrNotFound { + return err + } + + var elem *list.Element + + // if elem is not in the cache, push it to front of the list + if err == ErrNotFound { + elem = l.list.PushFront(&kv{k: key, v: val}) + } else { + // if elem is in the cache, update the data and move it the front + elem = res.(*list.Element) + + // update the data + elem.Value.(*kv).v = val + + // item already exists, so move it to the front of the list + l.list.MoveToFront(elem) + } + + // in any case, set the item to the cache + err = l.cache.Set(key, elem) + if err != nil { + return err + } + + // if the cache is full, evict last entry + if l.list.Len() > l.size { + // remove last element from cache + return l.removeElem(l.list.Back()) + } + + return nil +} + +// Delete deletes the given key-value pair from cache, this function doesnt +// return an error if item is not in the cache +func (l *LRUNoTS) Delete(key string) error { + res, err := l.cache.Get(key) + if err != nil && err != ErrNotFound { + return err + } + + // item already deleted + if err == ErrNotFound { + // surpress not found errors + return nil + } + + elem := res.(*list.Element) + + return l.removeElem(elem) +} + +func (l *LRUNoTS) removeElem(e *list.Element) error { + l.list.Remove(e) + return l.cache.Delete(e.Value.(*kv).k) +} diff --git a/vendor/github.com/koding/cache/lru_nots_test.go b/vendor/github.com/koding/cache/lru_nots_test.go new file mode 100644 index 000000000..2103369bb --- /dev/null +++ b/vendor/github.com/koding/cache/lru_nots_test.go @@ -0,0 +1,33 @@ +package cache + +import "testing" + +func TestLRUNoTSGetSet(t *testing.T) { + cache := NewLRUNoTS(2) + testCacheGetSet(t, cache) +} + +func TestLRUNoTSEviction(t *testing.T) { + cache := NewLRUNoTS(2) + testCacheGetSet(t, cache) + + err := cache.Set("test_key3", "test_data3") + if err != nil { + t.Fatal("should not give err while setting item") + } + + _, err = cache.Get("test_key") + if err == nil { + t.Fatal("test_key should not be in the cache") + } +} + +func TestLRUNoTSDelete(t *testing.T) { + cache := NewLRUNoTS(2) + testCacheDelete(t, cache) +} + +func TestLRUNoTSNilValue(t *testing.T) { + cache := NewLRUNoTS(2) + testCacheNilValue(t, cache) +} diff --git a/vendor/github.com/koding/cache/lru_test.go b/vendor/github.com/koding/cache/lru_test.go new file mode 100644 index 000000000..2bae93db5 --- /dev/null +++ b/vendor/github.com/koding/cache/lru_test.go @@ -0,0 +1,33 @@ +package cache + +import "testing" + +func TestLRUGetSet(t *testing.T) { + cache := NewLRU(2) + testCacheGetSet(t, cache) +} + +func TestLRUEviction(t *testing.T) { + cache := NewLRU(2) + testCacheGetSet(t, cache) + + err := cache.Set("test_key3", "test_data3") + if err != nil { + t.Fatal("should not give err while setting item") + } + + _, err = cache.Get("test_key") + if err == nil { + t.Fatal("test_key should not be in the cache") + } +} + +func TestLRUDelete(t *testing.T) { + cache := NewLRU(2) + testCacheDelete(t, cache) +} + +func TestLRUNilValue(t *testing.T) { + cache := NewLRU(2) + testCacheNilValue(t, cache) +} diff --git a/vendor/github.com/koding/cache/memory.go b/vendor/github.com/koding/cache/memory.go new file mode 100644 index 000000000..3a6eb8678 --- /dev/null +++ b/vendor/github.com/koding/cache/memory.go @@ -0,0 +1,46 @@ +package cache + +import "sync" + +// Memory provides an inmemory caching mechanism +type Memory struct { + // Mutex is used for handling the concurrent + // read/write requests for cache + sync.Mutex + + // cache holds the cache data + cache Cache +} + +// NewMemory creates an inmemory cache system +// Which everytime will return the true value about a cache hit +func NewMemory() Cache { + return &Memory{ + cache: NewMemoryNoTS(), + } +} + +// Get returns the value of a given key if it exists +func (r *Memory) Get(key string) (interface{}, error) { + r.Lock() + defer r.Unlock() + + return r.cache.Get(key) +} + +// Set sets a value to the cache or overrides existing one with the given value +func (r *Memory) Set(key string, value interface{}) error { + r.Lock() + defer r.Unlock() + + return r.cache.Set(key, value) +} + +// Delete deletes the given key-value pair from cache, this function doesnt +// return an error if item is not in the cache +func (r *Memory) Delete(key string) error { + r.Lock() + defer r.Unlock() + + return r.cache.Delete(key) +} diff --git a/vendor/github.com/koding/cache/memory_nots.go b/vendor/github.com/koding/cache/memory_nots.go new file mode 100644 index 000000000..ac6c52382 --- /dev/null +++ b/vendor/github.com/koding/cache/memory_nots.go @@ -0,0 +1,39 @@ +package cache + +// MemoryNoTS provides a non-thread safe caching mechanism +type MemoryNoTS struct { + // items holds the cache data + items map[string]interface{} +} + +// NewMemoryNoTS creates MemoryNoTS struct +func NewMemoryNoTS() *MemoryNoTS { + return &MemoryNoTS{ + items: map[string]interface{}{}, + } +} + +// Get returns a value of a given key if it exists +// and valid for the time being +func (r *MemoryNoTS) Get(key string) (interface{}, error) { + value, ok := r.items[key] + if !ok { + return nil, ErrNotFound + } + + return value, nil +} + +// Set will persist a value to the cache or +// override existing one with the new one +func (r *MemoryNoTS) Set(key string, value interface{}) error { + r.items[key] = value + return nil +} + +// Delete deletes a given key, it doesnt return error if the item is not in the +// system +func (r *MemoryNoTS) Delete(key string) error { + delete(r.items, key) + return nil +} diff --git a/vendor/github.com/koding/cache/memory_nots_test.go b/vendor/github.com/koding/cache/memory_nots_test.go new file mode 100644 index 000000000..1dc1e563b --- /dev/null +++ b/vendor/github.com/koding/cache/memory_nots_test.go @@ -0,0 +1,18 @@ +package cache + +import "testing" + +func TestMemoryCacheNoTSGetSet(t *testing.T) { + cache := NewMemoryNoTS() + testCacheGetSet(t, cache) +} + +func TestMemoryCacheNoTSDelete(t *testing.T) { + cache := NewMemoryNoTS() + testCacheDelete(t, cache) +} + +func TestMemoryCacheNoTSNilValue(t *testing.T) { + cache := NewMemoryNoTS() + testCacheNilValue(t, cache) +} diff --git a/vendor/github.com/koding/cache/memory_test.go b/vendor/github.com/koding/cache/memory_test.go new file mode 100644 index 000000000..6b4b74443 --- /dev/null +++ b/vendor/github.com/koding/cache/memory_test.go @@ -0,0 +1,18 @@ +package cache + +import "testing" + +func TestMemoryGetSet(t *testing.T) { + cache := NewMemory() + testCacheGetSet(t, cache) +} + +func TestMemoryDelete(t *testing.T) { + cache := NewMemory() + testCacheDelete(t, cache) +} + +func TestMemoryNilValue(t *testing.T) { + cache := NewMemory() + testCacheNilValue(t, cache) +} diff --git a/vendor/github.com/koding/cache/memory_ttl.go b/vendor/github.com/koding/cache/memory_ttl.go new file mode 100644 index 000000000..2b9a024d3 --- /dev/null +++ b/vendor/github.com/koding/cache/memory_ttl.go @@ -0,0 +1,111 @@ +package cache + +import ( + "sync" + "time" +) + +var zeroTTL = time.Duration(0) + +// MemoryTTL holds the required variables to compose an in memory cache system +// which also provides expiring key mechanism +type MemoryTTL struct { + // Mutex is used for handling the concurrent + // read/write requests for cache + sync.Mutex + + // cache holds the cache data + cache *MemoryNoTS + + // setAts holds the time that related item's set at + setAts map[string]time.Time + + // ttl is a duration for a cache key to expire + ttl time.Duration + + // gcInterval is a duration for garbage collection + gcInterval time.Duration +} + +// NewMemoryWithTTL creates an inmemory cache system +// Which everytime will return the true values about a cache hit +// and never will leak memory +// ttl is used for expiration of a key from cache +func NewMemoryWithTTL(ttl time.Duration) *MemoryTTL { + return &MemoryTTL{ + cache: NewMemoryNoTS(), + setAts: map[string]time.Time{}, + ttl: ttl, + } +} + +// StartGC starts the garbage collection process in a go routine +func (r *MemoryTTL) StartGC(gcInterval time.Duration) { + r.gcInterval = gcInterval + go func() { + for _ = range time.Tick(gcInterval) { + for key := range r.cache.items { + if !r.isValid(key) { + r.Delete(key) + } + } + } + }() +} + +// Get returns a value of a given key if it exists +// and valid for the time being +func (r *MemoryTTL) Get(key string) (interface{}, error) { + r.Lock() + defer r.Unlock() + + if !r.isValid(key) { + r.delete(key) + return nil, ErrNotFound + } + + value, err := r.cache.Get(key) + if err != nil { + return nil, err + } + + return value, nil +} + +// Set will persist a value to the cache or +// override existing one with the new one +func (r *MemoryTTL) Set(key string, value interface{}) error { + r.Lock() + defer r.Unlock() + + r.cache.Set(key, value) + r.setAts[key] = time.Now() + return nil +} + +// Delete deletes a given key if exists +func (r *MemoryTTL) Delete(key string) error { + r.Lock() + defer r.Unlock() + + r.delete(key) + return nil +} + +func (r *MemoryTTL) delete(key string) { + r.cache.Delete(key) + delete(r.setAts, key) +} + +func (r *MemoryTTL) isValid(key string) bool { + setAt, ok := r.setAts[key] + if !ok { + return false + } + + if r.ttl == zeroTTL { + return true + } + + return setAt.Add(r.ttl).After(time.Now()) +} diff --git a/vendor/github.com/koding/cache/memory_ttl_test.go b/vendor/github.com/koding/cache/memory_ttl_test.go new file mode 100644 index 000000000..f19150d48 --- /dev/null +++ b/vendor/github.com/koding/cache/memory_ttl_test.go @@ -0,0 +1,43 @@ +package cache + +import ( + "testing" + "time" +) + +func TestMemoryCacheGetSet(t *testing.T) { + cache := NewMemoryWithTTL(2 * time.Second) + cache.StartGC(time.Millisecond * 10) + cache.Set("test_key", "test_data") + data, err := cache.Get("test_key") + if err != nil { + t.Fatal("data not found") + } + if data != "test_data" { + t.Fatal("data is not \"test_data\"") + } +} + +func TestMemoryCacheTTL(t *testing.T) { + cache := NewMemoryWithTTL(100 * time.Millisecond) + cache.StartGC(time.Millisecond * 10) + cache.Set("test_key", "test_data") + time.Sleep(200 * time.Millisecond) + _, err := cache.Get("test_key") + if err == nil { + t.Fatal("data found") + } +} + +func TestMemoryCacheTTLNilValue(t *testing.T) { + cache := NewMemoryWithTTL(100 * time.Millisecond) + cache.StartGC(time.Millisecond * 10) + cache.Set("test_key", nil) + data, err := cache.Get("test_key") + if err != nil { + t.Fatal("data found") + } + if data != nil { + t.Fatal("data is not null") + } +}