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