Skip to content

Commit 71e00b2

Browse files
committed
refactor!: eliminate global state and improve architecture
BREAKING CHANGES: - Move errors package to sentinel with enhanced documentation - Add context parameter to Get, GetWithInfo, and Count methods - Change GetMultiple return signature for consistency Features: - Extract configuration constants to internal/constants package - Replace serializer and stats collector global registries with dependency injection - Add ItemPoolManager for thread-safe memory pool management - Implement lazy initialization patterns to avoid package-level globals Performance: - Replace encoding/json with faster goccy/go-json - Use slices.Sort instead of sort.Slice for better performance - Optimize item size calculation with local buffers Quality: - Enhance error handling with ewrap throughout codebase - Add comprehensive documentation to sentinel package - Improve worker pool error handling - Standardize interface organization with crud separation This refactoring eliminates all global variables and init() functions, improving testability, thread safety, and following Go best practices while maintaining backward compatibility where possible.
1 parent 65b1a2f commit 71e00b2

53 files changed

Lines changed: 1013 additions & 1184 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.golangci.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ linters:
9999
# Default: 1
100100
tab-width: 1
101101

102+
mnd:
103+
ignored-numbers:
104+
- "0666"
105+
- "0755"
106+
- "2"
107+
102108
ireturn:
103109
# ireturn does not allow using `allow` and `reject` settings at the same time.
104110
# Both settings are lists of the keywords and regular expressions matched to interface or package names.
@@ -174,6 +180,7 @@ linters:
174180
- r *http.Request
175181
- w http.ResponseWriter
176182
- T any
183+
- T backend.IBackendConstrain
177184
- m map[string]int
178185

179186
# Enable only fast linters from enabled linters set (first run won't be fast)

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ GITVERSION_NOT_INSTALLED = "gitversion is not installed: https://github.com/GitT
1010
test:
1111
go test -v -timeout 5m -cover ./...
1212

13+
# bench runs the benchmark tests in the benchmark subpackage of the tests package.
14+
bench:
15+
cd tests/benchmark && go test -bench=. -benchmem -benchtime=4s . -timeout 30m
16+
1317
benchmark:
1418
go test -bench=. -benchmem ./pkg/ewrap
1519
go test -bench=Benchmark -benchmem ./test
@@ -87,4 +91,4 @@ help:
8791
@echo
8892
@echo "For more information, see the project README."
8993

90-
.PHONY: prepare-toolchain test benchmark update-deps lint help
94+
.PHONY: prepare-toolchain test bench update-deps lint help

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ config.InMemoryOptions = []backend.Option[backend.InMemory]{
103103
// Create a new HyperCache with a capacity of 10
104104
cache, err := hypercache.New(config)
105105
if err != nil {
106-
fmt.Println(err)
106+
fmt.Fprintln(os.Stderr, err)
107107
return
108108
}
109109
```

backend/backend.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ type IBackendConstrain interface {
1515
type IBackend[T IBackendConstrain] interface {
1616
// Get retrieves the item with the given key from the cache.
1717
// If the key is not found in the cache, it returns nil.
18-
Get(key string) (item *types.Item, ok bool)
18+
Get(ctx context.Context, key string) (item *types.Item, ok bool)
1919
// Set adds a new item to the cache.
20-
Set(item *types.Item) error
20+
Set(ctx context.Context, item *types.Item) error
2121
// Capacity returns the maximum number of items that can be stored in the cache.
2222
Capacity() int
2323
// SetCapacity sets the maximum number of items that can be stored in the cache.
2424
SetCapacity(capacity int)
2525
// Count returns the number of items currently stored in the cache.
26-
Count() int
26+
Count(ctx context.Context) int
2727
// Remove deletes the item with the given key from the cache.
2828
Remove(ctx context.Context, keys ...string) error
2929
// List the items in the cache that meet the specified criteria.

backend/filters.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func WithFilterFunc(fn func(item *types.Item) bool) IFilter {
5454
}
5555

5656
// ApplyFilter applies the sort by filter to the given list of items.
57-
func (f sortByFilter) ApplyFilter(backendType string, items []*types.Item) ([]*types.Item, error) {
57+
func (f sortByFilter) ApplyFilter(_ string, items []*types.Item) ([]*types.Item, error) {
5858
var sorter *itemSorter
5959

6060
switch f.field {
@@ -96,7 +96,7 @@ func (f sortByFilter) ApplyFilter(backendType string, items []*types.Item) ([]*t
9696
}
9797

9898
// ApplyFilter applies the sort order filter to the given list of items.
99-
func (f SortOrderFilter) ApplyFilter(backendType string, items []*types.Item) ([]*types.Item, error) {
99+
func (f SortOrderFilter) ApplyFilter(_ string, items []*types.Item) ([]*types.Item, error) {
100100
if !f.ascending {
101101
for i, j := 0, len(items)-1; i < j; i, j = i+1, j-1 {
102102
items[i], items[j] = items[j], items[i]
@@ -107,7 +107,7 @@ func (f SortOrderFilter) ApplyFilter(backendType string, items []*types.Item) ([
107107
}
108108

109109
// ApplyFilter applies the filter function to the given list of items.
110-
func (f filterFunc) ApplyFilter(backendType string, items []*types.Item) ([]*types.Item, error) {
110+
func (f filterFunc) ApplyFilter(_ string, items []*types.Item) ([]*types.Item, error) {
111111
filteredItems := make([]*types.Item, 0)
112112

113113
for _, item := range items {

backend/inmemory.go

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,32 @@ import (
44
"context"
55
"sync"
66

7-
datastructure "github.com/hyp3rd/hypercache/cache/v4"
8-
"github.com/hyp3rd/hypercache/errors"
7+
cache "github.com/hyp3rd/hypercache/cache/v4"
8+
"github.com/hyp3rd/hypercache/internal/constants"
9+
"github.com/hyp3rd/hypercache/sentinel"
910
"github.com/hyp3rd/hypercache/types"
1011
)
1112

1213
// InMemory is a cache backend that stores the items in memory, leveraging a custom `ConcurrentMap`.
1314
type InMemory struct {
1415
sync.RWMutex // mutex to protect the cache from concurrent access
1516

16-
items datastructure.ConcurrentMap // map to store the items in the cache
17-
capacity int // capacity of the cache, limits the number of items that can be stored in the cache
17+
items cache.ConcurrentMap // map to store the items in the cache
18+
itemPoolManager *types.ItemPoolManager // item pool manager to manage the item pool
19+
capacity int // capacity of the cache, limits the number of items that can be stored in the cache
1820
}
1921

2022
// NewInMemory creates a new in-memory cache with the given options.
21-
func NewInMemory(opts ...Option[InMemory]) (backend IBackend[InMemory], err error) {
23+
func NewInMemory(opts ...Option[InMemory]) (IBackend[InMemory], error) {
2224
InMemory := &InMemory{
23-
items: datastructure.New(),
25+
items: cache.New(),
26+
itemPoolManager: types.NewItemPoolManager(),
2427
}
2528
// Apply the backend options
2629
ApplyOptions(InMemory, opts...)
2730
// Check if the `capacity` is valid
2831
if InMemory.capacity < 0 {
29-
return nil, errors.ErrInvalidCapacity
32+
return nil, sentinel.ErrInvalidCapacity
3033
}
3134

3235
return InMemory, nil
@@ -47,13 +50,13 @@ func (cacheBackend *InMemory) Capacity() int {
4750
}
4851

4952
// Count returns the number of items in the cache.
50-
func (cacheBackend *InMemory) Count() int {
53+
func (cacheBackend *InMemory) Count(_ context.Context) int {
5154
return cacheBackend.items.Count()
5255
}
5356

5457
// Get retrieves the item with the given key from the cacheBackend. If the item is not found, it returns nil.
55-
func (cacheBackend *InMemory) Get(key string) (item *types.Item, ok bool) {
56-
item, ok = cacheBackend.items.Get(key)
58+
func (cacheBackend *InMemory) Get(_ context.Context, key string) (*types.Item, bool) {
59+
item, ok := cacheBackend.items.Get(key)
5760
if !ok {
5861
return nil, false
5962
}
@@ -62,11 +65,11 @@ func (cacheBackend *InMemory) Get(key string) (item *types.Item, ok bool) {
6265
}
6366

6467
// Set adds a Item to the cache.
65-
func (cacheBackend *InMemory) Set(item *types.Item) error {
68+
func (cacheBackend *InMemory) Set(_ context.Context, item *types.Item) error {
6669
// Check for invalid key, value, or duration
6770
err := item.Valid()
6871
if err != nil {
69-
types.ItemPool.Put(item)
72+
cacheBackend.itemPoolManager.Put(item)
7073

7174
return err
7275
}
@@ -80,30 +83,35 @@ func (cacheBackend *InMemory) Set(item *types.Item) error {
8083
}
8184

8285
// List returns a list of all items in the cache filtered and ordered by the given options.
83-
func (cacheBackend *InMemory) List(ctx context.Context, filters ...IFilter) (items []*types.Item, err error) {
86+
func (cacheBackend *InMemory) List(_ context.Context, filters ...IFilter) ([]*types.Item, error) {
8487
// Apply the filters
8588
cacheBackend.RLock()
8689
defer cacheBackend.RUnlock()
8790

88-
items = make([]*types.Item, 0, cacheBackend.items.Count())
91+
var err error
92+
93+
items := make([]*types.Item, 0, cacheBackend.items.Count())
8994

9095
for item := range cacheBackend.items.IterBuffered() {
91-
copy := item
92-
items = append(items, &copy.Val)
96+
cloned := item
97+
items = append(items, &cloned.Val)
9398
}
9499

95100
// Apply the filters
96101
if len(filters) > 0 {
97102
for _, filter := range filters {
98-
items, err = filter.ApplyFilter("in-memory", items)
103+
items, err = filter.ApplyFilter(constants.InMemoryBackend, items)
104+
if err != nil {
105+
return nil, err
106+
}
99107
}
100108
}
101109

102-
return items, err
110+
return items, nil
103111
}
104112

105113
// Remove removes items with the given key from the cacheBackend. If an item is not found, it does nothing.
106-
func (cacheBackend *InMemory) Remove(ctx context.Context, keys ...string) (err error) {
114+
func (cacheBackend *InMemory) Remove(ctx context.Context, keys ...string) error {
107115
done := make(chan struct{})
108116

109117
go func() {
@@ -118,7 +126,7 @@ func (cacheBackend *InMemory) Remove(ctx context.Context, keys ...string) (err e
118126
case <-done:
119127
return nil
120128
case <-ctx.Done():
121-
return errors.ErrTimeoutOrCanceled
129+
return sentinel.ErrTimeoutOrCanceled
122130
}
123131
}
124132

@@ -136,6 +144,6 @@ func (cacheBackend *InMemory) Clear(ctx context.Context) error {
136144
case <-done:
137145
return nil
138146
case <-ctx.Done():
139-
return errors.ErrTimeoutOrCanceled
147+
return sentinel.ErrTimeoutOrCanceled
140148
}
141149
}

0 commit comments

Comments
 (0)