From 55d0c043c16dff5b524de033de5ddb06c1d28a2d Mon Sep 17 00:00:00 2001 From: Francesco Cosentino Date: Sun, 15 Jan 2023 11:22:47 +0100 Subject: [PATCH 1/4] version fix --- go.mod | 2 +- go.sum | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 7dfab79..fe7f0b4 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/google/go-cmp v0.5.9 github.com/longbridgeapp/assert v1.1.0 github.com/shamaton/msgpack/v2 v2.1.1 - go.uber.org/zap v1.24.0 ) require ( @@ -18,5 +17,6 @@ require ( github.com/stretchr/testify v1.8.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.24.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index fdeca8d..78ab241 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -34,7 +33,6 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE= github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 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/shamaton/msgpack/v2 v2.1.1 h1:gAMxOtVJz93R0EwewwUc8tx30n34aV6BzJuwHE8ogAk= @@ -49,7 +47,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= From 36b5308be8efed77309351bdf934753b146ed308 Mon Sep 17 00:00:00 2001 From: Francesco Cosentino Date: Sun, 15 Jan 2023 11:44:46 +0100 Subject: [PATCH 2/4] minor cosmetics --- go.mod | 2 +- go.sum | 3 +++ middleware/stats.go | 2 +- models/item.go | 1 + stats/{histogramstatscollector.go => histogramcollector.go} | 0 5 files changed, 6 insertions(+), 2 deletions(-) rename stats/{histogramstatscollector.go => histogramcollector.go} (100%) diff --git a/go.mod b/go.mod index fe7f0b4..7dfab79 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/google/go-cmp v0.5.9 github.com/longbridgeapp/assert v1.1.0 github.com/shamaton/msgpack/v2 v2.1.1 + go.uber.org/zap v1.24.0 ) require ( @@ -17,6 +18,5 @@ require ( github.com/stretchr/testify v1.8.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect - go.uber.org/zap v1.24.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 78ab241..fdeca8d 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -33,6 +34,7 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE= github.com/onsi/gomega v1.24.2/go.mod h1:gs3J10IS7Z7r7eXRoNJIrNqU4ToQukCJhFtKrWgHWnk= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 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/shamaton/msgpack/v2 v2.1.1 h1:gAMxOtVJz93R0EwewwUc8tx30n34aV6BzJuwHE8ogAk= @@ -47,6 +49,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= diff --git a/middleware/stats.go b/middleware/stats.go index 6946b38..02b0c2d 100644 --- a/middleware/stats.go +++ b/middleware/stats.go @@ -20,7 +20,7 @@ func NewStatsCollectorMiddleware(next hypercache.Service, statsCollector stats.C return &StatsCollectorMiddleware{next: next, statsCollector: statsCollector} } -// Get +// Get collects stats for the Get method. func (mw StatsCollectorMiddleware) Get(key string) (interface{}, bool) { start := time.Now() defer func() { diff --git a/models/item.go b/models/item.go index 47309b0..2b5b8b1 100644 --- a/models/item.go +++ b/models/item.go @@ -98,6 +98,7 @@ func (item *Item) Expired() bool { // return dec.Decode(item) // } // + // MarshalBinary implements the encoding.BinaryMarshaler interface. func (item *Item) MarshalBinary() (data []byte, err error) { return msgpack.Marshal(item) diff --git a/stats/histogramstatscollector.go b/stats/histogramcollector.go similarity index 100% rename from stats/histogramstatscollector.go rename to stats/histogramcollector.go From 8ddb0bd46c66418259af906b8cd5bd231dadee20 Mon Sep 17 00:00:00 2001 From: Francesco Cosentino Date: Sun, 15 Jan 2023 12:07:58 +0100 Subject: [PATCH 3/4] cosmetics on code documentation and naming --- README.md | 26 +++--- backend/backend.go | 22 ++--- backend/inmemory.go | 36 ++++---- backend/options.go | 22 ++--- backend/redis.go | 4 +- config.go | 44 +++++----- examples/clear/clear.go | 2 +- examples/eviction/eviction.go | 16 ++-- examples/get/get.go | 2 +- examples/list/list.go | 8 +- examples/service/service.go | 4 +- examples/stats/stats.go | 20 ++--- hypercache.go | 88 ++++++++++--------- hypercache_test.go | 48 +++++----- middleware/stats.go | 4 +- stats/collector.go | 10 +-- .../hypercache_get_benchmark_test.go | 16 ++-- .../hypercache_list_benchmark_test.go | 4 +- .../hypercache_set_benchmark_test.go | 14 +-- tests/hypercache_get_multiple_test.go | 18 ++-- tests/hypercache_get_or_set_test.go | 2 +- tests/hypercache_get_test.go | 2 +- tests/hypercache_set_test.go | 2 +- utils/types.go | 6 +- 24 files changed, 211 insertions(+), 209 deletions(-) diff --git a/README.md b/README.md index 1b30790..8df032b 100644 --- a/README.md +++ b/README.md @@ -70,37 +70,37 @@ For a full list of examples, refer to the [examples](./examples/README.md) direc ## API -The `NewHyperCacheInMemoryWithDefaults` function creates a new `HyperCache` instance with the defaults: +The `NewInMemoryWithDefaults` function creates a new `HyperCache` instance with the defaults: 1. The eviction interval is set to 10 minutes. 2. The eviction algorithm is set to LRU. 3. The expiration interval is set to 30 minutes. 4. The capacity of the in-memory backend is set to 1000 items. -To create a new cache with a given capacity, use the NewHyperCache function as described below: +To create a new cache with a given capacity, use the New function as described below: ```golang -cache, err := hypercache.NewHyperCacheInMemoryWithDefaults(100) +cache, err := hypercache.NewInMemoryWithDefaults(100) if err != nil { // handle error } ``` -For a fine grained control over the cache configuration, use the `NewHyperCache` function, for instance: +For a fine grained control over the cache configuration, use the `New` function, for instance: ```golang -config := hypercache.NewConfig[backend.InMemoryBackend]() -config.HyperCacheOptions = []hypercache.HyperCacheOption[backend.InMemoryBackend]{ - hypercache.WithEvictionInterval[backend.InMemoryBackend](time.Minute * 10), - hypercache.WithEvictionAlgorithm[backend.InMemoryBackend]("cawolfu"), +config := hypercache.NewConfig[backend.InMemory]() +config.HyperCacheOptions = []hypercache.HyperCacheOption[backend.InMemory]{ + hypercache.WithEvictionInterval[backend.InMemory](time.Minute * 10), + hypercache.WithEvictionAlgorithm[backend.InMemory]("cawolfu"), } -config.InMemoryBackendOptions = []backend.BackendOption[backend.InMemoryBackend]{ +config.InMemoryOptions = []backend.Option[backend.InMemory]{ backend.WithCapacity(10), } // Create a new HyperCache with a capacity of 10 -cache, err := hypercache.NewHyperCache(config) +cache, err := hypercache.New(config) if err != nil { fmt.Println(err) return @@ -156,7 +156,7 @@ The `Service` interface allows intercepting cache methods and decorate them with ```golang var svc hypercache.HyperCacheService -hyperCache, err := hypercache.NewHyperCacheInMemoryWithDefaults(10) +hyperCache, err := hypercache.NewInMemoryWithDefaults(10) if err != nil { fmt.Println(err) @@ -185,7 +185,7 @@ svc = hypercache.ApplyMiddleware(svc, return middleware.NewLoggingMiddleware(next, sugar) }, func(next hypercache.HyperCacheService) hypercache.HyperCacheService { - return middleware.NewStatsCollectorMiddleware(next, statsCollector) + return middleware.NewCollectorMiddleware(next, statsCollector) }, ) @@ -219,7 +219,7 @@ import ( func main() { // Create a new HyperCache with a capacity of 10 - cache, err := hypercache.NewHyperCacheInMemoryWithDefaults(10) + cache, err := hypercache.NewInMemoryWithDefaults(10) if err != nil { fmt.Println(err) return diff --git a/backend/backend.go b/backend/backend.go index 4e17b35..ba12f2e 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -7,15 +7,15 @@ import ( // IBackendConstrain is the interface that defines the constrain type that must be implemented by cache backends. type IBackendConstrain interface { - InMemoryBackend | RedisBackend + InMemory | RedisBackend } -// IInMemoryBackend is the interface that must be implemented by in-memory cache backends. -type IInMemoryBackend[T IBackendConstrain] interface { +// IInMemory is the interface that must be implemented by in-memory cache backends. +type IInMemory[T IBackendConstrain] interface { // IBackend[T] is the interface that must be implemented by cache backends. IBackend[T] // List the items in the cache that meet the specified criteria. - List(options ...FilterOption[InMemoryBackend]) ([]*models.Item, error) + List(options ...FilterOption[InMemory]) ([]*models.Item, error) // Clear removes all items from the cache. Clear() } @@ -48,21 +48,21 @@ type IBackend[T IBackendConstrain] interface { } // NewBackend creates a new cache backend. -// Deprecated: Use specific backend constructors instead, e.g. NewInMemoryBackend or NewRedisBackend. +// Deprecated: Use specific backend constructors instead, e.g. NewInMemory or NewRedisBackend. func NewBackend[T IBackendConstrain](backendType string, opts ...any) (IBackend[T], error) { switch backendType { case "memory": - backendOptions := make([]BackendOption[InMemoryBackend], len(opts)) + Options := make([]Option[InMemory], len(opts)) for i, option := range opts { - backendOptions[i] = option.(BackendOption[InMemoryBackend]) + Options[i] = option.(Option[InMemory]) } - return NewInMemoryBackend(backendOptions...) + return NewInMemory(Options...) case "redis": - backendOptions := make([]BackendOption[RedisBackend], len(opts)) + Options := make([]Option[RedisBackend], len(opts)) for i, option := range opts { - backendOptions[i] = option.(BackendOption[RedisBackend]) + Options[i] = option.(Option[RedisBackend]) } - return NewRedisBackend(backendOptions...) + return NewRedisBackend(Options...) default: return nil, errors.ErrInvalidBackendType } diff --git a/backend/inmemory.go b/backend/inmemory.go index 97d218d..07f63cd 100644 --- a/backend/inmemory.go +++ b/backend/inmemory.go @@ -11,32 +11,32 @@ import ( "github.com/hyp3rd/hypercache/types" ) -// InMemoryBackend is a cache backend that stores the items in memory, leveraging a custom `ConcurrentMap`. -type InMemoryBackend struct { +// InMemory is a cache backend that stores the items in memory, leveraging a custom `ConcurrentMap`. +type InMemory struct { items datastructure.ConcurrentMap[string, *models.Item] // map to store the items in the cache capacity int // capacity of the cache, limits the number of items that can be stored in the cache mutex sync.RWMutex // mutex to protect the cache from concurrent access SortFilters // filters applied when listing the items in the cache } -// NewInMemoryBackend creates a new in-memory cache with the given options. -func NewInMemoryBackend[T InMemoryBackend](opts ...BackendOption[InMemoryBackend]) (backend IInMemoryBackend[T], err error) { +// NewInMemory creates a new in-memory cache with the given options. +func NewInMemory[T InMemory](opts ...Option[InMemory]) (backend IInMemory[T], err error) { - inMemoryBackend := &InMemoryBackend{ + InMemory := &InMemory{ items: datastructure.New[*models.Item](), } - ApplyBackendOptions(inMemoryBackend, opts...) + ApplyOptions(InMemory, opts...) - if inMemoryBackend.capacity < 0 { + if InMemory.capacity < 0 { return nil, errors.ErrInvalidCapacity } - return inMemoryBackend, nil + return InMemory, nil } // SetCapacity sets the capacity of the cache. -func (cacheBackend *InMemoryBackend) SetCapacity(capacity int) { +func (cacheBackend *InMemory) SetCapacity(capacity int) { if capacity < 0 { return } @@ -45,12 +45,12 @@ func (cacheBackend *InMemoryBackend) SetCapacity(capacity int) { } // itemCount returns the number of items in the cache. -func (cacheBackend *InMemoryBackend) itemCount() int { +func (cacheBackend *InMemory) itemCount() int { return cacheBackend.items.Count() } // Get retrieves the item with the given key from the cacheBackend. If the item is not found, it returns nil. -func (cacheBackend *InMemoryBackend) Get(key string) (item *models.Item, ok bool) { +func (cacheBackend *InMemory) Get(key string) (item *models.Item, ok bool) { item, ok = cacheBackend.items.Get(key) if !ok { return nil, false @@ -61,7 +61,7 @@ func (cacheBackend *InMemoryBackend) Get(key string) (item *models.Item, ok bool } // Set adds a Item to the cache. -func (cacheBackend *InMemoryBackend) Set(item *models.Item) error { +func (cacheBackend *InMemory) Set(item *models.Item) error { // Check for invalid key, value, or duration if err := item.Valid(); err != nil { models.ItemPool.Put(item) @@ -76,7 +76,7 @@ func (cacheBackend *InMemoryBackend) Set(item *models.Item) error { } // List the items in the cache that meet the specified criteria. -// func (cacheBackend *InMemoryBackend) List(options ...FilterOption[InMemoryBackend]) ([]*models.Item, error) { +// func (cacheBackend *InMemory) List(options ...FilterOption[InMemory]) ([]*models.Item, error) { // // Apply the filter options // ApplyFilterOptions(cacheBackend, options...) @@ -129,7 +129,7 @@ func (cacheBackend *InMemoryBackend) Set(item *models.Item) error { // } // List returns a list of all items in the cache filtered and ordered by the given options -func (cacheBackend *InMemoryBackend) List(options ...FilterOption[InMemoryBackend]) ([]*models.Item, error) { +func (cacheBackend *InMemory) List(options ...FilterOption[InMemory]) ([]*models.Item, error) { // Apply the filter options ApplyFilterOptions(cacheBackend, options...) @@ -169,7 +169,7 @@ func (cacheBackend *InMemoryBackend) List(options ...FilterOption[InMemoryBacken } // Remove removes items with the given key from the cacheBackend. If an item is not found, it does nothing. -func (cacheBackend *InMemoryBackend) Remove(keys ...string) (err error) { +func (cacheBackend *InMemory) Remove(keys ...string) (err error) { //TODO: determine if handling the error or not for _, key := range keys { cacheBackend.items.Remove(key) @@ -178,18 +178,18 @@ func (cacheBackend *InMemoryBackend) Remove(keys ...string) (err error) { } // Clear removes all items from the cacheBackend. -func (cacheBackend *InMemoryBackend) Clear() { +func (cacheBackend *InMemory) Clear() { for item := range cacheBackend.items.IterBuffered() { cacheBackend.items.Remove(item.Key) } } // Capacity returns the capacity of the cacheBackend. -func (cacheBackend *InMemoryBackend) Capacity() int { +func (cacheBackend *InMemory) Capacity() int { return cacheBackend.capacity } // Size returns the number of items in the cacheBackend. -func (cacheBackend *InMemoryBackend) Size() int { +func (cacheBackend *InMemory) Size() int { return cacheBackend.itemCount() } diff --git a/backend/options.go b/backend/options.go index 4b4a4c5..72e1507 100644 --- a/backend/options.go +++ b/backend/options.go @@ -6,25 +6,25 @@ import ( "github.com/hyp3rd/hypercache/types" ) -// BackendOption is a function type that can be used to configure the `HyperCache` struct. -type BackendOption[T IBackendConstrain] func(*T) +// Option is a function type that can be used to configure the `HyperCache` struct. +type Option[T IBackendConstrain] func(*T) -// ApplyBackendOptions applies the given options to the given backend. -func ApplyBackendOptions[T IBackendConstrain](backend *T, options ...BackendOption[T]) { +// ApplyOptions applies the given options to the given backend. +func ApplyOptions[T IBackendConstrain](backend *T, options ...Option[T]) { for _, option := range options { option(backend) } } // WithCapacity is an option that sets the capacity of the cache. -func WithCapacity[T InMemoryBackend](capacity int) BackendOption[InMemoryBackend] { - return func(backend *InMemoryBackend) { +func WithCapacity[T InMemory](capacity int) Option[InMemory] { + return func(backend *InMemory) { backend.capacity = capacity } } // WithRedisClient is an option that sets the redis client to use. -func WithRedisClient[T RedisBackend](client *redis.Client) BackendOption[RedisBackend] { +func WithRedisClient[T RedisBackend](client *redis.Client) Option[RedisBackend] { return func(backend *RedisBackend) { backend.client = client } @@ -45,7 +45,7 @@ func ApplyFilterOptions[T any](backend *T, options ...FilterOption[T]) { func WithSortBy[T any](field types.SortingField) FilterOption[T] { return func(a *T) { switch filter := any(a).(type) { - case *InMemoryBackend: + case *InMemory: filter.SortBy = field.String() } } @@ -56,7 +56,7 @@ func WithSortBy[T any](field types.SortingField) FilterOption[T] { func WithSortAscending[T any]() FilterOption[T] { return func(a *T) { switch filter := any(a).(type) { - case *InMemoryBackend: + case *InMemory: filter.SortAscending = true } } @@ -67,7 +67,7 @@ func WithSortAscending[T any]() FilterOption[T] { func WithSortDescending[T any]() FilterOption[T] { return func(a *T) { switch filter := any(a).(type) { - case *InMemoryBackend: + case *InMemory: filter.SortAscending = false } } @@ -78,7 +78,7 @@ func WithSortDescending[T any]() FilterOption[T] { func WithFilterFunc[T any](fn func(item *models.Item) bool) FilterOption[T] { return func(a *T) { switch filter := any(a).(type) { - case *InMemoryBackend: + case *InMemory: filter.FilterFunc = fn } } diff --git a/backend/redis.go b/backend/redis.go index 70d7a45..13aaec8 100644 --- a/backend/redis.go +++ b/backend/redis.go @@ -18,10 +18,10 @@ type RedisBackend struct { } // NewRedisBackend creates a new redis cache with the given options. -func NewRedisBackend[T RedisBackend](redisOptions ...BackendOption[RedisBackend]) (backend IRedisBackend[T], err error) { +func NewRedisBackend[T RedisBackend](redisOptions ...Option[RedisBackend]) (backend IRedisBackend[T], err error) { rb := &RedisBackend{} // Apply the backend options - ApplyBackendOptions(rb, redisOptions...) + ApplyOptions(rb, redisOptions...) // Check if the client is nil if rb.client == nil { diff --git a/config.go b/config.go index bc6f1d7..0cec5ae 100644 --- a/config.go +++ b/config.go @@ -8,28 +8,28 @@ import ( // Config is a struct that wraps all the configuration options to setup `HyperCache` and its backend. type Config[T backend.IBackendConstrain] struct { - // InMemoryBackendOptions is a slice of options that can be used to configure the `InMemoryBackend`. - InMemoryBackendOptions []backend.BackendOption[backend.InMemoryBackend] - // RedisBackendOptions is a slice of options that can be used to configure the `RedisBackend`. - RedisBackendOptions []backend.BackendOption[backend.RedisBackend] + // InMemoryOptions is a slice of options that can be used to configure the `InMemory`. + InMemoryOptions []backend.Option[backend.InMemory] + // RedisOptions is a slice of options that can be used to configure the `RedisBackend`. + RedisOptions []backend.Option[backend.RedisBackend] // HyperCacheOptions is a slice of options that can be used to configure `HyperCache`. HyperCacheOptions []Option[T] } // NewConfig returns a new `Config` struct with default values: -// - `InMemoryBackendOptions` is empty -// - `RedisBackendOptions` is empty -// - `HyperCacheOptions` is set to: -// - `WithExpirationInterval[T](30 * time.Minute)` -// - `WithEvictionAlgorithm[T]("lfu")` -// - `WithEvictionInterval[T](10 * time.Minute)` +// - `InMemoryOptions` is empty +// - `RedisOptions` is empty +// - `HyperCacheOptions` is set to: +// -- `WithExpirationInterval[T](30 * time.Minute)` +// -- `WithEvictionAlgorithm[T]("lfu")` +// -- `WithEvictionInterval[T](10 * time.Minute)` // // Each of the above options can be overridden by passing a different option to the `NewConfig` function. // It can be used to configure `HyperCache` and its backend and customize the behavior of the cache. func NewConfig[T backend.IBackendConstrain]() *Config[T] { return &Config[T]{ - InMemoryBackendOptions: []backend.BackendOption[backend.InMemoryBackend]{}, - RedisBackendOptions: []backend.BackendOption[backend.RedisBackend]{}, + InMemoryOptions: []backend.Option[backend.InMemory]{}, + RedisOptions: []backend.Option[backend.RedisBackend]{}, HyperCacheOptions: []Option[T]{ WithExpirationInterval[T](30 * time.Minute), WithEvictionAlgorithm[T]("lfu"), @@ -51,16 +51,16 @@ func ApplyHyperCacheOptions[T backend.IBackendConstrain](cache *HyperCache[T], o // WithEvictionAlgorithm is an option that sets the eviction algorithm name field of the `HyperCache` struct. // The eviction algorithm name determines which eviction algorithm will be used to evict items from the cache. // The eviction algorithm name must be one of the following: -// - "LRU" (Least Recently Used) - Implemented in the `lru.go` file -// - "LFU" (Least Frequently Used) - Implemented in the `lfu.go` file -// - "CAWOLFU" (Cache-Aware Write-Optimized LFU) - Implemented in the `cawolfu.go` file -// - "FIFO" (First In First Out) -// - "RANDOM" (Random) -// - "CLOCK" (Clock) - Implemented in the `clock.go` file -// - "ARC" (Adaptive Replacement Cache) - Implemented in the `arc.go` file -// - "TTL" (Time To Live) -// - "LFUDA" (Least Frequently Used with Dynamic Aging) -// - "SLRU" (Segmented Least Recently Used) +// - "LRU" (Least Recently Used) - Implemented in the `eviction/lru.go` file +// - "LFU" (Least Frequently Used) - Implemented in the `eviction/lfu.go` file +// - "CAWOLFU" (Cache-Aware Write-Optimized LFU) - Implemented in the `eviction/cawolfu.go` file +// - "FIFO" (First In First Out) +// - "RANDOM" (Random) +// - "CLOCK" (Clock) - Implemented in the `eviction/clock.go` file +// - "ARC" (Adaptive Replacement Cache) - Implemented in the `eviction/arc.go` file +// - "TTL" (Time To Live) +// - "LFUDA" (Least Frequently Used with Dynamic Aging) +// - "SLRU" (Segmented Least Recently Used) func WithEvictionAlgorithm[T backend.IBackendConstrain](name string) Option[T] { return func(cache *HyperCache[T]) { cache.evictionAlgorithmName = name diff --git a/examples/clear/clear.go b/examples/clear/clear.go index b34b147..57d85e3 100644 --- a/examples/clear/clear.go +++ b/examples/clear/clear.go @@ -8,7 +8,7 @@ import ( func main() { // Create a new HyperCache with a capacity of 10000 - cache, err := hypercache.NewHyperCacheInMemoryWithDefaults(100000) + cache, err := hypercache.NewInMemoryWithDefaults(100000) if err != nil { fmt.Println(err) return diff --git a/examples/eviction/eviction.go b/examples/eviction/eviction.go index ce566c0..1462141 100644 --- a/examples/eviction/eviction.go +++ b/examples/eviction/eviction.go @@ -22,18 +22,18 @@ func main() { // executeExample runs the example func executeExample(evictionInterval time.Duration) { // Create a new HyperCache with a capacity of 10 - config := hypercache.NewConfig[backend.InMemoryBackend]() - config.HyperCacheOptions = []hypercache.Option[backend.InMemoryBackend]{ - hypercache.WithEvictionInterval[backend.InMemoryBackend](evictionInterval), - hypercache.WithEvictionAlgorithm[backend.InMemoryBackend]("cawolfu"), + config := hypercache.NewConfig[backend.InMemory]() + config.HyperCacheOptions = []hypercache.Option[backend.InMemory]{ + hypercache.WithEvictionInterval[backend.InMemory](evictionInterval), + hypercache.WithEvictionAlgorithm[backend.InMemory]("cawolfu"), } - config.InMemoryBackendOptions = []backend.BackendOption[backend.InMemoryBackend]{ + config.InMemoryOptions = []backend.Option[backend.InMemory]{ backend.WithCapacity(10), } // Create a new HyperCache with a capacity of 10 - cache, err := hypercache.NewHyperCache(config) + cache, err := hypercache.New(config) if err != nil { fmt.Println(err) return @@ -60,7 +60,7 @@ func executeExample(evictionInterval time.Duration) { log.Println("capacity after adding 15 items", cache.Capacity()) log.Println("listing all items in the cache") - list, err := cache.List(backend.WithSortBy[backend.InMemoryBackend](types.SortByValue)) + list, err := cache.List(backend.WithSortBy[backend.InMemory](types.SortByValue)) if err != nil { fmt.Println(err) return @@ -75,7 +75,7 @@ func executeExample(evictionInterval time.Duration) { fmt.Println("sleeping to allow the evition loop to complete", evictionInterval+2*time.Second) time.Sleep(evictionInterval + 2*time.Second) log.Println("listing all items in the cache the eviction is triggered") - list, err = cache.List(backend.WithSortBy[backend.InMemoryBackend](types.SortByValue)) + list, err = cache.List(backend.WithSortBy[backend.InMemory](types.SortByValue)) if err != nil { fmt.Println(err) return diff --git a/examples/get/get.go b/examples/get/get.go index d212cdd..53c2f8f 100644 --- a/examples/get/get.go +++ b/examples/get/get.go @@ -10,7 +10,7 @@ import ( func main() { // Create a new HyperCache with a capacity of 10 - cache, err := hypercache.NewHyperCacheInMemoryWithDefaults(10) + cache, err := hypercache.NewInMemoryWithDefaults(10) if err != nil { fmt.Println(err) return diff --git a/examples/list/list.go b/examples/list/list.go index 64e3a3d..2d2c22b 100644 --- a/examples/list/list.go +++ b/examples/list/list.go @@ -13,7 +13,7 @@ import ( // This example demonstrates how to list items from the cache func main() { // Create a new HyperCache with a capacity of 100 - hyperCache, err := hypercache.NewHyperCacheInMemoryWithDefaults(100) + hyperCache, err := hypercache.NewInMemoryWithDefaults(100) if err != nil { fmt.Println(err) @@ -37,9 +37,9 @@ func main() { // Retrieve the list of items from the cache list, err := hyperCache.List( - backend.WithSortBy[backend.InMemoryBackend](types.SortByValue), - backend.WithSortAscending[backend.InMemoryBackend](), - backend.WithFilterFunc[backend.InMemoryBackend](func(item *models.Item) bool { + backend.WithSortBy[backend.InMemory](types.SortByValue), + backend.WithSortAscending[backend.InMemory](), + backend.WithFilterFunc[backend.InMemory](func(item *models.Item) bool { return item.Value != "val98" }), ) diff --git a/examples/service/service.go b/examples/service/service.go index 3ab7987..ad65c91 100644 --- a/examples/service/service.go +++ b/examples/service/service.go @@ -10,7 +10,7 @@ import ( func main() { var svc hypercache.Service - hyperCache, err := hypercache.NewHyperCacheInMemoryWithDefaults(10) + hyperCache, err := hypercache.NewInMemoryWithDefaults(10) defer hyperCache.Stop() if err != nil { @@ -40,7 +40,7 @@ func main() { return middleware.NewLoggingMiddleware(next, sugar) }, func(next hypercache.Service) hypercache.Service { - return middleware.NewStatsCollectorMiddleware(next, statsCollector) + return middleware.NewCollectorMiddleware(next, statsCollector) }, ) defer svc.Stop() diff --git a/examples/stats/stats.go b/examples/stats/stats.go index 9adc9b0..38fdc2b 100644 --- a/examples/stats/stats.go +++ b/examples/stats/stats.go @@ -12,19 +12,19 @@ import ( func main() { // Create a new HyperCache with a capacity of 100 - config := hypercache.NewConfig[backend.InMemoryBackend]() - config.HyperCacheOptions = []hypercache.Option[backend.InMemoryBackend]{ - hypercache.WithEvictionInterval[backend.InMemoryBackend](3 * time.Second), - hypercache.WithEvictionAlgorithm[backend.InMemoryBackend]("lru"), - hypercache.WithExpirationInterval[backend.InMemoryBackend](3 * time.Second), + config := hypercache.NewConfig[backend.InMemory]() + config.HyperCacheOptions = []hypercache.Option[backend.InMemory]{ + hypercache.WithEvictionInterval[backend.InMemory](3 * time.Second), + hypercache.WithEvictionAlgorithm[backend.InMemory]("lru"), + hypercache.WithExpirationInterval[backend.InMemory](3 * time.Second), } - config.InMemoryBackendOptions = []backend.BackendOption[backend.InMemoryBackend]{ + config.InMemoryOptions = []backend.Option[backend.InMemory]{ backend.WithCapacity(100), } // Create a new HyperCache with a capacity of 10 - hyperCache, err := hypercache.NewHyperCache(config) + hyperCache, err := hypercache.New(config) if err != nil { fmt.Println(err) return @@ -51,9 +51,9 @@ func main() { // Retrieve the list of items from the cache list, err := hyperCache.List( - backend.WithSortBy[backend.InMemoryBackend](types.SortByValue), - backend.WithSortDescending[backend.InMemoryBackend](), - backend.WithFilterFunc[backend.InMemoryBackend](func(item *models.Item) bool { + backend.WithSortBy[backend.InMemory](types.SortByValue), + backend.WithSortDescending[backend.InMemory](), + backend.WithFilterFunc[backend.InMemory](func(item *models.Item) bool { return item.Expiration > time.Second }), ) diff --git a/hypercache.go b/hypercache.go index 44d0144..8578fdd 100644 --- a/hypercache.go +++ b/hypercache.go @@ -24,11 +24,13 @@ import ( // The default in-memory implementation has a custom `ConcurrentMap` to store the items in the cache, // The configuration is provided by the `Config` struct and can be customized by using the `With` functions. // The cache has two loops that run in the background: -// 1. The expiration loop runs every `expirationInterval` and checks for expired items. -// 2. The eviction loop runs every `evictionInterval` and evicts items using the eviction algorithm. -// The cache also has two channels that are used to signal the expiration and eviction loops to start: -// 1. The expirationTriggerCh channel is used to signal the expiration loop to start. -// 2. The evictCh channel is used to signal the eviction loop to start. +// - The expiration loop runs every `expirationInterval` and checks for expired items. +// - The eviction loop runs every `evictionInterval` and evicts items using the eviction algorithm. +// +// The cache leverages two channels to signal the expiration and eviction loops to start: +// - The expirationTriggerCh channel is used to signal the expiration loop to start. +// - The evictCh channel is used to signal the eviction loop to start. +// // The cache also has a mutex that is used to protect the eviction algorithm from concurrent access. // The stop channel is used to signal the expiration and eviction loops to stop. The evictCh channel is used to signal the eviction loop to start. type HyperCache[T backend.IBackendConstrain] struct { @@ -50,41 +52,41 @@ type HyperCache[T backend.IBackendConstrain] struct { StatsCollector stats.Collector } -// NewHyperCacheInMemoryWithDefaults initializes a new HyperCache with the default configuration. +// NewInMemoryWithDefaults initializes a new HyperCache with the default configuration. // The default configuration is: -// 1. The eviction interval is set to 10 minutes. -// 2. The eviction algorithm is set to LRU. -// 3. The expiration interval is set to 30 minutes. -// 4. The capacity of the in-memory backend is set to 1000 items. -func NewHyperCacheInMemoryWithDefaults(capacity int) (hyperCache *HyperCache[backend.InMemoryBackend], err error) { +// - The eviction interval is set to 10 minutes. +// - The eviction algorithm is set to LRU. +// - The expiration interval is set to 30 minutes. +// - The capacity of the in-memory backend is set to 1000 items. +func NewInMemoryWithDefaults(capacity int) (hyperCache *HyperCache[backend.InMemory], err error) { // Initialize the configuration - config := NewConfig[backend.InMemoryBackend]() + config := NewConfig[backend.InMemory]() // Set the default options - config.HyperCacheOptions = []Option[backend.InMemoryBackend]{ - WithEvictionInterval[backend.InMemoryBackend](10 * time.Minute), - WithEvictionAlgorithm[backend.InMemoryBackend]("lru"), - WithExpirationInterval[backend.InMemoryBackend](30 * time.Minute), + config.HyperCacheOptions = []Option[backend.InMemory]{ + WithEvictionInterval[backend.InMemory](10 * time.Minute), + WithEvictionAlgorithm[backend.InMemory]("lru"), + WithExpirationInterval[backend.InMemory](30 * time.Minute), } // Set the in-memory backend options - config.InMemoryBackendOptions = []backend.BackendOption[backend.InMemoryBackend]{ + config.InMemoryOptions = []backend.Option[backend.InMemory]{ backend.WithCapacity(capacity), } // Initialize the cache - hyperCache, err = NewHyperCache(config) + hyperCache, err = New(config) if err != nil { return nil, err } return hyperCache, nil } -// NewHyperCache initializes a new HyperCache with the given configuration. +// New initializes a new HyperCache with the given configuration. // The default configuration is: -// 1. The eviction interval is set to 10 minutes. -// 2. The eviction algorithm is set to CAWOLFU. -// 3. The expiration interval is set to 30 minutes. -// 4. The stats collector is set to the HistogramStatsCollector stats collector. -func NewHyperCache[T backend.IBackendConstrain](config *Config[T]) (hyperCache *HyperCache[T], err error) { +// - The eviction interval is set to 10 minutes. +// - The eviction algorithm is set to CAWOLFU. +// - The expiration interval is set to 30 minutes. +// - The stats collector is set to the HistogramStatsCollector stats collector. +func New[T backend.IBackendConstrain](config *Config[T]) (hyperCache *HyperCache[T], err error) { // Initialize the cache hyperCache = &HyperCache[T]{ @@ -96,10 +98,10 @@ func NewHyperCache[T backend.IBackendConstrain](config *Config[T]) (hyperCache * // Initialize the backend t, _ := utils.TypeName(hyperCache.backendType) // Get the backend type name switch t { - case "backend.InMemoryBackend": - hyperCache.backend, err = backend.NewInMemoryBackend(config.InMemoryBackendOptions...) + case "backend.InMemory": + hyperCache.backend, err = backend.NewInMemory(config.InMemoryOptions...) case "backend.RedisBackend": - hyperCache.backend, err = backend.NewRedisBackend(config.RedisBackendOptions...) + hyperCache.backend, err = backend.NewRedisBackend(config.RedisOptions...) default: err = errors.ErrInvalidBackendType } @@ -136,11 +138,11 @@ func NewHyperCache[T backend.IBackendConstrain](config *Config[T]) (hyperCache * // Initialize the stats collector if hyperCache.statsCollectorName == "" { // Use the default stats collector if none is specified - // hyperCache.statsCollector, err = NewStatsCollector("default") + // hyperCache.statsCollector, err = NewCollector("default") hyperCache.StatsCollector = stats.NewHistogramStatsCollector() } else { // Use the specified stats collector - hyperCache.StatsCollector, err = stats.NewStatsCollector(hyperCache.statsCollectorName) + hyperCache.StatsCollector, err = stats.NewCollector(hyperCache.statsCollectorName) if err != nil { return } @@ -209,10 +211,10 @@ func (hyperCache *HyperCache[T]) expirationLoop() { ) // get all expired items - if cb, ok := hyperCache.backend.(*backend.InMemoryBackend); ok { + if cb, ok := hyperCache.backend.(*backend.InMemory); ok { items, err = cb.List( - backend.WithSortBy[backend.InMemoryBackend](types.SortByExpiration), - backend.WithFilterFunc[backend.InMemoryBackend](func(item *models.Item) bool { + backend.WithSortBy[backend.InMemory](types.SortByExpiration), + backend.WithFilterFunc[backend.InMemory](func(item *models.Item) bool { return item.Expiration > 0 && time.Since(item.LastAccess) > item.Expiration }), ) @@ -478,9 +480,9 @@ func (hyperCache *HyperCache[T]) List(filters ...any) ([]*models.Item, error) { var listInstance listFunc // checking the backend type - if hyperCache.cacheBackendChecker.IsInMemoryBackend() { - // if the backend is an InMemoryBackend, we set the listFunc to the ListInMemory function - listInstance = listInMemory(hyperCache.backend.(*backend.InMemoryBackend)) + if hyperCache.cacheBackendChecker.IsInMemory() { + // if the backend is an InMemory, we set the listFunc to the ListInMemory function + listInstance = listInMemory(hyperCache.backend.(*backend.InMemory)) } // calling the corresponding implementation of the list function @@ -491,15 +493,15 @@ func (hyperCache *HyperCache[T]) List(filters ...any) ([]*models.Item, error) { // a slice of Item pointers, and an error type listFunc func(options ...any) ([]*models.Item, error) -// listInMemory is a function that takes in an InMemoryBackend, and returns a ListFunc -// it takes any type as filters, and converts them to the specific FilterOption type for the InMemoryBackend, -// and calls the InMemoryBackend's List function with these filters. -func listInMemory(cacheBackend *backend.InMemoryBackend) listFunc { +// listInMemory is a function that takes in an InMemory, and returns a ListFunc +// it takes any type as filters, and converts them to the specific FilterOption type for the InMemory, +// and calls the InMemory's List function with these filters. +func listInMemory(cacheBackend *backend.InMemory) listFunc { return func(options ...any) ([]*models.Item, error) { - // here we are converting the filters of any type to the specific FilterOption type for the InMemoryBackend - filterOptions := make([]backend.FilterOption[backend.InMemoryBackend], len(options)) + // here we are converting the filters of any type to the specific FilterOption type for the InMemory + filterOptions := make([]backend.FilterOption[backend.InMemory], len(options)) for i, option := range options { - filterOptions[i] = option.(backend.FilterOption[backend.InMemoryBackend]) + filterOptions[i] = option.(backend.FilterOption[backend.InMemory]) } return cacheBackend.List(filterOptions...) } @@ -521,7 +523,7 @@ func (hyperCache *HyperCache[T]) Clear() error { ) // get all expired items - if cb, ok := hyperCache.backend.(*backend.InMemoryBackend); ok { + if cb, ok := hyperCache.backend.(*backend.InMemory); ok { items, err = cb.List() cb.Clear() } else if cb, ok := hyperCache.backend.(*backend.RedisBackend); ok { diff --git a/hypercache_test.go b/hypercache_test.go index 5397d72..98343c2 100644 --- a/hypercache_test.go +++ b/hypercache_test.go @@ -8,15 +8,15 @@ import ( "github.com/longbridgeapp/assert" ) -func TestHyperCache_NewHyperCache(t *testing.T) { +func TestHyperCache_New(t *testing.T) { // Test that an error is returned when the capacity is negative - _, err := NewHyperCacheInMemoryWithDefaults(-1) + _, err := NewInMemoryWithDefaults(-1) if err == nil { t.Error("Expected an error when capacity is negative, got nil") } // Test that a new HyperCache is returned when the capacity is 0 - cache, err := NewHyperCacheInMemoryWithDefaults(0) + cache, err := NewInMemoryWithDefaults(0) if err != nil { t.Errorf("Unexpected error when capacity is 0: %v", err) } @@ -25,7 +25,7 @@ func TestHyperCache_NewHyperCache(t *testing.T) { } // Test that a new HyperCache is returned when the capacity is positive - cache, err = NewHyperCacheInMemoryWithDefaults(10) + cache, err = NewInMemoryWithDefaults(10) if err != nil { t.Errorf("Unexpected error when capacity is positive: %v", err) } @@ -36,69 +36,69 @@ func TestHyperCache_NewHyperCache(t *testing.T) { func TestHyperCache_WithStatsCollector(t *testing.T) { // Test with default stats collector - cache, err := NewHyperCacheInMemoryWithDefaults(10) + cache, err := NewInMemoryWithDefaults(10) assert.Nil(t, err) assert.NotNil(t, cache.StatsCollector) } func TestHyperCache_WithExpirationInterval(t *testing.T) { // Test with default expiration interval - cache, err := NewHyperCacheInMemoryWithDefaults(10) + cache, err := NewInMemoryWithDefaults(10) assert.Nil(t, err) assert.Equal(t, 30*time.Minute, cache.expirationInterval) - config := &Config[backend.InMemoryBackend]{ - HyperCacheOptions: []Option[backend.InMemoryBackend]{ - WithExpirationInterval[backend.InMemoryBackend](1 * time.Hour), + config := &Config[backend.InMemory]{ + HyperCacheOptions: []Option[backend.InMemory]{ + WithExpirationInterval[backend.InMemory](1 * time.Hour), }, - InMemoryBackendOptions: []backend.BackendOption[backend.InMemoryBackend]{ + InMemoryOptions: []backend.Option[backend.InMemory]{ backend.WithCapacity(10), }, } // Test with custom expiration interval - cache, err = NewHyperCache(config) + cache, err = New(config) assert.Nil(t, err) assert.Equal(t, 1*time.Hour, cache.expirationInterval) } func TestHyperCache_WithEvictionInterval(t *testing.T) { // Test with default eviction interval - cache, err := NewHyperCacheInMemoryWithDefaults(10) + cache, err := NewInMemoryWithDefaults(10) assert.Nil(t, err) assert.Equal(t, 10*time.Minute, cache.evictionInterval) // Test with custom eviction interval - config := &Config[backend.InMemoryBackend]{ - HyperCacheOptions: []Option[backend.InMemoryBackend]{ - WithEvictionInterval[backend.InMemoryBackend](1 * time.Hour), + config := &Config[backend.InMemory]{ + HyperCacheOptions: []Option[backend.InMemory]{ + WithEvictionInterval[backend.InMemory](1 * time.Hour), }, - InMemoryBackendOptions: []backend.BackendOption[backend.InMemoryBackend]{ + InMemoryOptions: []backend.Option[backend.InMemory]{ backend.WithCapacity(10), }, } // Test with custom eviction interval - cache, err = NewHyperCache(config) + cache, err = New(config) assert.Nil(t, err) assert.Equal(t, 1*time.Hour, cache.evictionInterval) } func TestHyperCache_WithMaxEvictionCount(t *testing.T) { // Test with default max eviction count - cache, err := NewHyperCacheInMemoryWithDefaults(10) + cache, err := NewInMemoryWithDefaults(10) assert.Nil(t, err) assert.Equal(t, uint(10), cache.maxEvictionCount) // Test with custom max eviction count - config := &Config[backend.InMemoryBackend]{ - HyperCacheOptions: []Option[backend.InMemoryBackend]{ - WithEvictionInterval[backend.InMemoryBackend](1 * time.Hour), - WithMaxEvictionCount[backend.InMemoryBackend](5), + config := &Config[backend.InMemory]{ + HyperCacheOptions: []Option[backend.InMemory]{ + WithEvictionInterval[backend.InMemory](1 * time.Hour), + WithMaxEvictionCount[backend.InMemory](5), }, - InMemoryBackendOptions: []backend.BackendOption[backend.InMemoryBackend]{ + InMemoryOptions: []backend.Option[backend.InMemory]{ backend.WithCapacity(10), }, } - cache, err = NewHyperCache(config) + cache, err = New(config) assert.Nil(t, err) assert.Equal(t, uint(5), cache.maxEvictionCount) } diff --git a/middleware/stats.go b/middleware/stats.go index 02b0c2d..dd8c36e 100644 --- a/middleware/stats.go +++ b/middleware/stats.go @@ -15,8 +15,8 @@ type StatsCollectorMiddleware struct { statsCollector stats.Collector } -// NewStatsCollectorMiddleware returns a new StatsCollectorMiddleware -func NewStatsCollectorMiddleware(next hypercache.Service, statsCollector stats.Collector) hypercache.Service { +// NewCollectorMiddleware returns a new StatsCollectorMiddleware +func NewCollectorMiddleware(next hypercache.Service, statsCollector stats.Collector) hypercache.Service { return &StatsCollectorMiddleware{next: next, statsCollector: statsCollector} } diff --git a/stats/collector.go b/stats/collector.go index b9f9105..4e6a881 100644 --- a/stats/collector.go +++ b/stats/collector.go @@ -26,9 +26,9 @@ type Collector interface { // StatsCollectorRegistry holds the a registry of stats collectors. var StatsCollectorRegistry = make(map[string]func() (Collector, error)) -// NewStatsCollector creates a new stats collector. +// NewCollector creates a new stats collector. // The statsCollectorName parameter is used to select the stats collector from the registry. -func NewStatsCollector(statsCollectorName string) (Collector, error) { +func NewCollector(statsCollectorName string) (Collector, error) { // Check the parameters. if statsCollectorName == "" { return nil, fmt.Errorf("%s: %s", errors.ErrParamCannotBeEmpty, "statsCollectorName") @@ -42,14 +42,14 @@ func NewStatsCollector(statsCollectorName string) (Collector, error) { return createFunc() } -// RegisterStatsCollector registers a new stats collector with the given name. -func RegisterStatsCollector(name string, createFunc func() (Collector, error)) { +// RegisterCollector registers a new stats collector with the given name. +func RegisterCollector(name string, createFunc func() (Collector, error)) { StatsCollectorRegistry[name] = createFunc } func init() { // Register the default stats collector. - RegisterStatsCollector("default", func() (Collector, error) { + RegisterCollector("default", func() (Collector, error) { var err error collector := NewHistogramStatsCollector() if collector == nil { diff --git a/tests/benchmark/hypercache_get_benchmark_test.go b/tests/benchmark/hypercache_get_benchmark_test.go index 820a651..e72b7e7 100644 --- a/tests/benchmark/hypercache_get_benchmark_test.go +++ b/tests/benchmark/hypercache_get_benchmark_test.go @@ -10,8 +10,8 @@ import ( func BenchmarkHyperCache_Get(b *testing.B) { // Create a new HyperCache with a capacity of 1000 - // cache, _ := hypercache.NewHyperCache(1000, hypercache.WithEvictionInterval[backend.InMemoryBackend](30*time.Second)) - cache, _ := hypercache.NewHyperCacheInMemoryWithDefaults(1000) + // cache, _ := hypercache.New(1000, hypercache.WithEvictionInterval[backend.InMemory](30*time.Second)) + cache, _ := hypercache.NewInMemoryWithDefaults(1000) // Store a value in the cache with a key and expiration duration cache.Set("key", "value", time.Hour) @@ -25,18 +25,18 @@ func BenchmarkHyperCache_Get(b *testing.B) { func BenchmarkHyperCache_Get_ProactiveEviction(b *testing.B) { // Create a new HyperCache with a capacity of 1000 - config := hypercache.NewConfig[backend.InMemoryBackend]() - config.HyperCacheOptions = []hypercache.Option[backend.InMemoryBackend]{ - hypercache.WithEvictionInterval[backend.InMemoryBackend](0), - hypercache.WithEvictionAlgorithm[backend.InMemoryBackend]("lru"), + config := hypercache.NewConfig[backend.InMemory]() + config.HyperCacheOptions = []hypercache.Option[backend.InMemory]{ + hypercache.WithEvictionInterval[backend.InMemory](0), + hypercache.WithEvictionAlgorithm[backend.InMemory]("lru"), } - config.InMemoryBackendOptions = []backend.BackendOption[backend.InMemoryBackend]{ + config.InMemoryOptions = []backend.Option[backend.InMemory]{ backend.WithCapacity(1000), } // Create a new HyperCache with a capacity of 10 - cache, _ := hypercache.NewHyperCache(config) + cache, _ := hypercache.New(config) // Store a value in the cache with a key and expiration duration cache.Set("key", "value", time.Hour) diff --git a/tests/benchmark/hypercache_list_benchmark_test.go b/tests/benchmark/hypercache_list_benchmark_test.go index b3d5973..a1d1b63 100644 --- a/tests/benchmark/hypercache_list_benchmark_test.go +++ b/tests/benchmark/hypercache_list_benchmark_test.go @@ -9,7 +9,7 @@ import ( func BenchmarkHyperCache_List(b *testing.B) { // Create a new HyperCache with a capacity of 1000 - cache, _ := hypercache.NewHyperCacheInMemoryWithDefaults(1000) + cache, _ := hypercache.NewInMemoryWithDefaults(1000) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -26,7 +26,7 @@ func BenchmarkHyperCache_List(b *testing.B) { // func BenchmarkHyperCache_List_ProactiveEviction(b *testing.B) { // // Create a new HyperCache with a capacity of 1000 -// cache, _ := hypercache.NewHyperCache(1000, hypercache.WithEvictionInterval[backend.InMemoryBackend](0)) +// cache, _ := hypercache.New(1000, hypercache.WithEvictionInterval[backend.InMemory](0)) // // Store a value in the cache with a key and expiration duration // cache.Set("key", "value", time.Hour) diff --git a/tests/benchmark/hypercache_set_benchmark_test.go b/tests/benchmark/hypercache_set_benchmark_test.go index b2a23c0..e6c590a 100644 --- a/tests/benchmark/hypercache_set_benchmark_test.go +++ b/tests/benchmark/hypercache_set_benchmark_test.go @@ -11,7 +11,7 @@ import ( func BenchmarkHyperCache_Set(b *testing.B) { // Create a new HyperCache with a capacity of 1000 - cache, _ := hypercache.NewHyperCacheInMemoryWithDefaults(1000) + cache, _ := hypercache.NewInMemoryWithDefaults(1000) b.ResetTimer() for i := 0; i < b.N; i++ { @@ -22,18 +22,18 @@ func BenchmarkHyperCache_Set(b *testing.B) { func BenchmarkHyperCache_Set_Proactive_Eviction(b *testing.B) { // Create a new HyperCache with a capacity of 1000 - config := hypercache.NewConfig[backend.InMemoryBackend]() - config.HyperCacheOptions = []hypercache.Option[backend.InMemoryBackend]{ - hypercache.WithEvictionInterval[backend.InMemoryBackend](0), - hypercache.WithEvictionAlgorithm[backend.InMemoryBackend]("cawolfu"), + config := hypercache.NewConfig[backend.InMemory]() + config.HyperCacheOptions = []hypercache.Option[backend.InMemory]{ + hypercache.WithEvictionInterval[backend.InMemory](0), + hypercache.WithEvictionAlgorithm[backend.InMemory]("cawolfu"), } - config.InMemoryBackendOptions = []backend.BackendOption[backend.InMemoryBackend]{ + config.InMemoryOptions = []backend.Option[backend.InMemory]{ backend.WithCapacity(1000), } // Create a new HyperCache with a capacity of 10 - cache, _ := hypercache.NewHyperCache(config) + cache, _ := hypercache.New(config) b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/tests/hypercache_get_multiple_test.go b/tests/hypercache_get_multiple_test.go index 9e0b710..de5336e 100644 --- a/tests/hypercache_get_multiple_test.go +++ b/tests/hypercache_get_multiple_test.go @@ -16,14 +16,14 @@ func TestGetMultiple(t *testing.T) { keys []string wantValues map[string]interface{} wantErrs map[string]error - setup func(cache *hypercache.HyperCache[backend.InMemoryBackend]) + setup func(cache *hypercache.HyperCache[backend.InMemory]) }{ { name: "get multiple keys with values", keys: []string{"key1", "key2", "key3"}, wantValues: map[string]interface{}{"key1": 1, "key2": 2, "key3": 3}, wantErrs: map[string]error(map[string]error{}), - setup: func(cache *hypercache.HyperCache[backend.InMemoryBackend]) { + setup: func(cache *hypercache.HyperCache[backend.InMemory]) { cache.Set("key1", 1, 0) cache.Set("key2", 2, 0) cache.Set("key3", 3, 0) @@ -34,7 +34,7 @@ func TestGetMultiple(t *testing.T) { keys: []string{"key1", "key2", "key3"}, wantValues: map[string]interface{}{"key1": 1, "key3": 3}, wantErrs: map[string]error{"key2": errors.ErrKeyNotFound}, - setup: func(cache *hypercache.HyperCache[backend.InMemoryBackend]) { + setup: func(cache *hypercache.HyperCache[backend.InMemory]) { cache.Set("key1", 1, 0) cache.Set("key3", 3, 0) }, @@ -44,7 +44,7 @@ func TestGetMultiple(t *testing.T) { keys: []string{"key1", "key2", "key3"}, wantValues: map[string]interface{}{"key2": 2, "key3": 3}, wantErrs: map[string]error{"key1": errors.ErrKeyNotFound}, - setup: func(cache *hypercache.HyperCache[backend.InMemoryBackend]) { + setup: func(cache *hypercache.HyperCache[backend.InMemory]) { cache.Set("key1", 1, time.Millisecond) time.Sleep(2 * time.Millisecond) cache.Set("key2", 2, 0) @@ -55,15 +55,15 @@ func TestGetMultiple(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - config := &hypercache.Config[backend.InMemoryBackend]{ - HyperCacheOptions: []hypercache.Option[backend.InMemoryBackend]{ - hypercache.WithExpirationInterval[backend.InMemoryBackend](time.Millisecond), + config := &hypercache.Config[backend.InMemory]{ + HyperCacheOptions: []hypercache.Option[backend.InMemory]{ + hypercache.WithExpirationInterval[backend.InMemory](time.Millisecond), }, - InMemoryBackendOptions: []backend.BackendOption[backend.InMemoryBackend]{ + InMemoryOptions: []backend.Option[backend.InMemory]{ backend.WithCapacity(10), }, } - cache, err := hypercache.NewHyperCache(config) + cache, err := hypercache.New(config) assert.Nil(t, err) test.setup(cache) diff --git a/tests/hypercache_get_or_set_test.go b/tests/hypercache_get_or_set_test.go index 1aaf04f..f02d40e 100644 --- a/tests/hypercache_get_or_set_test.go +++ b/tests/hypercache_get_or_set_test.go @@ -67,7 +67,7 @@ func TestHyperCache_GetOrSet(t *testing.T) { expectedErr: nil, }, } - cache, err := hypercache.NewHyperCacheInMemoryWithDefaults(10) + cache, err := hypercache.NewInMemoryWithDefaults(10) assert.Nil(t, err) for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/tests/hypercache_get_test.go b/tests/hypercache_get_test.go index b9e90d9..875da07 100644 --- a/tests/hypercache_get_test.go +++ b/tests/hypercache_get_test.go @@ -63,7 +63,7 @@ func TestHyperCache_Get(t *testing.T) { shouldSet: false, }, } - cache, err := hypercache.NewHyperCacheInMemoryWithDefaults(10) + cache, err := hypercache.NewInMemoryWithDefaults(10) assert.Nil(t, err) for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/tests/hypercache_set_test.go b/tests/hypercache_set_test.go index 4a665bf..f544b48 100644 --- a/tests/hypercache_set_test.go +++ b/tests/hypercache_set_test.go @@ -67,7 +67,7 @@ func TestHyperCache_Set(t *testing.T) { expectedErr: nil, }, } - cache, err := hypercache.NewHyperCacheInMemoryWithDefaults(10) + cache, err := hypercache.NewInMemoryWithDefaults(10) assert.Nil(t, err) defer cache.Stop() diff --git a/utils/types.go b/utils/types.go index ca9117a..c56c23c 100644 --- a/utils/types.go +++ b/utils/types.go @@ -26,9 +26,9 @@ type CacheBackendChecker[T backend.IBackendConstrain] struct { Backend backend.IBackend[T] } -// IsInMemoryBackend returns true if the backend is an InMemoryBackend -func (c *CacheBackendChecker[T]) IsInMemoryBackend() bool { - _, ok := c.Backend.(*backend.InMemoryBackend) +// IsInMemory returns true if the backend is an InMemory +func (c *CacheBackendChecker[T]) IsInMemory() bool { + _, ok := c.Backend.(*backend.InMemory) return ok } From 1350acbb2512fe953b64c2718fa231a66b2ff596 Mon Sep 17 00:00:00 2001 From: Francesco Cosentino Date: Sun, 15 Jan 2023 12:11:05 +0100 Subject: [PATCH 4/4] cosmetics --- README.md | 12 ++++++------ examples/service/service.go | 2 +- middleware/stats.go | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8df032b..52d1d37 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ HyperCache is a **thread-safe** **high-performance** cache implementation in Go that supports multiple backends with the expiration and eviction of items supporting custom algorithms alongside the defaults. It can be used as a standalone cache or as a cache middleware for a service. It can implement a [service interface](./service.go) to intercept cache methods and decorate em with middleware (default or custom). It is optimized for performance and flexibility allowing to specify the expiration and eviction intervals, provide and register new eviction algorithms, stats collectors, middleware(s). -It ships with a default [historigram stats collector](./stats/statscollector.go) and several eviction algorithms, but you can develop and register your own as long as it implements the [EvictionAlgorithm interface](./eviction/eviction.go).: +It ships with a default [historigram stats collector](./stats/statscollector.go) and several eviction algorithms, but you can develop and register your own as long as it implements the [Eviction Algorithm interface](./eviction/eviction.go).: - [Recently Used (LRU) eviction algorithm](./eviction/lru.go) - [The Least Frequently Used (LFU) algorithm](./eviction/lfu.go) @@ -27,7 +27,7 @@ It ships with a default [historigram stats collector](./stats/statscollector.go) - Clear the cache of all items - Evitc items in the background based on the cache capacity and items access leveraging several custom eviction algorithms - Expire items in the background based on their duration -- [EvictionAlgorithm interface](./eviction.go) to implement custom eviction algorithms. +- [Eviction Algorithm interface](./eviction.go) to implement custom eviction algorithms. - Stats collection with a default [stats collector](./stats/statscollector.go) or a custom one that implements the StatsCollector interface. - [Service interface implementation](./service.go) to allow intercepting cache methods and decorate them with custom or default middleware(s). @@ -155,7 +155,7 @@ The `Remove` function takes a variadic number of keys as arguments and returns a The `Service` interface allows intercepting cache methods and decorate them with custom or default middleware(s). ```golang -var svc hypercache.HyperCacheService +var svc hypercache.Service hyperCache, err := hypercache.NewInMemoryWithDefaults(10) if err != nil { @@ -181,11 +181,11 @@ defer logger.Sync() // apply middleware in the same order as you want to execute them svc = hypercache.ApplyMiddleware(svc, // middleware.YourMiddleware, - func(next hypercache.HyperCacheService) hypercache.HyperCacheService { + func(next hypercache.Service) hypercache.Service { return middleware.NewLoggingMiddleware(next, sugar) }, - func(next hypercache.HyperCacheService) hypercache.HyperCacheService { - return middleware.NewCollectorMiddleware(next, statsCollector) + func(next hypercache.Service) hypercache.Service { + return middleware.NewStatsCollectorMiddleware(next, statsCollector) }, ) diff --git a/examples/service/service.go b/examples/service/service.go index ad65c91..7169d61 100644 --- a/examples/service/service.go +++ b/examples/service/service.go @@ -40,7 +40,7 @@ func main() { return middleware.NewLoggingMiddleware(next, sugar) }, func(next hypercache.Service) hypercache.Service { - return middleware.NewCollectorMiddleware(next, statsCollector) + return middleware.NewStatsCollectorMiddleware(next, statsCollector) }, ) defer svc.Stop() diff --git a/middleware/stats.go b/middleware/stats.go index dd8c36e..02b0c2d 100644 --- a/middleware/stats.go +++ b/middleware/stats.go @@ -15,8 +15,8 @@ type StatsCollectorMiddleware struct { statsCollector stats.Collector } -// NewCollectorMiddleware returns a new StatsCollectorMiddleware -func NewCollectorMiddleware(next hypercache.Service, statsCollector stats.Collector) hypercache.Service { +// NewStatsCollectorMiddleware returns a new StatsCollectorMiddleware +func NewStatsCollectorMiddleware(next hypercache.Service, statsCollector stats.Collector) hypercache.Service { return &StatsCollectorMiddleware{next: next, statsCollector: statsCollector} }