Documentation ¶
Overview ¶
Package ttl is set of "time-to-live" container type such that after a given amount of time, items in the container are deleted.
Index ¶
- type Map
- func (m *Map[K, V]) Clear()
- func (m *Map[K, V]) Close()
- func (m *Map[K, V]) Delete(key K)
- func (m *Map[K, V]) DeleteFunc(del func(key K, value V) bool)
- func (m *Map[K, V]) Length() int
- func (m *Map[K, V]) Load(key K) (value V, ok bool)
- func (m *Map[K, V]) LoadPassive(key K) (value V, ok bool)
- func (m *Map[K, V]) Range(f func(key K, value V) bool)
- func (m *Map[K, V]) Store(key K, value V)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Map ¶
type Map[K comparable, V any] struct { // contains filtered or unexported fields }
Map is a "time-to-live" map such that after a given amount of time, items in the map are deleted. Map is safe for concurrent use.
When a Map.Load or Map.Store occurs, the lastAccess time is set to the current time. Therefore, only items that are not called by Map.Load or Map.Store will be deleted after the TTL expires.
Map.LoadPassive can be used in which case the lastAccess time will *not* be updated.
Adapted from: https://stackoverflow.com/a/25487392/452281
Example ¶
package main import ( "context" "fmt" "time" "github.com/glenvan/ttl" ) func main() { maxTTL := 300 * time.Millisecond // a key's time to live startSize := 3 // initial number of items in map pruneInterval := 100 * time.Millisecond // prune expired items each time pruneInterval elapses refreshLastAccessOnGet := true // update item's 'lastAccessTime' on ttl.Map.Load() // Any comparable data type such as int, uint64, pointers and struct types (if all field // types are comparable) can be used as the key type t := ttl.NewMap[string, string]( context.Background(), maxTTL, startSize, pruneInterval, refreshLastAccessOnGet) defer t.Close() // Populate the ttl.Map t.Store("hello", "world") t.Store("goodbye", "universe") fmt.Printf("ttl.Map length: %d\n", t.Length()) t.Delete("goodbye") // Display all items in ttl.Map t.Range(func(key string, value string) bool { fmt.Printf("[%7s] '%v'\n", key, value) return true }) sleepTime := maxTTL + pruneInterval fmt.Printf("Sleeping %s, items should be expired and removed afterward\n", sleepTime) time.Sleep(sleepTime) v, ok := t.Load("hello") fmt.Printf("[%7s] '%v' (exists: %t)\n", "hello", v, ok) v, ok = t.Load("goodbye") fmt.Printf("[%7s] '%v' (exists: %t)\n", "goodbye", v, ok) fmt.Printf("ttl.Map length: %d\n", t.Length()) }
Output: ttl.Map length: 2 [ hello] 'world' Sleeping 400ms, items should be expired and removed afterward [ hello] '' (exists: false) [goodbye] '' (exists: false) ttl.Map length: 0
func NewMap ¶
func NewMap[K comparable, V any]( ctx context.Context, maxTTL time.Duration, length int, pruneInterval time.Duration, refreshLastAccessOnGet bool, ) (m *Map[K, V])
NewMap returns a new Map with items expiring according to the maxTTL specified if they have not been accessed within that duration. Access refresh can be overridden so that items expire after the TTL whether they have been accessed or not.
NewMap accepts a context. If the context is cancelled, the pruning process will automatically stop whether you've called Map.Close or not. It's safe to use either approach.
context.Background() is perfectly acceptable as the default context, however you should Map.Close the Map yourself in that case.
func (*Map[K, V]) Clear ¶
func (m *Map[K, V]) Clear()
Clear will remove all key/value pairs from the Map. Clear is safe for concurrent use.
func (*Map[K, V]) Close ¶
func (m *Map[K, V]) Close()
Close will terminate TTL pruning of the Map. If Close is not called on a Map after it's no longer needed, the Map will leak (unless the context has been cancelled).
Close may be called multiple times and is safe to call even if the context has been cancelled.
func (*Map[K, V]) Delete ¶
func (m *Map[K, V]) Delete(key K)
Delete will remove a key and its value from the Map. Delete is safe for concurrent use.
func (*Map[K, V]) DeleteFunc ¶ added in v0.2.0
DeleteFunc deletes any key/value pairs from the Map for which del returns true. DeleteFunc is safe for concurrent use.
Example ¶
package main import ( "context" "fmt" "time" "github.com/glenvan/ttl" ) func main() { tm := ttl.NewMap[string, int](context.Background(), 30*time.Second, 0, 2*time.Second, true) defer tm.Close() tm.Store("zero", 0) tm.Store("one", 1) tm.Store("two", 2) // Delete all even keys tm.DeleteFunc(func(key string, val int) bool { return val%2 == 0 }) tm.Range(func(key string, val int) bool { fmt.Printf("%s: %d\n", key, val) return true }) }
Output: one: 1
func (*Map[K, V]) Length ¶
Length returns the current length of the Map's internal map. Length is safe for concurrent use.
func (*Map[K, V]) Load ¶
Load will retrieve a value from the Map, as well as a bool indicating whether the key was found. If the item was not found the value returned is undefined. Load is safe for concurrent use.
Example ¶
package main import ( "context" "fmt" "time" "github.com/glenvan/ttl" ) func main() { tm := ttl.NewMap[string, string](context.Background(), 30*time.Second, 0, 2*time.Second, true) defer tm.Close() tm.Store("hello", "world") value, ok := tm.Load("hello") if ok { fmt.Println(value) } }
Output: world
func (*Map[K, V]) LoadPassive ¶
LoadPassive will retrieve a value from the Map (without updating that value's time to live), as well as a bool indicating whether the key was found. If the item was not found the value returned is undefined. LoadPassive is safe for concurrent use.
func (*Map[K, V]) Range ¶
Range calls f sequentially for each key and value present in the Map. If f returns false, Range stops the iteration.
Range is safe for concurrent use and supports modifying the value (assuming it's a reference type like a slice, map, or a pointer) within the range function. However, this requires a write lock on the Map – so you are not able to perform Map.Delete or Map.Store operations on the original Map directly within the range func, as that would cause a panic. Even an accessor like Map.Load or Map.LoadPassive would lock indefinitely.
If you need to perform operations on the original Map, do so in a new goroutine from within the range func – effectively deferring the operation until the Range completes.
If you just need to delete items with a certain key or value, use Map.DeleteFunc instead.
Example ¶
package main import ( "context" "fmt" "time" "github.com/glenvan/ttl" ) func main() { tm := ttl.NewMap[string, string](context.Background(), 30*time.Second, 0, 2*time.Second, true) defer tm.Close() tm.Store("hello", "world") tm.Store("goodbye", "universe") fmt.Printf("Length before: %d\n", tm.Length()) tm.Range(func(key string, val string) bool { if key == "goodbye" { // defer deletion in the original Map using a goroutine go func() { tm.Delete(key) }() return false // break } return true // continue }) time.Sleep(20 * time.Millisecond) fmt.Printf("Length after: %d\n", tm.Length()) }
Output: Length before: 2 Length after: 1