Skip to content

Commit

Permalink
chore: minor refactor + docs
Browse files Browse the repository at this point in the history
  • Loading branch information
nowayhecodes committed Aug 14, 2022
1 parent bbd1f4b commit 6272cfd
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 29 deletions.
38 changes: 33 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,43 @@
</div>

<div align="center" style="margin-top: -4rem;">
<h3>Easily switch your caching algorithm between: LRU, LFU and 2Q</h3>
<h3>A deadly simple and thread-safe map cache.</h3>
</div>
<br />

#### This package implements some caching algorithms as discribed below:
#### What?

- LRU (Least Recently Used): Keeps track of the least recently used item in the cache and discards it. The LRU eviction algorithm evicts the page from the buffer which has not been accessed for the longest.
Polacache is a deadly simple and thread-safe map cache.
In it's constructor, you set a cleanupInterval, which launchs a goroutine to perform the cleanup loop.

- LFU (Least Frequently Used): Does almost the same as LRU, but tracks the least frequently used. This also implements a Dynamic Aging and a GreedyDual-Size with Frequency cache policy.
#### Why?

- 2Q Cache: 2Q addresses the above-illustrated issues by introducing parallel buffers and supporting queues. 2Q algorithm works with two buffers. A primary LRU buffer and a secondary FIFO buffer. Instead of considering just recency as a factor, 2Q also considers access frequency while making the decision to ensure the page that is really warm gets a place in the LRU cache. It admits only hot pages to the main buffer and tests every page for a second reference.
For the fun

#### How?

```go

package main

import (
"time"

pc "github.com/nowayhecodes/polacache"
)

func main() {
cache := pc.New(1 * time.Minute)

exampleItem := pc.Item{
Key: "example",
Value: 42,
}

cache.Set(exampleItem, time.Now().Add(1*time.Hour).Unix())
cache.Get(exampleItem.Key)
cache.Delete(exampleItem.Key)

}

```
31 changes: 29 additions & 2 deletions doc.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
// Polacache is a simple and thread-safe map cache.

// Polacache is deadly a simple and thread-safe map cache.
//
// In it's constructor, you set a cleanupInterval, which launchs
// a goroutine to perform the cleanup loop.
//
//
// Example:
//
// package main
//
// import (
// "time"
//
// pc "github.com/nowayhecodes/polacache"
// )
//
// func main() {
// cache := pc.New(1 * time.Minute)
//
// exampleItem := pc.Item{
// Key: "example",
// Value: 42,
// }
//
// cache.Set(exampleItem, time.Now().Add(1*time.Hour).Unix())
// cache.Get(exampleItem.Key)
// cache.Delete(exampleItem.Key)
//
// }
//
// ...
package polacache
25 changes: 14 additions & 11 deletions polacache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import (
"time"
)

type item struct {
key string
value interface{}
type Item struct {
Key string
Value interface{}
}

type cachedItem struct {
item item
item Item
expiresAt int64
}

Expand Down Expand Up @@ -50,38 +50,41 @@ func New(cleanupInterval time.Duration) *cache {
}

// Puts an item in the cache with the given expiration timestamp
//
// Example:
// polacache.Set(item, time.Now().Add(1*time.Hour).Unix())
// ...
func (c *cache) Set(i item, expiresAt int64) {
func (c *cache) Set(i Item, expiresAt int64) {
c.lock.Lock()
defer c.lock.Unlock()

c.items[i.key] = cachedItem{
c.items[i.Key] = cachedItem{
item: i,
expiresAt: expiresAt,
}
}

// Looks up the given key's value in the cache
//
// Example:
// polacache.Get(item.key)
// polacache.Get(item.Key)
// ...
func (c *cache) Get(key string) (interface{}, error) {
c.lock.Lock()
defer c.lock.Unlock()

cached, ok := c.items[key]
if !ok {
return item{}, fmt.Errorf("key %v not in cache", key)
return Item{}, fmt.Errorf("key %v not in cache", key)
}

return cached.item.value, nil
return cached.item.Value, nil
}

// Given a key removes the item from the cache
// Given a key, removes the item from the cache
//
// Example:
// polacache.Delete(item.key)
// polacache.Delete(item.Key)
// ...
func (c *cache) Delete(key string) {
c.lock.Lock()
Expand Down
22 changes: 11 additions & 11 deletions polacache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,31 @@ import (

var polacache = New(1 * time.Minute)

var testItem = item{
key: "polatest",
value: "testifying polacache",
var testItem = Item{
Key: "polatest",
Value: "testifying polacache",
}

func TestPolacache_GetSet(t *testing.T) {
requires := require.New(t)

polacache.Set(testItem, time.Now().Add(1*time.Hour).Unix())

it, err := polacache.Get(testItem.key)
it, err := polacache.Get(testItem.Key)
requires.NoError(err)
requires.Equal(testItem.value, it)
requires.Equal(testItem.Value, it)

polacache.stopCleanup()
}

func TestPolacache_ErrorMessage(t *testing.T) {
requires := require.New(t)

polacache.Delete(testItem.key)
polacache.Delete(testItem.Key)

it, err := polacache.Get(testItem.key)
it, err := polacache.Get(testItem.Key)
requires.EqualError(err, "key polatest not in cache")
requires.Equal(item{}, it)
requires.Equal(Item{}, it)

polacache.stopCleanup()
}
Expand All @@ -46,9 +46,9 @@ func BenchmarkPolacache(b *testing.B) {
b.ResetTimer()
b.RunParallel(func(p *testing.PB) {
for p.Next() {
polacache.Set(item{
key: strconv.Itoa(int(rand.Int63())),
value: "polacache test",
polacache.Set(Item{
Key: strconv.Itoa(int(rand.Int63())),
Value: "polacache test",
}, time.Now().Add(1*time.Hour).Unix())
}
})
Expand Down

0 comments on commit 6272cfd

Please sign in to comment.