init
This commit is contained in:
33
vendor/github.com/bluele/gcache/BUILD.bazel
generated
vendored
Normal file
33
vendor/github.com/bluele/gcache/BUILD.bazel
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"arc.go",
|
||||
"cache.go",
|
||||
"clock.go",
|
||||
"lfu.go",
|
||||
"lru.go",
|
||||
"simple.go",
|
||||
"singleflight.go",
|
||||
"stats.go",
|
||||
"utils.go",
|
||||
],
|
||||
importmap = "go-common/vendor/github.com/bluele/gcache",
|
||||
importpath = "github.com/bluele/gcache",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
21
vendor/github.com/bluele/gcache/LICENSE
generated
vendored
Normal file
21
vendor/github.com/bluele/gcache/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Jun Kimura
|
||||
|
||||
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.
|
319
vendor/github.com/bluele/gcache/README.md
generated
vendored
Normal file
319
vendor/github.com/bluele/gcache/README.md
generated
vendored
Normal file
@ -0,0 +1,319 @@
|
||||
# GCache
|
||||
|
||||
[](https://app.wercker.com/project/bykey/1471b6c9cbc9ebbd15f8f9fe8f71ac67)[](https://godoc.org/github.com/bluele/gcache)
|
||||
|
||||
Cache library for golang. It supports expirable Cache, LFU, LRU and ARC.
|
||||
|
||||
## Features
|
||||
|
||||
* Supports expirable Cache, LFU, LRU and ARC.
|
||||
|
||||
* Goroutine safe.
|
||||
|
||||
* Supports event handlers which evict, purge, and add entry. (Optional)
|
||||
|
||||
* Automatically load cache if it doesn't exists. (Optional)
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
$ go get github.com/bluele/gcache
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
### Manually set a key-value pair.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/bluele/gcache"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gc := gcache.New(20).
|
||||
LRU().
|
||||
Build()
|
||||
gc.Set("key", "ok")
|
||||
value, err := gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Get:", value)
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
Get: ok
|
||||
```
|
||||
|
||||
### Manually set a key-value pair, with an expiration time.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/bluele/gcache"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gc := gcache.New(20).
|
||||
LRU().
|
||||
Build()
|
||||
gc.SetWithExpire("key", "ok", time.Second*10)
|
||||
value, _ := gc.Get("key")
|
||||
fmt.Println("Get:", value)
|
||||
|
||||
// Wait for value to expire
|
||||
time.Sleep(time.Second*10)
|
||||
|
||||
value, err = gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Get:", value)
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
Get: ok
|
||||
// 10 seconds later, new attempt:
|
||||
panic: ErrKeyNotFound
|
||||
```
|
||||
|
||||
|
||||
### Automatically load value
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/bluele/gcache"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gc := gcache.New(20).
|
||||
LRU().
|
||||
LoaderFunc(func(key interface{}) (interface{}, error) {
|
||||
return "ok", nil
|
||||
}).
|
||||
Build()
|
||||
value, err := gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Get:", value)
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
Get: ok
|
||||
```
|
||||
|
||||
### Automatically load value with expiration
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/bluele/gcache"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var evictCounter, loaderCounter, purgeCounter int
|
||||
gc := gcache.New(20).
|
||||
LRU().
|
||||
LoaderExpireFunc(func(key interface{}) (interface{}, *time.Duration, error) {
|
||||
loaderCounter++
|
||||
expire := 1 * time.Second
|
||||
return "ok", &expire, nil
|
||||
}).
|
||||
EvictedFunc(func(key, value interface{}) {
|
||||
evictCounter++
|
||||
fmt.Println("evicted key:", key)
|
||||
}).
|
||||
PurgeVisitorFunc(func(key, value interface{}) {
|
||||
purgeCounter++
|
||||
fmt.Println("purged key:", key)
|
||||
}).
|
||||
Build()
|
||||
value, err := gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Get:", value)
|
||||
time.Sleep(1 * time.Second)
|
||||
value, err = gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("Get:", value)
|
||||
gc.Purge()
|
||||
if loaderCounter != evictCounter+purgeCounter {
|
||||
panic("bad")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
Get: ok
|
||||
evicted key: key
|
||||
Get: ok
|
||||
purged key: key
|
||||
```
|
||||
|
||||
|
||||
## Cache Algorithm
|
||||
|
||||
* Least-Frequently Used (LFU)
|
||||
|
||||
Discards the least frequently used items first.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// size: 10
|
||||
gc := gcache.New(10).
|
||||
LFU().
|
||||
Build()
|
||||
gc.Set("key", "value")
|
||||
}
|
||||
```
|
||||
|
||||
* Least Recently Used (LRU)
|
||||
|
||||
Discards the least recently used items first.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// size: 10
|
||||
gc := gcache.New(10).
|
||||
LRU().
|
||||
Build()
|
||||
gc.Set("key", "value")
|
||||
}
|
||||
```
|
||||
|
||||
* Adaptive Replacement Cache (ARC)
|
||||
|
||||
Constantly balances between LRU and LFU, to improve the combined result.
|
||||
|
||||
detail: http://en.wikipedia.org/wiki/Adaptive_replacement_cache
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// size: 10
|
||||
gc := gcache.New(10).
|
||||
ARC().
|
||||
Build()
|
||||
gc.Set("key", "value")
|
||||
}
|
||||
```
|
||||
|
||||
* SimpleCache (Default)
|
||||
|
||||
SimpleCache has no clear priority for evict cache. It depends on key-value map order.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// size: 10
|
||||
gc := gcache.New(10).Build()
|
||||
gc.Set("key", "value")
|
||||
v, err := gc.Get("key")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Loading Cache
|
||||
|
||||
If specified `LoaderFunc`, values are automatically loaded by the cache, and are stored in the cache until either evicted or manually invalidated.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
gc := gcache.New(10).
|
||||
LRU().
|
||||
LoaderFunc(func(key interface{}) (interface{}, error) {
|
||||
return "value", nil
|
||||
}).
|
||||
Build()
|
||||
v, _ := gc.Get("key")
|
||||
// output: "value"
|
||||
fmt.Println(v)
|
||||
}
|
||||
```
|
||||
|
||||
GCache coordinates cache fills such that only one load in one process of an entire replicated set of processes populates the cache, then multiplexes the loaded value to all callers.
|
||||
|
||||
## Expirable cache
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// LRU cache, size: 10, expiration: after a hour
|
||||
gc := gcache.New(10).
|
||||
LRU().
|
||||
Expiration(time.Hour).
|
||||
Build()
|
||||
}
|
||||
```
|
||||
|
||||
## Event handlers
|
||||
|
||||
### Evicted handler
|
||||
|
||||
Event handler for evict the entry.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
gc := gcache.New(2).
|
||||
EvictedFunc(func(key, value interface{}) {
|
||||
fmt.Println("evicted key:", key)
|
||||
}).
|
||||
Build()
|
||||
for i := 0; i < 3; i++ {
|
||||
gc.Set(i, i*i)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
evicted key: 0
|
||||
```
|
||||
|
||||
### Added handler
|
||||
|
||||
Event handler for add the entry.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
gc := gcache.New(2).
|
||||
AddedFunc(func(key, value interface{}) {
|
||||
fmt.Println("added key:", key)
|
||||
}).
|
||||
Build()
|
||||
for i := 0; i < 3; i++ {
|
||||
gc.Set(i, i*i)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
added key: 0
|
||||
added key: 1
|
||||
added key: 2
|
||||
```
|
||||
|
||||
# Author
|
||||
|
||||
**Jun Kimura**
|
||||
|
||||
* <http://github.com/bluele>
|
||||
* <junkxdev@gmail.com>
|
418
vendor/github.com/bluele/gcache/arc.go
generated
vendored
Normal file
418
vendor/github.com/bluele/gcache/arc.go
generated
vendored
Normal file
@ -0,0 +1,418 @@
|
||||
package gcache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Constantly balances between LRU and LFU, to improve the combined result.
|
||||
type ARC struct {
|
||||
baseCache
|
||||
items map[interface{}]*arcItem
|
||||
|
||||
part int
|
||||
t1 *arcList
|
||||
t2 *arcList
|
||||
b1 *arcList
|
||||
b2 *arcList
|
||||
}
|
||||
|
||||
func newARC(cb *CacheBuilder) *ARC {
|
||||
c := &ARC{}
|
||||
buildCache(&c.baseCache, cb)
|
||||
|
||||
c.init()
|
||||
c.loadGroup.cache = c
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ARC) init() {
|
||||
c.items = make(map[interface{}]*arcItem)
|
||||
c.t1 = newARCList()
|
||||
c.t2 = newARCList()
|
||||
c.b1 = newARCList()
|
||||
c.b2 = newARCList()
|
||||
}
|
||||
|
||||
func (c *ARC) replace(key interface{}) {
|
||||
var old interface{}
|
||||
if (c.t1.Len() > 0 && c.b2.Has(key) && c.t1.Len() == c.part) || (c.t1.Len() > c.part) {
|
||||
old = c.t1.RemoveTail()
|
||||
c.b1.PushFront(old)
|
||||
} else if c.t2.l.Len() > 0 {
|
||||
old = c.t2.RemoveTail()
|
||||
c.b2.PushFront(old)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
item, ok := c.items[old]
|
||||
if ok {
|
||||
delete(c.items, old)
|
||||
if c.evictedFunc != nil {
|
||||
c.evictedFunc(item.key, item.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ARC) Set(key, value interface{}) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
_, err := c.set(key, value)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set a new key-value pair with an expiration time
|
||||
func (c *ARC) SetWithExpire(key, value interface{}, expiration time.Duration) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
item, err := c.set(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := c.clock.Now().Add(expiration)
|
||||
item.(*arcItem).expiration = &t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ARC) set(key, value interface{}) (interface{}, error) {
|
||||
var err error
|
||||
if c.serializeFunc != nil {
|
||||
value, err = c.serializeFunc(key, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
item, ok := c.items[key]
|
||||
if ok {
|
||||
item.value = value
|
||||
} else {
|
||||
item = &arcItem{
|
||||
clock: c.clock,
|
||||
key: key,
|
||||
value: value,
|
||||
}
|
||||
c.items[key] = item
|
||||
}
|
||||
|
||||
if c.expiration != nil {
|
||||
t := c.clock.Now().Add(*c.expiration)
|
||||
item.expiration = &t
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if c.addedFunc != nil {
|
||||
c.addedFunc(key, value)
|
||||
}
|
||||
}()
|
||||
|
||||
if c.t1.Has(key) || c.t2.Has(key) {
|
||||
return item, nil
|
||||
}
|
||||
|
||||
if elt := c.b1.Lookup(key); elt != nil {
|
||||
c.part = minInt(c.size, c.part+maxInt(c.b2.Len()/c.b1.Len(), 1))
|
||||
c.replace(key)
|
||||
c.b1.Remove(key, elt)
|
||||
c.t2.PushFront(key)
|
||||
return item, nil
|
||||
}
|
||||
|
||||
if elt := c.b2.Lookup(key); elt != nil {
|
||||
c.part = maxInt(0, c.part-maxInt(c.b1.Len()/c.b2.Len(), 1))
|
||||
c.replace(key)
|
||||
c.b2.Remove(key, elt)
|
||||
c.t2.PushFront(key)
|
||||
return item, nil
|
||||
}
|
||||
|
||||
if c.t1.Len()+c.b1.Len() == c.size {
|
||||
if c.t1.Len() < c.size {
|
||||
c.b1.RemoveTail()
|
||||
c.replace(key)
|
||||
} else {
|
||||
pop := c.t1.RemoveTail()
|
||||
item, ok := c.items[pop]
|
||||
if ok {
|
||||
delete(c.items, pop)
|
||||
if c.evictedFunc != nil {
|
||||
c.evictedFunc(item.key, item.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
total := c.t1.Len() + c.b1.Len() + c.t2.Len() + c.b2.Len()
|
||||
if total >= c.size {
|
||||
if total == (2 * c.size) {
|
||||
c.b2.RemoveTail()
|
||||
}
|
||||
c.replace(key)
|
||||
}
|
||||
}
|
||||
c.t1.PushFront(key)
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// Get a value from cache pool using key if it exists. If not exists and it has LoaderFunc, it will generate the value using you have specified LoaderFunc method returns value.
|
||||
func (c *ARC) Get(key interface{}) (interface{}, error) {
|
||||
v, err := c.get(key, false)
|
||||
if err == KeyNotFoundError {
|
||||
return c.getWithLoader(key, true)
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Get a value from cache pool using key if it exists.
|
||||
// If it dose not exists key, returns KeyNotFoundError.
|
||||
// And send a request which refresh value for specified key if cache object has LoaderFunc.
|
||||
func (c *ARC) GetIFPresent(key interface{}) (interface{}, error) {
|
||||
v, err := c.get(key, false)
|
||||
if err == KeyNotFoundError {
|
||||
return c.getWithLoader(key, false)
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (c *ARC) get(key interface{}, onLoad bool) (interface{}, error) {
|
||||
v, err := c.getValue(key, onLoad)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.deserializeFunc != nil {
|
||||
return c.deserializeFunc(key, v)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (c *ARC) getValue(key interface{}, onLoad bool) (interface{}, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if elt := c.t1.Lookup(key); elt != nil {
|
||||
c.t1.Remove(key, elt)
|
||||
item := c.items[key]
|
||||
if !item.IsExpired(nil) {
|
||||
c.t2.PushFront(key)
|
||||
if !onLoad {
|
||||
c.stats.IncrHitCount()
|
||||
}
|
||||
return item.value, nil
|
||||
} else {
|
||||
delete(c.items, key)
|
||||
c.b1.PushFront(key)
|
||||
if c.evictedFunc != nil {
|
||||
c.evictedFunc(item.key, item.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
if elt := c.t2.Lookup(key); elt != nil {
|
||||
item := c.items[key]
|
||||
if !item.IsExpired(nil) {
|
||||
c.t2.MoveToFront(elt)
|
||||
if !onLoad {
|
||||
c.stats.IncrHitCount()
|
||||
}
|
||||
return item.value, nil
|
||||
} else {
|
||||
delete(c.items, key)
|
||||
c.t2.Remove(key, elt)
|
||||
c.b2.PushFront(key)
|
||||
if c.evictedFunc != nil {
|
||||
c.evictedFunc(item.key, item.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !onLoad {
|
||||
c.stats.IncrMissCount()
|
||||
}
|
||||
return nil, KeyNotFoundError
|
||||
}
|
||||
|
||||
func (c *ARC) getWithLoader(key interface{}, isWait bool) (interface{}, error) {
|
||||
if c.loaderExpireFunc == nil {
|
||||
return nil, KeyNotFoundError
|
||||
}
|
||||
value, _, err := c.load(key, func(v interface{}, expiration *time.Duration, e error) (interface{}, error) {
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
item, err := c.set(key, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if expiration != nil {
|
||||
t := c.clock.Now().Add(*expiration)
|
||||
item.(*arcItem).expiration = &t
|
||||
}
|
||||
return v, nil
|
||||
}, isWait)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// Remove removes the provided key from the cache.
|
||||
func (c *ARC) Remove(key interface{}) bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.remove(key)
|
||||
}
|
||||
|
||||
func (c *ARC) remove(key interface{}) bool {
|
||||
if elt := c.t1.Lookup(key); elt != nil {
|
||||
c.t1.Remove(key, elt)
|
||||
item := c.items[key]
|
||||
delete(c.items, key)
|
||||
c.b1.PushFront(key)
|
||||
if c.evictedFunc != nil {
|
||||
c.evictedFunc(key, item.value)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if elt := c.t2.Lookup(key); elt != nil {
|
||||
c.t2.Remove(key, elt)
|
||||
item := c.items[key]
|
||||
delete(c.items, key)
|
||||
c.b2.PushFront(key)
|
||||
if c.evictedFunc != nil {
|
||||
c.evictedFunc(key, item.value)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *ARC) keys() []interface{} {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
keys := make([]interface{}, len(c.items))
|
||||
var i = 0
|
||||
for k := range c.items {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Keys returns a slice of the keys in the cache.
|
||||
func (c *ARC) Keys() []interface{} {
|
||||
keys := []interface{}{}
|
||||
for _, k := range c.keys() {
|
||||
_, err := c.GetIFPresent(k)
|
||||
if err == nil {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Returns all key-value pairs in the cache.
|
||||
func (c *ARC) GetALL() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{})
|
||||
for _, k := range c.keys() {
|
||||
v, err := c.GetIFPresent(k)
|
||||
if err == nil {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Len returns the number of items in the cache.
|
||||
func (c *ARC) Len() int {
|
||||
return len(c.GetALL())
|
||||
}
|
||||
|
||||
// Purge is used to completely clear the cache
|
||||
func (c *ARC) Purge() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.purgeVisitorFunc != nil {
|
||||
for _, item := range c.items {
|
||||
c.purgeVisitorFunc(item.key, item.value)
|
||||
}
|
||||
}
|
||||
|
||||
c.init()
|
||||
}
|
||||
|
||||
// returns boolean value whether this item is expired or not.
|
||||
func (it *arcItem) IsExpired(now *time.Time) bool {
|
||||
if it.expiration == nil {
|
||||
return false
|
||||
}
|
||||
if now == nil {
|
||||
t := it.clock.Now()
|
||||
now = &t
|
||||
}
|
||||
return it.expiration.Before(*now)
|
||||
}
|
||||
|
||||
type arcList struct {
|
||||
l *list.List
|
||||
keys map[interface{}]*list.Element
|
||||
}
|
||||
|
||||
type arcItem struct {
|
||||
clock Clock
|
||||
key interface{}
|
||||
value interface{}
|
||||
expiration *time.Time
|
||||
}
|
||||
|
||||
func newARCList() *arcList {
|
||||
return &arcList{
|
||||
l: list.New(),
|
||||
keys: make(map[interface{}]*list.Element),
|
||||
}
|
||||
}
|
||||
|
||||
func (al *arcList) Has(key interface{}) bool {
|
||||
_, ok := al.keys[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (al *arcList) Lookup(key interface{}) *list.Element {
|
||||
elt := al.keys[key]
|
||||
return elt
|
||||
}
|
||||
|
||||
func (al *arcList) MoveToFront(elt *list.Element) {
|
||||
al.l.MoveToFront(elt)
|
||||
}
|
||||
|
||||
func (al *arcList) PushFront(key interface{}) {
|
||||
if elt, ok := al.keys[key]; ok {
|
||||
al.l.MoveToFront(elt)
|
||||
return
|
||||
}
|
||||
elt := al.l.PushFront(key)
|
||||
al.keys[key] = elt
|
||||
}
|
||||
|
||||
func (al *arcList) Remove(key interface{}, elt *list.Element) {
|
||||
delete(al.keys, key)
|
||||
al.l.Remove(elt)
|
||||
}
|
||||
|
||||
func (al *arcList) RemoveTail() interface{} {
|
||||
elt := al.l.Back()
|
||||
al.l.Remove(elt)
|
||||
|
||||
key := elt.Value
|
||||
delete(al.keys, key)
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
func (al *arcList) Len() int {
|
||||
return al.l.Len()
|
||||
}
|
204
vendor/github.com/bluele/gcache/cache.go
generated
vendored
Normal file
204
vendor/github.com/bluele/gcache/cache.go
generated
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
package gcache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
TYPE_SIMPLE = "simple"
|
||||
TYPE_LRU = "lru"
|
||||
TYPE_LFU = "lfu"
|
||||
TYPE_ARC = "arc"
|
||||
)
|
||||
|
||||
var KeyNotFoundError = errors.New("Key not found.")
|
||||
|
||||
type Cache interface {
|
||||
Set(interface{}, interface{}) error
|
||||
SetWithExpire(interface{}, interface{}, time.Duration) error
|
||||
Get(interface{}) (interface{}, error)
|
||||
GetIFPresent(interface{}) (interface{}, error)
|
||||
GetALL() map[interface{}]interface{}
|
||||
get(interface{}, bool) (interface{}, error)
|
||||
Remove(interface{}) bool
|
||||
Purge()
|
||||
Keys() []interface{}
|
||||
Len() int
|
||||
|
||||
statsAccessor
|
||||
}
|
||||
|
||||
type baseCache struct {
|
||||
clock Clock
|
||||
size int
|
||||
loaderExpireFunc LoaderExpireFunc
|
||||
evictedFunc EvictedFunc
|
||||
purgeVisitorFunc PurgeVisitorFunc
|
||||
addedFunc AddedFunc
|
||||
deserializeFunc DeserializeFunc
|
||||
serializeFunc SerializeFunc
|
||||
expiration *time.Duration
|
||||
mu sync.RWMutex
|
||||
loadGroup Group
|
||||
*stats
|
||||
}
|
||||
|
||||
type (
|
||||
LoaderFunc func(interface{}) (interface{}, error)
|
||||
LoaderExpireFunc func(interface{}) (interface{}, *time.Duration, error)
|
||||
EvictedFunc func(interface{}, interface{})
|
||||
PurgeVisitorFunc func(interface{}, interface{})
|
||||
AddedFunc func(interface{}, interface{})
|
||||
DeserializeFunc func(interface{}, interface{}) (interface{}, error)
|
||||
SerializeFunc func(interface{}, interface{}) (interface{}, error)
|
||||
)
|
||||
|
||||
type CacheBuilder struct {
|
||||
clock Clock
|
||||
tp string
|
||||
size int
|
||||
loaderExpireFunc LoaderExpireFunc
|
||||
evictedFunc EvictedFunc
|
||||
purgeVisitorFunc PurgeVisitorFunc
|
||||
addedFunc AddedFunc
|
||||
expiration *time.Duration
|
||||
deserializeFunc DeserializeFunc
|
||||
serializeFunc SerializeFunc
|
||||
}
|
||||
|
||||
func New(size int) *CacheBuilder {
|
||||
return &CacheBuilder{
|
||||
clock: NewRealClock(),
|
||||
tp: TYPE_SIMPLE,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) Clock(clock Clock) *CacheBuilder {
|
||||
cb.clock = clock
|
||||
return cb
|
||||
}
|
||||
|
||||
// Set a loader function.
|
||||
// loaderFunc: create a new value with this function if cached value is expired.
|
||||
func (cb *CacheBuilder) LoaderFunc(loaderFunc LoaderFunc) *CacheBuilder {
|
||||
cb.loaderExpireFunc = func(k interface{}) (interface{}, *time.Duration, error) {
|
||||
v, err := loaderFunc(k)
|
||||
return v, nil, err
|
||||
}
|
||||
return cb
|
||||
}
|
||||
|
||||
// Set a loader function with expiration.
|
||||
// loaderExpireFunc: create a new value with this function if cached value is expired.
|
||||
// If nil returned instead of time.Duration from loaderExpireFunc than value will never expire.
|
||||
func (cb *CacheBuilder) LoaderExpireFunc(loaderExpireFunc LoaderExpireFunc) *CacheBuilder {
|
||||
cb.loaderExpireFunc = loaderExpireFunc
|
||||
return cb
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) EvictType(tp string) *CacheBuilder {
|
||||
cb.tp = tp
|
||||
return cb
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) Simple() *CacheBuilder {
|
||||
return cb.EvictType(TYPE_SIMPLE)
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) LRU() *CacheBuilder {
|
||||
return cb.EvictType(TYPE_LRU)
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) LFU() *CacheBuilder {
|
||||
return cb.EvictType(TYPE_LFU)
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) ARC() *CacheBuilder {
|
||||
return cb.EvictType(TYPE_ARC)
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) EvictedFunc(evictedFunc EvictedFunc) *CacheBuilder {
|
||||
cb.evictedFunc = evictedFunc
|
||||
return cb
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) PurgeVisitorFunc(purgeVisitorFunc PurgeVisitorFunc) *CacheBuilder {
|
||||
cb.purgeVisitorFunc = purgeVisitorFunc
|
||||
return cb
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) AddedFunc(addedFunc AddedFunc) *CacheBuilder {
|
||||
cb.addedFunc = addedFunc
|
||||
return cb
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) DeserializeFunc(deserializeFunc DeserializeFunc) *CacheBuilder {
|
||||
cb.deserializeFunc = deserializeFunc
|
||||
return cb
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) SerializeFunc(serializeFunc SerializeFunc) *CacheBuilder {
|
||||
cb.serializeFunc = serializeFunc
|
||||
return cb
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) Expiration(expiration time.Duration) *CacheBuilder {
|
||||
cb.expiration = &expiration
|
||||
return cb
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) Build() Cache {
|
||||
if cb.size <= 0 && cb.tp != TYPE_SIMPLE {
|
||||
panic("gcache: Cache size <= 0")
|
||||
}
|
||||
|
||||
return cb.build()
|
||||
}
|
||||
|
||||
func (cb *CacheBuilder) build() Cache {
|
||||
switch cb.tp {
|
||||
case TYPE_SIMPLE:
|
||||
return newSimpleCache(cb)
|
||||
case TYPE_LRU:
|
||||
return newLRUCache(cb)
|
||||
case TYPE_LFU:
|
||||
return newLFUCache(cb)
|
||||
case TYPE_ARC:
|
||||
return newARC(cb)
|
||||
default:
|
||||
panic("gcache: Unknown type " + cb.tp)
|
||||
}
|
||||
}
|
||||
|
||||
func buildCache(c *baseCache, cb *CacheBuilder) {
|
||||
c.clock = cb.clock
|
||||
c.size = cb.size
|
||||
c.loaderExpireFunc = cb.loaderExpireFunc
|
||||
c.expiration = cb.expiration
|
||||
c.addedFunc = cb.addedFunc
|
||||
c.deserializeFunc = cb.deserializeFunc
|
||||
c.serializeFunc = cb.serializeFunc
|
||||
c.evictedFunc = cb.evictedFunc
|
||||
c.purgeVisitorFunc = cb.purgeVisitorFunc
|
||||
c.stats = &stats{}
|
||||
}
|
||||
|
||||
// load a new value using by specified key.
|
||||
func (c *baseCache) load(key interface{}, cb func(interface{}, *time.Duration, error) (interface{}, error), isWait bool) (interface{}, bool, error) {
|
||||
v, called, err := c.loadGroup.Do(key, func() (v interface{}, e error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
e = fmt.Errorf("Loader panics: %v", r)
|
||||
}
|
||||
}()
|
||||
return cb(c.loaderExpireFunc(key))
|
||||
}, isWait)
|
||||
if err != nil {
|
||||
return nil, called, err
|
||||
}
|
||||
return v, called, nil
|
||||
}
|
53
vendor/github.com/bluele/gcache/clock.go
generated
vendored
Normal file
53
vendor/github.com/bluele/gcache/clock.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
package gcache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
type RealClock struct{}
|
||||
|
||||
func NewRealClock() Clock {
|
||||
return RealClock{}
|
||||
}
|
||||
|
||||
func (rc RealClock) Now() time.Time {
|
||||
t := time.Now()
|
||||
return t
|
||||
}
|
||||
|
||||
type FakeClock interface {
|
||||
Clock
|
||||
|
||||
Advance(d time.Duration)
|
||||
}
|
||||
|
||||
func NewFakeClock() FakeClock {
|
||||
return &fakeclock{
|
||||
// Taken from github.com/jonboulle/clockwork: use a fixture that does not fulfill Time.IsZero()
|
||||
now: time.Date(1984, time.April, 4, 0, 0, 0, 0, time.UTC),
|
||||
}
|
||||
}
|
||||
|
||||
type fakeclock struct {
|
||||
now time.Time
|
||||
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (fc *fakeclock) Now() time.Time {
|
||||
fc.mutex.RLock()
|
||||
defer fc.mutex.RUnlock()
|
||||
t := fc.now
|
||||
return t
|
||||
}
|
||||
|
||||
func (fc *fakeclock) Advance(d time.Duration) {
|
||||
fc.mutex.Lock()
|
||||
defer fc.mutex.Unlock()
|
||||
fc.now = fc.now.Add(d)
|
||||
}
|
319
vendor/github.com/bluele/gcache/lfu.go
generated
vendored
Normal file
319
vendor/github.com/bluele/gcache/lfu.go
generated
vendored
Normal file
@ -0,0 +1,319 @@
|
||||
package gcache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Discards the least frequently used items first.
|
||||
type LFUCache struct {
|
||||
baseCache
|
||||
items map[interface{}]*lfuItem
|
||||
freqList *list.List // list for freqEntry
|
||||
}
|
||||
|
||||
func newLFUCache(cb *CacheBuilder) *LFUCache {
|
||||
c := &LFUCache{}
|
||||
buildCache(&c.baseCache, cb)
|
||||
|
||||
c.init()
|
||||
c.loadGroup.cache = c
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *LFUCache) init() {
|
||||
c.freqList = list.New()
|
||||
c.items = make(map[interface{}]*lfuItem, c.size+1)
|
||||
c.freqList.PushFront(&freqEntry{
|
||||
freq: 0,
|
||||
items: make(map[*lfuItem]struct{}),
|
||||
})
|
||||
}
|
||||
|
||||
// Set a new key-value pair
|
||||
func (c *LFUCache) Set(key, value interface{}) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
_, err := c.set(key, value)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set a new key-value pair with an expiration time
|
||||
func (c *LFUCache) SetWithExpire(key, value interface{}, expiration time.Duration) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
item, err := c.set(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := c.clock.Now().Add(expiration)
|
||||
item.(*lfuItem).expiration = &t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *LFUCache) set(key, value interface{}) (interface{}, error) {
|
||||
var err error
|
||||
if c.serializeFunc != nil {
|
||||
value, err = c.serializeFunc(key, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check for existing item
|
||||
item, ok := c.items[key]
|
||||
if ok {
|
||||
item.value = value
|
||||
} else {
|
||||
// Verify size not exceeded
|
||||
if len(c.items) >= c.size {
|
||||
c.evict(1)
|
||||
}
|
||||
item = &lfuItem{
|
||||
clock: c.clock,
|
||||
key: key,
|
||||
value: value,
|
||||
freqElement: nil,
|
||||
}
|
||||
el := c.freqList.Front()
|
||||
fe := el.Value.(*freqEntry)
|
||||
fe.items[item] = struct{}{}
|
||||
|
||||
item.freqElement = el
|
||||
c.items[key] = item
|
||||
}
|
||||
|
||||
if c.expiration != nil {
|
||||
t := c.clock.Now().Add(*c.expiration)
|
||||
item.expiration = &t
|
||||
}
|
||||
|
||||
if c.addedFunc != nil {
|
||||
c.addedFunc(key, value)
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// Get a value from cache pool using key if it exists.
|
||||
// If it dose not exists key and has LoaderFunc,
|
||||
// generate a value using `LoaderFunc` method returns value.
|
||||
func (c *LFUCache) Get(key interface{}) (interface{}, error) {
|
||||
v, err := c.get(key, false)
|
||||
if err == KeyNotFoundError {
|
||||
return c.getWithLoader(key, true)
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Get a value from cache pool using key if it exists.
|
||||
// If it dose not exists key, returns KeyNotFoundError.
|
||||
// And send a request which refresh value for specified key if cache object has LoaderFunc.
|
||||
func (c *LFUCache) GetIFPresent(key interface{}) (interface{}, error) {
|
||||
v, err := c.get(key, false)
|
||||
if err == KeyNotFoundError {
|
||||
return c.getWithLoader(key, false)
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (c *LFUCache) get(key interface{}, onLoad bool) (interface{}, error) {
|
||||
v, err := c.getValue(key, onLoad)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.deserializeFunc != nil {
|
||||
return c.deserializeFunc(key, v)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (c *LFUCache) getValue(key interface{}, onLoad bool) (interface{}, error) {
|
||||
c.mu.Lock()
|
||||
item, ok := c.items[key]
|
||||
if ok {
|
||||
if !item.IsExpired(nil) {
|
||||
c.increment(item)
|
||||
v := item.value
|
||||
c.mu.Unlock()
|
||||
if !onLoad {
|
||||
c.stats.IncrHitCount()
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
c.removeItem(item)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
if !onLoad {
|
||||
c.stats.IncrMissCount()
|
||||
}
|
||||
return nil, KeyNotFoundError
|
||||
}
|
||||
|
||||
func (c *LFUCache) getWithLoader(key interface{}, isWait bool) (interface{}, error) {
|
||||
if c.loaderExpireFunc == nil {
|
||||
return nil, KeyNotFoundError
|
||||
}
|
||||
value, _, err := c.load(key, func(v interface{}, expiration *time.Duration, e error) (interface{}, error) {
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
item, err := c.set(key, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if expiration != nil {
|
||||
t := c.clock.Now().Add(*expiration)
|
||||
item.(*lfuItem).expiration = &t
|
||||
}
|
||||
return v, nil
|
||||
}, isWait)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (c *LFUCache) increment(item *lfuItem) {
|
||||
currentFreqElement := item.freqElement
|
||||
currentFreqEntry := currentFreqElement.Value.(*freqEntry)
|
||||
nextFreq := currentFreqEntry.freq + 1
|
||||
delete(currentFreqEntry.items, item)
|
||||
|
||||
nextFreqElement := currentFreqElement.Next()
|
||||
if nextFreqElement == nil {
|
||||
nextFreqElement = c.freqList.InsertAfter(&freqEntry{
|
||||
freq: nextFreq,
|
||||
items: make(map[*lfuItem]struct{}),
|
||||
}, currentFreqElement)
|
||||
}
|
||||
nextFreqElement.Value.(*freqEntry).items[item] = struct{}{}
|
||||
item.freqElement = nextFreqElement
|
||||
}
|
||||
|
||||
// evict removes the least frequence item from the cache.
|
||||
func (c *LFUCache) evict(count int) {
|
||||
entry := c.freqList.Front()
|
||||
for i := 0; i < count; {
|
||||
if entry == nil {
|
||||
return
|
||||
} else {
|
||||
for item, _ := range entry.Value.(*freqEntry).items {
|
||||
if i >= count {
|
||||
return
|
||||
}
|
||||
c.removeItem(item)
|
||||
i++
|
||||
}
|
||||
entry = entry.Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removes the provided key from the cache.
|
||||
func (c *LFUCache) Remove(key interface{}) bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.remove(key)
|
||||
}
|
||||
|
||||
func (c *LFUCache) remove(key interface{}) bool {
|
||||
if item, ok := c.items[key]; ok {
|
||||
c.removeItem(item)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// removeElement is used to remove a given list element from the cache
|
||||
func (c *LFUCache) removeItem(item *lfuItem) {
|
||||
delete(c.items, item.key)
|
||||
delete(item.freqElement.Value.(*freqEntry).items, item)
|
||||
if c.evictedFunc != nil {
|
||||
c.evictedFunc(item.key, item.value)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LFUCache) keys() []interface{} {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
keys := make([]interface{}, len(c.items))
|
||||
var i = 0
|
||||
for k := range c.items {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Returns a slice of the keys in the cache.
|
||||
func (c *LFUCache) Keys() []interface{} {
|
||||
keys := []interface{}{}
|
||||
for _, k := range c.keys() {
|
||||
_, err := c.GetIFPresent(k)
|
||||
if err == nil {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Returns all key-value pairs in the cache.
|
||||
func (c *LFUCache) GetALL() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{})
|
||||
for _, k := range c.keys() {
|
||||
v, err := c.GetIFPresent(k)
|
||||
if err == nil {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Returns the number of items in the cache.
|
||||
func (c *LFUCache) Len() int {
|
||||
return len(c.GetALL())
|
||||
}
|
||||
|
||||
// Completely clear the cache
|
||||
func (c *LFUCache) Purge() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.purgeVisitorFunc != nil {
|
||||
for key, item := range c.items {
|
||||
c.purgeVisitorFunc(key, item.value)
|
||||
}
|
||||
}
|
||||
|
||||
c.init()
|
||||
}
|
||||
|
||||
type freqEntry struct {
|
||||
freq uint
|
||||
items map[*lfuItem]struct{}
|
||||
}
|
||||
|
||||
type lfuItem struct {
|
||||
clock Clock
|
||||
key interface{}
|
||||
value interface{}
|
||||
freqElement *list.Element
|
||||
expiration *time.Time
|
||||
}
|
||||
|
||||
// returns boolean value whether this item is expired or not.
|
||||
func (it *lfuItem) IsExpired(now *time.Time) bool {
|
||||
if it.expiration == nil {
|
||||
return false
|
||||
}
|
||||
if now == nil {
|
||||
t := it.clock.Now()
|
||||
now = &t
|
||||
}
|
||||
return it.expiration.Before(*now)
|
||||
}
|
285
vendor/github.com/bluele/gcache/lru.go
generated
vendored
Normal file
285
vendor/github.com/bluele/gcache/lru.go
generated
vendored
Normal file
@ -0,0 +1,285 @@
|
||||
package gcache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Discards the least recently used items first.
|
||||
type LRUCache struct {
|
||||
baseCache
|
||||
items map[interface{}]*list.Element
|
||||
evictList *list.List
|
||||
}
|
||||
|
||||
func newLRUCache(cb *CacheBuilder) *LRUCache {
|
||||
c := &LRUCache{}
|
||||
buildCache(&c.baseCache, cb)
|
||||
|
||||
c.init()
|
||||
c.loadGroup.cache = c
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *LRUCache) init() {
|
||||
c.evictList = list.New()
|
||||
c.items = make(map[interface{}]*list.Element, c.size+1)
|
||||
}
|
||||
|
||||
func (c *LRUCache) set(key, value interface{}) (interface{}, error) {
|
||||
var err error
|
||||
if c.serializeFunc != nil {
|
||||
value, err = c.serializeFunc(key, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check for existing item
|
||||
var item *lruItem
|
||||
if it, ok := c.items[key]; ok {
|
||||
c.evictList.MoveToFront(it)
|
||||
item = it.Value.(*lruItem)
|
||||
item.value = value
|
||||
} else {
|
||||
// Verify size not exceeded
|
||||
if c.evictList.Len() >= c.size {
|
||||
c.evict(1)
|
||||
}
|
||||
item = &lruItem{
|
||||
clock: c.clock,
|
||||
key: key,
|
||||
value: value,
|
||||
}
|
||||
c.items[key] = c.evictList.PushFront(item)
|
||||
}
|
||||
|
||||
if c.expiration != nil {
|
||||
t := c.clock.Now().Add(*c.expiration)
|
||||
item.expiration = &t
|
||||
}
|
||||
|
||||
if c.addedFunc != nil {
|
||||
c.addedFunc(key, value)
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// set a new key-value pair
|
||||
func (c *LRUCache) Set(key, value interface{}) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
_, err := c.set(key, value)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set a new key-value pair with an expiration time
|
||||
func (c *LRUCache) SetWithExpire(key, value interface{}, expiration time.Duration) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
item, err := c.set(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := c.clock.Now().Add(expiration)
|
||||
item.(*lruItem).expiration = &t
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get a value from cache pool using key if it exists.
|
||||
// If it dose not exists key and has LoaderFunc,
|
||||
// generate a value using `LoaderFunc` method returns value.
|
||||
func (c *LRUCache) Get(key interface{}) (interface{}, error) {
|
||||
v, err := c.get(key, false)
|
||||
if err == KeyNotFoundError {
|
||||
return c.getWithLoader(key, true)
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Get a value from cache pool using key if it exists.
|
||||
// If it dose not exists key, returns KeyNotFoundError.
|
||||
// And send a request which refresh value for specified key if cache object has LoaderFunc.
|
||||
func (c *LRUCache) GetIFPresent(key interface{}) (interface{}, error) {
|
||||
v, err := c.get(key, false)
|
||||
if err == KeyNotFoundError {
|
||||
return c.getWithLoader(key, false)
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (c *LRUCache) get(key interface{}, onLoad bool) (interface{}, error) {
|
||||
v, err := c.getValue(key, onLoad)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.deserializeFunc != nil {
|
||||
return c.deserializeFunc(key, v)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (c *LRUCache) getValue(key interface{}, onLoad bool) (interface{}, error) {
|
||||
c.mu.Lock()
|
||||
item, ok := c.items[key]
|
||||
if ok {
|
||||
it := item.Value.(*lruItem)
|
||||
if !it.IsExpired(nil) {
|
||||
c.evictList.MoveToFront(item)
|
||||
v := it.value
|
||||
c.mu.Unlock()
|
||||
if !onLoad {
|
||||
c.stats.IncrHitCount()
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
c.removeElement(item)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
if !onLoad {
|
||||
c.stats.IncrMissCount()
|
||||
}
|
||||
return nil, KeyNotFoundError
|
||||
}
|
||||
|
||||
func (c *LRUCache) getWithLoader(key interface{}, isWait bool) (interface{}, error) {
|
||||
if c.loaderExpireFunc == nil {
|
||||
return nil, KeyNotFoundError
|
||||
}
|
||||
value, _, err := c.load(key, func(v interface{}, expiration *time.Duration, e error) (interface{}, error) {
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
item, err := c.set(key, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if expiration != nil {
|
||||
t := c.clock.Now().Add(*expiration)
|
||||
item.(*lruItem).expiration = &t
|
||||
}
|
||||
return v, nil
|
||||
}, isWait)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// evict removes the oldest item from the cache.
|
||||
func (c *LRUCache) evict(count int) {
|
||||
for i := 0; i < count; i++ {
|
||||
ent := c.evictList.Back()
|
||||
if ent == nil {
|
||||
return
|
||||
} else {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removes the provided key from the cache.
|
||||
func (c *LRUCache) Remove(key interface{}) bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.remove(key)
|
||||
}
|
||||
|
||||
func (c *LRUCache) remove(key interface{}) bool {
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.removeElement(ent)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *LRUCache) removeElement(e *list.Element) {
|
||||
c.evictList.Remove(e)
|
||||
entry := e.Value.(*lruItem)
|
||||
delete(c.items, entry.key)
|
||||
if c.evictedFunc != nil {
|
||||
entry := e.Value.(*lruItem)
|
||||
c.evictedFunc(entry.key, entry.value)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LRUCache) keys() []interface{} {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
keys := make([]interface{}, len(c.items))
|
||||
var i = 0
|
||||
for k := range c.items {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Returns a slice of the keys in the cache.
|
||||
func (c *LRUCache) Keys() []interface{} {
|
||||
keys := []interface{}{}
|
||||
for _, k := range c.keys() {
|
||||
_, err := c.GetIFPresent(k)
|
||||
if err == nil {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Returns all key-value pairs in the cache.
|
||||
func (c *LRUCache) GetALL() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{})
|
||||
for _, k := range c.keys() {
|
||||
v, err := c.GetIFPresent(k)
|
||||
if err == nil {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Returns the number of items in the cache.
|
||||
func (c *LRUCache) Len() int {
|
||||
return len(c.GetALL())
|
||||
}
|
||||
|
||||
// Completely clear the cache
|
||||
func (c *LRUCache) Purge() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.purgeVisitorFunc != nil {
|
||||
for key, item := range c.items {
|
||||
it := item.Value.(*lruItem)
|
||||
v := it.value
|
||||
c.purgeVisitorFunc(key, v)
|
||||
}
|
||||
}
|
||||
|
||||
c.init()
|
||||
}
|
||||
|
||||
type lruItem struct {
|
||||
clock Clock
|
||||
key interface{}
|
||||
value interface{}
|
||||
expiration *time.Time
|
||||
}
|
||||
|
||||
// returns boolean value whether this item is expired or not.
|
||||
func (it *lruItem) IsExpired(now *time.Time) bool {
|
||||
if it.expiration == nil {
|
||||
return false
|
||||
}
|
||||
if now == nil {
|
||||
t := it.clock.Now()
|
||||
now = &t
|
||||
}
|
||||
return it.expiration.Before(*now)
|
||||
}
|
273
vendor/github.com/bluele/gcache/simple.go
generated
vendored
Normal file
273
vendor/github.com/bluele/gcache/simple.go
generated
vendored
Normal file
@ -0,0 +1,273 @@
|
||||
package gcache
|
||||
|
||||
import "time"
|
||||
|
||||
// SimpleCache has no clear priority for evict cache. It depends on key-value map order.
|
||||
type SimpleCache struct {
|
||||
baseCache
|
||||
items map[interface{}]*simpleItem
|
||||
}
|
||||
|
||||
func newSimpleCache(cb *CacheBuilder) *SimpleCache {
|
||||
c := &SimpleCache{}
|
||||
buildCache(&c.baseCache, cb)
|
||||
|
||||
c.init()
|
||||
c.loadGroup.cache = c
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *SimpleCache) init() {
|
||||
if c.size <= 0 {
|
||||
c.items = make(map[interface{}]*simpleItem)
|
||||
} else {
|
||||
c.items = make(map[interface{}]*simpleItem, c.size)
|
||||
}
|
||||
}
|
||||
|
||||
// Set a new key-value pair
|
||||
func (c *SimpleCache) Set(key, value interface{}) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
_, err := c.set(key, value)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set a new key-value pair with an expiration time
|
||||
func (c *SimpleCache) SetWithExpire(key, value interface{}, expiration time.Duration) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
item, err := c.set(key, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t := c.clock.Now().Add(expiration)
|
||||
item.(*simpleItem).expiration = &t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *SimpleCache) set(key, value interface{}) (interface{}, error) {
|
||||
var err error
|
||||
if c.serializeFunc != nil {
|
||||
value, err = c.serializeFunc(key, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check for existing item
|
||||
item, ok := c.items[key]
|
||||
if ok {
|
||||
item.value = value
|
||||
} else {
|
||||
// Verify size not exceeded
|
||||
if (len(c.items) >= c.size) && c.size > 0 {
|
||||
c.evict(1)
|
||||
}
|
||||
item = &simpleItem{
|
||||
clock: c.clock,
|
||||
value: value,
|
||||
}
|
||||
c.items[key] = item
|
||||
}
|
||||
|
||||
if c.expiration != nil {
|
||||
t := c.clock.Now().Add(*c.expiration)
|
||||
item.expiration = &t
|
||||
}
|
||||
|
||||
if c.addedFunc != nil {
|
||||
c.addedFunc(key, value)
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// Get a value from cache pool using key if it exists.
|
||||
// If it dose not exists key and has LoaderFunc,
|
||||
// generate a value using `LoaderFunc` method returns value.
|
||||
func (c *SimpleCache) Get(key interface{}) (interface{}, error) {
|
||||
v, err := c.get(key, false)
|
||||
if err == KeyNotFoundError {
|
||||
return c.getWithLoader(key, true)
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Get a value from cache pool using key if it exists.
|
||||
// If it dose not exists key, returns KeyNotFoundError.
|
||||
// And send a request which refresh value for specified key if cache object has LoaderFunc.
|
||||
func (c *SimpleCache) GetIFPresent(key interface{}) (interface{}, error) {
|
||||
v, err := c.get(key, false)
|
||||
if err == KeyNotFoundError {
|
||||
return c.getWithLoader(key, false)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (c *SimpleCache) get(key interface{}, onLoad bool) (interface{}, error) {
|
||||
v, err := c.getValue(key, onLoad)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if c.deserializeFunc != nil {
|
||||
return c.deserializeFunc(key, v)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (c *SimpleCache) getValue(key interface{}, onLoad bool) (interface{}, error) {
|
||||
c.mu.Lock()
|
||||
item, ok := c.items[key]
|
||||
if ok {
|
||||
if !item.IsExpired(nil) {
|
||||
v := item.value
|
||||
c.mu.Unlock()
|
||||
if !onLoad {
|
||||
c.stats.IncrHitCount()
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
c.remove(key)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
if !onLoad {
|
||||
c.stats.IncrMissCount()
|
||||
}
|
||||
return nil, KeyNotFoundError
|
||||
}
|
||||
|
||||
func (c *SimpleCache) getWithLoader(key interface{}, isWait bool) (interface{}, error) {
|
||||
if c.loaderExpireFunc == nil {
|
||||
return nil, KeyNotFoundError
|
||||
}
|
||||
value, _, err := c.load(key, func(v interface{}, expiration *time.Duration, e error) (interface{}, error) {
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
item, err := c.set(key, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if expiration != nil {
|
||||
t := c.clock.Now().Add(*expiration)
|
||||
item.(*simpleItem).expiration = &t
|
||||
}
|
||||
return v, nil
|
||||
}, isWait)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (c *SimpleCache) evict(count int) {
|
||||
now := c.clock.Now()
|
||||
current := 0
|
||||
for key, item := range c.items {
|
||||
if current >= count {
|
||||
return
|
||||
}
|
||||
if item.expiration == nil || now.After(*item.expiration) {
|
||||
defer c.remove(key)
|
||||
current++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removes the provided key from the cache.
|
||||
func (c *SimpleCache) Remove(key interface{}) bool {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
return c.remove(key)
|
||||
}
|
||||
|
||||
func (c *SimpleCache) remove(key interface{}) bool {
|
||||
item, ok := c.items[key]
|
||||
if ok {
|
||||
delete(c.items, key)
|
||||
if c.evictedFunc != nil {
|
||||
c.evictedFunc(key, item.value)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns a slice of the keys in the cache.
|
||||
func (c *SimpleCache) keys() []interface{} {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
keys := make([]interface{}, len(c.items))
|
||||
var i = 0
|
||||
for k := range c.items {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Returns a slice of the keys in the cache.
|
||||
func (c *SimpleCache) Keys() []interface{} {
|
||||
keys := []interface{}{}
|
||||
for _, k := range c.keys() {
|
||||
_, err := c.GetIFPresent(k)
|
||||
if err == nil {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Returns all key-value pairs in the cache.
|
||||
func (c *SimpleCache) GetALL() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{})
|
||||
for _, k := range c.keys() {
|
||||
v, err := c.GetIFPresent(k)
|
||||
if err == nil {
|
||||
m[k] = v
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Returns the number of items in the cache.
|
||||
func (c *SimpleCache) Len() int {
|
||||
return len(c.GetALL())
|
||||
}
|
||||
|
||||
// Completely clear the cache
|
||||
func (c *SimpleCache) Purge() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.purgeVisitorFunc != nil {
|
||||
for key, item := range c.items {
|
||||
c.purgeVisitorFunc(key, item.value)
|
||||
}
|
||||
}
|
||||
|
||||
c.init()
|
||||
}
|
||||
|
||||
type simpleItem struct {
|
||||
clock Clock
|
||||
value interface{}
|
||||
expiration *time.Time
|
||||
}
|
||||
|
||||
// returns boolean value whether this item is expired or not.
|
||||
func (si *simpleItem) IsExpired(now *time.Time) bool {
|
||||
if si.expiration == nil {
|
||||
return false
|
||||
}
|
||||
if now == nil {
|
||||
t := si.clock.Now()
|
||||
now = &t
|
||||
}
|
||||
return si.expiration.Before(*now)
|
||||
}
|
82
vendor/github.com/bluele/gcache/singleflight.go
generated
vendored
Normal file
82
vendor/github.com/bluele/gcache/singleflight.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
package gcache
|
||||
|
||||
/*
|
||||
Copyright 2012 Google Inc.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// This module provides a duplicate function call suppression
|
||||
// mechanism.
|
||||
|
||||
import "sync"
|
||||
|
||||
// call is an in-flight or completed Do call
|
||||
type call struct {
|
||||
wg sync.WaitGroup
|
||||
val interface{}
|
||||
err error
|
||||
}
|
||||
|
||||
// Group represents a class of work and forms a namespace in which
|
||||
// units of work can be executed with duplicate suppression.
|
||||
type Group struct {
|
||||
cache Cache
|
||||
mu sync.Mutex // protects m
|
||||
m map[interface{}]*call // lazily initialized
|
||||
}
|
||||
|
||||
// Do executes and returns the results of the given function, making
|
||||
// sure that only one execution is in-flight for a given key at a
|
||||
// time. If a duplicate comes in, the duplicate caller waits for the
|
||||
// original to complete and receives the same results.
|
||||
func (g *Group) Do(key interface{}, fn func() (interface{}, error), isWait bool) (interface{}, bool, error) {
|
||||
g.mu.Lock()
|
||||
v, err := g.cache.get(key, true)
|
||||
if err == nil {
|
||||
g.mu.Unlock()
|
||||
return v, false, nil
|
||||
}
|
||||
if g.m == nil {
|
||||
g.m = make(map[interface{}]*call)
|
||||
}
|
||||
if c, ok := g.m[key]; ok {
|
||||
g.mu.Unlock()
|
||||
if !isWait {
|
||||
return nil, false, KeyNotFoundError
|
||||
}
|
||||
c.wg.Wait()
|
||||
return c.val, false, c.err
|
||||
}
|
||||
c := new(call)
|
||||
c.wg.Add(1)
|
||||
g.m[key] = c
|
||||
g.mu.Unlock()
|
||||
if !isWait {
|
||||
go g.call(c, key, fn)
|
||||
return nil, false, KeyNotFoundError
|
||||
}
|
||||
v, err = g.call(c, key, fn)
|
||||
return v, true, err
|
||||
}
|
||||
|
||||
func (g *Group) call(c *call, key interface{}, fn func() (interface{}, error)) (interface{}, error) {
|
||||
c.val, c.err = fn()
|
||||
c.wg.Done()
|
||||
|
||||
g.mu.Lock()
|
||||
delete(g.m, key)
|
||||
g.mu.Unlock()
|
||||
|
||||
return c.val, c.err
|
||||
}
|
53
vendor/github.com/bluele/gcache/stats.go
generated
vendored
Normal file
53
vendor/github.com/bluele/gcache/stats.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
package gcache
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type statsAccessor interface {
|
||||
HitCount() uint64
|
||||
MissCount() uint64
|
||||
LookupCount() uint64
|
||||
HitRate() float64
|
||||
}
|
||||
|
||||
// statistics
|
||||
type stats struct {
|
||||
hitCount uint64
|
||||
missCount uint64
|
||||
}
|
||||
|
||||
// increment hit count
|
||||
func (st *stats) IncrHitCount() uint64 {
|
||||
return atomic.AddUint64(&st.hitCount, 1)
|
||||
}
|
||||
|
||||
// increment miss count
|
||||
func (st *stats) IncrMissCount() uint64 {
|
||||
return atomic.AddUint64(&st.missCount, 1)
|
||||
}
|
||||
|
||||
// HitCount returns hit count
|
||||
func (st *stats) HitCount() uint64 {
|
||||
return atomic.LoadUint64(&st.hitCount)
|
||||
}
|
||||
|
||||
// MissCount returns miss count
|
||||
func (st *stats) MissCount() uint64 {
|
||||
return atomic.LoadUint64(&st.missCount)
|
||||
}
|
||||
|
||||
// LookupCount returns lookup count
|
||||
func (st *stats) LookupCount() uint64 {
|
||||
return st.HitCount() + st.MissCount()
|
||||
}
|
||||
|
||||
// HitRate returns rate for cache hitting
|
||||
func (st *stats) HitRate() float64 {
|
||||
hc, mc := st.HitCount(), st.MissCount()
|
||||
total := hc + mc
|
||||
if total == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return float64(hc) / float64(total)
|
||||
}
|
15
vendor/github.com/bluele/gcache/utils.go
generated
vendored
Normal file
15
vendor/github.com/bluele/gcache/utils.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
package gcache
|
||||
|
||||
func minInt(x, y int) int {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
|
||||
func maxInt(x, y int) int {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
51
vendor/github.com/bluele/gcache/wercker.yml
generated
vendored
Normal file
51
vendor/github.com/bluele/gcache/wercker.yml
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
# This references the default golang container from
|
||||
# the Docker Hub: https://registry.hub.docker.com/u/library/golang/
|
||||
# If you want Google's container you would reference google/golang
|
||||
# Read more about containers on our dev center
|
||||
# http://devcenter.wercker.com/docs/containers/index.html
|
||||
box: golang
|
||||
# This is the build pipeline. Pipelines are the core of wercker
|
||||
# Read more about pipelines on our dev center
|
||||
# http://devcenter.wercker.com/docs/pipelines/index.html
|
||||
|
||||
# You can also use services such as databases. Read more on our dev center:
|
||||
# http://devcenter.wercker.com/docs/services/index.html
|
||||
# services:
|
||||
# - postgres
|
||||
# http://devcenter.wercker.com/docs/services/postgresql.html
|
||||
|
||||
# - mongo
|
||||
# http://devcenter.wercker.com/docs/services/mongodb.html
|
||||
build:
|
||||
# The steps that will be executed on build
|
||||
# Steps make up the actions in your pipeline
|
||||
# Read more about steps on our dev center:
|
||||
# http://devcenter.wercker.com/docs/steps/index.html
|
||||
steps:
|
||||
# Sets the go workspace and places you package
|
||||
# at the right place in the workspace tree
|
||||
- setup-go-workspace
|
||||
|
||||
# Prints go version
|
||||
- script:
|
||||
name: go version
|
||||
code: |
|
||||
go version
|
||||
|
||||
# Gets the dependencies
|
||||
- script:
|
||||
name: go get
|
||||
code: |
|
||||
go get
|
||||
|
||||
# Build the project
|
||||
- script:
|
||||
name: go build
|
||||
code: |
|
||||
go build
|
||||
|
||||
# Test the project
|
||||
- script:
|
||||
name: go test
|
||||
code: |
|
||||
go test -v
|
Reference in New Issue
Block a user