Skip to content
This repository has been archived by the owner on Mar 8, 2024. It is now read-only.

Commit

Permalink
feat(gotcha): add documentations and pubic (#12)
Browse files Browse the repository at this point in the history
* add docs

* add docs

* add docs

* add LFU docs

* remove TODO

* update readme

* update example in readme

* add constructor

* add default option

* add test

* add example to readme.md

* remove whitespace
  • Loading branch information
bxcodec committed Apr 13, 2019
1 parent 374970b commit 067ada9
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 17 deletions.
83 changes: 82 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,82 @@
# gotcha
# gotcha

gotcha: inmemory-cache in Go (Golang) with customizable algorithm

[![GoDoc](https://godoc.org/github.com/bxcodec/gotcha?status.svg)](https://godoc.org/github.com/bxcodec/gotcha)

## Index

* [Support](#support)
* [Getting Started](#getting-started)
* [Example](#example)
* [Contribution](#contribution)


## Support

You can file an [Issue](https://github.com/bxcodec/gotcha/issues/new).
See documentation in [Godoc](https://godoc.org/github.com/bxcodec/gotcha)


## Getting Started

#### Download

```shell
go get -u github.com/bxcodec/gotcha
```
## Example


### With Cache Client
```go
package main

import (
"fmt"
"log"

"github.com/bxcodec/gotcha"
)

func main() {
cache := gotcha.New()
err := cache.Set("name", "John Snow")
if err != nil {
log.Fatal(err)
}
val, err := cache.Get("name")
if err != nil {
log.Fatal(err)
}
fmt.Println(val)
}
```

### Without Cache Client
```go
package main

import (
"fmt"
"log"

"github.com/bxcodec/gotcha"
)

func main() {
err := gotcha.Set("name", "John Snow")
if err != nil {
log.Fatal(err)
}
val, err := gotcha.Get("name")
if err != nil {
log.Fatal(err)
}
fmt.Println(val)
}
```


## Contribution
- You can submit an issue or create a Pull Request (PR)
30 changes: 28 additions & 2 deletions cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const (
DefaultSize = 100
// DefaultExpiryTime ...
DefaultExpiryTime = time.Second * 10
// DefaultAlgorithm ...
DefaultAlgorithm = LRUAlgorithm
)

// Document ...
Expand All @@ -36,8 +38,32 @@ type Option struct {
MaxMemory uint64 // Max Memory of item stored for eviction
}

// Interactor ...
type Interactor interface {
// SetAlgorithm will set the algorithm value
func (o *Option) SetAlgorithm(algorithm string) *Option {
o.AlgorithmType = algorithm
return o
}

// SetExpiryTime will set the expiry time
func (o *Option) SetExpiryTime(expiry time.Duration) *Option {
o.ExpiryTime = expiry
return o
}

// SetMaxSizeItem will set the maximum size of item in cache
func (o *Option) SetMaxSizeItem(size uint64) *Option {
o.MaxSizeItem = size
return o
}

// SetMaxMemory will set the maximum memory will used for cache
func (o *Option) SetMaxMemory(memory uint64) *Option {
o.MaxMemory = memory
return o
}

// Cache represent the public API that will available used by user
type Cache interface {
Set(key string, value interface{}) error
Get(key string) (val interface{}, err error)
Delete(key string) (err error)
Expand Down
39 changes: 37 additions & 2 deletions gotcha.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ import (
"github.com/bxcodec/gotcha/lru"
)

// New ...
func New(options ...*cache.Option) (c cache.Interactor) {
var (
// DefaultCache use for default cache client
DefaultCache = New()
)

// New will create a new cache client. If the options not set, the cache will use the default options
func New(options ...*cache.Option) (c cache.Cache) {
option := mergeOptions(options...)
if option.MaxMemory == 0 { // Unlimited
// TODO: (bxcodec)
Expand Down Expand Up @@ -35,6 +40,11 @@ func New(options ...*cache.Option) (c cache.Interactor) {
return
}

// NewOption return an empty option
func NewOption() (op *cache.Option) {
return
}

func mergeOptions(options ...*cache.Option) (opts *cache.Option) {
opts = new(cache.Option)
for _, op := range options {
Expand All @@ -53,3 +63,28 @@ func mergeOptions(options ...*cache.Option) (opts *cache.Option) {
}
return
}

// Set will set an item to cache using default option
func Set(key string, value interface{}) (err error) {
return DefaultCache.Set(key, value)
}

// Get will get an item from cache using default option
func Get(key string) (value interface{}, err error) {
return DefaultCache.Get(key)
}

// Delete will delete an item from the cache using default option
func Delete(key string) (err error) {
return DefaultCache.Delete(key)
}

// GetKeys will get all keys from the cache using default option
func GetKeys() (keys []string, err error) {
return DefaultCache.GetKeys()
}

// ClearCache will Clear the cache using default option
func ClearCache() (err error) {
return DefaultCache.ClearCache()
}
104 changes: 104 additions & 0 deletions gotcha_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package gotcha_test

import (
"testing"

"github.com/bxcodec/gotcha"
)

func TestGotcha(t *testing.T) {
t.Run("set", func(t *testing.T) {
err := gotcha.Set("name", "John Snow")
if err != nil {
t.Fatalf("expected: %v, got %v", nil, err)
}
err = gotcha.Set("kingdom", "North Kingdom")
if err != nil {
t.Fatalf("expected: %v, got %v", nil, err)
}
})

t.Run("get", func(t *testing.T) {
val, err := gotcha.Get("name")
if err != nil {
t.Fatalf("expected: %v, got %v", nil, err)
}
if val.(string) != "John Snow" {
t.Fatalf("expected: %v, got %v", "John Snow", val)
}
})

t.Run("get-keys", func(t *testing.T) {
keys, err := gotcha.GetKeys()
if err != nil {
t.Fatalf("expected: %v, got %v", nil, err)
}
var contains = func(keys []string, k string) bool {
for _, item := range keys {
if item == k {
return true
}
}
return false
}
expectedKeys := []string{"name", "kingdom"}
for _, k := range expectedKeys {
if !contains(keys, k) {
t.Fatalf("expected: %v, got: %v", true, false)
}
}
})

t.Run("delete", func(t *testing.T) {
// Ensure the key is exists
val, err := gotcha.Get("kingdom")
if err != nil {
t.Fatalf("expected: %v, got %v", nil, err)
}
if val.(string) != "North Kingdom" {
t.Fatalf("expected: %v, got %v", "John Snow", val)
}

// Delete the Keys

err = gotcha.Delete("kingdom")
if err != nil {
t.Fatalf("expected: %v, got %v", nil, err)
}

// Re-Ensure the keys is deleted
val, err = gotcha.Get("kingdom")
if err == nil {
t.Fatalf("expected: %v, got %v", "error", err)
}

if val != nil {
t.Fatalf("expected: %v, got %v", nil, val)
}
})

t.Run("clear-cache", func(t *testing.T) {
// Ensure the cache is still contains item
keys, err := gotcha.GetKeys()
if err != nil {
t.Fatalf("expected: %v, got %v", nil, err)
}
if len(keys) == 0 {
t.Fatalf("expected: %v, got %v", "not zero", len(keys))
}

err = gotcha.ClearCache()
if err != nil {
t.Fatalf("expected: %v, got %v", nil, err)
}

// Re-Ensure the cache already cleared
keys, err = gotcha.GetKeys()
if err != nil {
t.Fatalf("expected: %v, got %v", nil, err)
}
if len(keys) != 0 {
t.Fatalf("expected: %v, got %v", "zero", len(keys))
}
})
}
7 changes: 7 additions & 0 deletions lfu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# LFU Algorithm

This LFU Algorithm implemented based on this paper: "[An O(1) algorithm for implementing the LFU
cache eviction scheme](http://dhruvbird.com/lfu.pdf)"
(by Prof. Ketan Shah, Anirban Mitra, and Dhruv Matani)

Well, to be honest, it's not really exaclty as is like they wrote in the pseudocode. Because I need to change a few flow of the code due to the lack of Golang itself.
2 changes: 1 addition & 1 deletion lfu/lfu.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type Repository interface {
}

// NewCache return the implementations of cache with LRU algorithm
func NewCache(option cache.Option) cache.Interactor {
func NewCache(option cache.Option) cache.Cache {
repo := repository.New(option.MaxSizeItem, option.MaxMemory, option.ExpiryTime)
return &Cache{
Option: option,
Expand Down
2 changes: 1 addition & 1 deletion lru/lru.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type Repository interface {
}

// NewCache return the implementations of cache with LRU algorithm
func NewCache(option cache.Option) cache.Interactor {
func NewCache(option cache.Option) cache.Cache {
repo := repository.New(option.MaxSizeItem, option.MaxMemory, option.ExpiryTime)
return &Cache{
Option: option,
Expand Down
10 changes: 0 additions & 10 deletions lru/repository/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ func (r *Repository) Get(key string) (res *cache.Document, err error) {

// GetOldest returns the oldest element
func (r *Repository) GetOldest() (res *cache.Document, err error) {
// TODO: (bxcodec)
// Add Test for this function
elem := r.fragmentPositionList.Back()
if elem != nil {
res = elem.Value.(*cache.Document)
Expand All @@ -78,8 +76,6 @@ func (r *Repository) GetOldest() (res *cache.Document, err error) {
// Contains checks if a key is in the cache, without updating the recent-ness
// or deleting it for being stale.
func (r *Repository) Contains(key string) (ok bool) {
// TODO: (bxcodec)
// Add Test for this function
_, ok = r.items[key]
return ok
}
Expand All @@ -98,8 +94,6 @@ func (r *Repository) Peek(key string) (res *cache.Document, err error) {
// Delete removes the provided key from the cache, returning if the
// key was contained.
func (r *Repository) Delete(key string) (ok bool, err error) {
// TODO: (bxcodec)
// Add Test for this function
elem, ok := r.items[key]
if ok {
r.removeElement(elem)
Expand All @@ -125,8 +119,6 @@ func (r *Repository) removeOldest() {

// Keys returns a slice of the keys in the cache, from oldest to newest.
func (r *Repository) Keys() (keys []string, err error) {
// TODO: (bxcodec)
// Add Test for this function
keys = make([]string, len(r.items))
i := 0
for elem := r.fragmentPositionList.Back(); elem != nil; elem = elem.Prev() {
Expand All @@ -150,8 +142,6 @@ func (r *Repository) MemoryUsage() (size int64, err error) {

// Clear is used to completely clear the cache.
func (r *Repository) Clear() (err error) {
// TODO: (bxcodec)
// Add Test for this function
for k := range r.items {
delete(r.items, k)
}
Expand Down

0 comments on commit 067ada9

Please sign in to comment.