Merge remote-tracking branch 'upstream/0.4.0' into 0.4.0

This commit is contained in:
Brad Rydzewski 2015-10-21 16:18:21 -07:00
commit 45ee7853d0
15 changed files with 661 additions and 0 deletions

32
vendor/github.com/koding/cache/README.md generated vendored Normal file
View file

@ -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")
```

15
vendor/github.com/koding/cache/cache.go generated vendored Normal file
View file

@ -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
}

9
vendor/github.com/koding/cache/doc.go generated vendored Normal file
View file

@ -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

8
vendor/github.com/koding/cache/errors.go generated vendored Normal file
View file

@ -0,0 +1,8 @@
package cache
import "errors"
var (
// ErrNotFound holds exported `not found error` for not found items
ErrNotFound = errors.New("not found")
)

83
vendor/github.com/koding/cache/helper_test.go generated vendored Normal file
View file

@ -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")
}
}

51
vendor/github.com/koding/cache/lru.go generated vendored Normal file
View file

@ -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)
}

122
vendor/github.com/koding/cache/lru_nots.go generated vendored Normal file
View file

@ -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)
}

33
vendor/github.com/koding/cache/lru_nots_test.go generated vendored Normal file
View file

@ -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)
}

33
vendor/github.com/koding/cache/lru_test.go generated vendored Normal file
View file

@ -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)
}

46
vendor/github.com/koding/cache/memory.go generated vendored Normal file
View file

@ -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)
}

39
vendor/github.com/koding/cache/memory_nots.go generated vendored Normal file
View file

@ -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
}

18
vendor/github.com/koding/cache/memory_nots_test.go generated vendored Normal file
View file

@ -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)
}

18
vendor/github.com/koding/cache/memory_test.go generated vendored Normal file
View file

@ -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)
}

111
vendor/github.com/koding/cache/memory_ttl.go generated vendored Normal file
View file

@ -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())
}

43
vendor/github.com/koding/cache/memory_ttl_test.go generated vendored Normal file
View file

@ -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")
}
}