cachefunk

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Feb 23, 2023 License: MIT Imports: 7 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 the result as []byte
  • Wrap: encode as JSON and then store the result as []byte

Dreams for the Future

  • "Unvariadicize" to allow
    • passing through set number of args
    • through wrapper which caches using variadic
    • to wrapped function that takes set number of non-variadic args
  • Export wrapped functions at the package level more easily
  • Allow generic methods on types

Version History

  • 0.1
    • 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

This section is empty.

Functions

func Wrap added in v0.1.0

func Wrap[Params any, ResultType any](
	retrieveFunc func(bool, Params) (ResultType, error),
	cache Cache,
	config Config,
) func(bool, Params) (ResultType, error)

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

func WrapString

func WrapString[Params any, ResultType string | []byte](
	retrieveFunc func(bool, Params) (ResultType, error),
	cache Cache,
	config Config,
) 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](
	retrieveFunc func(context.Context, Params) (ResultType, error),
	cache Cache,
	config Config,
) func(context.Context, Params) (ResultType, error)

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

func WrapWithContext added in v0.2.0

func WrapWithContext[Params any, ResultType any](
	retrieveFunc func(context.Context, Params) (ResultType, error),
	cache Cache,
	config Config,
) func(context.Context, Params) (ResultType, error)

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

Types

type Cache

type Cache interface {
	// Get a value from the cache if it exists
	Get(config *Config, params string) (value []byte, found bool)
	// Set a value in the cache
	Set(config *Config, params string, value []byte)
	// 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(cutoff *time.Time) int64
	// Delete all entries in the cache
	Clear()
	// Delete entries that have expired in the cache compared to cutoff
	// entries expiry compared to utc now if cutoff is nil
	Cleanup(cutoff *time.Time)
	// GetIgnoreCacheCtxKey returns the 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"`
	CreatedAt time.Time  `json:"created_at"`
	ExpiresAt *time.Time `json:"expires_at"`
	Key       string     `json:"key" gorm:"uniqueIndex:idx_key_params;not null"`
	Params    string     `json:"params" gorm:"uniqueIndex:idx_key_params;not null"`
	Data      []byte     `json:"data" gorm:"not null"`
}

type Config added in v0.1.0

type Config struct {
	// Key is used with params to create an identifier to get / set cache values
	// It should be set to a unique value for each function that is wrapped
	Key string
	// TTL is time to live in seconds before the cache value can be deleted
	// If TTL is 0, cache value will expire immediately
	// If TTL is -1, cache value will never expire
	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
}

Config is used to configure the caching wrapper functions

type CtxKey

type CtxKey string
const DEFAULT_IGNORE_CACHE_CTX_KEY CtxKey = "ignoreCache"

type GORMCache

type GORMCache struct {
	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(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)
}

func NewGORMCache

func NewGORMCache(db *gorm.DB) *GORMCache

func (*GORMCache) Cleanup

func (c *GORMCache) Cleanup(cutoff *time.Time)

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(cutoff *time.Time) int64

func (*GORMCache) Get

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

func (*GORMCache) GetIgnoreCacheCtxKey

func (c *GORMCache) GetIgnoreCacheCtxKey() CtxKey

func (*GORMCache) Set

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

Set will set a cache value by its key and params

type InMemoryCache

type InMemoryCache struct {
	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(helloWorld, cache, cachefunk.Config{
		Key: "hello",
		TTL: 3600,
	})

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

func NewInMemoryCache

func NewInMemoryCache() *InMemoryCache

func (*InMemoryCache) Cleanup

func (c *InMemoryCache) Cleanup(cutoff *time.Time)

func (*InMemoryCache) Clear

func (c *InMemoryCache) Clear()

func (*InMemoryCache) EntryCount

func (c *InMemoryCache) EntryCount() int64

func (*InMemoryCache) ExpiredEntryCount

func (c *InMemoryCache) ExpiredEntryCount(cutoff *time.Time) int64

func (*InMemoryCache) Get

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

func (*InMemoryCache) GetIgnoreCacheCtxKey

func (c *InMemoryCache) GetIgnoreCacheCtxKey() CtxKey

func (*InMemoryCache) Set

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

type InMemoryCacheEntry

type InMemoryCacheEntry struct {
	Data      string
	ExpiresAt *time.Time
}

Jump to

Keyboard shortcuts

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