321 lines
5.2 KiB
Markdown
321 lines
5.2 KiB
Markdown
# GCache
|
|
|
|
![Test](https://github.com/bluele/gcache/workflows/Test/badge.svg)
|
|
[![GoDoc](https://godoc.org/github.com/bluele/gcache?status.svg)](https://pkg.go.dev/github.com/bluele/gcache?tab=doc)
|
|
|
|
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>
|