cache

package module
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 12, 2025 License: MIT Imports: 10 Imported by: 0

README

Simple cache package

This package is a simple in-memory key-value storage based on golang map type. Check docs for more details.

Import

import "github.com/emar-kar/cache/v2"

Increment/decrement values

Package has two exported functions, which recive Cache as first argument and support increment and decrement of the stored values by given N. It was done this way to support generics with Integer type:

type Integer interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
func Increment[T Integer](c *Cache, k string, n T) (T, error)

func Decrement[T Integer](c *Cache, k string, n T) (T, error)

Those functions will return error if key does not exist, was expired or it's data type assertion failed.

Eviction with goroutines

Since it was decided to remove explicit goroutine call for eviction functions in Delete* methods, here is the workaround how to implement this anyway:

goEviction := func() func(string, any) {
    return func(str string, a any) {
        go func() {
            // Do something with key and value...
        }()
    }
}

c := New(WithOnEvictionFn(goEviction()))

if err := c.Set("foo", "simple string"); err != nil {
    // Process error...
}

c.Delete("foo")

// Wait until goroutine finish onEviction...

Save/Load

Experimentally there is a support of marshal/unmarshal data to json format. Since data is stored as any interface, it can dump custom formats as well. Those features are experimental and might change in future. Check examples for more details.

c := New(
    WithDefaultLifetime(uint64(time.Hour)),
    WithMaxSize(1028),
)

if err := c.Set("foo", "simple string"); err != nil {
    // Process error...
}

type test struct {
    Str string `json:"str"`
}

testStruct := &test{"string in struct"}

if err := c.Set("foo2", testStruct); err != nil {
    // Process error...
}

if err := c.Set("foo3", []string{"string in slice"}); err != nil {
    // Process error...
}

if err := c.Set("foo4", map[string]string{"bar": "string in map"}); err != nil {
    // Process error...
}

dumpFile := "dump.json"

f, err := os.Create(dumpFile)
if err != nil {
    // Process error...
}

if err := c.Save(f); err != nil {
    f.Close()
    // Process error...
}

f.Close()
c.RemoveAll()

f, err = os.Open(dumpFile)
if err != nil {
    // Process error...
}
defer f.Close()

if err := c.Load(f); err != nil {
    // Process error...
}

str, err := c.Get("foo")
if err != nil {
    // Process error...
}

fmt.Println(str) // Prints: "simple string"

if str, err := c.Get("foo2"); err != nil {
    // Process error...
} else {
    jsonData, err := json.Marshal(str)
    if err != nil {
        // Process error...
    }

    var structData test
    if err := json.Unmarshal(jsonData, &structData); err != nil {
        // Process error...
    }

    // structData.Str == "string in struct".
}

if str, err := c.Get("foo3"); err != nil {
    // Process error...
} else {
    sl := make([]string, len(str.([]any)))
    for i, el := range str.([]any) {
        sl[i] = el.(string)
    }

    // sl[0] == "string in slice".
}

if str, err := c.Get("foo4"); err != nil {
    // Process error...
} else {
    m := make(map[string]string, len(str.(map[string]any)))
    for k, v := range str.(map[string]any) {
        m[k] = v.(string)
    }

    // m["bar"] == "string in map".
}

Some productivity tests:

BenchmarkCacheGetDataWithLifetime-10            34216148                35.00 ns/op            0 B/op          0 allocs/op
BenchmarkCacheGetData-10                        83719711                13.92 ns/op            0 B/op          0 allocs/op
BenchmarkCacheGetExpiredConcurrent-10            7922000                143.2 ns/op            0 B/op          0 allocs/op
BenchmarkCacheGetDataConcurrent-10              11062910                140.3 ns/op            0 B/op          0 allocs/op
BenchmarkCacheSetWithOpts-10                    20568798                59.04 ns/op            0 B/op          0 allocs/op
BenchmarkCacheSetData-10                        28863961                41.16 ns/op            0 B/op          0 allocs/op
BenchmarkCacheIncrement-10                      33345606                36.13 ns/op            0 B/op          0 allocs/op
BenchmarkCacheDecrement-10                      32169246                36.19 ns/op            0 B/op          0 allocs/op

Documentation

Overview

Package cache is a simple implementation of in-memory key-value storage based on golang map type. This package allows to setup various options, such as values expiration time (default, individual), max number of entries in cache, max byte size of data which can be stored in cache. Data can be dumped into json file and restored from it with all saved metadata. Cache usage is thread safe and it can be accessed from multiple goroutines.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrDuration  = errors.New("non-positive duration")
	ErrExists    = errors.New("already exists")
	ErrExpired   = errors.New("is expired")
	ErrNotExists = errors.New("does not exist")
	ErrMaxSize   = errors.New("max size limit")
	ErrMaxLength = errors.New("max data limit")
	ErrNotInt    = errors.New("data type is not integer")
)

Functions

func Decrement

func Decrement[T Integer](c *Cache[T], k string, n T) (T, error)

Decrement decrements data of the given key with n. Returns error if key does not exist, was expired or if type assertion to Integer has failed.

func Increment

func Increment[T Integer](c *Cache[T], k string, n T) (T, error)

Increment increments data of the given key with n. Returns error if key does not exist, was expired or if type assertion to Integer has failed.

func WithCleanupInteval

func WithCleanupInteval(d time.Duration) cacheOptFn

WithCleanupInteval sets interval for janitor to clean expired keys.

func WithDataSizeFn

func WithDataSizeFn(fn func(string, any) (uint64, error)) cacheOptFn

WithDataSizeFn sets function which defines data size.

func WithDefaultLifetime

func WithDefaultLifetime(lt uint64) cacheOptFn

WithDefaultLifetime sets default lifetime for key in cache.

func WithDisplacement

func WithDisplacement(co *cacheOpts)

WithDisplacement allows displacement of variables in cache. In case if adding new value will exceed max size or max length of the cache, random value will be removed to free up the space.

func WithLifetime

func WithLifetime[T any](lt uint64) unitOptFn[T]

WithLifetime sets custom lifetime for cache key.

func WithMaxSize

func WithMaxSize(ms uint64) cacheOptFn

WithMaxSize sets maximum cache data size in bytes.

func WithMaxUnits

func WithMaxUnits(mu uint64) cacheOptFn

WithMaxUnits sets maxixum number of keys, which can be stored in cache.

func WithOnEvictionFn

func WithOnEvictionFn(fn func(string, any)) cacheOptFn

WithOnEvictionFn sets custom function which is applied when key is being deleted from cache.

func WithSize

func WithSize[T any](s uint64) unitOptFn[T]

WithSize sets custom size for key data. If set, cache will ignore size calculation and use passed value. Adds default metadata size.

func WithoutJanitorEviction

func WithoutJanitorEviction(co *cacheOpts)

WithoutJanitorEviction sets janitor to clean expired keys without applying on eviction function even if it was set.

Types

type Cache

type Cache[T any] struct {
	// contains filtered or unexported fields
}
Example
c := New[any]()

if err := c.Set("foo", "bar"); err != nil {
	// Process error...
}

data, err := c.Get("foo")
if err != nil {
	// Process error...
}

fmt.Println(data) // Prints: "bar".
Example (SaveLoad)
c := New[any](
	WithDefaultLifetime(uint64(time.Hour)),
	WithMaxSize(1028),
)

if err := c.Set("foo", "simple string"); err != nil {
	// Process error...
}

type test struct {
	Str string `json:"str"`
}

testStruct := &test{"string in struct"}

if err := c.Set("foo2", testStruct); err != nil {
	// Process error...
}

if err := c.Set("foo3", []string{"string in slice"}); err != nil {
	// Process error...
}

if err := c.Set("foo4", map[string]string{"bar": "string in map"}); err != nil {
	// Process error...
}

dumpFile := "dump.json"

f, err := os.Create(dumpFile)
if err != nil {
	// Process error...
}

if err := c.Save(f); err != nil {
	f.Close()
	// Process error...
}

f.Close()
c.RemoveAll()

f, err = os.Open(dumpFile)
if err != nil {
	// Process error...
}
defer f.Close()

if err := c.Load(f); err != nil {
	// Process error...
}

str, err := c.Get("foo")
if err != nil {
	// Process error...
}

fmt.Println(str) // Prints: "simple string".

if str, err := c.Get("foo2"); err != nil {
	// Process error...
} else {
	jsonData, err := json.Marshal(str)
	if err != nil {
		// Process error...
	}

	var structData test
	if err := json.Unmarshal(jsonData, &structData); err != nil {
		// Process error...
	}

	// structData.Str == "string in struct".
}

if str, err := c.Get("foo3"); err != nil {
	// Process error...
} else {
	sl := make([]string, len(str.([]any)))
	for i, el := range str.([]any) {
		sl[i] = el.(string)
	}

	// sl[0] == "string in slice".
}

if str, err := c.Get("foo4"); err != nil {
	// Process error...
} else {
	m := make(map[string]string, len(str.(map[string]any)))
	for k, v := range str.(map[string]any) {
		m[k] = v.(string)
	}

	// m["bar"] == "string in map".
}
Example (WithOptions)
c := New[any](
	WithCleanupInteval(time.Minute),
	WithDefaultLifetime(uint64(500*time.Millisecond)),
)

if err := c.Set("foo", "bar"); err != nil {
	// Process error...
}

time.Sleep(1 * time.Second)

data, err := c.Get("foo")
fmt.Println(data) // Prints: "bar".
fmt.Println(err)  // Prints: key "foo": is expired.

c.RemoveExpired()

_, err = c.Get("foo")
fmt.Println(err) // Prints: key "foo": does not exist.

if err := c.Set("foo", "bar", WithLifetime[any](uint64(1*time.Second))); err != nil {
	// Process error...
}

time.Sleep(500 * time.Millisecond)

m := c.Alive()
if v, ok := m["foo"]; ok {
	fmt.Println(v) // Prints: "bar".
}

func New

func New[T any](opts ...cacheOptFn) *Cache[T]

New creates new Cache instance with given options.

func (*Cache[T]) Add

func (c *Cache[T]) Add(k string, a T, opts ...unitOptFn[T]) error

Add sets data in cache only if given key does not exist.

func (*Cache[T]) Alive

func (c *Cache[T]) Alive() map[string]T

Alive creates copy of the cache with not expired keys data.

func (*Cache[T]) ChangeDefaultLifeTime

func (c *Cache[T]) ChangeDefaultLifeTime(lt uint64)

ChangeDefaultLifeTime updates cache default options with new default lifetime for key. Does not affect keys already in cache.

func (*Cache[T]) ChangeDisplacementPolicy

func (c *Cache[T]) ChangeDisplacementPolicy(v bool)

ChangeDisplacementPolicy updates cache options with new displacement.

func (*Cache[T]) ChangeJanitorOnEviction

func (c *Cache[T]) ChangeJanitorOnEviction(b bool)

ChangeJanitorOnEviction updates cache default options with new janitor expiried keys removal behavior. Allows to control if janitor should apply on eviction function even if it was set. Restart janitor if it's currently running.

func (*Cache[T]) ChangeMaxLength

func (c *Cache[T]) ChangeMaxLength(ml uint64) error

ChangeMaxLength updates cache default options with new max number of keys. Returns ErrMaxLength if new value is lower than number of keys already in cache.

func (*Cache[T]) ChangeMaxSize

func (c *Cache[T]) ChangeMaxSize(i uint64) error

ChangeMaxSize updates cache default options with new cache max size in bytes.

func (*Cache[T]) ChangeOnEvictionFn

func (c *Cache[T]) ChangeOnEvictionFn(fn func(string, any))

ChangeOnEvictionFn updates cache default options with new function which runs when key is being cleaned after expiration. If janitor is cleaning cache, this function will wait until it finishes, before changing on eviction function.

func (*Cache[T]) ChangeSizeFn

func (c *Cache[T]) ChangeSizeFn(fn func(string, any) (uint64, error))

ChangeSizeFn updates cache default options with new function to define data size. Does not affect keys already in cache.

func (*Cache[T]) Delete

func (c *Cache[T]) Delete(k string)

Delete removes key with given name from cache. Do nothing if key does not exist. Applies on eviction function if it was set.

func (*Cache[T]) DeleteAll

func (c *Cache[T]) DeleteAll()

DeleteAll removes all keys from cache applying on eviction function if it was set.

func (*Cache[T]) DeleteExpired

func (c *Cache[T]) DeleteExpired()

DeleteExpired removes only expired keys applying on eviction function if it was set.

func (*Cache[T]) Get

func (c *Cache[T]) Get(k string) (T, error)

Get returns data of the given key. If key does not exist then ErrNotExists will be returned. If key is already expired, but was not yet cleaned, returns data and ErrExpired as error.

func (*Cache[T]) Length

func (c *Cache[T]) Length() int

Length returns number of keys in cache.

func (*Cache[T]) Load

func (c *Cache[T]) Load(r io.Reader) error

Load restore cache from the given io.Reader with json unmarshaller.

func (*Cache[T]) OrderCleaning

func (c *Cache[T]) OrderCleaning()

OrderCleaning stops current janitor if it was set and starts a new one with cache default cleanup interval.

func (*Cache[T]) Remove

func (c *Cache[T]) Remove(k string)

Remove removes key with given name from cache. Do nothing if key does not exist. Does not apply on eviction function even if it was set.

func (*Cache[T]) RemoveAll

func (c *Cache[T]) RemoveAll()

RemoveAll removes all keys from cache. Does not apply on eviction function even if it was set. Runs GC to collect released memory.

func (*Cache[T]) RemoveExpired

func (c *Cache[T]) RemoveExpired()

RemoveExpired removes only expired keys. Does not apply on eviction function even if it was set.

func (*Cache[T]) Rename

func (c *Cache[T]) Rename(oldKey, newKey string) error

Rename renames old key with a new name only if given key exists in cache and is not expired.

func (*Cache[T]) Replace

func (c *Cache[T]) Replace(k string, a T) error

Replace replaces data of the given key only if this key exists in cache and is not expired.

func (*Cache[T]) RescheduleCleaning

func (c *Cache[T]) RescheduleCleaning(d time.Duration) error

RescheduleCleaning stops current janitor if it was set, updates cache default cleanup interval with given duration and starts a new janitor.

func (*Cache[T]) Revive

func (c *Cache[T]) Revive(k string) error

Revive prolongs lifetime of the key with default value from cache options.

func (*Cache[T]) ReviveUntil

func (c *Cache[T]) ReviveUntil(k string, lt uint64) error

ReviveUntil prolongs lifetime of the key with specified value.

func (*Cache[T]) Save

func (c *Cache[T]) Save(w io.Writer) error

Save dumps cache into the given io.Writer with json marshaller.

func (*Cache[T]) Scan

func (c *Cache[T]) Scan(sub string) map[string]T

Scan scans current [Snapshot] of the cache data and returns key-value map if key contains given sub-string.

func (*Cache[T]) ScanFunc

func (c *Cache[T]) ScanFunc(fn func(string) bool) map[string]T

ScanFunc scans current [Snapshot] of the cache and returns key-value map if given func returns true for a key.

func (*Cache[T]) Set

func (c *Cache[T]) Set(k string, a T, opts ...unitOptFn[T]) error

Set saves data in cache with given key and options. If key already exists, it will be removed before adding new value.

func (*Cache[T]) Size

func (c *Cache[T]) Size() uint64

Size returns current size of the cache in bytes.

func (*Cache[T]) Snapshot

func (c *Cache[T]) Snapshot() map[string]T

Snapshot creates copy of the cache with all keys data.

func (*Cache[T]) Stats

func (c *Cache[T]) Stats() *Stats

Stats gets current cache state.

func (*Cache[T]) StopCleaning

func (c *Cache[T]) StopCleaning()

StopCleaning stops current janitor if it was set. This function waits until janitor is unlocked if it is in cleaning progress.

type Integer

type Integer interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

type Stats

type Stats struct {
	CleanupInterval time.Duration
	DefaultLifetime uint64
	CurrentLength   int
	MaxLength       uint64
	MaxSize         uint64
	CurrentSize     uint64
}

Stats represents current cache statistics.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL