summaryrefslogtreecommitdiffstats
path: root/store_test.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--store_test.go388
1 files changed, 357 insertions, 31 deletions
diff --git a/store_test.go b/store_test.go
index 89f4de6..e2c44e9 100644
--- a/store_test.go
+++ b/store_test.go
@@ -3,6 +3,7 @@ package cache
import (
"bytes"
"encoding/binary"
+ "errors"
"strconv"
"testing"
"time"
@@ -26,10 +27,30 @@ func TestStoreGetSet(t *testing.T) {
store := setupTestStore(t)
want := []byte("Value")
+ store.Set([]byte("Key"), want, 0)
+ got, ttl, ok := store.Get([]byte("Key"))
+ if !ok {
+ t.Fatalf("expected key to exist")
+ }
+ if !bytes.Equal(want, got) {
+ t.Errorf("got %v, want %v", got, want)
+ }
+ if ttl.Round(time.Second) != 0 {
+ t.Errorf("ttl same: got %v expected %v", ttl.Round(time.Second), 1*time.Hour)
+ }
+
+ })
+
+ t.Run("Exists Non Expiry", func(t *testing.T) {
+ t.Parallel()
+
+ store := setupTestStore(t)
+
+ want := []byte("Value")
store.Set([]byte("Key"), want, 1*time.Hour)
got, ttl, ok := store.Get([]byte("Key"))
if !ok {
- t.Errorf("expected key to exist")
+ t.Fatalf("expected key to exist")
}
if !bytes.Equal(want, got) {
t.Errorf("got %v, want %v", got, want)
@@ -71,12 +92,13 @@ func TestStoreGetSet(t *testing.T) {
want := []byte("Value")
store.Set([]byte("Key"), want, 0)
got, _, ok := store.Get([]byte("Key"))
+ if !ok {
+ t.Fatal("expected key to exist")
+ }
if !bytes.Equal(want, got) {
t.Errorf("got %v, want %v", got, want)
}
- if !ok {
- t.Errorf("expected key to exist")
- }
+
})
t.Run("Resize", func(t *testing.T) {
@@ -152,49 +174,353 @@ func TestStoreClear(t *testing.T) {
}
}
+func TestStoreUpdateInPlace(t *testing.T) {
+ t.Parallel()
+
+ t.Run("Exists", func(t *testing.T) {
+ t.Parallel()
+
+ store := setupTestStore(t)
+
+ want := []byte("Value")
+ store.Set([]byte("Key"), []byte("Initial"), 1*time.Hour)
+
+ processFunc := func(v []byte) ([]byte, error) {
+ return want, nil
+ }
+
+ if err := store.UpdateInPlace([]byte("Key"), processFunc, 1*time.Hour); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+
+ got, _, ok := store.Get([]byte("Key"))
+ if !ok {
+ t.Fatalf("expected key to exist")
+ }
+ if !bytes.Equal(want, got) {
+ t.Errorf("got %v, want %v", got, want)
+ }
+ })
+
+ t.Run("Not Exists", func(t *testing.T) {
+ t.Parallel()
+
+ store := setupTestStore(t)
+
+ processFunc := func(v []byte) ([]byte, error) {
+ return []byte("Value"), nil
+ }
+
+ if err := store.UpdateInPlace([]byte("Key"), processFunc, 1*time.Hour); !errors.Is(err, ErrKeyNotFound) {
+ t.Fatalf("expected error: %v, got: %v", ErrKeyNotFound, err)
+ }
+ })
+}
+
+func TestStoreMemoize(t *testing.T) {
+ t.Parallel()
+
+ t.Run("Cache Miss", func(t *testing.T) {
+ t.Parallel()
+
+ store := setupTestStore(t)
+
+ factoryFunc := func() ([]byte, error) {
+ return []byte("Value"), nil
+ }
+
+ got, err := store.Memoize([]byte("Key"), factoryFunc, 1*time.Hour)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if !bytes.Equal(got, []byte("Value")) {
+ t.Fatalf("expected: %v, got: %v", "Value", got)
+ }
+
+ got, _, ok := store.Get([]byte("Key"))
+ if !ok {
+ t.Fatalf("expected key to exist")
+ }
+ if !bytes.Equal(got, []byte("Value")) {
+ t.Fatalf("expected: %v, got: %v", "Value", got)
+ }
+ })
+
+ t.Run("Cache Hit", func(t *testing.T) {
+ t.Parallel()
+
+ store := setupTestStore(t)
+
+ store.Set([]byte("Key"), []byte("Value"), 1*time.Hour)
+
+ factoryFunc := func() ([]byte, error) {
+ return []byte("NewValue"), nil
+ }
+
+ got, err := store.Memoize([]byte("Key"), factoryFunc, 1*time.Hour)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if !bytes.Equal(got, []byte("Value")) {
+ t.Fatalf("expected: %v, got: %v", "Value", got)
+ }
+ })
+}
+
+func TestStoreCleanup(t *testing.T) {
+ t.Parallel()
+
+ t.Run("Cleanup Expired", func(t *testing.T) {
+ t.Parallel()
+
+ store := setupTestStore(t)
+
+ store.Set([]byte("1"), []byte("1"), 500*time.Millisecond)
+ store.Set([]byte("2"), []byte("2"), 1*time.Hour)
+
+ time.Sleep(600 * time.Millisecond)
+
+ store.Cleanup()
+
+ if _, _, ok := store.Get([]byte("1")); ok {
+ t.Fatalf("expected 1 to not exist")
+ }
+
+ if _, _, ok := store.Get([]byte("2")); !ok {
+ t.Fatalf("expected 2 to exist")
+ }
+ })
+
+ t.Run("No Cleanup", func(t *testing.T) {
+ t.Parallel()
+
+ store := setupTestStore(t)
+
+ store.Set([]byte("Key"), []byte("Value"), 1*time.Hour)
+
+ // No cleanup should occur
+ store.Cleanup()
+
+ if _, _, ok := store.Get([]byte("Key")); !ok {
+ t.Fatalf("expected key to exist")
+ }
+ })
+}
+
+func TestStoreEvict(t *testing.T) {
+ t.Parallel()
+
+ t.Run("Evict FIFO", func(t *testing.T) {
+ t.Parallel()
+
+ store := setupTestStore(t)
+ if err := store.Policy.SetPolicy(PolicyFIFO); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ store.MaxCost = 5
+
+ store.Set([]byte("1"), []byte("1"), 0)
+ store.Set([]byte("2"), []byte("2"), 0)
+
+ // Trigger eviction
+ store.Set([]byte("3"), []byte("3"), 0)
+ store.Evict()
+
+ if _, _, ok := store.Get([]byte("1")); ok {
+ t.Fatalf("expected key 1 to not exist")
+ }
+
+ if _, _, ok := store.Get([]byte("2")); !ok {
+ t.Fatalf("expected key 2 to exist")
+ }
+ })
+
+ t.Run("No Evict", func(t *testing.T) {
+ t.Parallel()
+
+ store := setupTestStore(t)
+ if err := store.Policy.SetPolicy(PolicyFIFO); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ store.MaxCost = 10
+
+ store.Set([]byte("1"), []byte("1"), 0)
+ store.Set([]byte("2"), []byte("2"), 0)
+
+ // No eviction should occur
+ store.Set([]byte("3"), []byte("3"), 0)
+ store.Evict()
+
+ if _, _, ok := store.Get([]byte("1")); !ok {
+ t.Fatalf("expected key 1 to exist")
+ }
+
+ if _, _, ok := store.Get([]byte("2")); !ok {
+ t.Fatalf("expected key 2 to exist")
+ }
+ })
+
+ t.Run("No Evict PolicyNone", func(t *testing.T) {
+ t.Parallel()
+
+ store := setupTestStore(t)
+ if err := store.Policy.SetPolicy(PolicyNone); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ store.MaxCost = 5
+
+ store.Set([]byte("1"), []byte("1"), 0)
+ store.Set([]byte("2"), []byte("2"), 0)
+
+ // No eviction should occur
+ store.Set([]byte("3"), []byte("3"), 0)
+ store.Evict()
+
+ if _, _, ok := store.Get([]byte("1")); !ok {
+ t.Fatalf("expected key 1 to exist")
+ }
+
+ if _, _, ok := store.Get([]byte("2")); !ok {
+ t.Fatalf("expected key 2 to exist")
+ }
+ })
+
+ t.Run("No Evict MaxCost Zero", func(t *testing.T) {
+ t.Parallel()
+
+ store := setupTestStore(t)
+ if err := store.Policy.SetPolicy(PolicyFIFO); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ store.MaxCost = 0
+
+ store.Set([]byte("1"), []byte("1"), 0)
+ store.Set([]byte("2"), []byte("2"), 0)
+
+ store.Evict()
+
+ if _, _, ok := store.Get([]byte("1")); !ok {
+ t.Fatalf("expected key 1 to exist")
+ }
+
+ if _, _, ok := store.Get([]byte("2")); !ok {
+ t.Fatalf("expected key 2 to exist")
+ }
+ })
+}
+
func BenchmarkStoreGet(b *testing.B) {
- for n := 1; n <= 10000; n *= 10 {
- b.Run(strconv.Itoa(n), func(b *testing.B) {
- want := setupTestStore(b)
+ policy := map[string]EvictionPolicyType{
+ "None": PolicyNone,
+ "FIFO": PolicyFIFO,
+ "LRU": PolicyLRU,
+ "LFU": PolicyLFU,
+ "LTR": PolicyLTR,
+ }
+ for k, v := range policy {
+ b.Run(k, func(b *testing.B) {
+ for n := 1; n <= 10000; n *= 10 {
+ b.Run(strconv.Itoa(n), func(b *testing.B) {
+ want := setupTestStore(b)
- for i := range n - 1 {
- buf := make([]byte, 8)
- binary.LittleEndian.PutUint64(buf, uint64(i))
- want.Set(buf, buf, 0)
- }
+ if err := want.Policy.SetPolicy(v); err != nil {
+ b.Fatalf("unexpected error: %v", err)
+ }
- key := []byte("Key")
- want.Set(key, []byte("Store"), 0)
- b.ReportAllocs()
+ for i := range n - 1 {
+ buf := make([]byte, 8)
+ binary.LittleEndian.PutUint64(buf, uint64(i))
+ want.Set(buf, buf, 0)
+ }
- b.ResetTimer()
+ key := []byte("Key")
+ want.Set(key, []byte("Store"), 0)
+ b.ReportAllocs()
- for b.Loop() {
- want.Get(key)
+ b.ResetTimer()
+
+ for b.Loop() {
+ want.Get(key)
+ }
+ })
}
})
}
}
func BenchmarkStoreSet(b *testing.B) {
- for n := 1; n <= 10000; n *= 10 {
- b.Run(strconv.Itoa(n), func(b *testing.B) {
- want := setupTestStore(b)
+ policy := map[string]EvictionPolicyType{
+ "None": PolicyNone,
+ "FIFO": PolicyFIFO,
+ "LRU": PolicyLRU,
+ "LFU": PolicyLFU,
+ "LTR": PolicyLTR,
+ }
+ for k, v := range policy {
+ b.Run(k, func(b *testing.B) {
+ for n := 1; n <= 10000; n *= 10 {
+ b.Run(strconv.Itoa(n), func(b *testing.B) {
+ want := setupTestStore(b)
- for i := range n - 1 {
- buf := make([]byte, 8)
- binary.LittleEndian.PutUint64(buf, uint64(i))
- want.Set(buf, buf, 0)
+ if err := want.Policy.SetPolicy(v); err != nil {
+ b.Fatalf("unexpected error: %v", err)
+ }
+
+ for i := range n - 1 {
+ buf := make([]byte, 8)
+ binary.LittleEndian.PutUint64(buf, uint64(i))
+ want.Set(buf, buf, 0)
+ }
+
+ key := []byte("Key")
+ store := []byte("Store")
+
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ for b.Loop() {
+ want.Set(key, store, 0)
+ }
+ })
}
+ })
+ }
+}
- key := []byte("Key")
- store := []byte("Store")
+func BenchmarkStoreSetInsert(b *testing.B) {
+ policy := map[string]EvictionPolicyType{
+ "None": PolicyNone,
+ "FIFO": PolicyFIFO,
+ "LRU": PolicyLRU,
+ "LFU": PolicyLFU,
+ "LTR": PolicyLTR,
+ }
+ for k, v := range policy {
+ b.Run(k, func(b *testing.B) {
+ for n := 1; n <= 10000; n *= 10 {
+ b.Run(strconv.Itoa(n), func(b *testing.B) {
+ want := setupTestStore(b)
- b.ReportAllocs()
- b.ResetTimer()
+ if err := want.Policy.SetPolicy(v); err != nil {
+ b.Fatalf("unexpected error: %v", err)
+ }
- for b.Loop() {
- want.Set(key, store, 0)
+ list := make([][]byte, n)
+ for i := range n {
+ buf := make([]byte, 8)
+ binary.LittleEndian.PutUint64(buf, uint64(i))
+ list = append(list, buf)
+ }
+
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ for b.Loop() {
+ for _, k := range list {
+ want.Set(k, k, 0)
+ }
+ }
+ })
}
})
}