An in-memory key:value store/cache library written in Go 1.18 generics

go-generics-cache

.github/workflows/test.yml codecov

go-generics-cache is an in-memory key:value store/cache that is suitable for applications running on a single machine. This in-memory cache uses Go Generics which will be introduced in 1.18.

  • implemented with Go Generics
  • a thread-safe map[string]interface{} with expiration times

Requirements

Go 1.18 or later.

If Go 1.18 has not been released but you want to try this package, you can easily do so by using the gotip command.

$ go install golang.org/dl/gotip@latest
$ gotip download # latest commit
$ gotip version
go version devel go1.18-c2397905e0 Sat Nov 13 03:33:55 2021 +0000 darwin/arm64

Install

$ go get github.com/Code-Hex/go-generics-cache

Usage

See also examples

playground: https://gotipplay.golang.org/p/FXRk6ngYV-s

package main

import (
	"fmt"
	"time"

	cache "github.com/Code-Hex/go-generics-cache"
)

func main() {
	// Create a cache. key as string, value as int.
	c1 := cache.New[string, int]()

	// Sets the value of int. you can set with expiration option.
	c1.Set("foo", 1, cache.WithExpiration(time.Hour))

	// the value never expires.
	c1.Set("bar", 2)

	foo, ok := c1.Get("foo")
	if ok {
		fmt.Println(foo) // 1
	}

	fmt.Println(c1.Keys()) // outputs "foo" "bar" may random

	// Create a cache. key as int, value as string.
	c2 := cache.New[int, string]()
	c2.Set(1, "baz")
	baz, ok := c2.Get(1)
	if ok {
		fmt.Println(baz) // "baz"
	}

	// Create a cache for Number constraint.. key as string, value as int.
	nc := cache.NewNumber[string, int]()
	nc.Set("age", 26)

	// This will be compile error, because string is not satisfied cache.Number constraint.
	// nc := cache.NewNumber[string, string]()

	incremented, _ := nc.Increment("age", 1)
	fmt.Println(incremented) // 27

	decremented, _ := nc.Decrement("age", 1)
	fmt.Println(decremented) // 26
}

Articles

Owner
Kei Kamikawa
Okinawapm, Previous: Mercari, Inc.
Kei Kamikawa
Comments
  • Improved performance/memory usage for items with an expiry

    Improved performance/memory usage for items with an expiry

    Hi! Thanks for a good cache library that provides generics – it's really handy.

    I've used this a bit for certain things, and found out that adding an expiry to items adds a new goroutine:

    https://github.com/Code-Hex/go-generics-cache/blob/a0612befe7b73dba691fcf989637c0d303fc2b84/cache.go#L170-L180

    A lot of goroutines is pretty expensive, both in terms of switching and in terms of memory usage (each is 2kb in size).

    Would you be open to a PR for a more performant implementation? github.com/patrickmn/go-cache uses a janitor, and I suppose this library could implement something like that -- falling back to the current behaviour if a cleanup interval isn't provided.

  • Maybe change containers/list to implementations on generics

    Maybe change containers/list to implementations on generics

    https://github.com/bahlo/generic-list-go - for example this looks very similar to containers/list but without empty interfaces. I noticed what the library uses containers/list because my program sometimes crashes

    panic: interface conversion: interface {} is nil, not *lru.entry[string,*github.com/Code-Hex/go-generics-cache.Item[string,int64]]
    
    goroutine 241845 [running]:
    github.com/Code-Hex/go-generics-cache/policy/lru.(*Cache[...]).delete(...)
     /go/pkg/mod/github.com/!code-!hex/[email protected]/policy/lru/lru.go:118
    github.com/Code-Hex/go-generics-cache/policy/lru.(*Cache[...]).deleteOldest(0xc000234330?)
     /go/pkg/mod/github.com/!code-!hex/[email protected]/policy/lru/lru.go:113 +0xed
    github.com/Code-Hex/go-generics-cache/policy/lru.(*Cache[...]).Set(0xc00023a048, {0xc0061324b0, 0x42}, 0xc007a885a0)
     /go/pkg/mod/github.com/!code-!hex/[email protected]/policy/lru/lru.go:85 +0x339
    github.com/Code-Hex/go-generics-cache.(*Cache[...]).Set(0xc0002340f0, {0xc0061324b0, 0x42}, 0x48, {0x0?, 0x0, 0x0})
     /go/pkg/mod/github.com/!code-!hex/[email protected]/cache.go:158 +0x1f3
    

    It also can be problem in logic (maybe some races)

  • Add Count() function

    Add Count() function

    Hey,

    I was browsing the code, looks nice. However, isn't sorting for deletion slow? Iteration through just once is faster.

    The Keys() function exposing sorted elements is dangerous, I almost used it to do counting of keys. Therefore I added a Count function to do the same, for monitoring/telemetry purposes.

    Is cleaning code finished? It does not appear to be, janitor.run() is not called from the cache at all, only from test. I guess this is still WIP.

    Cheers!

  • fatal error: concurrent map read and map write

    fatal error: concurrent map read and map write

    Problem: fatal error: concurrent map read and map write

    Reproduce Code:

    func BenchmarkConcurrentSet(b *testing.B) {
    	cache := clock.NewCache[string, string]()
    	group := sync.WaitGroup{}
    	for i := 0; i < b.N; i++ {
    		group.Add(1)
    		var count int = 1000
    		go func() {
    			for count > 0 {
    				cache.Set("key", "value")
    				count--
    				if count <= 0 {
    					group.Done()
    					return
    				}
    			}
    		}()
    	}
    	group.Wait()
    }
    

    Result: image

    Expected: RWLocker for Set/Read.

  • fixed clock policy algorithm around delete

    fixed clock policy algorithm around delete

    Fixes #29

    Until now, if the reference count was 0, it was assumed not to exist, but the value existed in the ring queue, causing an inconsistency. So I have fixed it to set nil value in the ring queue after deleting a value.

  • Clock policy: Not show all the page with sequential access pattern

    Clock policy: Not show all the page with sequential access pattern

    Example test:

    cache := clock.NewCache[int, int](clock.WithCapacity(10))
    for i := 0; i < 10; i++ {
    cache.Set(i, i)
    }
    cache.Set(10, 10)
    
    if len(cache.Keys()) != 10 {
    t.Errorf("want number of keys 10, but got %d, %v", len(cache.Keys()), cache.Keys())
    }
    

    When the cache is full, and new page is set, the hand will iterate through all the element in the cache and set referenceCount = 0. In that case, line 122 of clock.go failed to get the keys that is still in the page (but with ref count == 0).

  • Refactor cache

    Refactor cache

    • breaking change simple, lru, lfu package
      • w/o thread-safe
      • removed Contains method
      • item options
    • added cache.Cache
      • added a watcher for expiration
    • added cache.Interface
    • fixed README
  • lock Increment/Decrement to avoid lostupdate

    lock Increment/Decrement to avoid lostupdate

    In current implementation, if multiple goroutines calls Increment/Decrement simultaneously, the lost update will occur. Let's consider below scenario:

    • The record "counter:0" exists in the NumberCache store
    • Goroutine A and B want to increment that by 1
      • After all, the record should be "counter:2"
    • GetItem called by Goroutine A returns "counter:0"
    • GetItem called by Goroutine B returns "counter:0"
    • Goroutine A increments it by 1, so the record is now "counter:1"
    • Goroutine B increments it by 1, but the record is still "counter:1" because Goroutine B incremented stale record "counter:0"

    I have written unit tests to make sure above behavior occurs. You will see tests fail if you pull this branch locally and comment out the changes in cache.go, then run gotip test .

  • Panic when deleting expired items

    Panic when deleting expired items

    Related codes

    cacheStore := cache.New(cache.AsLFU[string, SomeStruct](lfu.WithCapacity(5)))
    cacheStore.Set(someKey, someValue, cache.WithExpiration(10 * time.Second))
    

    Logs

    panic: runtime error: index out of range [-1]
    
    goroutine 71 [running]:
    github.com/Code-Hex/go-generics-cache/policy/lfu.priorityQueue[...].Swap(...)
            github.com/Code-Hex/[email protected]/policy/lfu/priotiry_queue.go:51
    container/heap.Remove({0x1e0c8c0, 0xc0006982b8}, 0xffffffffffffffff)
            container/heap/heap.go:72 +0x53
    github.com/Code-Hex/go-generics-cache/policy/lfu.(*Cache[...]).Delete(0xc0006982d0, {0xc000322181, 0xbf})
            github.com/Code-Hex/[email protected]/policy/lfu/lfu.go:92 +0x65
    github.com/Code-Hex/go-generics-cache.(*Cache[...]).DeleteExpired(0xc0002c2a20)
            github.com/Code-Hex/[email protected]/cache.go:209 +0x158
    github.com/Code-Hex/go-generics-cache.NewContext[...].func1()
            github.com/Code-Hex/[email protected]/cache.go:175 +0x25
    github.com/Code-Hex/go-generics-cache.(*janitor).run.func1()
            github.com/Code-Hex/[email protected]/janitor.go:39 +0x122
    created by github.com/Code-Hex/go-generics-cache.(*janitor).run
            github.com/Code-Hex/[email protected]/janitor.go:33 +0x72
    
A REST-API service that works as an in memory key-value store with go-minimal-cache library.

A REST-API service that works as an in memory key-value store with go-minimal-cache library.

Aug 25, 2022
A zero-dependency cache library for storing data in memory with generics.

Memory Cache A zero-dependency cache library for storing data in memory with generics. Requirements Golang 1.18+ Installation go get -u github.com/rod

May 26, 2022
Gocodecache - An in-memory cache library for code value master in Golang

gocodecache An in-memory cache library for code master in Golang. Installation g

Jun 23, 2022
Fast key-value cache written on pure golang

GoCache Simple in-memory key-value cache with default or specific expiration time. Install go get github.com/DylanMrr/GoCache Features Key-value stor

Nov 16, 2021
🦉owlcache is a lightweight, high-performance, non-centralized, distributed Key/Value memory-cached data sharing application written by Go
 🦉owlcache is a lightweight, high-performance, non-centralized, distributed Key/Value memory-cached data sharing application written by Go

??owlcache is a lightweight, high-performance, non-centralized, distributed Key/Value memory-cached data sharing application written by Go . keyword : golang cache、go cache、golang nosql

Nov 5, 2022
🧩 Redify is the optimized key-value proxy for quick access and cache of any other database throught Redis and/or HTTP protocol.

Redify (Any database as redis) License Apache 2.0 Redify is the optimized key-value proxy for quick access and cache of any other database throught Re

Sep 25, 2022
Cache library for golang. It supports expirable Cache, LFU, LRU and ARC.
Cache library for golang. It supports expirable Cache, LFU, LRU and ARC.

GCache Cache library for golang. It supports expirable Cache, LFU, LRU and ARC. Features Supports expirable Cache, LFU, LRU and ARC. Goroutine safe. S

Dec 30, 2022
Lru - A simple LRU cache using go generics

LRU Cache A simple LRU cache using go generics. Examples Basic usage. func main(

Nov 9, 2022
An in-memory cache library for golang. It supports multiple eviction policies: LRU, LFU, ARC

GCache Cache library for golang. It supports expirable Cache, LFU, LRU and ARC. Features Supports expirable Cache, LFU, LRU and ARC. Goroutine safe. S

May 31, 2021
Dec 28, 2022
Package cache is a middleware that provides the cache management for Flamego.

cache Package cache is a middleware that provides the cache management for Flamego. Installation The minimum requirement of Go is 1.16. go get github.

Nov 9, 2022
A mem cache base on other populator cache, add following feacture

memcache a mem cache base on other populator cache, add following feacture add lazy load(using expired data, and load it asynchronous) add singlefligh

Oct 28, 2021
Cache - A simple cache implementation

Cache A simple cache implementation LRU Cache An in memory cache implementation

Jan 25, 2022
Gin-cache - Gin cache middleware with golang

Gin-cache - Gin cache middleware with golang

Nov 28, 2022
Cachy is a simple and lightweight in-memory cache api.
Cachy is a simple and lightweight in-memory cache api.

cachy Table of Contents cachy Table of Contents Description Features Structure Configurability settings.json default values for backup_file_path Run o

Apr 24, 2022
In Memory cache in GO Lang Api

In memory key-value store olarak çalışan bir REST-API servisi Standart Kütüphaneler kullanılmıştır Özellikler key ’i set etmek için bir endpoint key ’

Dec 16, 2021
Ristretto - A high performance memory-bound Go cache

Ristretto Ristretto is a fast, concurrent cache library built with a focus on pe

Dec 5, 2022
An embedded key/value database for Go.

bbolt bbolt is a fork of Ben Johnson's Bolt key/value store. The purpose of this fork is to provide the Go community with an active maintenance and de

Dec 31, 2022
Efficient cache for gigabytes of data written in Go.

BigCache Fast, concurrent, evicting in-memory cache written to keep big number of entries without impact on performance. BigCache keeps entries on hea

Dec 30, 2022