143 lines
4.4 KiB
Markdown
143 lines
4.4 KiB
Markdown
|
## TTLCache - an in-memory cache with item expiration and generics
|
||
|
|
||
|
[![Go Reference](https://pkg.go.dev/badge/github.com/jellydator/ttlcache/v3.svg)](https://pkg.go.dev/github.com/jellydator/ttlcache/v3)
|
||
|
[![Build Status](https://github.com/jellydator/ttlcache/actions/workflows/go.yml/badge.svg)](https://github.com/jellydator/ttlcache/actions/workflows/go.yml)
|
||
|
[![Coverage Status](https://coveralls.io/repos/github/jellydator/ttlcache/badge.svg?branch=master)](https://coveralls.io/github/jellydator/ttlcache?branch=master)
|
||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/jellydator/ttlcache/v3)](https://goreportcard.com/report/github.com/jellydator/ttlcache/v3)
|
||
|
|
||
|
## Features
|
||
|
- Simple API
|
||
|
- Type parameters
|
||
|
- Item expiration and automatic deletion
|
||
|
- Automatic expiration time extension on each `Get` call
|
||
|
- `Loader` interface that may be used to load/lazily initialize missing cache
|
||
|
items
|
||
|
- Event handlers (insertion and eviction)
|
||
|
- Metrics
|
||
|
|
||
|
## Installation
|
||
|
```
|
||
|
go get github.com/jellydator/ttlcache/v3
|
||
|
```
|
||
|
|
||
|
## Usage
|
||
|
The main type of `ttlcache` is `Cache`. It represents a single
|
||
|
in-memory data store.
|
||
|
|
||
|
To create a new instance of `ttlcache.Cache`, the `ttlcache.New()` function
|
||
|
should be called:
|
||
|
```go
|
||
|
func main() {
|
||
|
cache := ttlcache.New[string, string]()
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Note that by default, a new cache instance does not let any of its
|
||
|
items to expire or be automatically deleted. However, this feature
|
||
|
can be activated by passing a few additional options into the
|
||
|
`ttlcache.New()` function and calling the `cache.Start()` method:
|
||
|
```go
|
||
|
func main() {
|
||
|
cache := ttlcache.New[string, string](
|
||
|
ttlcache.WithTTL[string, string](30 * time.Minute),
|
||
|
)
|
||
|
|
||
|
go cache.Start() // starts automatic expired item deletion
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Even though the `cache.Start()` method handles expired item deletion well,
|
||
|
there may be times when the system that uses `ttlcache` needs to determine
|
||
|
when to delete the expired items itself. For example, it may need to
|
||
|
delete them only when the resource load is at its lowest (e.g., after
|
||
|
midnight, when the number of users/HTTP requests drops). So, in situations
|
||
|
like these, instead of calling `cache.Start()`, the system could
|
||
|
periodically call `cache.DeleteExpired()`:
|
||
|
```go
|
||
|
func main() {
|
||
|
cache := ttlcache.New[string, string](
|
||
|
ttlcache.WithTTL[string, string](30 * time.Minute),
|
||
|
)
|
||
|
|
||
|
for {
|
||
|
time.Sleep(4 * time.Hour)
|
||
|
cache.DeleteExpired()
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
The data stored in `ttlcache.Cache` can be retrieved, checked and updated with
|
||
|
`Set`, `Get`, `Delete`, `Has` etc. methods:
|
||
|
```go
|
||
|
func main() {
|
||
|
cache := ttlcache.New[string, string](
|
||
|
ttlcache.WithTTL[string, string](30 * time.Minute),
|
||
|
)
|
||
|
|
||
|
// insert data
|
||
|
cache.Set("first", "value1", ttlcache.DefaultTTL)
|
||
|
cache.Set("second", "value2", ttlcache.NoTTL)
|
||
|
cache.Set("third", "value3", ttlcache.DefaultTTL)
|
||
|
|
||
|
// retrieve data
|
||
|
item := cache.Get("first")
|
||
|
fmt.Println(item.Value(), item.ExpiresAt())
|
||
|
|
||
|
// check key
|
||
|
ok := cache.Has("third")
|
||
|
|
||
|
// delete data
|
||
|
cache.Delete("second")
|
||
|
cache.DeleteExpired()
|
||
|
cache.DeleteAll()
|
||
|
|
||
|
// retrieve data if in cache otherwise insert data
|
||
|
item, retrieved := cache.GetOrSet("fourth", "value4", WithTTL[string, string](ttlcache.DefaultTTL))
|
||
|
|
||
|
// retrieve and delete data
|
||
|
item, present := cache.GetAndDelete("fourth")
|
||
|
}
|
||
|
```
|
||
|
|
||
|
To subscribe to insertion and eviction events, `cache.OnInsertion()` and
|
||
|
`cache.OnEviction()` methods should be used:
|
||
|
```go
|
||
|
func main() {
|
||
|
cache := ttlcache.New[string, string](
|
||
|
ttlcache.WithTTL[string, string](30 * time.Minute),
|
||
|
ttlcache.WithCapacity[string, string](300),
|
||
|
)
|
||
|
|
||
|
cache.OnInsertion(func(ctx context.Context, item *ttlcache.Item[string, string]) {
|
||
|
fmt.Println(item.Value(), item.ExpiresAt())
|
||
|
})
|
||
|
cache.OnEviction(func(ctx context.Context, reason ttlcache.EvictionReason, item *ttlcache.Item[string, string]) {
|
||
|
if reason == ttlcache.EvictionReasonCapacityReached {
|
||
|
fmt.Println(item.Key(), item.Value())
|
||
|
}
|
||
|
})
|
||
|
|
||
|
cache.Set("first", "value1", ttlcache.DefaultTTL)
|
||
|
cache.DeleteAll()
|
||
|
}
|
||
|
```
|
||
|
|
||
|
To load data when the cache does not have it, a custom or
|
||
|
existing implementation of `ttlcache.Loader` can be used:
|
||
|
```go
|
||
|
func main() {
|
||
|
loader := ttlcache.LoaderFunc[string, string](
|
||
|
func(c *ttlcache.Cache[string, string], key string) *ttlcache.Item[string, string] {
|
||
|
// load from file/make an HTTP request
|
||
|
item := c.Set("key from file", "value from file")
|
||
|
return item
|
||
|
},
|
||
|
)
|
||
|
cache := ttlcache.New[string, string](
|
||
|
ttlcache.WithLoader[string, string](loader),
|
||
|
)
|
||
|
|
||
|
item := cache.Get("key from file")
|
||
|
}
|
||
|
```
|