Giter Site home page Giter Site logo

cache2go's Introduction

cache2go

Latest Release Build Status Coverage Status Go ReportCard GoDoc

Concurrency-safe golang caching library with expiration capabilities.

Installation

Make sure you have a working Go environment (Go 1.2 or higher is required). See the install instructions.

To install cache2go, simply run:

go get github.com/muesli/cache2go

To compile it from source:

cd $GOPATH/src/github.com/muesli/cache2go
go get -u -v
go build && go test -v

Example

package main

import (
	"github.com/muesli/cache2go"
	"fmt"
	"time"
)

// Keys & values in cache2go can be of arbitrary types, e.g. a struct.
type myStruct struct {
	text     string
	moreData []byte
}

func main() {
	// Accessing a new cache table for the first time will create it.
	cache := cache2go.Cache("myCache")

	// We will put a new item in the cache. It will expire after
	// not being accessed via Value(key) for more than 5 seconds.
	val := myStruct{"This is a test!", []byte{}}
	cache.Add("someKey", 5*time.Second, &val)

	// Let's retrieve the item from the cache.
	res, err := cache.Value("someKey")
	if err == nil {
		fmt.Println("Found value in cache:", res.Data().(*myStruct).text)
	} else {
		fmt.Println("Error retrieving value from cache:", err)
	}

	// Wait for the item to expire in cache.
	time.Sleep(6 * time.Second)
	res, err = cache.Value("someKey")
	if err != nil {
		fmt.Println("Item is not cached (anymore).")
	}

	// Add another item that never expires.
	cache.Add("someKey", 0, &val)

	// cache2go supports a few handy callbacks and loading mechanisms.
	cache.SetAboutToDeleteItemCallback(func(e *cache2go.CacheItem) {
		fmt.Println("Deleting:", e.Key(), e.Data().(*myStruct).text, e.CreatedOn())
	})

	// Remove the item from the cache.
	cache.Delete("someKey")

	// And wipe the entire cache table.
	cache.Flush()
}

To run this example, go to examples/mycachedapp/ and run:

go run mycachedapp.go

You can find a few more examples here. Also see our test-cases in cache_test.go for further working examples.

cache2go's People

Contributors

buptliuwei avatar ck89119 avatar dvrkps avatar jnchk avatar michiwend avatar muesli avatar nicewook avatar nofrish avatar rif avatar winxxp avatar yutita avatar zeisss avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cache2go's Issues

[BUG] Fatal ERROR when concurrent map write and read

go version

go version go1.9 darwin/amd64

log

fatal error: concurrent map iteration and map write

goroutine 177777 [running]:
runtime.throw(0xbeb3a9, 0x26)
	/go1.9/src/runtime/panic.go:605 +0x95 fp=0xc422b05610 sp=0xc422b055f0 pc=0x42e9f5
runtime.mapiternext(0xc422b05768)
	/go1.9/src/runtime/hashmap.go:778 +0x6f1 fp=0xc422b056a8 sp=0xc422b05610 pc=0x40d0a1
github.com/muesli/cache2go.(*CacheTable).expirationCheck(0xc42007e060)
	/src/github.com/muesli/cache2go/cachetable.go:111 +0x247 fp=0xc422b057d8 sp=0xc422b056a8 pc=0x578d97
runtime.goexit()
	/src/runtime/asm_amd64.s:2337 +0x1 fp=0xc422b057e0 sp=0xc422b057d8 pc=0x45f7b1
created by github.com/muesli/cache2go.(*CacheTable).expirationCheck.func1
	/src/github.com/muesli/cache2go/cachetable.go:137 +0x3e

Allow for setting an upper limit on memory usage

When a system is under high load the cache might grow to the point when it's consuming all available memory on the system it's running.

Consider adding some kind of upper limit to how much resources are occupied e.g. max items or max memory used. Memory usage could be calculated for structs using unsafe.Sizeof if the security requirements are accepting.

Proposal to Integrate SIEVE Eviction Algorithm

Hi there,

Our team (@1a1a11a) has developed a new cache eviction algorithm, called SIEVE. It’s simple, efficient, and scalable.

Why SIEVE could be a great addition:

  • Simplicity: Integrating SIEVE is straightforward, usually needing to change less than 20 lines of code on average.
  • Efficiency: On skewed workloads, which are typical in web caching scenarios, SIEVE is top-notch.
  • Cache Primitive: SIEVE is not just another algorithm; it's a primitive that could enhance or replace LRU/FIFO queues in advanced systems like LeCaR, TwoQ, ARC, and S3-FIFO.

Welcome to dive into the details on our website sievecache.com and on our SIEVE blog.

We would love to explore the possibility of integrating SIEVE into cache2go. We believe it could be a beneficial addition to the library and the community.

Looking forward to your feedback!

[Feature Request] Change item lifeSpan

I'd like to be able to change an item's life span after adding it.
In my case I want to have it so:

  1. An user interacts with my application
  2. I look for them in the cache, if they're not present I get them from a database.
  3. Make sure they stay in memory while I need them (which can take an arbitrary amount of time)
  4. ... (my program)
  5. After I'm done, set it so they expire in 60 seconds unless the user interacts (which would take us back to the first step).

Right now this can be done by:

  1. Looking for them in the cache. And if they're not present I get them BUT save it outside the cache.
  2. ... (my program)
  3. After I'm done, If they were in the cache and still are, remove them.
  4. Add them to the cache with a life span of 60 seconds.

This adds a lot of complexity which would be solved by being able to change the items life span (first to 0 and then to 60).

Issue implementing it

I tried implementing it by adding a method to CacheItem (you can see it here), but came across an issue if you:

  1. Create an item with a life span
  2. Set it to 0
  3. Wait until the next expirationCheck (here it stops running because there's no items with lifespan != 0)
  4. Set it to any value different than 0
  5. Now it will never expire because the expirationCheck stopped running

This issue is the reason the test currently doesn't pass.
If I run table.expirationCheck() after resetting the life span it works as expected, but currently there's no way to do it from the method.

Question about SetAddedItemCallback

hello
请看如下函数,是不是应该将这这段注释掉
func (table *CacheTable) SetAddedItemCallback(f func(*CacheItem)) {

//if len(table.addedItem) > 0 {
//	table.RemoveAddedItemCallbacks()
//}
table.Lock()
defer table.Unlock()
table.addedItem = append(table.addedItem, f)

}

[bug]: delete operation in Foreach causes a deadlock

test code:

package main

import (
	"fmt"
	"time"

	"github.com/muesli/cache2go"
)

func main() {
	store := cache2go.Cache("test")
	store.AddAboutToDeleteItemCallback(func(v *cache2go.CacheItem) {
		fmt.Println("delete:", v)
	})

	key := "test1"
	store.Add(key, time.Second*1800, "asdfasdfasd")
	fmt.Println("fitst delete:")
	store.Delete(key)

	store.Add(key, time.Second*1800, "asdfasdfasd")

	go func() {
		time.Sleep(time.Second * 2)
		fmt.Println("goroutine delete:")

		store.Foreach(func(key interface{}, v *cache2go.CacheItem) {
			fmt.Println("start")
			store.Delete(key)
			fmt.Println("end")
		})
	}()

	time.Sleep(time.Second * 10)
}

Foreach polls for all, while Rlock is released only after the Foreach execution completes, resulting in a deadlock when deleted in Foreach

PR to get cached value without updating it's keep alive

Hi,

we have a use case where we need to get cached value without touching it's keepAlive value. We need our cached item to expire at whatever time it was about to expire even if we pulled it's value this one time.

So I added a ValueOnly() function that works the same as CacheTable.Value(), but skips updating of keepAlive. In fact, I moved most of the code of the Value() function to new valueInternal() function and both (Value() and ValueOnly()) use it. It's like addInternal() or deleteInternal() that you also use.

Test case TestCacheExpire has been extended to cover the new function and all tests pass.

Please tell what would need to be changed also, to maybe get this additional function merged.

PR is here: #64

why not move table.Lock() into table.addInternal()?

func (table *CacheTable) Add(key interface{}, lifeSpan time.Duration, data interface{}) *CacheItem {
	item := NewCacheItem(key, lifeSpan, data)

	// Add item to cache.
	table.Lock()
	table.addInternal(item)

	return item
}

In general,Lock() and UnLock() is pair in function,why not move table.Lock() into table.addInternal()?Is there anything other to consider?

How about start a channel to exec callback

Thanks for your open source.

if the CacheTabls's items becomes very long and the 'expirationCheck' is runing with lots of callback function waiting to execute, it will lock the table for so many time. so I think it' better to make a channle to exec the callback function.

go1.8 can't iterate over map while writing to it

I upgrade golang to 1.8 and encounter this panic while using cache2go:

goroutine 363453 [running]:
runtime.throw(0x94121a, 0x26)
/usr/local/go/src/runtime/panic.go:596 +0x95 fp=0xc420513de0 sp=0xc420513dc0
runtime.mapiternext(0xc420513f68)
/usr/local/go/src/runtime/hashmap.go:737 +0x7ee fp=0xc420513e90 sp=0xc420513de0
github.com/muesli/cache2go.(*CacheTable).expirationCheck(0xc4201d2540)
/Users/geomantic/Documents/develop/gopath/src/github.com/muesli/cache2go/cachetable.go:111 +0x336 fp=0xc4205 13fd8 sp=0xc420513e90
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:2197 +0x1 fp=0xc420513fe0 sp=0xc420513fd8
created by github.com/muesli/cache2go.(*CacheTable).expirationCheck.func1
/Users/geomantic/Documents/develop/gopath/src/github.com/muesli/cache2go/cachetable.go:137 +0x3e

question about go use

In file cache.go

if !ok {
t = &CacheTable{
name: table,
items: make(map[interface{}]*CacheItem),
}

  mutex.Lock()
  cache[table] = t
  mutex.Unlock()

}

return t

the assigment function of cache item is

  t = &CacheTable{ ... }

The "t = &CacheTable" seems to be stored at stack , and can not be reentrant after function returns.
How could I understand this ?
Thanks.

[Feature Request] Persisting cache data to disk

Hi, I like this library because it is simple to use. But, I couldn't find a way to persisting cache to disk in order to retrieve cache after accidental application shutdown. Do you like to add this feature in the future?

Thanks

Maybe expirationCheck() which in cachetable.go have a little issues?

In expirationCheck function, whether below code should in the function's end(), in other words , i mean in the table.Unlock()'s front ?

if table.cleanupInterval > 0 {
	table.log("Expiration check triggered after", table.cleanupInterval, "for table", table.name)
} else {
	table.log("Expiration check installed for table", table.name)
}

i think, if do this, the log message is more exact and reasonable.

Why creating a Cache requires Double check whether the table exists or not.

// Cache returns the existing cache table with given name or creates a new one
// if the table does not exist yet.
func Cache(table string) *CacheTable {
	mutex.RLock()
	t, ok := cache[table]
	mutex.RUnlock()

	if !ok {
		mutex.Lock()
		t, ok = cache[table]
		// Double check whether the table exists or not.
		if !ok {
			t = &CacheTable{
				name:  table,
				items: make(map[interface{}]*CacheItem),
			}
			cache[table] = t
		}
		mutex.Unlock()
	}

	return t
}

AfterFunc with go

if smallestDuration > 0 {
		table.cleanupTimer = time.AfterFunc(smallestDuration, func() {
			go table.expirationCheck()
		})
}

AfterFunc creates a new goroute.Is it necessary to use 'go' in 'AfterFunc'

if registered dataload, Value() not thread safe

// Value returns an item from the cache and marks it to be kept alive. You can
// pass additional arguments to your DataLoader callback function.
func (table *CacheTable) Value(key interface{}, args ...interface{}) (*CacheItem, error) {
	table.RLock()
	r, ok := table.items[key]
	loadData := table.loadData
	table.RUnlock()

	if ok {
		// Update access counter and timestamp.
		r.KeepAlive()
		return r, nil
	}

	// Item doesn't exist in cache. Try and fetch it with a data-loader.
	if loadData != nil {
		item := loadData(key, args...)
		if item != nil {
			table.Add(key, item.lifeSpan, item.data)
			return item, nil
		}

		return nil, ErrKeyNotFoundOrLoadable
	}

	return nil, ErrKeyNotFound
}

table.addedItem does not need to be a array

if table.addedItem is set, we will remove it before we reset it. so table.addedItem will be always just one item, it didn't need to be a array.
table.aboutToDeleteItem is the same.

`func (table *CacheTable) SetAddedItemCallback(f func(*CacheItem)) {

// 如果已存在回调函数,则置空
if len(table.addedItem) > 0 {
	table.RemoveAddedItemCallbacks()
}
table.Lock()
defer table.Unlock()
table.addedItem = append(table.addedItem, f)

}`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.