From 83cbe8b7bf2e5958ea31941f6422b098e62d0d0c Mon Sep 17 00:00:00 2001 From: Houdini Date: Mon, 12 Aug 2024 15:33:42 +0100 Subject: [PATCH] feature: update readme and licenses --- README.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++ tests/arc_test.go | 2 +- zwis/arc.go | 2 +- zwis/factory.go | 29 ++++++++++++++++++ zwis/lru.go | 14 ++++++--- zwis/memory.go | 2 +- zwis/zwis.go | 9 ++++++ 7 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 zwis/factory.go diff --git a/README.md b/README.md index 2dc73f7..ce9c7e0 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,80 @@ An in-memory cache system in Go that supports expirable cache entries and various cache eviction policies including Least Frequently Used (LFU), Least Recently Used (LRU), and Adaptive Replacement Cache (ARC). [![zwis library workflow](https://github.com/NonsoAmadi10/zwis/actions/workflows/main.yaml/badge.svg)](https://github.com/NonsoAmadi10/zwis/actions/workflows/main.yaml) + + +## Installation +```bash +go get github.com/NonsoAmadi10/zwis +``` + +## Usage + +Here's a quick example of how to use the cache: + +```go +package main + +import ( + "context" + "fmt" + "time" + + "github.com/NonsoAmadi10/zwis/zwis" +) + +func main() { + ctx := context.Background() + + // Create an LRU cache + lruCache, err := zwis.NewCache(cache.LRUCacheType, 100) + if err != nil { + panic(err) + } + + // Set a value + lruCache.Set(ctx, "key1", "value1", 5*time.Second) + + // Get a value + if value, ok := lruCache.Get(ctx, "key1"); ok { + fmt.Printf("key1: %v\n", value) + } + + // Delete a value + lruCache.Delete(ctx, "key1") + + // Clear the cache + lruCache.Clear(ctx) +} +``` + +## Available Cache Types + +* MemoryCache: Simple in-memory cache +* LRUCache: Least Recently Used cache +* LFUCache: Least Frequently Used cache +* ARCCache: Adaptive Replacement Cache +* DiskStore: Implementing a Simple Disk-Backed Cache (coming soon) + +## Contributing +Contributions are welcome! Please feel free to submit a Pull Request. + +This project structure provides a solid foundation for a Go cache library. It demonstrates several important Go concepts and best practices: + +1. Interface-based design +2. Concurrency handling with mutexes +3. Use of context for cancelation and timeouts +4. Proper error handling +5. Package organization +6. Unit testing +7. Documentation + +Areas for potential improvement or expansion: + +1. Implement a cache using a diskstore +2. Add benchmarking tests to compare performance of different cache types +3. Implement cache statistics (hit rate, miss rate, etc.) +4. Add support for cache serialization/deserialization for persistence +5. Implement a distributed cache using Redis or similar + +Remember, this is a learning exercise, so feel free to experiment with different approaches and optimizations as you build out this library. Good luck with your project! diff --git a/tests/arc_test.go b/tests/arc_test.go index d5a73a4..cd43af8 100644 --- a/tests/arc_test.go +++ b/tests/arc_test.go @@ -49,7 +49,7 @@ func TestARCCache(t *testing.T) { // Test Clear cache.Set(ctx, "key7", "value7", 0) - cache.Clear(ctx) + cache.Flush(ctx) if _, ok := cache.Get(ctx, "key7"); ok { t.Error("Cache should be empty after Clear") } diff --git a/zwis/arc.go b/zwis/arc.go index 61ce3a1..6db6ca1 100644 --- a/zwis/arc.go +++ b/zwis/arc.go @@ -122,7 +122,7 @@ func (c *ARCCache) Delete(ctx context.Context, key string) error { } // Clear removes all items from the cache. -func (c *ARCCache) Clear(ctx context.Context) error { +func (c *ARCCache) Flush(ctx context.Context) error { c.mu.Lock() defer c.mu.Unlock() diff --git a/zwis/factory.go b/zwis/factory.go new file mode 100644 index 0000000..142b433 --- /dev/null +++ b/zwis/factory.go @@ -0,0 +1,29 @@ +package zwis + +import ( + "fmt" +) + +type CacheType string + +const ( + MemoryCacheType CacheType = "memory" + LRUCacheType CacheType = "lru" + LFUCacheType CacheType = "lfu" + ARCCacheType CacheType = "arc" +) + +func NewCache(cacheType CacheType, capacity int) (Cache, error) { + switch cacheType { + case MemoryCacheType: + return NewMemoryCache(), nil + case LRUCacheType: + return NewLRUCache(capacity), nil + case LFUCacheType: + return NewLFUCache(capacity), nil + case ARCCacheType: + return NewARCCache(capacity), nil + default: + return nil, fmt.Errorf("unknown cache type: %s", cacheType) + } +} diff --git a/zwis/lru.go b/zwis/lru.go index 150a0b0..4c40d6e 100644 --- a/zwis/lru.go +++ b/zwis/lru.go @@ -28,7 +28,7 @@ func NewLRUCache(capacity int) *LRUCache { } } -func (lru *LRUCache) Get(ctx context.Context, key interface{}) (interface{}, bool) { +func (lru *LRUCache) Get(ctx context.Context, key string) (interface{}, bool) { lru.mutex.RLock() elem, ok := lru.cache[key] lru.mutex.RUnlock() @@ -50,7 +50,7 @@ func (lru *LRUCache) Get(ctx context.Context, key interface{}) (interface{}, boo return entry.value, true } -func (lru *LRUCache) Set(ctx context.Context, key, value interface{}, ttl time.Duration) { +func (lru *LRUCache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error { lru.mutex.Lock() defer lru.mutex.Unlock() @@ -70,23 +70,29 @@ func (lru *LRUCache) Set(ctx context.Context, key, value interface{}, ttl time.D elem := lru.list.PushFront(&entry{key, value, expiration}) lru.cache[key] = elem } + + return nil } -func (lru *LRUCache) Delete(ctx context.Context, key interface{}) { +func (lru *LRUCache) Delete(ctx context.Context, key string) error { lru.mutex.Lock() defer lru.mutex.Unlock() if elem, ok := lru.cache[key]; ok { lru.removeElement(elem) } + + return nil } -func (lru *LRUCache) Clear(ctx context.Context) { +func (lru *LRUCache) Flush(ctx context.Context) error { lru.mutex.Lock() defer lru.mutex.Unlock() lru.list.Init() lru.cache = make(map[interface{}]*list.Element) + + return nil } func (lru *LRUCache) removeOldest() { diff --git a/zwis/memory.go b/zwis/memory.go index dfd3324..d3b5c53 100644 --- a/zwis/memory.go +++ b/zwis/memory.go @@ -63,7 +63,7 @@ func (c *MemoryCache) Delete(ctx context.Context, key string) error { return nil } -func (c *MemoryCache) Clear(ctx context.Context) error { +func (c *MemoryCache) Flush(ctx context.Context) error { c.mu.Lock() defer c.mu.Unlock() diff --git a/zwis/zwis.go b/zwis/zwis.go index 174766c..03cc8d3 100644 --- a/zwis/zwis.go +++ b/zwis/zwis.go @@ -1,3 +1,5 @@ +// Package zwis provides various cache implementations including in-memory, +// LRU (Least Recently Used), LFU (Least Frequently Used), and ARC (Adaptive Replacement Cache). package zwis import ( @@ -15,9 +17,16 @@ Flush() */ +// Cache interface defines the methods that all cache implementations must support. type Cache interface { + // Set adds an item to the cache, replacing any existing item. If the TTL + // is 0, the item never expires. Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error + // Get retrieves an item from the cache. It returns the item and a boolean + // indicating whether the key was found. Get(ctx context.Context, key string) (interface{}, bool) + // Delete removes the provided key from the cache. Delete(ctx context.Context, key string) error + // Clear removes all items from the cache. Flush(ctx context.Context) error }