Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: New WithMaxCost option for custom cache capacity management strategies #152

Merged
merged 36 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2d92605
new option to define the max memory the cache can use in bytes
dadrus Oct 22, 2024
6e92deb
cache.set updated to cope with memory limitations
dadrus Oct 22, 2024
666a987
options updated to implement the suggested new option
dadrus Nov 10, 2024
0a2236a
cache impl updated + tests
dadrus Nov 10, 2024
fa705e7
dependencies updated
dadrus Nov 10, 2024
dcc9743
imports organized
dadrus Nov 10, 2024
7d51be1
readme updated to document the new configuration option
dadrus Nov 10, 2024
fea314a
better explanation in the example
dadrus Nov 10, 2024
e7e24cc
readme line removed by accident restored
dadrus Nov 10, 2024
53b1934
function renamed (typo fixed)
dadrus Nov 10, 2024
7a9baa3
wording fixed
dadrus Nov 10, 2024
0b17592
useless sentence removed
dadrus Nov 10, 2024
d1bdbee
costs renamed to cost
dadrus Nov 14, 2024
a2f5915
cost used in tests simplified
dadrus Nov 14, 2024
8965baf
CostCalcFunc renamed to CostFunc
dadrus Nov 14, 2024
58dcb90
costsCalcFunc renamed to costFunc
dadrus Nov 14, 2024
23cfbf0
updateExpirations moved
dadrus Nov 14, 2024
08a47e8
totalCost renamed to maxCost
dadrus Nov 14, 2024
c594b8d
WithTotalCost option renamed to WithMaxCost
dadrus Nov 14, 2024
c4b0baa
example in README updated
dadrus Nov 14, 2024
2a04fee
signature of the WithMaxCost option changed
dadrus Nov 14, 2024
e595b68
Merge branch 'v3' into feat/cache_size_in_bytes
dadrus Nov 14, 2024
e36c05f
readme updated
dadrus Nov 14, 2024
ce9482f
Item impl updated to hold the current cost and the corresponding cost…
dadrus Nov 17, 2024
da1f07e
cache impl updated to make use of the new item properties
dadrus Nov 17, 2024
d275891
new item options
dadrus Nov 17, 2024
d20ea4d
tests for the new options
dadrus Nov 17, 2024
692baf0
new Item related tests
dadrus Nov 17, 2024
2308b72
cache tests fixed to make them compile
dadrus Nov 17, 2024
fd2c7ad
item option implementation updated to become a private interface
dadrus Nov 27, 2024
2a15bc1
cache set test updated
dadrus Nov 27, 2024
eebe1a8
made new item related functions private + eviction reason renamed
dadrus Nov 29, 2024
5cb43bf
moved initial item cost calsulation to the constructor function
dadrus Nov 29, 2024
e70f90d
test functions renamed to reflect the private nature of functions und…
dadrus Nov 29, 2024
43fc033
version tracking fixed
dadrus Nov 29, 2024
41f3351
small readme update
dadrus Nov 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,24 @@ func main() {
item := cache.Get("key from file")
}
```

To restrict the cache's capacity based on criteria beyond the number
of items it can hold, the `ttlcache.WithMaxCost` option allows for
implementing custom strategies. The following example demonstrates
how to limit the maximum memory usage of a cache to 5KiB:
```go
import (
"github.com/jellydator/ttlcache"
"github.com/DmitriyVTitov/size"
)

func main() {
cache := ttlcache.New[string, string](
ttlcache.WithMaxCost[string, string](5120, func(item *ttlcache.Item[string, string]) uint64 {
return uint64(size.Of(item))
}),
)

cache.Set("first", "value1", ttlcache.DefaultTTL)
}
```
28 changes: 27 additions & 1 deletion cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const (
EvictionReasonDeleted EvictionReason = iota + 1
EvictionReasonCapacityReached
EvictionReasonExpired
EvictionReasonMaxCostExceeded
)

// EvictionReason is used to specify why a certain item was
Expand All @@ -36,6 +37,7 @@ type Cache[K comparable, V any] struct {

timerCh chan time.Duration
}
cost uint64

metricsMu sync.RWMutex
metrics Metrics
Expand Down Expand Up @@ -137,9 +139,20 @@ func (c *Cache[K, V]) set(key K, value V, ttl time.Duration) *Item[K, V] {
if elem != nil {
// update/overwrite an existing item
item := elem.Value.(*Item[K, V])
oldItemCost := item.cost

item.update(value, ttl)

c.updateExpirations(false, elem)

if c.options.maxCost != 0 {
c.cost = c.cost - oldItemCost + item.cost

for c.cost > c.options.maxCost {
c.evict(EvictionReasonMaxCostExceeded, c.items.lru.Back())
}
}

return item
}

Expand All @@ -153,11 +166,19 @@ func (c *Cache[K, V]) set(key K, value V, ttl time.Duration) *Item[K, V] {
}

// create a new item
item := NewItem(key, value, ttl, c.options.enableVersionTracking)
item := newItemWithOpts(key, value, ttl, c.options.itemOpts...)
elem = c.items.lru.PushFront(item)
c.items.values[key] = elem
c.updateExpirations(true, elem)

if c.options.maxCost != 0 {
dadrus marked this conversation as resolved.
Show resolved Hide resolved
c.cost += item.cost

for c.cost > c.options.maxCost {
c.evict(EvictionReasonMaxCostExceeded, c.items.lru.Back())
}
}

c.metricsMu.Lock()
c.metrics.Insertions++
c.metricsMu.Unlock()
Expand Down Expand Up @@ -258,6 +279,11 @@ func (c *Cache[K, V]) evict(reason EvictionReason, elems ...*list.Element) {
for i := range elems {
item := elems[i].Value.(*Item[K, V])
delete(c.items.values, item.key)

if c.options.maxCost != 0 {
c.cost -= item.cost
}

c.items.lru.Remove(elems[i])
c.items.expQueue.remove(elems[i])

Expand Down
Loading
Loading