cachefunk

package module
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2023 License: MIT Imports: 15 Imported by: 0

README

cachefunk

Use wrapper functions to cache function output in golang.

Go Report Card Test Go Reference

Features

  • Currently supported cache adapters:
    • any GORM-supported database
    • in-memory caching
  • Configurable TTL and TTL jitter
  • Cleanup function for periodic removal of expired entries
  • Uses go generics, in IDE type checked parameters and result
  • Can ignore cached values

Getting Started

Dependencies
  • go version that supports generics (tested on v1.19)
Installing

go get -u github.com/rohfle/cachefunk

Example

import (
	"fmt"
	"testing"
	"time"

	"github.com/rohfle/cachefunk"

	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)


func main() {
	type HelloWorldParams struct {
		Name string
	}

	db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	cache := cachefunk.NewGORMCache(db)

    // Define a function
	// ignoreCache is passed through if the function calls other wrapped functions.
	// Note that the only other argument supported currently is params.
	// This params argument can be any value (typically a struct) that can be serialized into JSON
	// WrapString is used to wrap this function, so it must return string or []byte
	helloWorld := func(ignoreCache bool, params *HelloWorldParams) (string, error) {
		return "Hello " + params.Name, nil
	}

    // Wrap the function
	HelloWorld := cachefunk.WrapString(helloWorld, cache, cachefunk.Config{
		Key: "hello",
		TTL: 3600,
	})

	// First call will get value from wrapped function
	value, err := HelloWorld(false, &HelloWorldParams{
		Name: "bob",
	})
	fmt.Println("First call:", value, err)

	// Second call will get value from cache
	value, err = HelloWorld(false, &HelloWorldParams{
		Name: "bob",
	})
	fmt.Println("Second call:", value, err)
}

API

  • WrapString: store result as []byte
  • WrapObject: encode result as JSON and then store as []byte
  • WrapStringWithContext
  • WrapObjectWithContext
  • CacheString
  • CacheObject
  • CacheStringWithContext
  • CacheObjectWithContext

Version History

  • 0.3.0
    • Added disk cache
    • Changed from storing expiry time to storing cached at time (works better with disk cache)
    • Added gzip compression
    • Changed CacheResult to CacheObject, CacheWithContext to CacheObjectWithContext
    • Moved TTL configuration to cache initialization function
    • Removed TTL value for store indefinitely
    • Messed around with git version tags to try to erase history
  • 0.2.0
    • Created CacheResult, CacheString, CacheWithContext, CacheStringWithContext functions
  • 0.1.0
    • Initial release

License

© Rohan Fletcher 2023

This project is licensed under the MIT License - see the LICENSE file for details

Documentation

Overview

This library gives you wrapper functions to cache function output in golang

Index

Examples

Constants

This section is empty.

Variables

View Source
var DEFAULT_KEYCONFIG = &KeyConfig{
	TTL:            3600,
	TTLJitter:      300,
	UseCompression: true,
}

Functions

func CacheObject

func CacheObject[Params any, ResultType any](
	cache Cache,
	key string,
	retrieveFunc func(bool, Params) (ResultType, error),
	ignoreCache bool,
	params Params,
) (ResultType, error)

CacheObject is a function wrapper that caches responses of any json serializable type.

func CacheObjectWithContext

func CacheObjectWithContext[Params any, ResultType any](
	cache Cache,
	key string,
	retrieveFunc func(ctx context.Context, params Params) (ResultType, error),
	ctx context.Context,
	params Params,
) (ResultType, error)

CacheWithContext caches responses of any json serializable type.

func CacheString

func CacheString[Params any, ResultType string | []byte](
	cache Cache,
	key string,
	retrieveFunc func(bool, Params) (ResultType, error),
	ignoreCache bool,
	params Params,
) (ResultType, error)

func CacheStringWithContext

func CacheStringWithContext[Params any, ResultType string | []byte](
	cache Cache,
	key string,
	retrieveFunc func(ctx context.Context, params Params) (ResultType, error),
	ctx context.Context,
	params Params,
) (ResultType, error)

CacheWithStringContext caches string or []byte responses.

func DefaultCalculatePath

func DefaultCalculatePath(cacheKey string, params string) []string

Returns the

func RenderParameters

func RenderParameters(params interface{}) (string, error)

renderParameters returns a string representation of params

func WrapObject

func WrapObject[Params any, ResultType any](
	cache Cache,
	key string,
	retrieveFunc func(bool, Params) (ResultType, error),
) func(bool, Params) (ResultType, error)

WrapObjects is a function wrapper that caches responses of any json serializable type.

func WrapObjectWithContext

func WrapObjectWithContext[Params any, ResultType any](
	cache Cache,
	key string,
	retrieveFunc func(context.Context, Params) (ResultType, error),
) func(context.Context, Params) (ResultType, error)

WrapObjectWithContext is a function wrapper that caches responses of any json serializable type.

func WrapString

func WrapString[Params any, ResultType string | []byte](
	cache Cache,
	key string,
	retrieveFunc func(bool, Params) (ResultType, error),
) func(bool, Params) (ResultType, error)

WrapString is a function wrapper that caches string or []byte responses.

func WrapStringWithContext

func WrapStringWithContext[Params any, ResultType string | []byte](
	cache Cache,
	key string,
	retrieveFunc func(context.Context, Params) (ResultType, error),
) func(context.Context, Params) (ResultType, error)

WrapStringWithContext is a function wrapper that caches string or []byte responses.

Types

type Cache

type Cache interface {
	SetConfig(config *CacheFunkConfig)
	// Get a value from the cache if it exists
	Get(key string, params string) (value []byte, found bool)
	// Set a value in the cache
	Set(key string, params string, value []byte)
	// Set a raw value for key in the cache
	SetRaw(key string, params string, value []byte, timestamp time.Time, isCompressed bool)
	// Get the number of entries in the cache
	EntryCount() int64
	// Get how many entries have expired in the cache compared to cutoff
	// entries expiry compared to utc now if cutoff is nil
	ExpiredEntryCount() int64
	// Delete all entries in the cache
	Clear()
	// Delete entries that have timestamps in cache before cutoff
	// entries expiry compared to utc now if cutoff is nil
	Cleanup()
	// GetIgnoreCacheCtxKey returns Value key under which ignoreCache is stored
	GetIgnoreCacheCtxKey() CtxKey
}

Cache is an interface that supports get/set of values by key

type CacheEntry

type CacheEntry struct {
	ID           int64     `json:"id" gorm:"primaryKey"`
	Timestamp    time.Time `json:"timestamp" gorm:"not null"`
	Key          string    `json:"key" gorm:"uniqueIndex:idx_key_params;not null"`
	Params       string    `json:"params" gorm:"uniqueIndex:idx_key_params;not null"`
	IsCompressed bool      `json:"is_compressed" gorm:"default:false;not null"`
	Data         []byte    `json:"data" gorm:"not null"`
}

type CacheFunkConfig

type CacheFunkConfig struct {
	Defaults *KeyConfig
	Configs  map[string]*KeyConfig
}

func (*CacheFunkConfig) Get

func (c *CacheFunkConfig) Get(key string) *KeyConfig

type CtxKey

type CtxKey string
const DEFAULT_IGNORE_CACHE_CTX_KEY CtxKey = "ignoreCache"

type DiskCache

type DiskCache struct {
	CacheConfig       *CacheFunkConfig
	BasePath          string
	CalculatePath     func(cacheKey string, params string) []string
	IgnoreCacheCtxKey CtxKey
}
Example
package main

import (
	"fmt"

	"github.com/rohfle/cachefunk"
)

func main() {
	type HelloWorldParams struct {
		Name string
	}

	helloWorld := func(ignoreCache bool, params *HelloWorldParams) (string, error) {
		return "Hello " + params.Name, nil
	}

	cache := cachefunk.NewDiskCache("/path/to/cache")

	HelloWorld := cachefunk.WrapString(cache, "hello", helloWorld)
	params := &HelloWorldParams{
		Name: "bob",
	}

	// First call will get value from wrapped function
	value, err := HelloWorld(false, params)
	fmt.Println("First call:", value, err)
	// Second call will get value from cache
	value, err = HelloWorld(false, params)
	fmt.Println("Second call:", value, err)
}
Output:

func NewDiskCache

func NewDiskCache(basePath string, calcPathFn ...func(string, string) []string) *DiskCache

func (*DiskCache) Cleanup

func (c *DiskCache) Cleanup()

Cleanup will delete all cache entries that have expired

func (*DiskCache) Clear

func (c *DiskCache) Clear()

Clear will delete all cache entries

func (*DiskCache) EntryCount

func (c *DiskCache) EntryCount() int64

func (*DiskCache) ExpiredEntryCount

func (c *DiskCache) ExpiredEntryCount() int64

func (*DiskCache) Get

func (c *DiskCache) Get(key string, params string) ([]byte, bool)

func (*DiskCache) GetIgnoreCacheCtxKey

func (c *DiskCache) GetIgnoreCacheCtxKey() CtxKey

func (*DiskCache) IterateFiles

func (c *DiskCache) IterateFiles(basePath string, callback func(string, fs.DirEntry))

func (*DiskCache) Set

func (c *DiskCache) Set(key string, params string, value []byte)

Set will set a cache value by its key and params

func (*DiskCache) SetConfig

func (c *DiskCache) SetConfig(config *CacheFunkConfig)

func (*DiskCache) SetRaw

func (c *DiskCache) SetRaw(key string, params string, value []byte, timestamp time.Time, useCompression bool)

type GORMCache

type GORMCache struct {
	CacheConfig       *CacheFunkConfig
	DB                *gorm.DB
	IgnoreCacheCtxKey CtxKey
}
Example
package main

import (
	"fmt"

	"github.com/rohfle/cachefunk"

	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func main() {
	type HelloWorldParams struct {
		Name string
	}

	helloWorld := func(ignoreCache bool, params *HelloWorldParams) (string, error) {
		return "Hello " + params.Name, nil
	}

	db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	cache := cachefunk.NewGORMCache(db)

	HelloWorld := cachefunk.WrapString(cache, "hello", helloWorld)
	params := &HelloWorldParams{
		Name: "bob",
	}

	// First call will get value from wrapped function
	value, err := HelloWorld(false, params)
	fmt.Println("First call:", value, err)
	// Second call will get value from cache
	value, err = HelloWorld(false, params)
	fmt.Println("Second call:", value, err)
}
Output:

func NewGORMCache

func NewGORMCache(db *gorm.DB) *GORMCache

func (*GORMCache) Cleanup

func (c *GORMCache) Cleanup()

Cleanup will delete all cache entries that have expired

func (*GORMCache) Clear

func (c *GORMCache) Clear()

Clear will delete all cache entries

func (*GORMCache) EntryCount

func (c *GORMCache) EntryCount() int64

func (*GORMCache) ExpiredEntryCount

func (c *GORMCache) ExpiredEntryCount() int64

func (*GORMCache) Get

func (c *GORMCache) Get(key string, params string) ([]byte, bool)

func (*GORMCache) GetIgnoreCacheCtxKey

func (c *GORMCache) GetIgnoreCacheCtxKey() CtxKey

func (*GORMCache) Set

func (c *GORMCache) Set(key string, params string, value []byte)

Set will set a cache value by its key and params

func (*GORMCache) SetConfig

func (c *GORMCache) SetConfig(config *CacheFunkConfig)

func (*GORMCache) SetRaw

func (c *GORMCache) SetRaw(key string, params string, value []byte, timestamp time.Time, useCompression bool)

SetRaw will set a cache value by its key and params

type InMemoryCache

type InMemoryCache struct {
	CacheConfig       *CacheFunkConfig
	Store             map[string]*InMemoryCacheEntry
	IgnoreCacheCtxKey CtxKey
}
Example
package main

import (
	"fmt"

	"github.com/rohfle/cachefunk"
)

func main() {
	type HelloWorldParams struct {
		Name string
	}

	helloWorld := func(ignoreCache bool, params *HelloWorldParams) (string, error) {
		return "Hello " + params.Name, nil
	}

	cache := cachefunk.NewInMemoryCache()

	HelloWorld := cachefunk.WrapString(cache, "hello", helloWorld)
	params := &HelloWorldParams{
		Name: "bob",
	}

	// First call will get value from wrapped function
	value, err := HelloWorld(false, params)
	fmt.Println("First call:", value, err)
	// Second call will get value from cache
	value, err = HelloWorld(false, params)
	fmt.Println("Second call:", value, err)
}
Output:

func NewInMemoryCache

func NewInMemoryCache() *InMemoryCache

func (*InMemoryCache) Cleanup

func (c *InMemoryCache) Cleanup()

func (*InMemoryCache) Clear

func (c *InMemoryCache) Clear()

func (*InMemoryCache) EntryCount

func (c *InMemoryCache) EntryCount() int64

func (*InMemoryCache) ExpiredEntryCount

func (c *InMemoryCache) ExpiredEntryCount() int64

func (*InMemoryCache) Get

func (c *InMemoryCache) Get(key string, params string) ([]byte, bool)

func (*InMemoryCache) GetIgnoreCacheCtxKey

func (c *InMemoryCache) GetIgnoreCacheCtxKey() CtxKey

func (*InMemoryCache) Set

func (c *InMemoryCache) Set(key string, params string, value []byte)

func (*InMemoryCache) SetConfig

func (c *InMemoryCache) SetConfig(config *CacheFunkConfig)

func (*InMemoryCache) SetRaw

func (c *InMemoryCache) SetRaw(key string, params string, value []byte, timestamp time.Time, isCompressed bool)

type InMemoryCacheEntry

type InMemoryCacheEntry struct {
	Data         string
	Timestamp    time.Time
	IsCompressed bool
}

type KeyConfig

type KeyConfig struct {
	// TTL is time to live in seconds before the cache value can be deleted
	// If TTL is 0, cache value will expire immediately
	// Use a very large TTL to make the cached value last a long time
	// (for instance 31536000 will cache for one year)
	TTL int64
	// When TTLJitter is > 0, a random value from 1 to TTLJitter will be added to TTL
	// This spreads cache expiry out to stop getting fresh responses all at once
	TTLJitter int64
	// Enable compression of data by gzip
	UseCompression bool
}

Config is used to configure the caching wrapper functions

Jump to

Keyboard shortcuts

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