Documentation
¶
Overview ¶
Package expiremap provides a thread-safe map with expiring keys. You must pay attention for these facts:
- Current implementation may hold up to 1 billion keys
- After creating a new map (calling New()), goroutine is created for deletion expired keys. It exists until Close() method is called
- There are active and passive expirations. Active expiration is done during Get(), and SetTTL() calls. Passive expiration happens in background and is done by goroutine
- Passive expiration occurs every 100ms. It is done in two steps - first step is inspired by algorithm used in Redis and second step is sequential expiration
- It is guaranteed by sequential expiration, that no expired key will live more than map.Size() / 200 seconds
First step's (or random expire) algorithm is following:
- Check the size of the map. If it is less than 100, just iterate over all keys and stop algorithm
- Check 20 random keys. Remove all expired keys. If there were at least 5 deletions, do the step 2 again (step 2 is done maximum 10 times)
Second step's (or rotate expire) algorithm is following:
- Load to X a key on which we stopped on the previous call. If on previous call we hit the bottom of the map, load top key of the map
- Start from the key X and from that key expire 20 consecutive keys or stop if we hit a bottom of the map
It means that at maximum 2200 expires per second may occur (not counting active expiration). If you have a lot of insertions with unique keys, but you rarely call methods Get and SetTTL on these keys, your map will grow faster than expiration rate and you may hit 1 billion keys limit.
Example ¶
package main
import (
"fmt"
"time"
expiremap "github.com/nursik/go-expire-map"
)
func main() {
expireMap := expiremap.New()
// You must call Close(), when you do not need the map anymore
defer expireMap.Close()
// GMT Wednesday, 1 January 2025, 0:00:00
far := time.Unix(1735689600, 0)
ttl := time.Until(far)
// Insert
expireMap.Set(1, 1, ttl)
// Get value
v, ok := expireMap.Get(1)
fmt.Println(v, ok)
// Output 1 true
// Get TTL
v = expireMap.GetTTL(1)
// The output is equal to ~ ttl
fmt.Println(v)
// Update TTL
v, ok = expireMap.SetTTL(1, time.Second)
fmt.Println(v, ok)
// Output 1 true
time.Sleep(time.Second + time.Millisecond)
// Because key is already expired, it returns nil, false
v, ok = expireMap.SetTTL(1, ttl)
fmt.Println(v, ok)
// Output nil false
// Because key is already expired, it returns nil, false
v, ok = expireMap.Get(1)
fmt.Println(v, ok)
// Output nil false
}
Output:
Index ¶
- type Event
- type EventType
- type ExpireMap
- func (m *ExpireMap) Close()
- func (m *ExpireMap) Curtime() int64
- func (m *ExpireMap) Delete(key interface{})
- func (m *ExpireMap) Get(key interface{}) (interface{}, bool)
- func (m *ExpireMap) GetAll() []KeyValue
- func (m *ExpireMap) GetTTL(key interface{}) int64
- func (m *ExpireMap) Notify(c chan<- Event, events EventType)
- func (m *ExpireMap) Set(key interface{}, value interface{}, ttl time.Duration)
- func (m *ExpireMap) SetTTL(key interface{}, ttl time.Duration) (interface{}, bool)
- func (m *ExpireMap) Size() int
- func (m *ExpireMap) Stopped() bool
- type KeyValue
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Event ¶ added in v1.1.0
type Event struct {
Key interface{}
Value interface{}
// Time is when this event occurred (in Unix nanoseconds)
Time int64
// Due is when this key will expire (in Unix nanoseconds, 0 for expired)
Due int64
// Type of the Event. Equal to Expire, Delete, Update or Set.
Type EventType
}
Event is used for notification channel
type EventType ¶ added in v1.1.0
type EventType uint8
EventType is used for notification channel
const ( // Expire event is fired, when key is deleted due to expiration Expire EventType = 1 << iota // Delete event is fired, when key explicitly deleted using Delete // or SetTTL with non positive TTL Delete // Update event is fired, when TTL or value is updated Update // Set event is fired, when new key is inserted Set // AllEvents is a helper constant, which is the same as // Expire | Delete | Update | Set AllEvents = Expire | Delete | Update | Set // NoEvents is a helper constant, which is the same as 0 (no events) NoEvents = 0 )
type ExpireMap ¶
type ExpireMap struct {
// contains filtered or unexported fields
}
ExpireMap stores keys and corresponding values and TTLs.
func (*ExpireMap) Delete ¶
func (m *ExpireMap) Delete(key interface{})
Delete removes key from the map.
func (*ExpireMap) Get ¶
Get returns value for the given key. If map does not contain such key or key is expired, it returns nil and "false". If key is expired, then it waits for write lock, checks a ttl again (as during wait of write lock, value and ttl could be updated) and if it is still expired, removes the given key (otherwise it returns a value and "true").
func (*ExpireMap) GetTTL ¶
GetTTL returns time in nanoseconds, when key will die. If key is expired or does not exist in the map, it returns 0.
func (*ExpireMap) Notify ¶ added in v1.1.0
Notify sets a channel and causes a map to send Event to a channel based on the given EventType. To get events X1, X2... pass X1|X2|...(for example, to get Update and Set events pass Update|Set). To receive all events use AllEvents constant (the same as Update|Set|Delete|Expire). To receive no events use NoEvents constant or set nil chan. When the map stops, no events are guaranteed to be sent to the channel. It is up to the user to close the channel.
Example ¶
package main
import (
"fmt"
"time"
expiremap "github.com/nursik/go-expire-map"
)
func main() {
expireMap := expiremap.New()
defer expireMap.Close()
c := make(chan expiremap.Event, 10)
expireMap.Notify(c, expiremap.AllEvents)
var event expiremap.Event
// Set
expireMap.Set("key1", "value1", time.Second)
event = <-c
fmt.Println(event.Key, event.Value, event.Type == expiremap.Set)
// Update
expireMap.Set("key1", "value1", time.Second)
event = <-c
fmt.Println(event.Key, event.Value, event.Type == expiremap.Update)
expireMap.Set("key2", "value2", time.Hour)
<-c
expireMap.Delete("key2")
event = <-c
fmt.Println(event.Key, event.Value, event.Type == expiremap.Delete)
// Causes Expire event to be fired
time.Sleep(time.Second)
event = <-c
fmt.Println(event.Key, event.Value, event.Type == expiremap.Expire)
}
Output: key1 value1 true key1 value1 true key2 value2 true key1 value1 true
func (*ExpireMap) SetTTL ¶
SetTTL updates ttl for the given key. If ttl was successfully updated, it returns value and "true". It happens, if and only if key presents in the map and "ttl" variable is greater than timeResolution. In any other case it returns nil and "false". Also, if "ttl" variable is non positive, it just removes a key.