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

feat(gotcha): add documentations and public API #12

Merged
merged 12 commits into from
Apr 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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