Documentation
¶
Overview ¶
Package geche (GEneric Cache) implements several types of caches using Go generics (requires go 1.18+).
Index ¶
- Variables
- type BufferRec
- type Geche
- type KV
- func (kv *KV[V]) Del(key string) error
- func (kv *KV[V]) Get(key string) (V, error)
- func (kv *KV[V]) Len() int
- func (kv *KV[V]) ListByPrefix(prefix string) ([]V, error)
- func (kv *KV[V]) Set(key string, value V)
- func (kv *KV[V]) SetIfAbsent(key string, value V) (V, bool)
- func (kv *KV[V]) SetIfPresent(key string, value V) (V, bool)
- func (kv *KV[V]) Snapshot() map[string]V
- type KVCache
- func (kv *KVCache[K, V]) AllByPrefix(prefix string) iter.Seq2[string, V]
- func (kv *KVCache[K, V]) Del(key string) error
- func (kv *KVCache[K, V]) Get(key K) (V, error)
- func (kv *KVCache[K, V]) Len() int
- func (kv *KVCache[K, V]) ListByPrefix(prefix string) ([]V, error)
- func (kv *KVCache[K, V]) Set(key K, value V)
- func (kv *KVCache[K, V]) SetIfAbsent(key K, value V) (V, bool)
- func (kv *KVCache[K, V]) SetIfPresent(key K, value V) (V, bool)
- func (kv *KVCache[K, V]) Snapshot() map[string]V
- type Locker
- type MapCache
- func (c *MapCache[K, V]) Del(key K) error
- func (c *MapCache[K, V]) Get(key K) (V, error)
- func (c *MapCache[K, V]) Len() int
- func (c *MapCache[K, V]) Set(key K, value V)
- func (c *MapCache[K, V]) SetIfAbsent(key K, value V) (V, bool)
- func (c *MapCache[K, V]) SetIfPresent(key K, value V) (V, bool)
- func (c *MapCache[K, V]) Snapshot() map[K]V
- type MapTTLCache
- func (c *MapTTLCache[K, V]) Del(key K) error
- func (c *MapTTLCache[K, V]) Get(key K) (V, error)
- func (c *MapTTLCache[K, V]) Len() int
- func (c *MapTTLCache[K, V]) OnEvict(f onEvictFunc[K, V])
- func (c *MapTTLCache[K, V]) Set(key K, value V)
- func (c *MapTTLCache[K, V]) SetIfAbsent(key K, value V) (V, bool)
- func (c *MapTTLCache[K, V]) SetIfPresent(key K, value V) (V, bool)
- func (c *MapTTLCache[K, V]) Snapshot() map[K]V
- type Mapper
- type NumberMapper
- type RingBuffer
- func (c *RingBuffer[K, V]) All() iter.Seq2[K, V]
- func (c *RingBuffer[K, V]) Del(key K) error
- func (c *RingBuffer[K, V]) Get(key K) (V, error)
- func (c *RingBuffer[K, V]) Len() int
- func (c *RingBuffer[K, V]) ListAll() []BufferRec[K, V]
- func (c *RingBuffer[K, V]) ListAllKeys() []K
- func (c *RingBuffer[K, V]) ListAllValues() []V
- func (c *RingBuffer[K, V]) Set(key K, value V)
- func (c *RingBuffer[K, V]) SetIfAbsent(key K, value V) (V, bool)
- func (c *RingBuffer[K, V]) SetIfPresent(key K, value V) (V, bool)
- func (c *RingBuffer[K, V]) Snapshot() map[K]V
- type Sharded
- func (s *Sharded[K, V]) Del(key K) error
- func (s *Sharded[K, V]) Get(key K) (V, error)
- func (s *Sharded[K, V]) Len() int
- func (s *Sharded[K, V]) Set(key K, value V)
- func (s *Sharded[K, V]) SetIfAbsent(key K, value V) (V, bool)
- func (s *Sharded[K, V]) SetIfPresent(key K, value V) (V, bool)
- func (s *Sharded[K, V]) Snapshot() map[K]V
- type StringMapper
- type Tx
- func (tx *Tx[K, V]) Del(key K) error
- func (tx *Tx[K, V]) Get(key K) (V, error)
- func (tx *Tx[K, V]) Len() int
- func (tx *Tx[K, V]) ListByPrefix(prefix string) ([]V, error)
- func (tx *Tx[K, V]) Set(key K, value V)
- func (tx *Tx[K, V]) SetIfAbsent(key K, value V) (V, bool)
- func (tx *Tx[K, V]) SetIfPresent(key K, value V) (V, bool)
- func (tx *Tx[K, V]) Snapshot() map[K]V
- func (tx *Tx[K, V]) Unlock()
- type UpdateFn
- type Updater
- func (u *Updater[K, V]) Del(key K) error
- func (u *Updater[K, V]) Get(key K) (V, error)
- func (u *Updater[K, V]) Len() int
- func (u *Updater[K, V]) ListByPrefix(prefix string) ([]V, error)
- func (u *Updater[K, V]) Set(key K, value V)
- func (u *Updater[K, V]) SetIfAbsent(key K, value V) (V, bool)
- func (u *Updater[K, V]) SetIfPresent(key K, value V) (V, bool)
- func (u *Updater[K, V]) Snapshot() map[K]V
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNotFound = errors.New("not found")
Functions ¶
This section is empty.
Types ¶
type BufferRec ¶ added in v1.4.0
type BufferRec[K comparable, V any] struct { K K V V // contains filtered or unexported fields }
type Geche ¶
type Geche[K comparable, V any] interface { Set(K, V) // SetIfPresent sets the kv only if the key was already present, and returns the previous value (if any) and whether the insertion was performed SetIfPresent(K, V) (V, bool) // SetIfAbsent sets the kv only if the key didn't exist yet, and returns the existing value (if any) and whether the insertion was performed SetIfAbsent(K, V) (V, bool) Get(K) (V, error) Del(K) error Snapshot() map[K]V Len() int }
Geche interface is a common interface for all cache implementations.
type KV ¶
type KV[V any] struct { // contains filtered or unexported fields }
func NewKV ¶
Example ¶
cache := NewMapCache[string, string]()
kv := NewKV[string](cache)
kv.Set("foo", "bar")
kv.Set("foo2", "bar2")
kv.Set("foo3", "bar3")
kv.Set("foo1", "bar1")
res, _ := kv.ListByPrefix("foo")
fmt.Println(res)
Output: [bar bar1 bar2 bar3]
func (*KV[V]) ListByPrefix ¶
func (*KV[V]) SetIfAbsent ¶ added in v1.5.2
func (*KV[V]) SetIfPresent ¶ added in v1.3.0
type KVCache ¶ added in v1.5.0
type KVCache[K byteSlice, V any] struct { // contains filtered or unexported fields }
KVCache is a container that stores the values ordered by their keys using a trie index. It allows in order listing of values by prefix.
func NewKVCache ¶ added in v1.5.0
NewKVCache creates a new KVCache.
Example ¶
cache := NewKVCache[string, string]()
cache.Set("foo", "bar")
cache.Set("foo2", "bar2")
cache.Set("foo3", "bar3")
cache.Set("foo1", "bar1")
res, _ := cache.ListByPrefix("foo")
fmt.Println(res)
Output: [bar bar1 bar2 bar3]
func (*KVCache[K, V]) AllByPrefix ¶ added in v1.5.0
AllByPrefix returns an (read only) iterator over values with keys starting with the given prefix. The iterator yields key-value pairs. Attempting to modify the cache while iterating will lead to a deadlock.
Example ¶
cache := NewKVCache[string, string]()
cache.Set("foo", "bar")
cache.Set("foo2", "bar2")
cache.Set("foo3", "bar3")
cache.Set("foo1", "bar1")
for k, v := range cache.AllByPrefix("foo") {
fmt.Println(k, v)
}
Output: foo bar foo1 bar1 foo2 bar2 foo3 bar3
func (*KVCache[K, V]) Del ¶ added in v1.5.0
Del removes the record by key. Return value is always nil.
func (*KVCache[K, V]) ListByPrefix ¶ added in v1.5.0
ListByPrefix returns all values with keys starting with the given prefix.
func (*KVCache[K, V]) Set ¶ added in v1.5.0
func (kv *KVCache[K, V]) Set(key K, value V)
Set sets the value for the key.
func (*KVCache[K, V]) SetIfAbsent ¶ added in v1.5.2
func (*KVCache[K, V]) SetIfPresent ¶ added in v1.5.0
SetIfPresent sets the value only if the key already exists.
type Locker ¶ added in v1.1.0
type Locker[K comparable, V any] struct { // contains filtered or unexported fields }
Locker is a wrapper for any Geche interface implementation, that provides Lock() and RLock() methods that return Tx object implementing Geche interface. Returned object is not a transaction in a sense that it does not allow commit/rollback or isolation level higher than READ COMMITTED. It only provides a way to do multiple cache operations atomically.
func NewLocker ¶ added in v1.1.0
func NewLocker[K comparable, V any]( cache Geche[K, V], ) *Locker[K, V]
NewLocker creates a new Locker instance.
type MapCache ¶
type MapCache[K comparable, V any] struct { // contains filtered or unexported fields }
MapCache is the simplest thread-safe map-based cache implementation. Does not have any limits or TTL, can grow indefinitely. Should be used when number of distinct keys in the cache is fixed or grows very slow.
func NewMapCache ¶
func NewMapCache[K comparable, V any]() *MapCache[K, V]
func (*MapCache[K, V]) SetIfAbsent ¶ added in v1.5.2
func (*MapCache[K, V]) SetIfPresent ¶ added in v1.3.0
type MapTTLCache ¶
type MapTTLCache[K comparable, V any] struct { // contains filtered or unexported fields }
MapTTLCache is the thread-safe map-based cache with TTL cache invalidation support. MapTTLCache uses double linked list to maintain FIFO order of inserted values.
func NewMapTTLCache ¶
func NewMapTTLCache[K comparable, V any]( ctx context.Context, ttl time.Duration, cleanupInterval time.Duration, ) *MapTTLCache[K, V]
NewMapTTLCache creates MapTTLCache instance and spawns background cleanup goroutine, that periodically removes outdated records. Cleanup goroutine will run cleanup once in cleanupInterval until ctx is canceled. Each record in the cache is valid for ttl duration since it was Set.
func (*MapTTLCache[K, V]) Del ¶
func (c *MapTTLCache[K, V]) Del(key K) error
func (*MapTTLCache[K, V]) Get ¶
func (c *MapTTLCache[K, V]) Get(key K) (V, error)
Get returns ErrNotFound if key is not found in the cache or record is outdated.
func (*MapTTLCache[K, V]) Len ¶
func (c *MapTTLCache[K, V]) Len() int
Len returns the number of records in the cache.
func (*MapTTLCache[K, V]) OnEvict ¶ added in v1.6.0
func (c *MapTTLCache[K, V]) OnEvict(f onEvictFunc[K, V])
OnEvict sets a callback function that will be called when an entry is evicted from the cache due to TTL expiration. The callback receives the key and value of the evicted entry. Note that the eviction callback is not called for Del operation.
func (*MapTTLCache[K, V]) Set ¶
func (c *MapTTLCache[K, V]) Set(key K, value V)
func (*MapTTLCache[K, V]) SetIfAbsent ¶ added in v1.5.2
func (c *MapTTLCache[K, V]) SetIfAbsent(key K, value V) (V, bool)
func (*MapTTLCache[K, V]) SetIfPresent ¶ added in v1.3.0
func (c *MapTTLCache[K, V]) SetIfPresent(key K, value V) (V, bool)
SetIfPresent sets the given key to the given value if the key was already present, and resets the TTL
func (*MapTTLCache[K, V]) Snapshot ¶
func (c *MapTTLCache[K, V]) Snapshot() map[K]V
Snapshot returns a shallow copy of the cache data. Locks the cache from modification for the duration of the copy.
type NumberMapper ¶
type NumberMapper[K integer] struct{}
NumberMapper maps integer keys to N shards using modulo operation.
func (*NumberMapper[K]) Map ¶
func (nm *NumberMapper[K]) Map(key K, numShards int) int
Map key to shard number.
type RingBuffer ¶
type RingBuffer[K comparable, V any] struct { // contains filtered or unexported fields }
RingBuffer cache preallocates a fixed number of elements and starts overwriting oldest values when this number is reached. The idea is to reduce allocations and GC pressure while having fixed memory footprint (does not grow).
func NewRingBuffer ¶
func NewRingBuffer[K comparable, V any](size int) *RingBuffer[K, V]
NewRingBuffer creates RingBuffer instance with predifined size (number of records). This number of records is preallocated immediately. RingBuffer cache can't hold more than size values.
func (*RingBuffer[K, V]) All ¶ added in v1.4.0
func (c *RingBuffer[K, V]) All() iter.Seq2[K, V]
All is a (read-only) iterator over all key-value pairs in the cache. Attempt to modify the cache (Set/Del, etc.) while iterating will lead to a deadlock.
func (*RingBuffer[K, V]) Del ¶
func (c *RingBuffer[K, V]) Del(key K) error
Del removes key from the cache. Return value is always nil.
func (*RingBuffer[K, V]) Get ¶
func (c *RingBuffer[K, V]) Get(key K) (V, error)
Get returns cached value for the key, or ErrNotFound if the key does not exist.
func (*RingBuffer[K, V]) Len ¶
func (c *RingBuffer[K, V]) Len() int
Len returns number of items in the cache.
func (*RingBuffer[K, V]) ListAll ¶ added in v1.4.0
func (c *RingBuffer[K, V]) ListAll() []BufferRec[K, V]
ListAll returns all key-value pairs in the cache in the order they were added.
func (*RingBuffer[K, V]) ListAllKeys ¶ added in v1.4.0
func (c *RingBuffer[K, V]) ListAllKeys() []K
ListAllKeys returns all keys in the cache in the order they were added.
func (*RingBuffer[K, V]) ListAllValues ¶ added in v1.4.0
func (c *RingBuffer[K, V]) ListAllValues() []V
ListAllValues returns all values in the cache in the order they were added.
func (*RingBuffer[K, V]) Set ¶
func (c *RingBuffer[K, V]) Set(key K, value V)
Set adds value to the ring buffer and key index.
func (*RingBuffer[K, V]) SetIfAbsent ¶ added in v1.5.2
func (c *RingBuffer[K, V]) SetIfAbsent(key K, value V) (V, bool)
func (*RingBuffer[K, V]) SetIfPresent ¶ added in v1.3.0
func (c *RingBuffer[K, V]) SetIfPresent(key K, value V) (V, bool)
func (*RingBuffer[K, V]) Snapshot ¶
func (c *RingBuffer[K, V]) Snapshot() map[K]V
Snapshot returns a shallow copy of the cache data. Locks the cache from modification for the duration of the copy.
type Sharded ¶
type Sharded[K comparable, V any] struct { N int // contains filtered or unexported fields }
Sharded is a wrapper for any Geche interface implementation that provides the same interface itself. The idea is to better utilize CPUs using several thread safe shards.
func NewSharded ¶
func NewSharded[K comparable, V any]( shardFactory func() Geche[K, V], numShards int, keyMapper Mapper[K], ) *Sharded[K, V]
NewSharded creates numShards underlying cache containers using shardFactory function to initialize each, and returns Sharded instance that implements Geche interface and routes operations to shards using keyMapper function.
Example ¶
numShards := 4
c := NewSharded[int](
func() Geche[int, string] {
return NewMapCache[int, string]()
},
numShards,
&NumberMapper[int]{},
)
// Set 4000 records in 4 parallel goroutines.
wg := sync.WaitGroup{}
wg.Add(numShards)
for i := 0; i < numShards; i++ {
go func(i int) {
defer wg.Done()
for j := i * 1000; j < i*1000+1000; j++ {
c.Set(j, strconv.Itoa(j))
}
}(i)
}
wg.Wait()
for i := 0; i < 10; i++ {
v, _ := c.Get(i*1000 + 500)
fmt.Println(v)
}
Output: 500 1500 2500 3500
func (*Sharded[K, V]) Set ¶
func (s *Sharded[K, V]) Set(key K, value V)
Set key-value pair in the underlying sharded cache.
func (*Sharded[K, V]) SetIfAbsent ¶ added in v1.5.2
func (*Sharded[K, V]) SetIfPresent ¶ added in v1.3.0
type StringMapper ¶
type StringMapper struct{}
StringMapper is a simple implementation mapping string keys to N shards. It works best with number of shards that is power of 2, and it works up to 256 shards.
type Tx ¶ added in v1.1.0
type Tx[K comparable, V any] struct { // contains filtered or unexported fields }
Tx is a "transaction" object returned by Locker.Lock() and Locker.RLock() methods. See Locker for more details.
func (*Tx[K, V]) Del ¶ added in v1.1.0
Del key from the underlying locked cache. Will panic if called on RLocked Tx.
func (*Tx[K, V]) ListByPrefix ¶ added in v1.1.0
ListByPrefix should only be called if underlying cache supports ListByPrefix.
func (*Tx[K, V]) Set ¶ added in v1.1.0
func (tx *Tx[K, V]) Set(key K, value V)
Set key-value pair in the underlying locked cache. Will panic if called on RLocked Tx.
func (*Tx[K, V]) SetIfAbsent ¶ added in v1.5.2
func (*Tx[K, V]) SetIfPresent ¶ added in v1.3.0
type UpdateFn ¶
type UpdateFn[K comparable, V any] func(key K) (V, error)
UpdateFn is a type for a function to be called to get updated value when Updater has a cache miss.
type Updater ¶
type Updater[K comparable, V any] struct { // contains filtered or unexported fields }
Updater is a wrapper on any Geche interface implementation That calls cache update function if key does not exist in the cache. It only allows one Update function per key to be running at a single point of time, reducing odds to get a "cache centipede" situation.
func NewCacheUpdater ¶
func NewCacheUpdater[K comparable, V any]( cache Geche[K, V], updateFn UpdateFn[K, V], poolSize int, ) *Updater[K, V]
NewCacheUpdater returns cache wrapped with Updater. It calls updateFn whenever Get function returns ErrNotFound to update cache key. Only one updateFn for a given key can run at the same time, and only poolSize updateFn with different keys san run simultaneously.
Example ¶
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Create a cache updater with updaterFunction
// that sets cache value equal to the key.
u := NewCacheUpdater[string, string](
NewMapTTLCache[string, string](ctx, time.Minute, time.Minute),
updateFn,
2,
)
// Value is not in the cache yet.
// But it will be set by the updater function.
v, _ := u.Get("nonexistent")
fmt.Println(v)
Output: nonexistent
func (*Updater[K, V]) Get ¶
Get returns value from the cache. If the value is not in the cache, it calls updateFn to get the value and update the cache first. Since updateFn can return error, Get is not guaranteed to always return the value. When cache update fails, Get will return the error that updateFn returned, and not ErrNotFound.
func (*Updater[K, V]) ListByPrefix ¶ added in v1.1.0
ListByPrefix should only be called if underlying cache supports ListByPrefix. Otherwise it will panic.