cache

package module
v1.0.5 Latest Latest
Warning

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

Go to latest
Published: Jul 7, 2026 License: GPL-2.0 Imports: 5 Imported by: 0

README

cache

A flexible, thread-safe, and generic caching utility package for Go. Designed to be a drop-in dependency for multiple repositories, it features a robust in-memory implementation, time-to-live (TTL) expiration, atomic counters, and powerful transactional memory support with rollback capabilities.

Features

  • Generics Support: Type-safe caches using TypedCache[T] to avoid endless type assertions.
  • Transactions: Execute multiple operations in isolation using RunInTx. If the function returns an error (or the context cancels), all changes are safely discarded.
  • Context Propagation: Inject transactions directly into your context.Context. Global wrapper functions (cache.Get, cache.Set, etc.) will automatically route operations to the active transaction.
  • Atomic Counters: Built-in Increment and Decrement operations for numeric values, safely preserving TTLs.
  • Extensible Interfaces: Easily build custom backends (e.g., Redis, Memcached) by implementing the TransactionalCache or Cache interfaces.
  • In-Memory Engine: A concurrent MemoryCache with an automatic background cleanup worker for expired items.
    • Transactions support for this cache is limited to development or low traffic environments.

Installation

go get github.com/Nigel2392/cache


Usage

Basic Global Cache

The package initializes a default in-memory cache automatically. You can use the global wrapper functions right out of the box.

package main

import (
    "context"
    "fmt"
    "time"
    
    "github.com/Nigel2392/cache"
)

func main() {
    ctx := context.Background()
    
    // Set a value with a 5-minute TTL
    err := cache.Set(ctx, "session_id", "user-123", 5*time.Minute)
    if err != nil {
        panic(err)
    }
    
    // Retrieve the value
    val, err := cache.Get(ctx, "session_id")
    if err != nil {
        panic(err)
    }
    
    fmt.Printf("Session ID: %s\n", val)
}
Generic Type-Safe Cache

Avoid interface{} by instantiating a generic cache for specific data structures.

package main

import (
    "context"
    "fmt"
    "time"
    
    "github.com/Nigel2392/cache"
)

type User struct {
    ID   int
    Name string
}

func main() {
    ctx := context.Background()
    
    // Create a cache exclusively for User structs
    userCache := cache.NewGenericMemoryCache[User]()
    userCache.Run(1 * time.Second) // Start the background cleanup worker
    
    // Type-safe set
    userCache.Set(ctx, "user:1", User{ID: 1, Name: "Alice"}, 1*time.Hour)
    
    // Type-safe get (no casting required)
    user, err := userCache.Get(ctx, "user:1")
    if err == nil {
        fmt.Printf("Found user: %s\n", user.Name)
    }
}
Transactions & Rollbacks

Transactions allow you to group cache modifications. If the callback returns an error, the state reverts, leaving the main cache completely untouched.

package main

import (
    "context"
    "errors"
    "fmt"
    "github.com/Nigel2392/cache"
)

func main() {
    ctx := context.Background()
    
    err := cache.RunInTx(ctx, func(ctx context.Context, tx cache.Transaction) error {
        // Read a value (isolated)
        tx.Set(ctx, "temp_key", "temporary_value", 0)
        
        // Delete a value
        tx.Delete(ctx, "existing_key")
        
        // Something went wrong!
        return errors.New("abort transaction")
    })
    
    if err != nil {
        fmt.Println("Transaction rolled back!")
    }
    
    // "temp_key" does not exist in the main cache, 
    // and "existing_key" was never actually deleted.
}

Context Propagation

You can inject a transaction into a context. If you pass this context to the global cache functions, they will automatically use the transaction state instead of the global cache.

func processRequest(ctx context.Context) error {
    return cache.RunInTx(ctx, func(ctx context.Context, tx cache.Transaction) error {
        // Bind the transaction to the context
        txCtx := cache.ContextWithTransaction(ctx, tx)
        
        // This calls the GLOBAL cache.Set, but because txCtx is passed, 
        // it is secretly routed to the transaction.
        cache.Set(txCtx, "user_balance", 100, 0)
        
        return nil // Commits the transaction to the main cache
    })
}

Atomic Counters

Counters drop in strictly as int64 and allow thread-safe math operations without resetting an item's TTL.

ctx := context.Background()

// Initialize or increment a counter
newVal, _ := cache.Increment(ctx, "page_views", 1)
fmt.Println("Views:", newVal)

// Decrement
newVal, _ = cache.Decrement(ctx, "inventory", 5)


Core Interfaces

If you want to implement your own backend (like Redis), implement these interfaces:

TypedCache[T any]

The base interface for standard cache operations (Get, Set, Delete, Increment, TTL, Has, Clear, Keys).

TypedTransactionalCache[T any]

Embeds TypedCache[T] and adds the RunInTx method for executing isolated changes.

TypedTransaction[T any]

Represents the state during a transaction. Embeds TypedCache[T] and adds InTransaction() bool.

Documentation

Index

Constants

View Source
const (
	DefaultCache = "default"
	Infinity     = internal.Infinity
)
View Source
const (
	OP_UNKNOWN    = internal.OP_UNKNOWN
	OP_GET        = internal.OP_GET        //  Get(c context.Context, key string) (T, error)
	OP_GETDEFAULT = internal.OP_GETDEFAULT //  GetDefault(c context.Context, key string, defaultValue T) (T, error)
	OP_SET        = internal.OP_SET        //  Set(c context.Context, key string, value T, ttl time.Duration) error
	OP_INCR       = internal.OP_INCR       //  Increment(c context.Context, key string, amount int64) (int64, error)
	OP_DECR       = internal.OP_DECR       //  Decrement(c context.Context, key string, amount int64) (int64, error)
	OP_CVAL       = internal.OP_CVAL       //  CounterValue(c context.Context, key string) (int64, error)
	OP_EXPR       = internal.OP_EXPR       //  Expire(c context.Context, key string, ttl time.Duration) error
	OP_TTL        = internal.OP_TTL        //  TTL(c context.Context, key string) time.Duration
	OP_HAS        = internal.OP_HAS        //  Has(c context.Context, key string) bool
	OP_DEL        = internal.OP_DEL        //  Delete(c context.Context, key string) error
	OP_KEYS       = internal.OP_KEYS       //  Keys(c context.Context) ([]string, error)
	OP_CLEAR      = internal.OP_CLEAR      //  Clear(c context.Context) error
	OP_CLOSE      = internal.OP_CLOSE      //  Close(c context.Context) error
)

Variables

View Source
var (
	ErrItemNotFound = internal.ErrItemNotFound
	ErrInvalidType  = internal.ErrInvalidType
	ErrNotSupported = internal.ErrNotSupported
)

Functions

func Clear

func Clear(ctx context.Context) error

Clear removes all keys from the default cache backend.

If any error occurs, Clear should return the error.

If a transaction is active in the context, it will be called on the transaction instead.

func Close

func Close(ctx context.Context) error

Close closes the default cache backend.

If any error occurs, Close should return the error.

If a transaction is active in the context, it will be called on the transaction instead.

func ContextWithTransaction

func ContextWithTransaction(ctx context.Context, tx Transaction) context.Context

func CounterValue

func CounterValue(ctx context.Context, key string) (int64, error)

CounterValue retrieves the counter for the specified key. If the key does not exist in the cache, an error is returned.

func Decrement

func Decrement(ctx context.Context, key string, amount int64) (int64, error)

Decrement atomically decrements a numeric key by the given amount. If the key does not exist, it initializes it to -amount with an infinite TTL. It does NOT reset the TTL of an existing key.

func Delete

func Delete(ctx context.Context, key string) error

Delete removes a key from the default cache backend.

If the key does not exist, Delete should return ErrItemNotFound.

If a transaction is active in the context, it will be called on the transaction instead.

func Expire

func Expire(ctx context.Context, key string, ttl time.Duration) error

Expire sets the TTL for a given key. If the key does not exist in the cache, ErrItemNotFound is returned.

func Get

func Get(ctx context.Context, key string) (interface{}, error)

Get retrieves a value from the default cache backend.

If the key does not exist, Get returns nil and ErrItemNotFound.

If a transaction is active in the context, it will be called on the transaction instead.

func GetDefault

func GetDefault(ctx context.Context, key string, defaultValue interface{}) (interface{}, error)

GetDefault retrieves a value from the default cache backend.

If the key does not exist, GetDefault returns the defaultValue.

It may return an error if the key exists but the cache itself returns an error.

If a transaction is active in the context, it will be called on the transaction instead.

func GetDefaultTTL

func GetDefaultTTL(provided time.Duration) time.Duration

func Has

func Has(ctx context.Context, key string) bool

Has returns true if the key exists in the default cache backend.

If any error occurs, Has returns false.

If a transaction is active in the context, it will be called on the transaction instead.

func Increment

func Increment(ctx context.Context, key string, amount int64) (int64, error)

Increment atomically increments a numeric key by the given amount. If the key does not exist, it initializes it to the amount with an infinite TTL. It does NOT reset the TTL of an existing key.

func Keys

func Keys(ctx context.Context) ([]string, error)

Keys returns all keys in the default cache backend.

If any error occurs, Keys returns an empty slice and the error.

If a transaction is active in the context, it will be called on the transaction instead.

func RegisterCache

func RegisterCache(name string, cache TransactionalCache)

RegisterCache registers a cache backend with a name.

This can later be used to retrieve the cache backend using GetCache.

func RemoveCache

func RemoveCache(name string)

RemoveCache removes a cache backend from the cache backend registry.

This should be used when a cache backend is no longer needed.

func RunInTx

func RunInTx(ctx context.Context, fn func(ctx context.Context, tx Transaction) error) error

RunInTx executes the given function inside a transaction. The provided txCache should be used for all operations inside the function.

func Set

func Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error

Set sets a value in the default cache backend.

The value is stored in the cache with the specified key. The value will expire after the specified ttl.

If a transaction is active in the context, it will be called on the transaction instead.

func SetDefault

func SetDefault(cache TransactionalCache)

SetDefault sets the default cache backend.

The default cache backend is used by the cache package functions.

func SetGetDefaultTTL

func SetGetDefaultTTL(fn func(provided time.Duration) time.Duration)

func TTL

func TTL(ctx context.Context, key string) time.Duration

TTL returns the time to live for a key in the default cache backend.

If the key does not exist, TTL returns 0.

If any error occurs, TTL returns 0.

If a transaction is active in the context, it will be called on the transaction instead.

func Typed_ContextWithTransaction

func Typed_ContextWithTransaction[T any](ctx context.Context, tx TypedTransaction[T]) context.Context

Types

type Cache

type Cache = internal.Cache

Predefined TypedCaches with interface{} as their Type.

type CacheConnector

type CacheConnector = internal.CacheConnector

type CacheOperation

type CacheOperation = internal.CacheOperation

type MemoryCache

type MemoryCache[T any] struct {
	// contains filtered or unexported fields
}

A simple in-memory cache implementation based on a map of string[TYPE].

Look at the interface implementation in cache.go for more information on the methods.

func NewGenericMemoryCache

func NewGenericMemoryCache[T any]() *MemoryCache[T]

Might as well make it generic, right?

func NewMemoryCache

func NewMemoryCache(interval time.Duration) *MemoryCache[interface{}]

Returns a new in-memory cache.

func (*MemoryCache[T]) Clear

func (c *MemoryCache[T]) Clear(_ context.Context) (err error)

func (*MemoryCache[T]) Close

func (c *MemoryCache[T]) Close(_ context.Context) error

func (*MemoryCache[T]) CounterValue

func (c *MemoryCache[T]) CounterValue(ctx context.Context, key string) (int64, error)

func (*MemoryCache[T]) Decrement

func (c *MemoryCache[T]) Decrement(ctx context.Context, key string, amount int64) (int64, error)

Decrement atomically decrements a numeric key by the given amount. If the key does not exist, it initializes it to -amount with an infinite TTL. It does NOT reset the TTL of an existing key.

func (*MemoryCache[T]) Delete

func (c *MemoryCache[T]) Delete(_ context.Context, key string) error

func (*MemoryCache[T]) Expire

func (c *MemoryCache[T]) Expire(_ context.Context, key string, ttl time.Duration) error

func (*MemoryCache[T]) Get

func (c *MemoryCache[T]) Get(_ context.Context, key string) (value T, err error)

func (*MemoryCache[T]) GetDefault

func (c *MemoryCache[T]) GetDefault(_ context.Context, key string, defaultValue T) (value T, err error)

func (*MemoryCache[T]) Has

func (c *MemoryCache[T]) Has(_ context.Context, key string) (exists bool)

func (*MemoryCache[T]) Increment

func (c *MemoryCache[T]) Increment(_ context.Context, key string, amount int64) (int64, error)

Increment atomically increments a numeric key by the given amount. If the key does not exist, it initializes it to the amount with an infinite TTL. It does NOT reset the TTL of an existing key.

func (*MemoryCache[T]) Keys

func (c *MemoryCache[T]) Keys(_ context.Context) ([]string, error)

func (*MemoryCache[T]) Len

func (c *MemoryCache[T]) Len(_ context.Context) int

func (*MemoryCache[T]) Run

func (c *MemoryCache[T]) Run(interval time.Duration)

func (*MemoryCache[T]) RunInTx

func (c *MemoryCache[T]) RunInTx(ctx context.Context, fn func(ctx context.Context, txCache internal.TypedTransaction[T]) error) error

func (*MemoryCache[T]) Set

func (c *MemoryCache[T]) Set(_ context.Context, key string, value T, ttl time.Duration) error

func (*MemoryCache[T]) TTL

func (c *MemoryCache[T]) TTL(_ context.Context, key string) (ttl time.Duration)

type Transaction

type Transaction = internal.Transaction

func TransactionFromContext

func TransactionFromContext(ctx context.Context) (t Transaction, ok bool)

type TransactionalCache

type TransactionalCache = internal.TransactionalCache

func Default

func Default() TransactionalCache

GetDefault retrieves the default cache backend.

If the default cache backend does not exist, GetDefault returns nil.

func GetCache

func GetCache(names ...string) TransactionalCache

GetCache retrieves the first cache backend it can find by name.

If the cache backend does not exist, GetCache returns nil.

type TypedCache

type TypedCache[T any] = internal.TypedCache[T]

type TypedTransaction

type TypedTransaction[T any] = internal.TypedTransaction[T]

func Typed_TransactionFromContext

func Typed_TransactionFromContext[T any](ctx context.Context) (t TypedTransaction[T], ok bool)

type TypedTransactionalCache

type TypedTransactionalCache[T any] = internal.TypedTransactionalCache[T]

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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