cache

package module
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Jul 7, 2026 License: MIT Imports: 11 Imported by: 0

README

Cache

Go Reference Test

A concurrency-safe in-memory cache for Go with TTL expiration, atomic counters, singleflight-backed Remember, and cache-backed locks. The API mirrors the cache module of goravel/framework (itself modeled on Laravel's cache).

Installation

go get github.com/libtnb/cache

Quick start

package main

import (
	"fmt"
	"time"

	"github.com/libtnb/cache"
)

func main() {
	c := cache.NewCache()

	_ = c.Put("token", "abc123", time.Minute) // expires after 1 minute
	c.Forever("site", "libtnb")               // never expires

	fmt.Println(c.GetString("token"))     // "abc123"
	fmt.Println(c.GetInt("missing", 42))  // 42 (default)
	fmt.Println(c.Has("site"))            // true
	fmt.Println(c.Pull("token"))          // "abc123", then removed atomically
}
Expiration
  • NoExpiration (0) means an entry never expires.
  • A negative duration stores an entry that is already expired.
  • Expired entries are removed lazily on access. Caches built with NewCache additionally run a background janitor (default: every 5 minutes) that sweeps expired entries and stops on its own once the cache is garbage collected. Tune or disable it with WithCleanupInterval:
c := cache.NewCache(cache.WithCleanupInterval(time.Minute)) // custom interval
c = cache.NewCache(cache.WithCleanupInterval(0))            // lazy expiration only

Without a janitor (interval 0, or a zero-value &cache.Memory{}), expired entries are only reclaimed when accessed — write-only workloads should call DeleteExpired() periodically.

Counters

Increment/Decrement work on any integer value and are safe under concurrency. Missing keys start at zero without expiration; existing entries keep their expiration.

n, _ := c.Increment("visits")     // 1
n, _ = c.Increment("visits", 10)  // 11
n, _ = c.Decrement("visits")      // 10
Remember

Remember returns the cached value, or computes and stores it. Concurrent calls for the same key share a single callback execution.

user, err := c.Remember("user:1", time.Hour, func() (any, error) {
	return loadUser(1)
})
Locks

Every lock carries a unique owner token: a lock whose TTL already expired cannot release a competitor's lock.

lock := c.Lock("deploy", 10*time.Second)
if lock.Get() {
	defer lock.Release()
	// critical section
}

// Or wait up to 5 seconds for the lock, releasing automatically:
c.Lock("deploy").Block(5*time.Second, func() {
	// critical section
})
Typed access

GetBool/GetInt/GetInt64/GetString coerce the stored value. The generic GetAs type-asserts instead, returning the default (or zero value) on a type mismatch:

user := cache.GetAs[User](c, "user:1")
name := cache.GetAs(c, "name", "default")

Documentation

Overview

Package cache provides a concurrency-safe in-memory cache with TTL expiration, atomic counters, and cache-backed mutual-exclusion locks.

The API mirrors the cache module of goravel/framework (itself modeled on Laravel's cache): Put/Get/Add/Forever/Remember plus typed getters. Entries expire lazily on access; caches built with NewCache additionally run a background janitor that sweeps expired entries and stops on its own when the cache is garbage collected.

c := cache.NewCache()
_ = c.Put("token", "abc123", time.Minute)
token := c.GetString("token")

value, err := c.Remember("user:1", time.Hour, func() (any, error) {
	return loadUser(1)
})

Index

Constants

View Source
const (
	// NoExpiration indicates that an entry never expires.
	NoExpiration time.Duration = 0

	// DefaultCleanupInterval is how often the janitor started by NewCache
	// sweeps expired entries. Override it with WithCleanupInterval.
	DefaultCleanupInterval = 5 * time.Minute
)

Variables

View Source
var ErrNotInteger = errors.New("cache: value is not an integer")

ErrNotInteger is returned by Increment and Decrement when the stored value is not an integer.

Functions

func GetAs added in v1.3.0

func GetAs[T any](c Cache, key string, def ...T) T

GetAs retrieves the value stored under key and type-asserts it to T. It returns def (or T's zero value) when the key is missing or the stored value is not a T. Unlike GetBool/GetInt/GetString it performs no conversion between types.

Types

type Cache

type Cache interface {
	// Add an item in the cache if the key does not exist.
	Add(key string, value any, t time.Duration) bool
	// Decrement decrements the value of an item in the cache.
	Decrement(key string, value ...int64) (int64, error)
	// Forever add an item in the cache indefinitely.
	Forever(key string, value any) bool
	// Forget removes an item from the cache.
	Forget(key string) bool
	// Flush remove all items from the cache.
	Flush() bool
	// Get retrieve an item from the cache by key.
	// The optional default may be a plain value or a func() any that is
	// invoked lazily when the key is missing; any other function signature
	// is returned as-is.
	Get(key string, def ...any) any
	// GetBool retrieves an item from the cache by key as a boolean.
	GetBool(key string, def ...bool) bool
	// GetInt retrieves an item from the cache by key as an integer.
	GetInt(key string, def ...int) int
	// GetInt64 retrieves an item from the cache by key as a 64-bit integer.
	GetInt64(key string, def ...int64) int64
	// GetString retrieves an item from the cache by key as a string.
	GetString(key string, def ...string) string
	// Has check an item exists in the cache.
	Has(key string) bool
	// Increment increments the value of an item in the cache.
	Increment(key string, value ...int64) (int64, error)
	// Lock get a lock instance.
	Lock(key string, t ...time.Duration) *Lock
	// Put stores an item in the cache for a given time.
	// NoExpiration (0) means the entry never expires; a negative duration
	// stores an entry that is already expired.
	Put(key string, value any, t time.Duration) error
	// Pull retrieve an item from the cache and delete it.
	Pull(key string, def ...any) any
	// Remember gets an item from the cache, or execute the given Closure and store the result.
	Remember(key string, ttl time.Duration, callback func() (any, error)) (any, error)
	// RememberForever get an item from the cache, or execute the given Closure and store the result forever.
	RememberForever(key string, callback func() (any, error)) (any, error)
	// WithContext returns a Cache instance using the given context for
	// operations that support it.
	WithContext(ctx context.Context) Cache
}

func NewCache

func NewCache(opts ...Option) Cache

NewCache returns an in-memory Cache. Unless disabled via WithCleanupInterval, a background janitor sweeps expired entries every DefaultCleanupInterval and stops automatically once the returned Cache becomes unreachable.

type Lock

type Lock struct {
	// contains filtered or unexported fields
}

Lock is a mutual-exclusion lock backed by a LockStore. Every instance carries a unique owner token: releasing only succeeds while the lock is still held by that owner, so an instance whose lock already expired cannot release a competitor's lock.

func NewLock

func NewLock(instance LockStore, key string, t ...time.Duration) *Lock

func (*Lock) Block

func (r *Lock) Block(t time.Duration, callback ...func()) bool

Block waits up to t for the lock to become available, retrying every blockRetryInterval, and reports whether it was acquired. When a callback is given, the lock is released after the callback runs.

func (*Lock) ForceRelease

func (r *Lock) ForceRelease() bool

ForceRelease releases the lock regardless of its current owner.

func (*Lock) Get

func (r *Lock) Get(callback ...func()) bool

Get attempts to acquire the lock and reports whether it succeeded. When a callback is given and the lock is acquired, the callback runs before the lock is released again; Get still returns true when that release fails (e.g. the lock expired while the callback ran), since the callback did execute.

func (*Lock) Release

func (r *Lock) Release() bool

Release releases the lock if this instance still holds it. It returns false when the lock was never acquired, already released, or has expired and may since be held by another owner.

type LockStore added in v1.3.0

type LockStore interface {
	Add(key string, value any, t time.Duration) bool
	Forget(key string) bool
	ReleaseLock(key, owner string) bool
}

LockStore is the storage a Lock needs: acquisition via Add, owner-checked atomic release via ReleaseLock, and unconditional deletion via Forget. ReleaseLock must delete key only while it still holds owner's token, as a non-atomic check-then-delete could remove a lock acquired by someone else in between. Requiring it at construction time guarantees every acquirable lock can also be released safely.

type Memory

type Memory struct {
	// contains filtered or unexported fields
}

Memory is an in-memory Cache implementation. Expired entries are removed lazily on access and, when constructed via NewCache, periodically by a background janitor.

The zero value is ready to use but runs no janitor: entries then expire only lazily, so a workload that writes TTL keys it never reads back will retain them indefinitely. Prefer NewCache, or call DeleteExpired periodically when using the zero value.

func (*Memory) Add

func (r *Memory) Add(key string, value any, t time.Duration) bool

Add an item in the cache if the key does not exist or its current entry has expired.

func (*Memory) Decrement

func (r *Memory) Decrement(key string, value ...int64) (int64, error)

Decrement decrements the value of an item in the cache.

func (*Memory) DeleteExpired added in v1.3.0

func (r *Memory) DeleteExpired()

DeleteExpired removes all expired entries. It is called periodically by the janitor started by NewCache and may be called manually on instances constructed without one.

func (*Memory) Flush

func (r *Memory) Flush() bool

Flush Remove all items from the cache.

func (*Memory) Forever

func (r *Memory) Forever(key string, value any) bool

Forever Put an item in the cache indefinitely.

func (*Memory) Forget

func (r *Memory) Forget(key string) bool

Forget Remove an item from the cache.

func (*Memory) Get

func (r *Memory) Get(key string, def ...any) any

Get Retrieve an item from the cache by key.

func (*Memory) GetBool

func (r *Memory) GetBool(key string, def ...bool) bool

func (*Memory) GetInt

func (r *Memory) GetInt(key string, def ...int) int

func (*Memory) GetInt64

func (r *Memory) GetInt64(key string, def ...int64) int64

func (*Memory) GetString

func (r *Memory) GetString(key string, def ...string) string

func (*Memory) Has

func (r *Memory) Has(key string) bool

Has Checks an item exists in the cache.

func (*Memory) Increment

func (r *Memory) Increment(key string, value ...int64) (int64, error)

Increment increments the value of an item in the cache. Missing keys start at zero without expiration; existing entries keep their expiration and must hold an integer value.

func (*Memory) Lock

func (r *Memory) Lock(key string, t ...time.Duration) *Lock

func (*Memory) Pull

func (r *Memory) Pull(key string, def ...any) any

Pull Retrieve an item from the cache and delete it atomically.

func (*Memory) Put

func (r *Memory) Put(key string, value any, t time.Duration) error

Put stores an item in the cache for a given time, replacing any previous entry and its expiration. NoExpiration (0) means the entry never expires; a negative duration stores an entry that is already expired.

func (*Memory) ReleaseLock added in v1.3.0

func (r *Memory) ReleaseLock(key, owner string) bool

ReleaseLock atomically deletes key while it still holds owner's token, implementing LockReleaser.

func (*Memory) Remember

func (r *Memory) Remember(key string, ttl time.Duration, callback func() (any, error)) (any, error)

Remember Get an item from the cache, or execute the given Closure and store the result. Concurrent calls for the same key share a single callback execution, and a cached nil value counts as a hit.

func (*Memory) RememberForever

func (r *Memory) RememberForever(key string, callback func() (any, error)) (any, error)

RememberForever Get an item from the cache, or execute the given Closure and store the result forever.

func (*Memory) WithContext

func (r *Memory) WithContext(_ context.Context) Cache

WithContext implements Cache. The in-memory driver performs no I/O, so the context is unused and the same instance is returned.

type Option added in v1.3.0

type Option func(*options)

Option configures the Cache returned by NewCache.

func WithCleanupInterval added in v1.3.0

func WithCleanupInterval(d time.Duration) Option

WithCleanupInterval sets how often the background janitor sweeps expired entries. A zero or negative interval disables the janitor entirely, in which case expired entries are only removed lazily when accessed.

Jump to

Keyboard shortcuts

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