diff options
author | Marc Pervaz Boocha <marcpervaz@qburst.com> | 2025-04-08 16:15:27 +0530 |
---|---|---|
committer | Marc Pervaz Boocha <marcpervaz@qburst.com> | 2025-04-08 16:15:27 +0530 |
commit | ebb2dc540858155152b21549afd77e65118e1474 (patch) | |
tree | 74aa4929bb8dfd9a1823baa0b56cecadd99e91b4 | |
parent | Added documentation and tests (diff) | |
download | cache-ebb2dc540858155152b21549afd77e65118e1474.tar cache-ebb2dc540858155152b21549afd77e65118e1474.tar.gz cache-ebb2dc540858155152b21549afd77e65118e1474.tar.bz2 cache-ebb2dc540858155152b21549afd77e65118e1474.tar.lz cache-ebb2dc540858155152b21549afd77e65118e1474.tar.xz cache-ebb2dc540858155152b21549afd77e65118e1474.tar.zst cache-ebb2dc540858155152b21549afd77e65118e1474.zip |
Lint and bug fixes
Diffstat (limited to '')
-rw-r--r-- | README.md | 16 | ||||
-rw-r--r-- | conn.go | 183 | ||||
-rw-r--r-- | conn_test.go | 37 | ||||
-rw-r--r-- | encoding.go | 13 | ||||
-rw-r--r-- | encoding_test.go | 89 | ||||
-rw-r--r-- | evict_test.go | 14 | ||||
-rw-r--r-- | examples/basic_usage/main.go | 12 | ||||
-rw-r--r-- | examples/eviction_policy/main.go | 53 | ||||
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 4 | ||||
-rw-r--r-- | internal/pausedtimer/timer_test.go | 21 | ||||
-rw-r--r-- | store.go | 15 | ||||
-rw-r--r-- | store_test.go | 91 |
13 files changed, 384 insertions, 166 deletions
@@ -1,11 +1,13 @@ # Cache +An deamonless in-memory caching library with persistant snapshots. + ## Features - **In-Memory Cache**: Fast access to cached data. - **Uses Generics with serialization**: To make it type safe and support several types via msgpack. -s + - **File-Backed Storage**: Persistent storage of cache data. - **Eviction Policies**: Support for FIFO, LRU, LFU, and LTR eviction policies. @@ -86,13 +88,21 @@ func main() { } // Get a value by key - value, ttl, err := db.GetValue("key") if err != nil { log.Fatal(err) } fmt.Printf("Value: %s, TTL: %v\n", value, ttl) + + // Get a value with a pointer by key(Useful if the value type is an interface). + var value string + value, ttl, err := db.GetValue(&value) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Value: %s, TTL: %v\n", value, ttl) } ``` @@ -128,7 +138,7 @@ The Cache Library supports the following configuration options: ### Additional Methods -- `Get`: Retrieves a value from the cache by key and returns its TTL. +- `Get`: Retrieves a value from the cache by key and returns its TTL. It take an out pointer. - `GetValue`: Retrieves a value from the cache by key and returns the value and its TTL. @@ -2,8 +2,8 @@ package cache import ( "errors" + "fmt" "io" - "log" "os" "sync" "time" @@ -12,19 +12,20 @@ import ( "github.com/vmihailenco/msgpack/v5" ) -// db represents a cache database with file-backed storage and in-memory operation. -type db struct { +// cache represents a cache database with file-backed storage and in-memory operation. +type cache struct { File io.WriteSeeker Store store Stop chan struct{} wg sync.WaitGroup + err error } // Option is a function type for configuring the db. -type Option func(*db) error +type Option func(*cache) error // openFile opens a file-backed cache database with the given options. -func openFile(filename string, options ...Option) (*db, error) { +func openFile(filename string, options ...Option) (*cache, error) { ret, err := openMem(options...) if err != nil { return nil, err @@ -42,7 +43,9 @@ func openFile(filename string, options ...Option) (*db, error) { if fileInfo.Size() == 0 { ret.File = file - ret.Flush() + if err := ret.Flush(); err != nil { + return nil, err + } } else { err := ret.Store.LoadSnapshot(file) if err != nil { @@ -56,9 +59,10 @@ func openFile(filename string, options ...Option) (*db, error) { } // openMem initializes an in-memory cache database with the given options. -func openMem(options ...Option) (*db, error) { - ret := &db{} +func openMem(options ...Option) (*cache, error) { + ret := &cache{} ret.Store.Init() + if err := ret.SetConfig(options...); err != nil { return nil, err } @@ -66,21 +70,22 @@ func openMem(options ...Option) (*db, error) { return ret, nil } -// Start begins the background worker for periodic tasks. -func (d *db) Start() { - d.Stop = make(chan struct{}) - d.wg.Add(1) +// start begins the background worker for periodic tasks. +func (c *cache) start() { + c.Stop = make(chan struct{}) + + c.wg.Add(1) - go d.backgroundWorker() + go c.backgroundWorker() } // SetConfig applies configuration options to the db. -func (d *db) SetConfig(options ...Option) error { - d.Store.Lock.Lock() - defer d.Store.Lock.Unlock() +func (c *cache) SetConfig(options ...Option) error { + c.Store.Lock.Lock() + defer c.Store.Lock.Unlock() for _, opt := range options { - if err := opt(d); err != nil { + if err := opt(c); err != nil { return err } } @@ -90,14 +95,14 @@ func (d *db) SetConfig(options ...Option) error { // WithPolicy sets the eviction policy for the cache. func WithPolicy(e EvictionPolicyType) Option { - return func(d *db) error { + return func(d *cache) error { return d.Store.Policy.SetPolicy(e) } } // WithMaxCost sets the maximum cost for the cache. func WithMaxCost(maxCost uint64) Option { - return func(d *db) error { + return func(d *cache) error { d.Store.MaxCost = maxCost return nil @@ -106,7 +111,7 @@ func WithMaxCost(maxCost uint64) Option { // SetSnapshotTime sets the interval for taking snapshots of the cache. func SetSnapshotTime(t time.Duration) Option { - return func(d *db) error { + return func(d *cache) error { d.Store.SnapshotTicker.Reset(t) return nil @@ -115,7 +120,7 @@ func SetSnapshotTime(t time.Duration) Option { // SetCleanupTime sets the interval for cleaning up expired entries. func SetCleanupTime(t time.Duration) Option { - return func(d *db) error { + return func(d *cache) error { d.Store.CleanupTicker.Reset(t) return nil @@ -123,98 +128,115 @@ func SetCleanupTime(t time.Duration) Option { } // backgroundWorker performs periodic tasks such as snapshotting and cleanup. -func (d *db) backgroundWorker() { - defer d.wg.Done() +func (c *cache) backgroundWorker() { + defer c.wg.Done() defer func() { if r := recover(); r != nil { - log.Printf("Recovered from panic in background worker: %v", r) + c.err = fmt.Errorf("panic occurred: %v", r) } }() - d.Store.SnapshotTicker.Resume() - defer d.Store.SnapshotTicker.Stop() + c.Store.SnapshotTicker.Resume() + defer c.Store.SnapshotTicker.Stop() - d.Store.CleanupTicker.Resume() - defer d.Store.CleanupTicker.Stop() + c.Store.CleanupTicker.Resume() + defer c.Store.CleanupTicker.Stop() + + c.Store.Cleanup() + c.Store.Evict() for { select { - case <-d.Stop: + case <-c.Stop: return - case <-d.Store.SnapshotTicker.C: - d.Flush() - case <-d.Store.CleanupTicker.C: - d.Store.Cleanup() - d.Store.Evict() + case <-c.Store.SnapshotTicker.C: + if err := c.Flush(); err != nil { + c.err = err + } + case <-c.Store.CleanupTicker.C: + c.Store.Cleanup() + c.Store.Evict() } } } +func (c *cache) Error() error { + return c.err +} + +func (c *cache) Cost() uint64 { + return c.Store.Cost +} + // Close stops the background worker and cleans up resources. -func (d *db) Close() error { - close(d.Stop) - d.wg.Wait() - err := d.Flush() - d.Clear() +func (c *cache) Close() error { + close(c.Stop) + c.wg.Wait() + + err := c.Flush() + c.Clear() var err1 error - if d.File != nil { - closer, ok := d.File.(io.Closer) + + if c.File != nil { + closer, ok := c.File.(io.Closer) if ok { err1 = closer.Close() } } + if err != nil { return err } + return err1 } // Flush writes the current state of the store to the file. -func (d *db) Flush() error { - if d.File != nil { - return d.Store.Snapshot(d.File) +func (c *cache) Flush() error { + if c.File != nil { + return c.Store.Snapshot(c.File) } return nil } // Clear removes all entries from the in-memory store. -func (d *db) Clear() { - d.Store.Clear() +func (c *cache) Clear() { + c.Store.Clear() } var ErrKeyNotFound = errors.New("key not found") // ErrKeyNotFound is returned when a key is not found in the cache. -// The Cache database. Can be initialized by either OpenFile or OpenMem. Uses per DB Locks. -// DB represents a generic cache database with key-value pairs. -type DB[K any, V any] struct { - *db +// The Cache database. Can be initialized by either OpenFile or OpenMem. Uses per Cache Locks. +// Cache represents a generic cache database with key-value pairs. +type Cache[K any, V any] struct { + *cache } // OpenFile opens a file-backed cache database with the specified options. -func OpenFile[K any, V any](filename string, options ...Option) (DB[K, V], error) { +func OpenFile[K any, V any](filename string, options ...Option) (Cache[K, V], error) { ret, err := openFile(filename, options...) if err != nil { - return zero[DB[K, V]](), err + return zero[Cache[K, V]](), err } - ret.Start() + ret.start() - return DB[K, V]{db: ret}, nil + return Cache[K, V]{cache: ret}, nil } // OpenMem initializes an in-memory cache database with the specified options. -func OpenMem[K any, V any](options ...Option) (DB[K, V], error) { +func OpenMem[K any, V any](options ...Option) (Cache[K, V], error) { ret, err := openMem(options...) if err != nil { - return zero[DB[K, V]](), err + return zero[Cache[K, V]](), err } - ret.Start() + ret.start() - return DB[K, V]{db: ret}, nil + return Cache[K, V]{cache: ret}, nil } // marshal serializes a value using msgpack. @@ -228,13 +250,17 @@ func unmarshal[T any](data []byte, v *T) error { } // Get retrieves a value from the cache by key and returns its TTL. -func (h *DB[K, V]) Get(key K, value *V) (time.Duration, error) { +func (c *Cache[K, V]) Get(key K, value *V) (time.Duration, error) { keyData, err := marshal(key) if err != nil { return 0, err } - v, ttl, ok := h.Store.Get(keyData) + if err := c.err; err != nil { + return 0, err + } + + v, ttl, ok := c.Store.Get(keyData) if !ok { return 0, ErrKeyNotFound } @@ -249,15 +275,15 @@ func (h *DB[K, V]) Get(key K, value *V) (time.Duration, error) { } // GetValue retrieves a value from the cache by key and returns the value and its TTL. -func (h *DB[K, V]) GetValue(key K) (V, time.Duration, error) { +func (c *Cache[K, V]) GetValue(key K) (V, time.Duration, error) { value := zero[V]() - ttl, err := h.Get(key, &value) + ttl, err := c.Get(key, &value) return value, ttl, err } // Set adds a key-value pair to the cache with a specified TTL. -func (h *DB[K, V]) Set(key K, value V, ttl time.Duration) error { +func (c *Cache[K, V]) Set(key K, value V, ttl time.Duration) error { keyData, err := marshal(key) if err != nil { return err @@ -268,19 +294,27 @@ func (h *DB[K, V]) Set(key K, value V, ttl time.Duration) error { return err } - h.Store.Set(keyData, valueData, ttl) + if err := c.err; err != nil { + return err + } + + c.Store.Set(keyData, valueData, ttl) return nil } // Delete removes a key-value pair from the cache. -func (h *DB[K, V]) Delete(key K) error { +func (c *Cache[K, V]) Delete(key K) error { keyData, err := marshal(key) if err != nil { return err } - ok := h.Store.Delete(keyData) + if err := c.err; err != nil { + return err + } + + ok := c.Store.Delete(keyData) if !ok { return ErrKeyNotFound } @@ -290,13 +324,17 @@ func (h *DB[K, V]) Delete(key K) error { // UpdateInPlace retrieves a value from the cache, processes it using the provided function, // and then sets the result back into the cache with the same key. -func (h *DB[K, V]) UpdateInPlace(key K, processFunc func(V) (V, error), ttl time.Duration) error { +func (c *Cache[K, V]) UpdateInPlace(key K, processFunc func(V) (V, error), ttl time.Duration) error { keyData, err := marshal(key) if err != nil { return err } - return h.Store.UpdateInPlace(keyData, func(data []byte) ([]byte, error) { + if err := c.err; err != nil { + return err + } + + return c.Store.UpdateInPlace(keyData, func(data []byte) ([]byte, error) { var value V if err := unmarshal(data, &value); err != nil { return nil, err @@ -313,13 +351,17 @@ func (h *DB[K, V]) UpdateInPlace(key K, processFunc func(V) (V, error), ttl time // Memorize attempts to retrieve a value from the cache. If the retrieval fails, // it sets the result of the factory function into the cache and returns that result. -func (h *DB[K, V]) Memorize(key K, factoryFunc func() (V, error), ttl time.Duration) (V, error) { +func (c *Cache[K, V]) Memorize(key K, factoryFunc func() (V, error), ttl time.Duration) (V, error) { keyData, err := marshal(key) if err != nil { return zero[V](), err } - data, err := h.Store.Memorize(keyData, func() ([]byte, error) { + if err := c.err; err != nil { + return zero[V](), err + } + + data, err := c.Store.Memorize(keyData, func() ([]byte, error) { value, err := factoryFunc() if err != nil { return nil, err @@ -327,7 +369,6 @@ func (h *DB[K, V]) Memorize(key K, factoryFunc func() (V, error), ttl time.Durat return marshal(value) }, ttl) - if err != nil { return zero[V](), err } diff --git a/conn_test.go b/conn_test.go index c80072a..47bdde4 100644 --- a/conn_test.go +++ b/conn_test.go @@ -1,22 +1,24 @@ package cache import ( + "errors" "strconv" "testing" "time" - - "errors" ) -func setupTestDB[K any, V any](tb testing.TB) *DB[K, V] { +func setupTestDB[K any, V any](tb testing.TB) *Cache[K, V] { tb.Helper() db, err := OpenMem[K, V]() if err != nil { tb.Fatalf("unexpected error: %v", err) } + tb.Cleanup(func() { - db.Close() + if err := db.Close(); err != nil { + tb.Fatalf("unexpected error: %v", err) + } }) return &db @@ -40,6 +42,7 @@ func TestDBGetSet(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + if want != got { t.Fatalf("expected: %v, got: %v", want, got) } @@ -77,6 +80,7 @@ func TestDBGetSet(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + if want != got { t.Fatalf("expected: %v, got: %v", want, got) } @@ -107,6 +111,7 @@ func TestDBDelete(t *testing.T) { db := setupTestDB[string, string](t) want := "Value" + if err := db.Set("Key", want, 0); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -140,7 +145,10 @@ func TestDBUpdateInPlace(t *testing.T) { store := setupTestDB[string, string](t) want := "Value" - store.Set("Key", "Initial", 1*time.Hour) + + if err := store.Set("Key", "Initial", 1*time.Hour); err != nil { + t.Fatalf("unexpected error: %v", err) + } processFunc := func(v string) (string, error) { return want, nil @@ -154,6 +162,7 @@ func TestDBUpdateInPlace(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + if want != got { t.Errorf("got %v, want %v", got, want) } @@ -194,6 +203,7 @@ func TestDBMemoize(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + if got != "Value" { t.Fatalf("expected: %v, got: %v", "Value", got) } @@ -202,6 +212,7 @@ func TestDBMemoize(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + if want != got { t.Errorf("got %v, want %v", got, want) } @@ -214,7 +225,9 @@ func TestDBMemoize(t *testing.T) { want := "NewValue" - store.Set("Key", "Value", 1*time.Hour) + if err := store.Set("Key", "Value", 1*time.Hour); err != nil { + t.Fatalf("unexpected error: %v", err) + } factoryFunc := func() (string, error) { return want, nil @@ -224,6 +237,7 @@ func TestDBMemoize(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + if got != "Value" { t.Fatalf("expected: %v, got: %v", "Value", got) } @@ -231,7 +245,7 @@ func TestDBMemoize(t *testing.T) { } func BenchmarkDBGet(b *testing.B) { - for n := 1; n <= 10000; n *= 10 { + for n := 1; n <= 100000; n *= 10 { b.Run(strconv.Itoa(n), func(b *testing.B) { db := setupTestDB[int, int](b) for i := range n { @@ -242,8 +256,6 @@ func BenchmarkDBGet(b *testing.B) { b.ReportAllocs() - b.ResetTimer() - for b.Loop() { if _, _, err := db.GetValue(n - 1); err != nil { b.Fatalf("unexpected error: %v", err) @@ -254,7 +266,7 @@ func BenchmarkDBGet(b *testing.B) { } func BenchmarkDBSet(b *testing.B) { - for n := 1; n <= 10000; n *= 10 { + for n := 1; n <= 100000; n *= 10 { b.Run(strconv.Itoa(n), func(b *testing.B) { db := setupTestDB[int, int](b) for i := range n - 1 { @@ -264,7 +276,6 @@ func BenchmarkDBSet(b *testing.B) { } b.ReportAllocs() - b.ResetTimer() for b.Loop() { if err := db.Set(n, n, 0); err != nil { @@ -276,7 +287,7 @@ func BenchmarkDBSet(b *testing.B) { } func BenchmarkDBDelete(b *testing.B) { - for n := 1; n <= 10000; n *= 10 { + for n := 1; n <= 100000; n *= 10 { b.Run(strconv.Itoa(n), func(b *testing.B) { db := setupTestDB[int, int](b) for i := range n - 1 { @@ -286,12 +297,12 @@ func BenchmarkDBDelete(b *testing.B) { } b.ReportAllocs() - b.ResetTimer() for b.Loop() { if err := db.Set(n, n, 0); err != nil { b.Fatalf("unexpected error: %v", err) } + if err := db.Delete(n); err != nil { b.Fatalf("unexpected error: %v", err) } diff --git a/encoding.go b/encoding.go index fe376b2..a8778ab 100644 --- a/encoding.go +++ b/encoding.go @@ -121,6 +121,7 @@ func (d *decoder) DecodeTime() (time.Time, error) { if t.IsZero() { t = zero[time.Time]() } + return t, nil } @@ -231,17 +232,21 @@ func (d *decoder) DecodeStore(s *store) error { } func (s *store) Snapshot(w io.WriteSeeker) error { - s.Lock.Lock() - defer s.Lock.Unlock() + s.Lock.RLock() + defer s.Lock.RUnlock() if _, err := w.Seek(0, io.SeekStart); err != nil { return err } wr := newEncoder(w) - defer wr.Flush() - return wr.EncodeStore(s) + err := wr.EncodeStore(s) + if err != nil { + return err + } + + return wr.Flush() } func (s *store) LoadSnapshot(r io.ReadSeeker) error { diff --git a/encoding_test.go b/encoding_test.go index dadde12..43abb46 100644 --- a/encoding_test.go +++ b/encoding_test.go @@ -10,6 +10,8 @@ import ( ) func TestDecodeUint64Error(t *testing.T) { + t.Parallel() + buf := bytes.NewReader([]byte{0xFF}) decoder := newDecoder(buf) @@ -21,6 +23,8 @@ func TestDecodeUint64Error(t *testing.T) { } func TestEncodeDecodeUint64(t *testing.T) { + t.Parallel() + tests := []struct { name string value uint64 @@ -32,6 +36,8 @@ func TestEncodeDecodeUint64(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var buf bytes.Buffer e := newEncoder(&buf) @@ -58,6 +64,8 @@ func TestEncodeDecodeUint64(t *testing.T) { } func TestEncodeDecodeTime(t *testing.T) { + t.Parallel() + tests := []struct { name string value time.Time @@ -69,12 +77,15 @@ func TestEncodeDecodeTime(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var buf bytes.Buffer e := newEncoder(&buf) if err := e.EncodeTime(tt.value); err != nil { t.Fatalf("unexpected error: %v", err) } + if err := e.Flush(); err != nil { t.Fatalf("unexpected error: %v", err) } @@ -89,18 +100,20 @@ func TestEncodeDecodeTime(t *testing.T) { if tt.value.Unix() != decodedValue.Unix() { t.Errorf("expected %v, got %v", tt.value, decodedValue) } - }) } } func TestDecodeBytesError(t *testing.T) { + t.Parallel() + var buf bytes.Buffer e := newEncoder(&buf) if err := e.EncodeBytes([]byte("DEADBEEF")); err != nil { t.Errorf("unexpected error: %v", err) } + if err := e.Flush(); err != nil { t.Errorf("unexpected error: %v", err) } @@ -113,6 +126,8 @@ func TestDecodeBytesError(t *testing.T) { } func TestEncodeDecodeBytes(t *testing.T) { + t.Parallel() + tests := []struct { name string value []byte @@ -124,12 +139,15 @@ func TestEncodeDecodeBytes(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var buf bytes.Buffer e := newEncoder(&buf) if err := e.EncodeBytes(tt.value); err != nil { t.Errorf("unexpected error: %v", err) } + if err := e.Flush(); err != nil { t.Errorf("unexpected error: %v", err) } @@ -149,6 +167,8 @@ func TestEncodeDecodeBytes(t *testing.T) { } func TestEncodeDecodeNode(t *testing.T) { + t.Parallel() + tests := []struct { name string value *node @@ -187,6 +207,8 @@ func TestEncodeDecodeNode(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var buf bytes.Buffer e := newEncoder(&buf) @@ -208,15 +230,21 @@ func TestEncodeDecodeNode(t *testing.T) { if tt.value.Hash != decodedValue.Hash { t.Errorf("expected %v, got %v", tt.value.Hash, decodedValue.Hash) } - if !tt.value.Expiration.Equal(decodedValue.Expiration) && tt.value.Expiration.Sub(decodedValue.Expiration) > time.Second { - t.Errorf("expected %v to be within %v of %v", decodedValue.Expiration, time.Second, tt.value.Expiration) + + if !tt.value.Expiration.Equal(decodedValue.Expiration) && + tt.value.Expiration.Sub(decodedValue.Expiration) > time.Second { + t.Errorf("expected %v to be within %v of %v", + decodedValue.Expiration, time.Second, tt.value.Expiration) } + if tt.value.Access != decodedValue.Access { t.Errorf("expected %v, got %v", tt.value.Access, decodedValue.Access) } + if !bytes.Equal(tt.value.Key, decodedValue.Key) { t.Errorf("expected %v, got %v", tt.value.Key, decodedValue.Key) } + if !bytes.Equal(tt.value.Value, decodedValue.Value) { t.Errorf("expected %v, got %v", tt.value.Value, decodedValue.Value) } @@ -225,6 +253,8 @@ func TestEncodeDecodeNode(t *testing.T) { } func TestEncodeDecodeStrorage(t *testing.T) { + t.Parallel() + tests := []struct { name string store map[string]string @@ -264,6 +294,8 @@ func TestEncodeDecodeStrorage(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var buf bytes.Buffer e := newEncoder(&buf) @@ -281,6 +313,7 @@ func TestEncodeDecodeStrorage(t *testing.T) { if err := e.EncodeStore(want); err != nil { t.Errorf("unexpected error: %v", err) } + if err := e.Flush(); err != nil { t.Errorf("unexpected error: %v", err) } @@ -295,9 +328,11 @@ func TestEncodeDecodeStrorage(t *testing.T) { if want.MaxCost != got.MaxCost { t.Errorf("expected %v, got %v", want.MaxCost, got.MaxCost) } + if want.Length != got.Length { t.Errorf("expected %v, got %v", want.Length, got.Length) } + if want.Policy.Type != got.Policy.Type { t.Errorf("expected %v, got %v", want.Policy.Type, got.Policy.Type) } @@ -314,6 +349,7 @@ func TestEncodeDecodeStrorage(t *testing.T) { if !ok { t.Fatalf("expected condition to be true") } + if !bytes.Equal([]byte(v), gotVal) { t.Fatalf("expected %v, got %v", []byte(v), gotVal) } @@ -322,14 +358,27 @@ func TestEncodeDecodeStrorage(t *testing.T) { } } -func BenchmarkStoreLoadSnapshot(b *testing.B) { - file, err := os.CreateTemp(b.TempDir(), "benchmark_test_") +func createTestFile(tb testing.TB, pattern string) *os.File { + tb.Helper() + + file, err := os.CreateTemp(tb.TempDir(), pattern) if err != nil { - b.Fatal(err) + tb.Fatal(err) } - defer os.Remove(file.Name()) - defer file.Close() + tb.Cleanup(func() { + if err := os.Remove(file.Name()); err != nil { + tb.Fatalf("unexpected error: %v", err) + } + + _ = file.Close() + }) + + return file +} + +func BenchmarkStoreSnapshot(b *testing.B) { + file := createTestFile(b, "benchmark_test_") for n := 1; n <= 10000; n *= 10 { b.Run(strconv.Itoa(n), func(b *testing.B) { @@ -341,7 +390,7 @@ func BenchmarkStoreLoadSnapshot(b *testing.B) { want.Set(buf, buf, 0) } - if err = want.Snapshot(file); err != nil { + if err := want.Snapshot(file); err != nil { b.Fatalf("unexpected error: %v", err) } @@ -349,11 +398,10 @@ func BenchmarkStoreLoadSnapshot(b *testing.B) { if err != nil { b.Fatalf("unexpected error: %v", err) } + b.SetBytes(int64(fileInfo.Size())) b.ReportAllocs() - b.ResetTimer() - for b.Loop() { if err := want.Snapshot(file); err != nil { b.Fatalf("unexpected error: %v", err) @@ -363,15 +411,8 @@ func BenchmarkStoreLoadSnapshot(b *testing.B) { } } -func BenchmarkStoreLoadSnapsot(b *testing.B) { - file, err := os.CreateTemp(b.TempDir(), "benchmark_test_") - - if err != nil { - b.Errorf("unexpected error: %v", err) - } - - defer os.Remove(file.Name()) - defer file.Close() +func BenchmarkStoreLoadSnapshot(b *testing.B) { + file := createTestFile(b, "benchmark_test_") for n := 1; n <= 10000; n *= 10 { b.Run(strconv.Itoa(n), func(b *testing.B) { @@ -383,19 +424,21 @@ func BenchmarkStoreLoadSnapsot(b *testing.B) { want.Set(buf, buf, 0) } - if err = want.Snapshot(file); err != nil { + if err := want.Snapshot(file); err != nil { b.Fatalf("unexpected error: %v", err) } + fileInfo, err := file.Stat() if err != nil { b.Fatalf("unexpected error: %v", err) } + b.SetBytes(int64(fileInfo.Size())) b.ReportAllocs() - b.ResetTimer() - for b.Loop() { + want.Clear() + if err := want.LoadSnapshot(file); err != nil { b.Fatalf("unexpected error: %v", err) } diff --git a/evict_test.go b/evict_test.go index d771844..4a24a74 100644 --- a/evict_test.go +++ b/evict_test.go @@ -30,7 +30,9 @@ func createPolicy(tb testing.TB, policyType EvictionPolicyType, flag bool) evict case PolicyLFU: return &lfuPolicy{List: createSentinel(tb), Lock: &sync.RWMutex{}} } + tb.Fatalf("unknown policy type: %v", policyType) + return nil } @@ -45,6 +47,7 @@ func getListOrder(tb testing.TB, evict *node) []*node { for _, n := range order { tb.Helper() + if n != n.EvictPrev.EvictNext { tb.Fatalf("expected %#v, got %#v", n, n.EvictPrev.EvictNext) } @@ -286,7 +289,6 @@ func TestPolicyEvict(t *testing.T) { actions: func(policy evictOrderedPolicy, nodes []*node) { policy.OnInsert(nodes[0]) policy.OnInsert(nodes[1]) - }, expected: func(nodes []*node) *node { return nodes[0] @@ -299,7 +301,6 @@ func TestPolicyEvict(t *testing.T) { actions: func(policy evictOrderedPolicy, nodes []*node) { policy.OnInsert(nodes[0]) policy.OnInsert(nodes[1]) - }, expected: func(nodes []*node) *node { return nil @@ -326,7 +327,6 @@ func TestPolicyEvict(t *testing.T) { actions: func(policy evictOrderedPolicy, nodes []*node) { policy.OnInsert(nodes[0]) policy.OnInsert(nodes[1]) - }, expected: func(nodes []*node) *node { return nodes[0] @@ -340,7 +340,6 @@ func TestPolicyEvict(t *testing.T) { policy.OnInsert(nodes[1]) policy.OnAccess(nodes[0]) - }, expected: func(nodes []*node) *node { return nodes[1] @@ -380,7 +379,6 @@ func TestPolicyEvict(t *testing.T) { actions: func(policy evictOrderedPolicy, nodes []*node) { policy.OnInsert(nodes[0]) policy.OnInsert(nodes[1]) - }, expected: func(nodes []*node) *node { return nodes[0] @@ -394,7 +392,6 @@ func TestPolicyEvict(t *testing.T) { policy.OnInsert(nodes[1]) policy.OnAccess(nodes[0]) - }, expected: func(nodes []*node) *node { return nodes[1] @@ -628,6 +625,7 @@ func TestSetPolicyMultipleTimes(t *testing.T) { if err != nil { t.Errorf("expected no error, got %v", err) } + if policy.Type != PolicyFIFO { t.Errorf("expected policy type %v, got %v", PolicyFIFO, policy.Type) } @@ -637,6 +635,7 @@ func TestSetPolicyMultipleTimes(t *testing.T) { if err != nil { t.Errorf("expected no error, got %v", err) } + if policy.Type != PolicyLRU { t.Errorf("expected policy type %v, got %v", PolicyLRU, policy.Type) } @@ -646,6 +645,7 @@ func TestSetPolicyMultipleTimes(t *testing.T) { if err != nil { t.Errorf("expected no error, got %v", err) } + if policy.Type != PolicyLFU { t.Errorf("expected policy type %v, got %v", PolicyLFU, policy.Type) } @@ -655,6 +655,7 @@ func TestSetPolicyMultipleTimes(t *testing.T) { if err != nil { t.Errorf("expected no error, got %v", err) } + if policy.Type != PolicyLTR { t.Errorf("expected policy type %v, got %v", PolicyLTR, policy.Type) } @@ -664,6 +665,7 @@ func TestSetPolicyMultipleTimes(t *testing.T) { if err != nil { t.Errorf("expected no error, got %v", err) } + if policy.Type != PolicyNone { t.Errorf("expected policy type %v, got %v", PolicyNone, policy.Type) } diff --git a/examples/basic_usage/main.go b/examples/basic_usage/main.go index 73e88ea..5b44cd9 100644 --- a/examples/basic_usage/main.go +++ b/examples/basic_usage/main.go @@ -14,11 +14,16 @@ func main() { fmt.Println("Error:", err) return } - defer db.Close() + + defer func() { + err := db.Close() + if err != nil { + fmt.Println("Error:", err) + } + }() // Set a value with a TTL of 5 seconds - err = db.Set("key1", "value1", 5*time.Second) - if err != nil { + if err = db.Set("key1", "value1", 5*time.Second); err != nil { fmt.Println("Set Error:", err) return } @@ -33,6 +38,7 @@ func main() { // Wait for 6 seconds and try to get the value again time.Sleep(6 * time.Second) + value, ttl, err = db.GetValue("key1") if err != nil { fmt.Println("Get Error after TTL:", err) diff --git a/examples/eviction_policy/main.go b/examples/eviction_policy/main.go index 5b5599a..fa56b8f 100644 --- a/examples/eviction_policy/main.go +++ b/examples/eviction_policy/main.go @@ -2,30 +2,65 @@ package main import ( "fmt" + "os" + "time" "github.com/marcthe12/cache" ) func main() { // Create an in-memory cache with LRU eviction policy - db, err := cache.OpenMem[string, string](cache.WithPolicy(cache.PolicyLRU)) + db, err := cache.OpenMem[string, string]( + cache.WithPolicy(cache.PolicyLRU), + cache.WithMaxCost(30), + cache.SetCleanupTime(1*time.Second), + ) if err != nil { fmt.Println("Error:", err) - return + os.Exit(1) } - defer db.Close() + + defer func() { + err := db.Close() + if err != nil { + fmt.Println("Error:", err) + } + }() // Set values - db.Set("key1", "value1", 0) - db.Set("key2", "value2", 0) - db.Set("key3", "value3", 0) + if err := db.Set("key1", "value1", 0); err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + if err := db.Set("key2", "value2", 0); err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + if err := db.Set("key3", "value3", 0); err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } // Access some keys - db.GetValue("key1") - db.GetValue("key2") + if _, _, err := db.GetValue("key1"); err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + if _, _, err := db.GetValue("key2"); err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } // Add another key to trigger eviction - db.Set("key4", "value4", 0) + if err := db.Set("key4", "value4", 0); err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + time.Sleep(2 * time.Second) // Check which keys are present for _, key := range []string{"key1", "key2", "key3", "key4"} { @@ -3,7 +3,7 @@ module github.com/marcthe12/cache go 1.24.0 require ( - github.com/rogpeppe/go-internal v1.14.0 + github.com/rogpeppe/go-internal v1.14.1 github.com/vmihailenco/msgpack/v5 v5.4.1 ) @@ -2,8 +2,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.14.0 h1:unbRd941gNa8SS77YznHXOYVBDgWcF9xhzECdm8juZc= -github.com/rogpeppe/go-internal v1.14.0/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= diff --git a/internal/pausedtimer/timer_test.go b/internal/pausedtimer/timer_test.go index 924c07c..5c54133 100644 --- a/internal/pausedtimer/timer_test.go +++ b/internal/pausedtimer/timer_test.go @@ -6,35 +6,47 @@ import ( ) func TestNew(t *testing.T) { + t.Parallel() + d := 1 * time.Second timer := New(d) + if timer.duration != d { t.Errorf("expected duration %#v, got %v", d, timer.duration) } + if timer.Ticker == nil { t.Error("expected Ticker to be non-nil") } } func TestPauseTimerZeroDuration(t *testing.T) { + t.Parallel() + timer := New(0) if timer.GetDuration() != 0 { t.Errorf("expected duration %v, got %v", time.Duration(0), timer.GetDuration()) } + if timer.Ticker == nil { t.Error("expected Ticker to be non-nil") } } func TestPauseTimerResetToZero(t *testing.T) { + t.Parallel() + timer := New(1 * time.Second) timer.Reset(0) + if timer.GetDuration() != 0 { t.Errorf("expected duration %v, got %v", time.Duration(0), timer.GetDuration()) } } func TestPauseTimerPauseAndResume(t *testing.T) { + t.Parallel() + d := 1 * time.Second timer := New(d) timer.Stop() // Simulate pause @@ -51,27 +63,36 @@ func TestPauseTimerPauseAndResume(t *testing.T) { } func TestPauseTimerReset(t *testing.T) { + t.Parallel() + d := 1 * time.Second timer := New(d) got := 2 * time.Second timer.Reset(got) + if timer.duration != got { t.Errorf("expected duration %v, got %v", got, timer.duration) } } func TestPauseTimerResume(t *testing.T) { + t.Parallel() + d := 1 * time.Second timer := NewStopped(d) timer.Resume() + if timer.duration != d { t.Errorf("expected duration %v, got %v", d, timer.duration) } } func TestPauseTimerGetDuration(t *testing.T) { + t.Parallel() + d := 1 * time.Second timer := New(d) + if timer.GetDuration() != d { t.Errorf("expected duration %v, got %v", d, timer.GetDuration()) } @@ -10,7 +10,7 @@ import ( const ( initialBucketSize uint64 = 8 - loadFactor float64 = 0.75 + loadFactor float64 = 0.9 ) // node represents an entry in the cache with metadata for eviction and expiration. @@ -142,8 +142,6 @@ func (s *store) Get(key []byte) ([]byte, time.Duration, bool) { v, _, _ := s.lookup(key) if v != nil { if !v.IsValid() { - //deleteNode(s, v) - return nil, 0, false } @@ -196,9 +194,11 @@ func (s *store) Cleanup() { for v := s.EvictList.EvictNext; v != &s.EvictList; { n := v.EvictNext + if !v.IsValid() { deleteNode(s, v) } + v = n } } @@ -220,6 +220,7 @@ func (s *store) Evict() bool { if n == nil { break } + deleteNode(s, n) } @@ -231,7 +232,7 @@ func (s *store) insert(key []byte, value []byte, ttl time.Duration) { idx, hash := lookupIdx(s, key) bucket := &s.Bucket[idx] - if float64(s.Length)/float64(len(s.Bucket)) > float64(loadFactor) { + if float64(s.Length) > loadFactor*float64(len(s.Bucket)) { s.Resize() // resize may invalidate pointer to bucket idx, _ = lookupIdx(s, key) @@ -270,14 +271,17 @@ func (s *store) Set(key []byte, value []byte, ttl time.Duration) { v, _, _ := s.lookup(key) if v != nil { cost := v.Cost() + v.Value = value if ttl != 0 { v.Expiration = time.Now().Add(ttl) } else { v.Expiration = zero[time.Time]() } + s.Cost = s.Cost + v.Cost() - cost s.Policy.OnUpdate(v) + return } @@ -330,12 +334,14 @@ func (s *store) UpdateInPlace(key []byte, processFunc func([]byte) ([]byte, erro } cost := v.Cost() + v.Value = value if ttl != 0 { v.Expiration = time.Now().Add(ttl) } else { v.Expiration = zero[time.Time]() } + s.Cost = s.Cost + v.Cost() - cost s.Policy.OnUpdate(v) @@ -360,5 +366,6 @@ func (s *store) Memorize(key []byte, factory func() ([]byte, error), ttl time.Du } s.insert(key, value, ttl) + return value, nil } diff --git a/store_test.go b/store_test.go index 4b9d2f3..2dc8a87 100644 --- a/store_test.go +++ b/store_test.go @@ -19,14 +19,19 @@ func setupTestStore(tb testing.TB) *store { } func TestNodeIsValid(t *testing.T) { + t.Parallel() + tests := []struct { name string node *node isValid bool }{ { - name: "Valid node with non-zero expiration", - node: &node{Key: []byte("key1"), Value: []byte("value1"), Expiration: time.Now().Add(10 * time.Minute)}, + name: "Valid node with non-zero expiration", + node: &node{ + Key: []byte("key1"), Value: []byte("value1"), + Expiration: time.Now().Add(10 * time.Minute), + }, isValid: true, }, { @@ -35,14 +40,19 @@ func TestNodeIsValid(t *testing.T) { isValid: true, }, { - name: "Expired node", - node: &node{Key: []byte("key3"), Value: []byte("value3"), Expiration: time.Now().Add(-1 * time.Minute)}, + name: "Expired node", + node: &node{ + Key: []byte("key3"), Value: []byte("value3"), + Expiration: time.Now().Add(-1 * time.Minute), + }, isValid: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + if got := tt.node.IsValid(); got != tt.isValid { t.Errorf("IsValid() = %v, want %v", got, tt.isValid) } @@ -51,6 +61,8 @@ func TestNodeIsValid(t *testing.T) { } func TestNodeTTL(t *testing.T) { + t.Parallel() + tests := []struct { name string node *node @@ -58,8 +70,11 @@ func TestNodeTTL(t *testing.T) { }{ { name: "Node with non-zero expiration", - node: &node{Key: []byte("key1"), Value: []byte("value1"), Expiration: time.Now().Add(10 * time.Minute)}, - ttl: 10 * time.Minute, + node: &node{ + Key: []byte("key1"), Value: []byte("value1"), + Expiration: time.Now().Add(10 * time.Minute), + }, + ttl: 10 * time.Minute, }, { name: "Node with zero expiration", @@ -68,13 +83,18 @@ func TestNodeTTL(t *testing.T) { }, { name: "Expired node", - node: &node{Key: []byte("key3"), Value: []byte("value3"), Expiration: time.Now().Add(-1 * time.Minute)}, - ttl: -1 * time.Minute, // Should be negative or 0, depending on the implementation + node: &node{ + Key: []byte("key3"), Value: []byte("value3"), + Expiration: time.Now().Add(-1 * time.Minute), + }, + ttl: -1 * time.Minute, // Should be negative or 0, depending on the implementation }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + if got := tt.node.TTL().Round(time.Second); got != tt.ttl { t.Errorf("TTL() = %v, want %v", got, tt.ttl) } @@ -83,6 +103,8 @@ func TestNodeTTL(t *testing.T) { } func TestNodeCost(t *testing.T) { + t.Parallel() + tests := []struct { name string node *node @@ -112,6 +134,8 @@ func TestNodeCost(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() + if got := tt.node.Cost(); got != tt.cost { t.Errorf("Cost() = %v, want %v", got, tt.cost) } @@ -129,17 +153,19 @@ func TestStoreGetSet(t *testing.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) { @@ -149,17 +175,19 @@ func TestStoreGetSet(t *testing.T) { want := []byte("Value") store.Set([]byte("Key"), want, 1*time.Hour) + 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) != 1*time.Hour { t.Errorf("ttl same: got %v expected %v", ttl.Round(time.Second), 1*time.Hour) } - }) t.Run("Exists TTL", func(t *testing.T) { @@ -169,6 +197,7 @@ func TestStoreGetSet(t *testing.T) { want := []byte("Value") store.Set([]byte("Key"), want, time.Nanosecond) + if _, _, ok := store.Get([]byte("Key")); ok { t.Errorf("expected key to not exist") } @@ -192,14 +221,15 @@ 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) } - }) t.Run("Resize", func(t *testing.T) { @@ -207,7 +237,9 @@ func TestStoreGetSet(t *testing.T) { store := setupTestStore(t) - for i := range initialBucketSize { + size := uint64(float64(len(store.Bucket))*loadFactor + 1) + + for i := range size + 1 { key := binary.LittleEndian.AppendUint64(nil, i) store.Set(key, key, 0) } @@ -220,7 +252,7 @@ func TestStoreGetSet(t *testing.T) { } if len(store.Bucket) != int(initialBucketSize)*2 { - t.Errorf("expected bucket size to be %v, got %v", initialBucketSize*2, len(store.Bucket)) + t.Errorf("expected bucket size to be %v, got %v", size*2, len(store.Bucket)) } for i := range store.Length { @@ -246,6 +278,7 @@ func TestStoreDelete(t *testing.T) { if !store.Delete([]byte("Key")) { t.Errorf("expected key to be deleted") } + if _, _, ok := store.Get([]byte("Key")); ok { t.Errorf("expected key to not exist") } @@ -270,6 +303,7 @@ func TestStoreClear(t *testing.T) { want := []byte("Value") store.Set([]byte("Key"), want, 0) store.Clear() + if _, _, ok := store.Get([]byte("Key")); ok { t.Errorf("expected key to not exist") } @@ -284,6 +318,7 @@ func TestStoreUpdateInPlace(t *testing.T) { store := setupTestStore(t) want := []byte("Value") + store.Set([]byte("Key"), []byte("Initial"), 1*time.Hour) processFunc := func(v []byte) ([]byte, error) { @@ -298,6 +333,7 @@ func TestStoreUpdateInPlace(t *testing.T) { if !ok { t.Fatalf("expected key to exist") } + if !bytes.Equal(want, got) { t.Errorf("got %v, want %v", got, want) } @@ -334,6 +370,7 @@ func TestStoreMemoize(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + if !bytes.Equal(got, []byte("Value")) { t.Fatalf("expected: %v, got: %v", "Value", got) } @@ -342,6 +379,7 @@ func TestStoreMemoize(t *testing.T) { if !ok { t.Fatalf("expected key to exist") } + if !bytes.Equal(got, []byte("Value")) { t.Fatalf("expected: %v, got: %v", "Value", got) } @@ -362,6 +400,7 @@ func TestStoreMemoize(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } + if !bytes.Equal(got, []byte("Value")) { t.Fatalf("expected: %v, got: %v", "Value", got) } @@ -444,6 +483,7 @@ func TestStoreEvict(t *testing.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) @@ -469,6 +509,7 @@ func TestStoreEvict(t *testing.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) @@ -494,6 +535,7 @@ func TestStoreEvict(t *testing.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) @@ -521,7 +563,7 @@ func BenchmarkStoreGet(b *testing.B) { } for k, v := range policy { b.Run(k, func(b *testing.B) { - for n := 1; n <= 10000; n *= 10 { + for n := 1; n <= 100000; n *= 10 { b.Run(strconv.Itoa(n), func(b *testing.B) { want := setupTestStore(b) @@ -539,8 +581,6 @@ func BenchmarkStoreGet(b *testing.B) { want.Set(key, []byte("Store"), 0) b.ReportAllocs() - b.ResetTimer() - for b.Loop() { want.Get(key) } @@ -560,7 +600,7 @@ func BenchmarkStoreGetParallel(b *testing.B) { } for k, v := range policy { b.Run(k, func(b *testing.B) { - for n := 1; n <= 10000; n *= 10 { + for n := 1; n <= 100000; n *= 10 { b.Run(strconv.Itoa(n), func(b *testing.B) { want := setupTestStore(b) @@ -578,8 +618,6 @@ func BenchmarkStoreGetParallel(b *testing.B) { want.Set(key, []byte("Store"), 0) b.ReportAllocs() - b.ResetTimer() - b.RunParallel(func(pb *testing.PB) { for pb.Next() { want.Get(key) @@ -601,7 +639,7 @@ func BenchmarkStoreSet(b *testing.B) { } for k, v := range policy { b.Run(k, func(b *testing.B) { - for n := 1; n <= 10000; n *= 10 { + for n := 1; n <= 100000; n *= 10 { b.Run(strconv.Itoa(n), func(b *testing.B) { want := setupTestStore(b) @@ -619,7 +657,6 @@ func BenchmarkStoreSet(b *testing.B) { store := []byte("Store") b.ReportAllocs() - b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { @@ -642,7 +679,7 @@ func BenchmarkStoreSetParallel(b *testing.B) { } for k, v := range policy { b.Run(k, func(b *testing.B) { - for n := 1; n <= 10000; n *= 10 { + for n := 1; n <= 100000; n *= 10 { b.Run(strconv.Itoa(n), func(b *testing.B) { want := setupTestStore(b) @@ -660,7 +697,6 @@ func BenchmarkStoreSetParallel(b *testing.B) { store := []byte("Store") b.ReportAllocs() - b.ResetTimer() for b.Loop() { want.Set(key, store, 0) @@ -681,7 +717,7 @@ func BenchmarkStoreSetInsert(b *testing.B) { } for k, v := range policy { b.Run(k, func(b *testing.B) { - for n := 1; n <= 10000; n *= 10 { + for n := 1; n <= 100000; n *= 10 { b.Run(strconv.Itoa(n), func(b *testing.B) { want := setupTestStore(b) @@ -690,6 +726,7 @@ func BenchmarkStoreSetInsert(b *testing.B) { } list := make([][]byte, n) + for i := range n { buf := make([]byte, 8) binary.LittleEndian.PutUint64(buf, uint64(i)) @@ -697,12 +734,13 @@ func BenchmarkStoreSetInsert(b *testing.B) { } b.ReportAllocs() - b.ResetTimer() for b.Loop() { for _, k := range list { want.Set(k, k, 0) } + + want.Clear() } }) } @@ -711,7 +749,7 @@ func BenchmarkStoreSetInsert(b *testing.B) { } func BenchmarkStoreDelete(b *testing.B) { - for n := 1; n <= 10000; n *= 10 { + for n := 1; n <= 100000; n *= 10 { b.Run(strconv.Itoa(n), func(b *testing.B) { want := setupTestStore(b) @@ -725,7 +763,6 @@ func BenchmarkStoreDelete(b *testing.B) { store := []byte("Store") b.ReportAllocs() - b.ResetTimer() for b.Loop() { want.Set(key, store, 0) |